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;

Reply via email to