Module Name:    src
Committed By:   martin
Date:           Mon Apr  6 14:57:42 UTC 2020

Modified Files:
        src/sys/dev [netbsd-9]: cgd.c cgdvar.h

Log Message:
Pull up following revision(s) (requested by riastradh in ticket #823):

        sys/dev/cgdvar.h: revision 1.19
        sys/dev/cgd.c: revision 1.122
        sys/dev/cgd.c: revision 1.123
        sys/dev/cgd.c: revision 1.124

Defer crypto operations to a workqueue and make it utilize all CPUs.

Make device mpsafe.

Some code cleanup.

Don't wait for data buffer.

cgd: switch from malloc(9) to kmem(9)
XXX might be worthwhile to use pool_cache(9) in the write path


To generate a diff of this commit:
cvs rdiff -u -r1.116.10.2 -r1.116.10.3 src/sys/dev/cgd.c
cvs rdiff -u -r1.18 -r1.18.24.1 src/sys/dev/cgdvar.h

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/cgd.c
diff -u src/sys/dev/cgd.c:1.116.10.2 src/sys/dev/cgd.c:1.116.10.3
--- src/sys/dev/cgd.c:1.116.10.2	Sat Mar 21 15:52:09 2020
+++ src/sys/dev/cgd.c	Mon Apr  6 14:57:42 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: cgd.c,v 1.116.10.2 2020/03/21 15:52:09 martin Exp $ */
+/* $NetBSD: cgd.c,v 1.116.10.3 2020/04/06 14:57:42 martin Exp $ */
 
 /*-
  * Copyright (c) 2002 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: cgd.c,v 1.116.10.2 2020/03/21 15:52:09 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: cgd.c,v 1.116.10.3 2020/04/06 14:57:42 martin Exp $");
 
 #include <sys/types.h>
 #include <sys/param.h>
@@ -39,7 +39,7 @@ __KERNEL_RCSID(0, "$NetBSD: cgd.c,v 1.11
 #include <sys/errno.h>
 #include <sys/buf.h>
 #include <sys/bufq.h>
-#include <sys/malloc.h>
+#include <sys/kmem.h>
 #include <sys/module.h>
 #include <sys/pool.h>
 #include <sys/ioctl.h>
@@ -51,6 +51,8 @@ __KERNEL_RCSID(0, "$NetBSD: cgd.c,v 1.11
 #include <sys/vnode.h>
 #include <sys/conf.h>
 #include <sys/syslog.h>
+#include <sys/workqueue.h>
+#include <sys/cpu.h>
 
 #include <dev/dkvar.h>
 #include <dev/cgdvar.h>
@@ -90,7 +92,7 @@ const struct bdevsw cgd_bdevsw = {
 	.d_dump = cgddump,
 	.d_psize = cgdsize,
 	.d_discard = nodiscard,
-	.d_flag = D_DISK
+	.d_flag = D_DISK | D_MPSAFE
 };
 
 const struct cdevsw cgd_cdevsw = {
@@ -105,7 +107,7 @@ const struct cdevsw cgd_cdevsw = {
 	.d_mmap = nommap,
 	.d_kqfilter = nokqfilter,
 	.d_discard = nodiscard,
-	.d_flag = D_DISK
+	.d_flag = D_DISK | D_MPSAFE
 };
 
 /*
@@ -207,12 +209,20 @@ static int cgd_match(device_t, cfdata_t,
 static void cgd_attach(device_t, device_t, void *);
 static int cgd_detach(device_t, int);
 static struct cgd_softc	*cgd_spawn(int);
+static struct cgd_worker *cgd_create_one_worker(void);
+static void cgd_destroy_one_worker(struct cgd_worker *);
+static struct cgd_worker *cgd_create_worker(void);
+static void cgd_destroy_worker(struct cgd_worker *);
 static int cgd_destroy(device_t);
 
 /* Internal Functions */
 
 static int	cgd_diskstart(device_t, struct buf *);
+static void	cgd_diskstart2(struct cgd_softc *, struct cgd_xfer *);
 static void	cgdiodone(struct buf *);
+static void	cgd_iodone2(struct cgd_softc *, struct cgd_xfer *);
+static void	cgd_enqueue(struct cgd_softc *, struct cgd_xfer *);
+static void	cgd_process(struct work *, void *);
 static int	cgd_dumpblocks(device_t, void *, daddr_t, int);
 
 static int	cgd_ioctl_set(struct cgd_softc *, void *, struct lwp *);
@@ -272,25 +282,49 @@ static void	hexprint(const char *, void 
 
 /* Global variables */
 
+static kmutex_t cgd_spawning_mtx;
+static kcondvar_t cgd_spawning_cv;
+static bool cgd_spawning;
+static struct cgd_worker *cgd_worker;
+static u_int cgd_refcnt;	/* number of users of cgd_worker */
+
 /* Utility Functions */
 
 #define CGDUNIT(x)		DISKUNIT(x)
-#define GETCGD_SOFTC(_cs, x)	if (!((_cs) = getcgd_softc(x))) return ENXIO
 
 /* The code */
 
-static struct cgd_softc *
-getcgd_softc(dev_t dev)
+static int
+cgd_lock(bool intr)
 {
-	int	unit = CGDUNIT(dev);
-	struct cgd_softc *sc;
+	int error = 0;
 
-	DPRINTF_FOLLOW(("getcgd_softc(0x%"PRIx64"): unit = %d\n", dev, unit));
+	mutex_enter(&cgd_spawning_mtx);
+	while (cgd_spawning) {
+		if (intr)
+			error = cv_wait_sig(&cgd_spawning_cv, &cgd_spawning_mtx);
+		else
+			cv_wait(&cgd_spawning_cv, &cgd_spawning_mtx);
+	}
+	if (error == 0)
+		cgd_spawning = true;
+	mutex_exit(&cgd_spawning_mtx);
+	return error;
+}
 
-	sc = device_lookup_private(&cgd_cd, unit);
-	if (sc == NULL)
-		sc = cgd_spawn(unit);
-	return sc;
+static void
+cgd_unlock(void)
+{
+	mutex_enter(&cgd_spawning_mtx);
+	cgd_spawning = false;
+	cv_broadcast(&cgd_spawning_cv);
+	mutex_exit(&cgd_spawning_mtx);
+}
+
+static struct cgd_softc *
+getcgd_softc(dev_t dev)
+{
+	return device_lookup_private(&cgd_cd, CGDUNIT(dev));
 }
 
 static int
@@ -306,6 +340,7 @@ cgd_attach(device_t parent, device_t sel
 	struct cgd_softc *sc = device_private(self);
 
 	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_BIO);
+	cv_init(&sc->sc_cv, "cgdcv");
 	dk_init(&sc->sc_dksc, self, DKTYPE_CGD);
 	disk_init(&sc->sc_dksc.sc_dkdev, sc->sc_dksc.sc_xname, &cgddkdriver);
 
@@ -331,6 +366,7 @@ cgd_detach(device_t self, int flags)
 		return ret;
 
 	disk_destroy(&dksc->sc_dkdev);
+	cv_destroy(&sc->sc_cv);
 	mutex_destroy(&sc->sc_lock);
 
 	return 0;
@@ -339,89 +375,224 @@ cgd_detach(device_t self, int flags)
 void
 cgdattach(int num)
 {
+#ifndef _MODULE
 	int error;
 
+	mutex_init(&cgd_spawning_mtx, MUTEX_DEFAULT, IPL_NONE);
+	cv_init(&cgd_spawning_cv, "cgspwn");
+
 	error = config_cfattach_attach(cgd_cd.cd_name, &cgd_ca);
 	if (error != 0)
 		aprint_error("%s: unable to register cfattach\n",
 		    cgd_cd.cd_name);
+#endif
 }
 
 static struct cgd_softc *
 cgd_spawn(int unit)
 {
 	cfdata_t cf;
+	struct cgd_worker *cw;
+	struct cgd_softc *sc;
 
-	cf = malloc(sizeof(*cf), M_DEVBUF, M_WAITOK);
+	cf = kmem_alloc(sizeof(*cf), KM_SLEEP);
 	cf->cf_name = cgd_cd.cd_name;
 	cf->cf_atname = cgd_cd.cd_name;
 	cf->cf_unit = unit;
 	cf->cf_fstate = FSTATE_STAR;
 
-	return device_private(config_attach_pseudo(cf));
+	cw = cgd_create_one_worker();
+	if (cw == NULL) {
+		kmem_free(cf, sizeof(*cf));
+		return NULL;
+	}
+
+	sc = device_private(config_attach_pseudo(cf));
+	if (sc == NULL) {
+		cgd_destroy_one_worker(cw);
+		return NULL;
+	}
+
+	sc->sc_worker = cw;
+
+	return sc;
 }
 
 static int
 cgd_destroy(device_t dev)
 {
-	int error;
+	struct cgd_softc *sc = device_private(dev);
+	struct cgd_worker *cw = sc->sc_worker;
 	cfdata_t cf;
+	int error;
 
 	cf = device_cfdata(dev);
 	error = config_detach(dev, DETACH_QUIET);
 	if (error)
 		return error;
-	free(cf, M_DEVBUF);
+
+	cgd_destroy_one_worker(cw);
+
+	kmem_free(cf, sizeof(*cf));
 	return 0;
 }
 
+static void
+cgd_busy(struct cgd_softc *sc)
+{
+
+	mutex_enter(&sc->sc_lock);
+	while (sc->sc_busy)
+		cv_wait(&sc->sc_cv, &sc->sc_lock);
+	sc->sc_busy = true;
+	mutex_exit(&sc->sc_lock);
+}
+
+static void
+cgd_unbusy(struct cgd_softc *sc)
+{
+
+	mutex_enter(&sc->sc_lock);
+	sc->sc_busy = false;
+	cv_broadcast(&sc->sc_cv);
+	mutex_exit(&sc->sc_lock);
+}
+
+static struct cgd_worker *
+cgd_create_one_worker(void)
+{
+	KASSERT(cgd_spawning);
+
+	if (cgd_refcnt++ == 0) {
+		KASSERT(cgd_worker == NULL);
+		cgd_worker = cgd_create_worker();
+	}
+
+	KASSERT(cgd_worker != NULL);
+	return cgd_worker;
+}
+
+static void
+cgd_destroy_one_worker(struct cgd_worker *cw)
+{
+	KASSERT(cgd_spawning);
+	KASSERT(cw == cgd_worker);
+
+	if (--cgd_refcnt == 0) {
+		cgd_destroy_worker(cgd_worker);
+		cgd_worker = NULL;
+	}
+}
+
+static struct cgd_worker *
+cgd_create_worker(void)
+{
+	struct cgd_worker *cw;
+	struct workqueue *wq;
+	struct pool *cp;
+	int error;
+
+	cw = kmem_alloc(sizeof(struct cgd_worker), KM_SLEEP);
+	cp = kmem_alloc(sizeof(struct pool), KM_SLEEP);
+
+	error = workqueue_create(&wq, "cgd", cgd_process, NULL,
+	                         PRI_BIO, IPL_BIO, WQ_MPSAFE | WQ_PERCPU);
+	if (error) {
+		kmem_free(cp, sizeof(struct pool));
+		kmem_free(cw, sizeof(struct cgd_worker));
+		return NULL;
+	}
+
+	cw->cw_cpool = cp;
+	cw->cw_wq = wq;
+	pool_init(cw->cw_cpool, sizeof(struct cgd_xfer), 0,
+	    0, 0, "cgdcpl", NULL, IPL_BIO);
+
+	mutex_init(&cw->cw_lock, MUTEX_DEFAULT, IPL_BIO);
+	
+	return cw;
+}
+
+static void
+cgd_destroy_worker(struct cgd_worker *cw)
+{
+	mutex_destroy(&cw->cw_lock);
+
+	if (cw->cw_cpool) {
+		pool_destroy(cw->cw_cpool);
+		kmem_free(cw->cw_cpool, sizeof(struct pool));
+	}
+	if (cw->cw_wq)
+		workqueue_destroy(cw->cw_wq);
+
+	kmem_free(cw, sizeof(struct cgd_worker));
+}
+
 static int
 cgdopen(dev_t dev, int flags, int fmt, struct lwp *l)
 {
-	struct	cgd_softc *cs;
+	struct	cgd_softc *sc;
+	int error;
 
 	DPRINTF_FOLLOW(("cgdopen(0x%"PRIx64", %d)\n", dev, flags));
-	GETCGD_SOFTC(cs, dev);
-	return dk_open(&cs->sc_dksc, dev, flags, fmt, l);
+
+	error = cgd_lock(true);
+	if (error)
+		return error;
+	sc = getcgd_softc(dev);
+	if (sc == NULL)
+		sc = cgd_spawn(CGDUNIT(dev));
+	cgd_unlock();
+	if (sc == NULL)
+		return ENXIO;
+
+	return dk_open(&sc->sc_dksc, dev, flags, fmt, l);
 }
 
 static int
 cgdclose(dev_t dev, int flags, int fmt, struct lwp *l)
 {
-	int error;
-	struct	cgd_softc *cs;
+	struct	cgd_softc *sc;
 	struct	dk_softc *dksc;
+	int error;
 
 	DPRINTF_FOLLOW(("cgdclose(0x%"PRIx64", %d)\n", dev, flags));
-	GETCGD_SOFTC(cs, dev);
-	dksc = &cs->sc_dksc;
-	if ((error =  dk_close(dksc, dev, flags, fmt, l)) != 0)
+
+	error = cgd_lock(false);
+	if (error)
 		return error;
+	sc = getcgd_softc(dev);
+	if (sc == NULL) {
+		error = ENXIO;
+		goto done;
+	}
+
+	dksc = &sc->sc_dksc;
+	if ((error =  dk_close(dksc, dev, flags, fmt, l)) != 0)
+		goto done;
 
 	if (!DK_ATTACHED(dksc)) {
-		if ((error = cgd_destroy(cs->sc_dksc.sc_dev)) != 0) {
-			aprint_error_dev(dksc->sc_dev,
+		if ((error = cgd_destroy(sc->sc_dksc.sc_dev)) != 0) {
+			device_printf(dksc->sc_dev,
 			    "unable to detach instance\n");
-			return error;
+			goto done;
 		}
 	}
-	return 0;
+
+done:
+	cgd_unlock();
+
+	return error;
 }
 
 static void
 cgdstrategy(struct buf *bp)
 {
-	struct	cgd_softc *cs;
+	struct	cgd_softc *sc = getcgd_softc(bp->b_dev);
 
 	DPRINTF_FOLLOW(("cgdstrategy(%p): b_bcount = %ld\n", bp,
 	    (long)bp->b_bcount));
 
-	cs = getcgd_softc(bp->b_dev);
-	if (!cs) {
-		bp->b_error = ENXIO;
-		goto bail;
-	}
-
 	/*
 	 * Reject unaligned writes.
 	 */
@@ -430,7 +601,7 @@ cgdstrategy(struct buf *bp)
 		goto bail;
 	}
 
-	dk_strategy(&cs->sc_dksc, bp);
+	dk_strategy(&sc->sc_dksc, bp);
 	return;
 
 bail:
@@ -442,66 +613,62 @@ bail:
 static int
 cgdsize(dev_t dev)
 {
-	struct cgd_softc *cs = getcgd_softc(dev);
+	struct cgd_softc *sc = getcgd_softc(dev);
 
 	DPRINTF_FOLLOW(("cgdsize(0x%"PRIx64")\n", dev));
-	if (!cs)
+	if (!sc)
 		return -1;
-	return dk_size(&cs->sc_dksc, dev);
+	return dk_size(&sc->sc_dksc, dev);
 }
 
 /*
  * cgd_{get,put}data are functions that deal with getting a buffer
- * for the new encrypted data.  We have a buffer per device so that
- * we can ensure that we can always have a transaction in flight.
- * We use this buffer first so that we have one less piece of
- * malloc'ed data at any given point.
+ * for the new encrypted data.
+ * We can no longer have a buffer per device, we need a buffer per
+ * work queue...
  */
 
 static void *
-cgd_getdata(struct dk_softc *dksc, unsigned long size)
+cgd_getdata(struct cgd_softc *sc, unsigned long size)
 {
-	struct	cgd_softc *cs = (struct cgd_softc *)dksc;
-	void *	data = NULL;
+	void *data = NULL;
 
-	mutex_enter(&cs->sc_lock);
-	if (cs->sc_data_used == 0) {
-		cs->sc_data_used = 1;
-		data = cs->sc_data;
+	mutex_enter(&sc->sc_lock);
+	if (!sc->sc_data_used) {
+		sc->sc_data_used = true;
+		data = sc->sc_data;
 	}
-	mutex_exit(&cs->sc_lock);
+	mutex_exit(&sc->sc_lock);
 
 	if (data)
 		return data;
 
-	return malloc(size, M_DEVBUF, M_NOWAIT);
+	return kmem_intr_alloc(size, KM_NOSLEEP);
 }
 
 static void
-cgd_putdata(struct dk_softc *dksc, void *data)
+cgd_putdata(struct cgd_softc *sc, void *data, unsigned long size)
 {
-	struct	cgd_softc *cs = (struct cgd_softc *)dksc;
 
-	if (data == cs->sc_data) {
-		mutex_enter(&cs->sc_lock);
-		cs->sc_data_used = 0;
-		mutex_exit(&cs->sc_lock);
-	} else {
-		free(data, M_DEVBUF);
-	}
+	if (data == sc->sc_data) {
+		mutex_enter(&sc->sc_lock);
+		sc->sc_data_used = false;
+		mutex_exit(&sc->sc_lock);
+	} else
+		kmem_intr_free(data, size);
 }
 
 static int
 cgd_diskstart(device_t dev, struct buf *bp)
 {
-	struct	cgd_softc *cs = device_private(dev);
-	struct	dk_softc *dksc = &cs->sc_dksc;
+	struct	cgd_softc *sc = device_private(dev);
+	struct	cgd_worker *cw = sc->sc_worker;
+	struct	dk_softc *dksc = &sc->sc_dksc;
 	struct	disk_geom *dg = &dksc->sc_dkdev.dk_geom;
+	struct	cgd_xfer *cx;
 	struct	buf *nbp;
-	void *	addr;
 	void *	newaddr;
 	daddr_t	bn;
-	struct	vnode *vp;
 
 	DPRINTF_FOLLOW(("cgd_diskstart(%p, %p)\n", dksc, bp));
 
@@ -511,34 +678,66 @@ cgd_diskstart(device_t dev, struct buf *
 	 * We attempt to allocate all of our resources up front, so that
 	 * we can fail quickly if they are unavailable.
 	 */
-	nbp = getiobuf(cs->sc_tvn, false);
+	nbp = getiobuf(sc->sc_tvn, false);
 	if (nbp == NULL)
 		return EAGAIN;
 
+	cx = pool_get(cw->cw_cpool, PR_NOWAIT);
+	if (cx == NULL) {
+		putiobuf(nbp);
+		return EAGAIN;
+	}
+
+	cx->cx_sc = sc;
+	cx->cx_obp = bp;
+	cx->cx_nbp = nbp;
+	cx->cx_srcv = cx->cx_dstv = bp->b_data;
+	cx->cx_blkno = bn;
+	cx->cx_secsize = dg->dg_secsize;
+
 	/*
 	 * If we are writing, then we need to encrypt the outgoing
 	 * block into a new block of memory.
 	 */
-	newaddr = addr = bp->b_data;
 	if ((bp->b_flags & B_READ) == 0) {
-		newaddr = cgd_getdata(dksc, bp->b_bcount);
+		newaddr = cgd_getdata(sc, bp->b_bcount);
 		if (!newaddr) {
+			pool_put(cw->cw_cpool, cx);
 			putiobuf(nbp);
 			return EAGAIN;
 		}
-		cgd_cipher(cs, newaddr, addr, bp->b_bcount, bn,
-		    dg->dg_secsize, CGD_CIPHER_ENCRYPT);
+
+		cx->cx_dstv = newaddr;
+		cx->cx_len = bp->b_bcount;
+		cx->cx_dir = CGD_CIPHER_ENCRYPT;
+
+		cgd_enqueue(sc, cx);
+		return 0;
 	}
 
-	nbp->b_data = newaddr;
+	cgd_diskstart2(sc, cx);
+	return 0;
+}
+
+static void
+cgd_diskstart2(struct cgd_softc *sc, struct cgd_xfer *cx)
+{
+	struct	vnode *vp;
+	struct	buf *bp;
+	struct	buf *nbp;
+
+	bp = cx->cx_obp;
+	nbp = cx->cx_nbp;
+
+	nbp->b_data = cx->cx_dstv;
 	nbp->b_flags = bp->b_flags;
 	nbp->b_oflags = bp->b_oflags;
 	nbp->b_cflags = bp->b_cflags;
 	nbp->b_iodone = cgdiodone;
 	nbp->b_proc = bp->b_proc;
-	nbp->b_blkno = btodb(bn * dg->dg_secsize);
+	nbp->b_blkno = btodb(cx->cx_blkno * cx->cx_secsize);
 	nbp->b_bcount = bp->b_bcount;
-	nbp->b_private = bp;
+	nbp->b_private = cx;
 
 	BIO_COPYPRIO(nbp, bp);
 
@@ -548,21 +747,20 @@ cgd_diskstart(device_t dev, struct buf *
 		vp->v_numoutput++;
 		mutex_exit(vp->v_interlock);
 	}
-	VOP_STRATEGY(cs->sc_tvn, nbp);
-
-	return 0;
+	VOP_STRATEGY(sc->sc_tvn, nbp);
 }
 
 static void
 cgdiodone(struct buf *nbp)
 {
-	struct	buf *obp = nbp->b_private;
-	struct	cgd_softc *cs = getcgd_softc(obp->b_dev);
-	struct	dk_softc *dksc = &cs->sc_dksc;
+	struct	cgd_xfer *cx = nbp->b_private;
+	struct	buf *obp = cx->cx_obp;
+	struct	cgd_softc *sc = getcgd_softc(obp->b_dev);
+	struct	dk_softc *dksc = &sc->sc_dksc;
 	struct	disk_geom *dg = &dksc->sc_dkdev.dk_geom;
 	daddr_t	bn;
 
-	KDASSERT(cs);
+	KDASSERT(sc);
 
 	DPRINTF_FOLLOW(("cgdiodone(%p)\n", nbp));
 	DPRINTF(CGDB_IO, ("cgdiodone: bp %p bcount %d resid %d\n",
@@ -584,13 +782,36 @@ cgdiodone(struct buf *nbp)
 
 	if (nbp->b_flags & B_READ) {
 		bn = dbtob(nbp->b_blkno) / dg->dg_secsize;
-		cgd_cipher(cs, obp->b_data, obp->b_data, obp->b_bcount,
-		    bn, dg->dg_secsize, CGD_CIPHER_DECRYPT);
+
+		cx->cx_obp     = obp;
+		cx->cx_nbp     = nbp;
+		cx->cx_dstv    = obp->b_data;
+		cx->cx_srcv    = obp->b_data;
+		cx->cx_len     = obp->b_bcount;
+		cx->cx_blkno   = bn;
+		cx->cx_secsize = dg->dg_secsize;
+		cx->cx_dir     = CGD_CIPHER_DECRYPT;
+
+		cgd_enqueue(sc, cx);
+		return;
 	}
 
+	cgd_iodone2(sc, cx);
+}
+
+static void
+cgd_iodone2(struct cgd_softc *sc, struct cgd_xfer *cx)
+{
+	struct cgd_worker *cw = sc->sc_worker;
+	struct buf *obp = cx->cx_obp;
+	struct buf *nbp = cx->cx_nbp;
+	struct dk_softc *dksc = &sc->sc_dksc;
+
+	pool_put(cw->cw_cpool, cx);
+
 	/* If we allocated memory, free it now... */
 	if (nbp->b_data != obp->b_data)
-		cgd_putdata(dksc, nbp->b_data);
+		cgd_putdata(sc, nbp->b_data, nbp->b_bcount);
 
 	putiobuf(nbp);
 
@@ -631,7 +852,7 @@ cgd_dumpblocks(device_t dev, void *va, d
 	nbytes = nblk*blksize;
 
 	/* Try to acquire a buffer to store the ciphertext.  */
-	buf = cgd_getdata(dksc, nbytes);
+	buf = cgd_getdata(sc, nbytes);
 	if (buf == NULL)
 		/* Out of memory: give up.  */
 		return ENOMEM;
@@ -643,7 +864,7 @@ cgd_dumpblocks(device_t dev, void *va, d
 	error = bdev_dump(sc->sc_tdev, blkno, buf, nbytes);
 
 	/* Release the buffer.  */
-	cgd_putdata(dksc, buf);
+	cgd_putdata(sc, buf, nbytes);
 
 	/* Return any error from the underlying disk device.  */
 	return error;
@@ -653,13 +874,15 @@ cgd_dumpblocks(device_t dev, void *va, d
 static int
 cgdread(dev_t dev, struct uio *uio, int flags)
 {
-	struct	cgd_softc *cs;
+	struct	cgd_softc *sc;
 	struct	dk_softc *dksc;
 
 	DPRINTF_FOLLOW(("cgdread(0x%llx, %p, %d)\n",
 	    (unsigned long long)dev, uio, flags));
-	GETCGD_SOFTC(cs, dev);
-	dksc = &cs->sc_dksc;
+	sc = getcgd_softc(dev);
+	if (sc == NULL)
+		return ENXIO;
+	dksc = &sc->sc_dksc;
 	if (!DK_ATTACHED(dksc))
 		return ENXIO;
 	return physio(cgdstrategy, NULL, dev, B_READ, minphys, uio);
@@ -669,12 +892,14 @@ cgdread(dev_t dev, struct uio *uio, int 
 static int
 cgdwrite(dev_t dev, struct uio *uio, int flags)
 {
-	struct	cgd_softc *cs;
+	struct	cgd_softc *sc;
 	struct	dk_softc *dksc;
 
 	DPRINTF_FOLLOW(("cgdwrite(0x%"PRIx64", %p, %d)\n", dev, uio, flags));
-	GETCGD_SOFTC(cs, dev);
-	dksc = &cs->sc_dksc;
+	sc = getcgd_softc(dev);
+	if (sc == NULL)
+		return ENXIO;
+	dksc = &sc->sc_dksc;
 	if (!DK_ATTACHED(dksc))
 		return ENXIO;
 	return physio(cgdstrategy, NULL, dev, B_WRITE, minphys, uio);
@@ -683,10 +908,11 @@ cgdwrite(dev_t dev, struct uio *uio, int
 static int
 cgdioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
 {
-	struct	cgd_softc *cs;
+	struct	cgd_softc *sc;
 	struct	dk_softc *dksc;
 	int	part = DISKPART(dev);
 	int	pmask = 1 << part;
+	int	error;
 
 	DPRINTF_FOLLOW(("cgdioctl(0x%"PRIx64", %ld, %p, %d, %p)\n",
 	    dev, cmd, data, flag, l));
@@ -700,39 +926,60 @@ cgdioctl(dev_t dev, u_long cmd, void *da
 			return EBADF;
 		/* FALLTHROUGH */
 	default:
-		GETCGD_SOFTC(cs, dev);
-		dksc = &cs->sc_dksc;
+		sc = getcgd_softc(dev);
+		if (sc == NULL)
+			return ENXIO;
+		dksc = &sc->sc_dksc;
 		break;
 	}
 
 	switch (cmd) {
 	case CGDIOCSET:
+		cgd_busy(sc);
 		if (DK_ATTACHED(dksc))
-			return EBUSY;
-		return cgd_ioctl_set(cs, data, l);
+			error = EBUSY;
+		else
+			error = cgd_ioctl_set(sc, data, l);
+		cgd_unbusy(sc);
+		break;
 	case CGDIOCCLR:
-		if (DK_BUSY(&cs->sc_dksc, pmask))
-			return EBUSY;
-		return cgd_ioctl_clr(cs, l);
+		cgd_busy(sc);
+		if (DK_BUSY(&sc->sc_dksc, pmask))
+			error = EBUSY;
+		else
+			error = cgd_ioctl_clr(sc, l);
+		cgd_unbusy(sc);
+		break;
 	case DIOCGCACHE:
 	case DIOCCACHESYNC:
-		if (!DK_ATTACHED(dksc))
-			return ENOENT;
+		cgd_busy(sc);
+		if (!DK_ATTACHED(dksc)) {
+			cgd_unbusy(sc);
+			error = ENOENT;
+			break;
+		}
 		/*
 		 * We pass this call down to the underlying disk.
 		 */
-		return VOP_IOCTL(cs->sc_tvn, cmd, data, flag, l->l_cred);
+		error = VOP_IOCTL(sc->sc_tvn, cmd, data, flag, l->l_cred);
+		cgd_unbusy(sc);
+		break;
 	case DIOCGSECTORALIGN: {
 		struct disk_sectoralign *dsa = data;
-		int error;
 
-		if (!DK_ATTACHED(dksc))
-			return ENOENT;
+		cgd_busy(sc);
+		if (!DK_ATTACHED(dksc)) {
+			cgd_unbusy(sc);
+			error = ENOENT;
+			break;
+		}
 
 		/* Get the underlying disk's sector alignment.  */
-		error = VOP_IOCTL(cs->sc_tvn, cmd, data, flag, l->l_cred);
-		if (error)
-			return error;
+		error = VOP_IOCTL(sc->sc_tvn, cmd, data, flag, l->l_cred);
+		if (error) {
+			cgd_unbusy(sc);
+			break;
+		}
 
 		/* Adjust for the disklabel partition if necessary.  */
 		if (part != RAW_PART) {
@@ -747,30 +994,38 @@ cgdioctl(dev_t dev, u_long cmd, void *da
 				dsa->dsa_firstaligned = (dsa->dsa_firstaligned
 				    + dsa->dsa_alignment) - r;
 		}
-		return 0;
+		cgd_unbusy(sc);
+		break;
 	}
 	case DIOCGSTRATEGY:
 	case DIOCSSTRATEGY:
-		if (!DK_ATTACHED(dksc))
-			return ENOENT;
+		if (!DK_ATTACHED(dksc)) {
+			error = ENOENT;
+			break;
+		}
 		/*FALLTHROUGH*/
 	default:
-		return dk_ioctl(dksc, dev, cmd, data, flag, l);
+		error = dk_ioctl(dksc, dev, cmd, data, flag, l);
+		break;
 	case CGDIOCGET:
 		KASSERT(0);
-		return EINVAL;
+		error = EINVAL;
 	}
+
+	return error;
 }
 
 static int
 cgddump(dev_t dev, daddr_t blkno, void *va, size_t size)
 {
-	struct	cgd_softc *cs;
+	struct	cgd_softc *sc;
 
 	DPRINTF_FOLLOW(("cgddump(0x%"PRIx64", %" PRId64 ", %p, %lu)\n",
 	    dev, blkno, va, (unsigned long)size));
-	GETCGD_SOFTC(cs, dev);
-	return dk_dump(&cs->sc_dksc, dev, blkno, va, size, DK_DUMP_RECURSIVE);
+	sc = getcgd_softc(dev);
+	if (sc == NULL)
+		return ENXIO;
+	return dk_dump(&sc->sc_dksc, dev, blkno, va, size, DK_DUMP_RECURSIVE);
 }
 
 /*
@@ -791,7 +1046,7 @@ static const struct {
 
 /* ARGSUSED */
 static int
-cgd_ioctl_set(struct cgd_softc *cs, void *data, struct lwp *l)
+cgd_ioctl_set(struct cgd_softc *sc, void *data, struct lwp *l)
 {
 	struct	 cgd_ioctl *ci = data;
 	struct	 vnode *vp;
@@ -801,7 +1056,7 @@ cgd_ioctl_set(struct cgd_softc *cs, void
 	const char *cp;
 	struct pathbuf *pb;
 	char	 *inbuf;
-	struct dk_softc *dksc = &cs->sc_dksc;
+	struct dk_softc *dksc = &sc->sc_dksc;
 
 	cp = ci->ci_disk;
 
@@ -815,17 +1070,17 @@ cgd_ioctl_set(struct cgd_softc *cs, void
 		return ret;
 	}
 
-	inbuf = malloc(MAX_KEYSIZE, M_TEMP, M_WAITOK);
+	inbuf = kmem_alloc(MAX_KEYSIZE, KM_SLEEP);
 
-	if ((ret = cgdinit(cs, cp, vp, l)) != 0)
+	if ((ret = cgdinit(sc, cp, vp, l)) != 0)
 		goto bail;
 
 	(void)memset(inbuf, 0, MAX_KEYSIZE);
 	ret = copyinstr(ci->ci_alg, inbuf, 256, NULL);
 	if (ret)
 		goto bail;
-	cs->sc_cfuncs = cryptfuncs_find(inbuf);
-	if (!cs->sc_cfuncs) {
+	sc->sc_cfuncs = cryptfuncs_find(inbuf);
+	if (!sc->sc_cfuncs) {
 		ret = EINVAL;
 		goto bail;
 	}
@@ -855,15 +1110,15 @@ cgd_ioctl_set(struct cgd_softc *cs, void
 	if (ret)
 		goto bail;
 
-	cs->sc_cdata.cf_blocksize = ci->ci_blocksize;
-	cs->sc_cdata.cf_mode = encblkno[i].v;
-	cs->sc_cdata.cf_keylen = ci->ci_keylen;
-	cs->sc_cdata.cf_priv = cs->sc_cfuncs->cf_init(ci->ci_keylen, inbuf,
-	    &cs->sc_cdata.cf_blocksize);
-	if (cs->sc_cdata.cf_blocksize > CGD_MAXBLOCKSIZE) {
+	sc->sc_cdata.cf_blocksize = ci->ci_blocksize;
+	sc->sc_cdata.cf_mode = encblkno[i].v;
+	sc->sc_cdata.cf_keylen = ci->ci_keylen;
+	sc->sc_cdata.cf_priv = sc->sc_cfuncs->cf_init(ci->ci_keylen, inbuf,
+	    &sc->sc_cdata.cf_blocksize);
+	if (sc->sc_cdata.cf_blocksize > CGD_MAXBLOCKSIZE) {
 	    log(LOG_WARNING, "cgd: Disallowed cipher with blocksize %zu > %u\n",
-		cs->sc_cdata.cf_blocksize, CGD_MAXBLOCKSIZE);
-	    cs->sc_cdata.cf_priv = NULL;
+		sc->sc_cdata.cf_blocksize, CGD_MAXBLOCKSIZE);
+	    sc->sc_cdata.cf_priv = NULL;
 	}
 
 	/*
@@ -871,18 +1126,18 @@ cgd_ioctl_set(struct cgd_softc *cs, void
 	 * it was expressed in bits. For compatibility we maintain encblkno
 	 * and encblkno8.
 	 */
-	cs->sc_cdata.cf_blocksize /= encblkno[i].d;
+	sc->sc_cdata.cf_blocksize /= encblkno[i].d;
 	(void)explicit_memset(inbuf, 0, MAX_KEYSIZE);
-	if (!cs->sc_cdata.cf_priv) {
+	if (!sc->sc_cdata.cf_priv) {
 		ret = EINVAL;		/* XXX is this the right error? */
 		goto bail;
 	}
-	free(inbuf, M_TEMP);
+	kmem_free(inbuf, MAX_KEYSIZE);
 
 	bufq_alloc(&dksc->sc_bufq, "fcfs", 0);
 
-	cs->sc_data = malloc(MAXPHYS, M_DEVBUF, M_WAITOK);
-	cs->sc_data_used = 0;
+	sc->sc_data = kmem_alloc(MAXPHYS, KM_SLEEP);
+	sc->sc_data_used = false;
 
 	/* Attach the disk. */
 	dk_attach(dksc);
@@ -896,16 +1151,16 @@ cgd_ioctl_set(struct cgd_softc *cs, void
 	return 0;
 
 bail:
-	free(inbuf, M_TEMP);
+	kmem_free(inbuf, MAX_KEYSIZE);
 	(void)vn_close(vp, FREAD|FWRITE, l->l_cred);
 	return ret;
 }
 
 /* ARGSUSED */
 static int
-cgd_ioctl_clr(struct cgd_softc *cs, struct lwp *l)
+cgd_ioctl_clr(struct cgd_softc *sc, struct lwp *l)
 {
-	struct	dk_softc *dksc = &cs->sc_dksc;
+	struct	dk_softc *dksc = &sc->sc_dksc;
 
 	if (!DK_ATTACHED(dksc))
 		return ENXIO;
@@ -917,11 +1172,11 @@ cgd_ioctl_clr(struct cgd_softc *cs, stru
 	dk_drain(dksc);
 	bufq_free(dksc->sc_bufq);
 
-	(void)vn_close(cs->sc_tvn, FREAD|FWRITE, l->l_cred);
-	cs->sc_cfuncs->cf_destroy(cs->sc_cdata.cf_priv);
-	free(cs->sc_tpath, M_DEVBUF);
-	free(cs->sc_data, M_DEVBUF);
-	cs->sc_data_used = 0;
+	(void)vn_close(sc->sc_tvn, FREAD|FWRITE, l->l_cred);
+	sc->sc_cfuncs->cf_destroy(sc->sc_cdata.cf_priv);
+	kmem_free(sc->sc_tpath, sc->sc_tpathlen);
+	kmem_free(sc->sc_data, MAXPHYS);
+	sc->sc_data_used = false;
 	dk_detach(dksc);
 	disk_detach(&dksc->sc_dkdev);
 
@@ -931,10 +1186,9 @@ cgd_ioctl_clr(struct cgd_softc *cs, stru
 static int
 cgd_ioctl_get(dev_t dev, void *data, struct lwp *l)
 {
-	struct cgd_softc *cs = getcgd_softc(dev);
+	struct cgd_softc *sc;
 	struct cgd_user *cgu;
-	int unit;
-	struct	dk_softc *dksc = &cs->sc_dksc;
+	int unit, error;
 
 	unit = CGDUNIT(dev);
 	cgu = (struct cgd_user *)data;
@@ -942,14 +1196,21 @@ cgd_ioctl_get(dev_t dev, void *data, str
 	DPRINTF_FOLLOW(("cgd_ioctl_get(0x%"PRIx64", %d, %p, %p)\n",
 			   dev, unit, data, l));
 
+	/* XXX, we always return this units data, so if cgu_unit is
+	 * not -1, that field doesn't match the rest
+	 */
 	if (cgu->cgu_unit == -1)
 		cgu->cgu_unit = unit;
 
 	if (cgu->cgu_unit < 0)
 		return EINVAL;	/* XXX: should this be ENXIO? */
 
-	cs = device_lookup_private(&cgd_cd, unit);
-	if (cs == NULL || !DK_ATTACHED(dksc)) {
+	error = cgd_lock(false);
+	if (error)
+		return error;
+
+	sc = device_lookup_private(&cgd_cd, unit);
+	if (sc == NULL || !DK_ATTACHED(&sc->sc_dksc)) {
 		cgu->cgu_dev = 0;
 		cgu->cgu_alg[0] = '\0';
 		cgu->cgu_blocksize = 0;
@@ -957,18 +1218,22 @@ cgd_ioctl_get(dev_t dev, void *data, str
 		cgu->cgu_keylen = 0;
 	}
 	else {
-		cgu->cgu_dev = cs->sc_tdev;
-		strlcpy(cgu->cgu_alg, cs->sc_cfuncs->cf_name,
+		mutex_enter(&sc->sc_lock);
+		cgu->cgu_dev = sc->sc_tdev;
+		strncpy(cgu->cgu_alg, sc->sc_cfuncs->cf_name,
 		    sizeof(cgu->cgu_alg));
-		cgu->cgu_blocksize = cs->sc_cdata.cf_blocksize;
-		cgu->cgu_mode = cs->sc_cdata.cf_mode;
-		cgu->cgu_keylen = cs->sc_cdata.cf_keylen;
+		cgu->cgu_blocksize = sc->sc_cdata.cf_blocksize;
+		cgu->cgu_mode = sc->sc_cdata.cf_mode;
+		cgu->cgu_keylen = sc->sc_cdata.cf_keylen;
+		mutex_exit(&sc->sc_lock);
 	}
+
+	cgd_unlock();
 	return 0;
 }
 
 static int
-cgdinit(struct cgd_softc *cs, const char *cpath, struct vnode *vp,
+cgdinit(struct cgd_softc *sc, const char *cpath, struct vnode *vp,
 	struct lwp *l)
 {
 	struct	disk_geom *dg;
@@ -976,19 +1241,19 @@ cgdinit(struct cgd_softc *cs, const char
 	char	*tmppath;
 	uint64_t psize;
 	unsigned secsize;
-	struct dk_softc *dksc = &cs->sc_dksc;
+	struct dk_softc *dksc = &sc->sc_dksc;
 
-	cs->sc_tvn = vp;
-	cs->sc_tpath = NULL;
+	sc->sc_tvn = vp;
+	sc->sc_tpath = NULL;
 
-	tmppath = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
-	ret = copyinstr(cpath, tmppath, MAXPATHLEN, &cs->sc_tpathlen);
+	tmppath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+	ret = copyinstr(cpath, tmppath, MAXPATHLEN, &sc->sc_tpathlen);
 	if (ret)
 		goto bail;
-	cs->sc_tpath = malloc(cs->sc_tpathlen, M_DEVBUF, M_WAITOK);
-	memcpy(cs->sc_tpath, tmppath, cs->sc_tpathlen);
+	sc->sc_tpath = kmem_alloc(sc->sc_tpathlen, KM_SLEEP);
+	memcpy(sc->sc_tpath, tmppath, sc->sc_tpathlen);
 
-	cs->sc_tdev = vp->v_rdev;
+	sc->sc_tdev = vp->v_rdev;
 
 	if ((ret = getdisksize(vp, &psize, &secsize)) != 0)
 		goto bail;
@@ -1013,9 +1278,9 @@ cgdinit(struct cgd_softc *cs, const char
 	dg->dg_ncylinders = dg->dg_secperunit / dg->dg_nsectors;
 
 bail:
-	free(tmppath, M_TEMP);
-	if (ret && cs->sc_tpath)
-		free(cs->sc_tpath, M_DEVBUF);
+	kmem_free(tmppath, MAXPATHLEN);
+	if (ret && sc->sc_tpath)
+		kmem_free(sc->sc_tpath, sc->sc_tpathlen);
 	return ret;
 }
 
@@ -1056,19 +1321,80 @@ blkno2blkno_buf(char *sbuf, daddr_t blkn
 	}
 }
 
+static struct cpu_info *
+cgd_cpu(struct cgd_softc *sc)
+{
+	struct cgd_worker *cw = sc->sc_worker;
+	struct cpu_info *ci = NULL;
+	u_int cidx, i;
+
+	if (cw->cw_busy == 0) {
+		cw->cw_last = cpu_index(curcpu());
+		return NULL;
+	}
+
+	for (i=0, cidx = cw->cw_last+1; i<maxcpus; ++i, ++cidx) {
+		if (cidx >= maxcpus)
+			cidx = 0;
+		ci = cpu_lookup(cidx);
+		if (ci) {
+			cw->cw_last = cidx;
+			break;
+		}
+	}
+
+	return ci;
+}
+
+static void
+cgd_enqueue(struct cgd_softc *sc, struct cgd_xfer *cx)
+{
+	struct cgd_worker *cw = sc->sc_worker;
+	struct cpu_info *ci;
+
+	mutex_enter(&cw->cw_lock);
+	ci = cgd_cpu(sc);
+	cw->cw_busy++;
+	mutex_exit(&cw->cw_lock);
+
+	workqueue_enqueue(cw->cw_wq, &cx->cx_work, ci);
+}
+
+static void
+cgd_process(struct work *wk, void *arg)
+{
+	struct cgd_xfer *cx = (struct cgd_xfer *)wk;
+	struct cgd_softc *sc = cx->cx_sc;
+	struct cgd_worker *cw = sc->sc_worker;
+
+	cgd_cipher(sc, cx->cx_dstv, cx->cx_srcv, cx->cx_len,
+	    cx->cx_blkno, cx->cx_secsize, cx->cx_dir);
+
+	if (cx->cx_dir == CGD_CIPHER_ENCRYPT) {
+		cgd_diskstart2(sc, cx);
+	} else {
+		cgd_iodone2(sc, cx);
+	}
+
+	mutex_enter(&cw->cw_lock);
+	if (cw->cw_busy > 0)
+		cw->cw_busy--;
+	mutex_exit(&cw->cw_lock);
+}
+
 static void
-cgd_cipher(struct cgd_softc *cs, void *dstv, void *srcv,
+cgd_cipher(struct cgd_softc *sc, void *dstv, void *srcv,
     size_t len, daddr_t blkno, size_t secsize, int dir)
 {
 	char		*dst = dstv;
 	char		*src = srcv;
-	cfunc_cipher_prep	*ciprep = cs->sc_cfuncs->cf_cipher_prep;
-	cfunc_cipher	*cipher = cs->sc_cfuncs->cf_cipher;
+	cfunc_cipher_prep	*ciprep = sc->sc_cfuncs->cf_cipher_prep;
+	cfunc_cipher	*cipher = sc->sc_cfuncs->cf_cipher;
 	struct uio	dstuio;
 	struct uio	srcuio;
 	struct iovec	dstiov[2];
 	struct iovec	srciov[2];
-	size_t		blocksize = cs->sc_cdata.cf_blocksize;
+	size_t		blocksize = sc->sc_cdata.cf_blocksize;
 	size_t		todo;
 	char		blkno_buf[CGD_MAXBLOCKSIZE], *iv;
 
@@ -1108,10 +1434,10 @@ cgd_cipher(struct cgd_softc *cs, void *d
 		 * can convert blkno_buf in-place.
 		 */
 		iv = blkno_buf;
-		ciprep(cs->sc_cdata.cf_priv, iv, blkno_buf, blocksize, dir);
+		ciprep(sc->sc_cdata.cf_priv, iv, blkno_buf, blocksize, dir);
 		IFDEBUG(CGDB_CRYPTO, hexprint("step 2: iv", iv, blocksize));
 
-		cipher(cs->sc_cdata.cf_priv, &dstuio, &srcuio, iv, dir);
+		cipher(sc->sc_cdata.cf_priv, &dstuio, &srcuio, iv, dir);
 
 		dst += todo;
 		src += todo;
@@ -1135,7 +1461,7 @@ hexprint(const char *start, void *buf, i
 static void
 selftest(void)
 {
-	struct cgd_softc cs;
+	struct cgd_softc sc;
 	void *buf;
 
 	printf("running cgd selftest ");
@@ -1148,40 +1474,40 @@ selftest(void)
 
 		printf("%s-%d ", alg, keylen);
 
-		memset(&cs, 0, sizeof(cs));
+		memset(&sc, 0, sizeof(sc));
 
-		cs.sc_cfuncs = cryptfuncs_find(alg);
-		if (cs.sc_cfuncs == NULL)
+		sc.sc_cfuncs = cryptfuncs_find(alg);
+		if (sc.sc_cfuncs == NULL)
 			panic("%s not implemented", alg);
 
-		cs.sc_cdata.cf_blocksize = 8 * selftests[i].blocksize;
-		cs.sc_cdata.cf_mode = CGD_CIPHER_CBC_ENCBLKNO1;
-		cs.sc_cdata.cf_keylen = keylen;
-
-		cs.sc_cdata.cf_priv = cs.sc_cfuncs->cf_init(keylen,
-		    key, &cs.sc_cdata.cf_blocksize);
-		if (cs.sc_cdata.cf_priv == NULL)
+		sc.sc_cdata.cf_blocksize = 8 * selftests[i].blocksize;
+		sc.sc_cdata.cf_mode = CGD_CIPHER_CBC_ENCBLKNO1;
+		sc.sc_cdata.cf_keylen = keylen;
+
+		sc.sc_cdata.cf_priv = sc.sc_cfuncs->cf_init(keylen,
+		    key, &sc.sc_cdata.cf_blocksize);
+		if (sc.sc_cdata.cf_priv == NULL)
 			panic("cf_priv is NULL");
-		if (cs.sc_cdata.cf_blocksize > CGD_MAXBLOCKSIZE)
-			panic("bad block size %zu", cs.sc_cdata.cf_blocksize);
+		if (sc.sc_cdata.cf_blocksize > CGD_MAXBLOCKSIZE)
+			panic("bad block size %zu", sc.sc_cdata.cf_blocksize);
 
-		cs.sc_cdata.cf_blocksize /= 8;
+		sc.sc_cdata.cf_blocksize /= 8;
 
-		buf = malloc(txtlen, M_DEVBUF, M_WAITOK);
+		buf = kmem_alloc(txtlen, KM_SLEEP);
 		memcpy(buf, selftests[i].ptxt, txtlen);
 
-		cgd_cipher(&cs, buf, buf, txtlen, selftests[i].blkno,
+		cgd_cipher(&sc, buf, buf, txtlen, selftests[i].blkno,
 				selftests[i].secsize, CGD_CIPHER_ENCRYPT);
 		if (memcmp(buf, selftests[i].ctxt, txtlen) != 0)
 			panic("encryption is broken");
 
-		cgd_cipher(&cs, buf, buf, txtlen, selftests[i].blkno,
+		cgd_cipher(&sc, buf, buf, txtlen, selftests[i].blkno,
 				selftests[i].secsize, CGD_CIPHER_DECRYPT);
 		if (memcmp(buf, selftests[i].ptxt, txtlen) != 0)
 			panic("decryption is broken");
 
-		free(buf, M_DEVBUF);
-		cs.sc_cfuncs->cf_destroy(cs.sc_cdata.cf_priv);
+		kmem_free(buf, txtlen);
+		sc.sc_cfuncs->cf_destroy(sc.sc_cdata.cf_priv);
 	}
 
 	printf("done\n");
@@ -1204,6 +1530,9 @@ cgd_modcmd(modcmd_t cmd, void *arg)
 	case MODULE_CMD_INIT:
 		selftest();
 #ifdef _MODULE
+		mutex_init(&cgd_spawning_mtx, MUTEX_DEFAULT, IPL_NONE);
+		cv_init(&cgd_spawning_cv, "cgspwn");
+
 		error = config_cfdriver_attach(&cgd_cd);
 		if (error)
 			break;
@@ -1261,6 +1590,9 @@ cgd_modcmd(modcmd_t cmd, void *arg)
 			    "error %d\n", __func__, cgd_cd.cd_name, error);
 			break;
 		}
+
+		cv_destroy(&cgd_spawning_cv);
+		mutex_destroy(&cgd_spawning_mtx);
 #endif
 		break;
 

Index: src/sys/dev/cgdvar.h
diff -u src/sys/dev/cgdvar.h:1.18 src/sys/dev/cgdvar.h:1.18.24.1
--- src/sys/dev/cgdvar.h:1.18	Sun Sep  6 06:00:59 2015
+++ src/sys/dev/cgdvar.h	Mon Apr  6 14:57:42 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: cgdvar.h,v 1.18 2015/09/06 06:00:59 dholland Exp $ */
+/* $NetBSD: cgdvar.h,v 1.18.24.1 2020/04/06 14:57:42 martin Exp $ */
 
 /*-
  * Copyright (c) 2002 The NetBSD Foundation, Inc.
@@ -80,17 +80,41 @@ struct cryptdata {
 	void		*cf_priv;	/* enc alg private data */
 };
 
+struct cgd_xfer {
+	struct work		 cx_work;
+	struct cgd_softc	*cx_sc;
+	struct buf		*cx_obp;
+	struct buf		*cx_nbp;
+	void			*cx_dstv;
+	void			*cx_srcv;
+	size_t			 cx_len;
+	daddr_t			 cx_blkno;
+	size_t			 cx_secsize;
+	int			 cx_dir;
+};
+
+struct cgd_worker {
+	struct workqueue	*cw_wq;		/* work queue */
+	struct pool		*cw_cpool;	/* cgd_xfer contexts */
+	u_int		 	 cw_busy;	/* number of busy contexts */
+	u_int			 cw_last;	/* index of last CPU used */
+	kmutex_t		 cw_lock;
+};
+
 struct cgd_softc {
 	struct dk_softc		 sc_dksc;	/* generic disk interface */
 	struct vnode		*sc_tvn;	/* target device's vnode */
 	dev_t			 sc_tdev;	/* target device */
 	char			*sc_tpath;	/* target device's path */
-	void *			 sc_data;	/* emergency buffer */
-	int			 sc_data_used;	/* Really lame, we'll change */
+	void			*sc_data;	/* emergency buffer */
+	bool			 sc_data_used;	/* Really lame, we'll change */
 	size_t			 sc_tpathlen;	/* length of prior string */
 	struct cryptdata	 sc_cdata;	/* crypto data */
 	const struct cryptfuncs	*sc_cfuncs;	/* encryption functions */
-	kmutex_t		 sc_lock;	/* our lock */
+	kmutex_t		 sc_lock;
+	kcondvar_t		 sc_cv;
+	bool			 sc_busy;
+	struct cgd_worker	*sc_worker;	/* shared worker data */
 };
 #endif
 

Reply via email to