Module Name: src
Committed By: hannken
Date: Sun Mar 31 14:56:41 UTC 2024
Modified Files:
src/sys/dev: ccd.c
Log Message:
Using a ccd(4) with GPT (dk* at ccd*) the disk framework will call
ccdstrategy() -> ccdstart() -> ccdbuffer() from softint context.
Allocating the buffer with PR_WAITOK here is forbidden.
Change ccdstart() / ccdbuffer() to report failure back to caller and
pass PR_WAITOK / PR_NOWAIT as an additional argument.
Call ccdstart() with PR_NOPWAIT from ccdstrategy() and on error defer
to the kthread. Call ccdstart() with PR_WAITOK from kthread so requests
from kthread always succeed to allocate the buffers.
Remove the (non working) throttling on low memory as it is no longer needed.
Fixes PR kern/58043 "kernel crash in assert_sleepable() in -current,
dk(4) driver?"
To generate a diff of this commit:
cvs rdiff -u -r1.189 -r1.190 src/sys/dev/ccd.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/dev/ccd.c
diff -u src/sys/dev/ccd.c:1.189 src/sys/dev/ccd.c:1.190
--- src/sys/dev/ccd.c:1.189 Mon Mar 28 12:48:35 2022
+++ src/sys/dev/ccd.c Sun Mar 31 14:56:41 2024
@@ -1,4 +1,4 @@
-/* $NetBSD: ccd.c,v 1.189 2022/03/28 12:48:35 riastradh Exp $ */
+/* $NetBSD: ccd.c,v 1.190 2024/03/31 14:56:41 hannken Exp $ */
/*-
* Copyright (c) 1996, 1997, 1998, 1999, 2007, 2009 The NetBSD Foundation, Inc.
@@ -88,7 +88,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ccd.c,v 1.189 2022/03/28 12:48:35 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ccd.c,v 1.190 2024/03/31 14:56:41 hannken Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -152,7 +152,7 @@ struct ccdbuf {
/* component buffer pool */
static pool_cache_t ccd_cache;
-#define CCD_GETBUF() pool_cache_get(ccd_cache, PR_WAITOK)
+#define CCD_GETBUF(wait) pool_cache_get(ccd_cache, wait)
#define CCD_PUTBUF(cbp) pool_cache_put(ccd_cache, cbp)
#define CCDLABELDEV(dev) \
@@ -168,11 +168,11 @@ static void ccdinterleave(struct ccd_sof
static int ccdinit(struct ccd_softc *, char **, struct vnode **,
struct lwp *);
static struct ccdbuf *ccdbuffer(struct ccd_softc *, struct buf *,
- daddr_t, void *, long);
+ daddr_t, void *, long, int);
static void ccdgetdefaultlabel(struct ccd_softc *, struct disklabel *);
static void ccdgetdisklabel(dev_t);
static void ccdmakedisklabel(struct ccd_softc *);
-static void ccdstart(struct ccd_softc *);
+static int ccdstart(struct ccd_softc *, struct buf *, int);
static void ccdthread(void *);
static dev_type_open(ccdopen);
@@ -702,19 +702,12 @@ ccdclose(dev_t dev, int flags, int fmt,
return (0);
}
-static bool
-ccdbackoff(struct ccd_softc *cs)
-{
-
- /* XXX Arbitrary, should be a uvm call. */
- return uvm_availmem(true) < (uvmexp.freemin >> 1) &&
- disk_isbusy(&cs->sc_dkdev);
-}
-
static void
ccdthread(void *cookie)
{
+ int error;
struct ccd_softc *cs;
+ struct buf *bp;
cs = cookie;
@@ -725,21 +718,18 @@ ccdthread(void *cookie)
mutex_enter(cs->sc_iolock);
while (__predict_true(!cs->sc_zap)) {
- if (bufq_peek(cs->sc_bufq) == NULL) {
+ bp = bufq_get(cs->sc_bufq);
+ if (bp == NULL) {
/* Nothing to do. */
cv_wait(&cs->sc_push, cs->sc_iolock);
continue;
}
- if (ccdbackoff(cs)) {
- /* Wait for memory to become available. */
- (void)cv_timedwait(&cs->sc_push, cs->sc_iolock, 1);
- continue;
- }
#ifdef DEBUG
if (ccddebug & CCDB_FOLLOW)
printf("ccdthread: dispatching I/O\n");
#endif
- ccdstart(cs);
+ error = ccdstart(cs, bp, PR_WAITOK);
+ KASSERT(error == 0);
mutex_enter(cs->sc_iolock);
}
cs->sc_thread = NULL;
@@ -777,21 +767,16 @@ ccdstrategy(struct buf *bp)
return;
}
- /* Defer to thread if system is low on memory. */
- bufq_put(cs->sc_bufq, bp);
- if (__predict_false(ccdbackoff(cs))) {
+ if (ccdstart(cs, bp, PR_NOWAIT) != 0) {
+ /* Defer to thread if system is low on memory. */
+ bufq_put(cs->sc_bufq, bp);
+ cv_broadcast(&cs->sc_push);
mutex_exit(cs->sc_iolock);
-#ifdef DEBUG
- if (ccddebug & CCDB_FOLLOW)
- printf("ccdstrategy: holding off on I/O\n");
-#endif
- return;
}
- ccdstart(cs);
}
-static void
-ccdstart(struct ccd_softc *cs)
+static int
+ccdstart(struct ccd_softc *cs, struct buf *bp, int wait)
{
daddr_t blkno;
int wlabel;
@@ -801,11 +786,9 @@ ccdstart(struct ccd_softc *cs)
char *addr;
daddr_t bn;
vnode_t *vp;
- buf_t *bp;
+ SIMPLEQ_HEAD(, ccdbuf) cbufq;
KASSERT(mutex_owned(cs->sc_iolock));
-
- bp = bufq_get(cs->sc_bufq);
KASSERT(bp != NULL);
disk_busy(&cs->sc_dkdev);
@@ -836,15 +819,32 @@ ccdstart(struct ccd_softc *cs)
mutex_exit(cs->sc_iolock);
bp->b_rawblkno = blkno;
- /* Allocate the component buffers and start I/O! */
+ /* Allocate the component buffers. */
+ SIMPLEQ_INIT(&cbufq);
bp->b_resid = bp->b_bcount;
bn = bp->b_rawblkno;
addr = bp->b_data;
for (bcount = bp->b_bcount; bcount > 0; bcount -= rcount) {
- cbp = ccdbuffer(cs, bp, bn, addr, bcount);
+ cbp = ccdbuffer(cs, bp, bn, addr, bcount, wait);
+ KASSERT(cbp != NULL || wait == PR_NOWAIT);
+ if (cbp == NULL) {
+ while ((cbp = SIMPLEQ_FIRST(&cbufq)) != NULL) {
+ SIMPLEQ_REMOVE_HEAD(&cbufq, cb_q);
+ CCD_PUTBUF(cbp);
+ }
+ mutex_enter(cs->sc_iolock);
+ disk_unbusy(&cs->sc_dkdev, 0, 0);
+ return ENOMEM;
+ }
+ SIMPLEQ_INSERT_TAIL(&cbufq, cbp, cb_q);
rcount = cbp->cb_buf.b_bcount;
bn += btodb(rcount);
addr += rcount;
+ }
+
+ /* All buffers set up, now fire off the requests. */
+ while ((cbp = SIMPLEQ_FIRST(&cbufq)) != NULL) {
+ SIMPLEQ_REMOVE_HEAD(&cbufq, cb_q);
vp = cbp->cb_buf.b_vp;
if ((cbp->cb_buf.b_flags & B_READ) == 0) {
mutex_enter(vp->v_interlock);
@@ -853,7 +853,7 @@ ccdstart(struct ccd_softc *cs)
}
(void)VOP_STRATEGY(vp, &cbp->cb_buf);
}
- return;
+ return 0;
done:
disk_unbusy(&cs->sc_dkdev, 0, 0);
@@ -862,6 +862,7 @@ ccdstart(struct ccd_softc *cs)
mutex_exit(cs->sc_iolock);
bp->b_resid = bp->b_bcount;
biodone(bp);
+ return 0;
}
/*
@@ -869,7 +870,7 @@ ccdstart(struct ccd_softc *cs)
*/
static struct ccdbuf *
ccdbuffer(struct ccd_softc *cs, struct buf *bp, daddr_t bn, void *addr,
- long bcount)
+ long bcount, int wait)
{
struct ccdcinfo *ci;
struct ccdbuf *cbp;
@@ -929,8 +930,9 @@ ccdbuffer(struct ccd_softc *cs, struct b
/*
* Fill in the component buf structure.
*/
- cbp = CCD_GETBUF();
- KASSERT(cbp != NULL);
+ cbp = CCD_GETBUF(wait);
+ if (cbp == NULL)
+ return NULL;
buf_init(&cbp->cb_buf);
cbp->cb_buf.b_flags = bp->b_flags;
cbp->cb_buf.b_oflags = bp->b_oflags;