diff options
-rw-r--r-- | sys/kern/kern_bufq.c | 244 | ||||
-rw-r--r-- | sys/scsi/sd.c | 27 | ||||
-rw-r--r-- | sys/scsi/sdvar.h | 5 | ||||
-rw-r--r-- | sys/sys/buf.h | 69 |
4 files changed, 322 insertions, 23 deletions
diff --git a/sys/kern/kern_bufq.c b/sys/kern/kern_bufq.c new file mode 100644 index 00000000000..402ca4ec473 --- /dev/null +++ b/sys/kern/kern_bufq.c @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2010 Thordur I. Bjornsson <thib@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/buf.h> +#include <sys/errno.h> +#include <sys/mutex.h> +#include <sys/queue.h> + +#include <sys/disklabel.h> + +#ifdef BUFQ_DEBUG +#define BUFQDBG_INIT 0x0001 +#define BUFQDBG_DRAIN 0x0002 +#define BUFQDBG_DISKSORT 0x0004 +#define BUFQDBG_FIFO 0x0008 +int bqdebug = 0; +#define DPRINTF(p...) do { if (bqdebug) printf(p); } while (0) +#define DNPRINTF(n, p...) do { if ((n) & bqdebug) printf(p); } while (0) +#else +#define DPRINTF(p...) /* p */ +#define DNPRINTF(n, p...) /* n, p */ +#endif + +struct buf *(*bufq_dequeue[BUFQ_HOWMANY])(struct bufq *, int) = { + bufq_disksort_dequeue, + bufq_fifo_dequeue +}; +void (*bufq_queue[BUFQ_HOWMANY])(struct bufq *, struct buf *) = { + bufq_disksort_queue, + bufq_fifo_queue +}; +void (*bufq_requeue[BUFQ_HOWMANY])(struct bufq *, struct buf *) = { + bufq_disksort_requeue, + bufq_fifo_requeue +}; + + +struct bufq * +bufq_init(int type) +{ + struct bufq *bq; + int error; + + bq = malloc(sizeof(*bq), M_DEVBUF, M_NOWAIT|M_ZERO); + KASSERT(bq != NULL); + + DNPRINTF(BUFQDBG_INIT, "%s: initing bufq %p of type %i\n", + __func__, bq, type); + + mtx_init(&bq->bufq_mtx, IPL_BIO); + bq->bufq_type = type; + + switch (type) { + case BUFQ_DISKSORT: + error = bufq_disksort_init(bq); + break; + case BUFQ_FIFO: + error = bufq_fifo_init(bq); + break; + default: + panic("bufq_init: type %i unknown", type); + break; + }; + + KASSERT(error == 0); + + return (bq); +} + +void +bufq_destroy(struct bufq *bq) +{ + bufq_drain(bq); + + DNPRINTF(BUFQDBG_INIT, "%s: destroying bufq %p\n", __func__, bq); + + if (bq->bufq_data != NULL) + free(bq->bufq_data, M_DEVBUF); + + free(bq, M_DEVBUF); +} + +void +bufq_drain(struct bufq *bq) +{ + struct buf *bp; + int s; + + DNPRINTF(BUFQDBG_DRAIN, "%s: draining bufq %p\n", + __func__, bq); + + while ((bp = BUFQ_DEQUEUE(bq)) != NULL) { + bp->b_error = ENXIO; + bp->b_flags |= B_ERROR; + s = splbio(); + biodone(bp); + splx(s); + } +} + +void +bufq_disksort_queue(struct bufq *bq, struct buf *bp) +{ + struct buf *bufq; + + bufq = (struct buf *)bq->bufq_data; + + DNPRINTF(BUFQDBG_DISKSORT, "%s: queueing bp %p in bufq %p\n", + __func__, bp, bq); + + mtx_enter(&bq->bufq_mtx); + disksort(bufq, bp); + mtx_leave(&bq->bufq_mtx); +} + +void +bufq_disksort_requeue(struct bufq *bq, struct buf *bp) +{ + struct buf *bufq; + + bufq = (struct buf *)bq->bufq_data; + + DNPRINTF(BUFQDBG_DISKSORT, "%s: requeueing bp % in bufq %p\n", + __func__, bp, bufq); + + mtx_enter(&bq->bufq_mtx); + bp->b_actf = bufq->b_actf; + if (bp->b_actf == NULL) + bufq->b_actb = &bp->b_actf; + mtx_leave(&bq->bufq_mtx); +} + +struct buf * +bufq_disksort_dequeue(struct bufq *bq, int peeking) +{ + struct buf *bufq, *bp; + + mtx_enter(&bq->bufq_mtx); + bufq = (struct buf *)bq->bufq_data; + bp = bufq->b_actf; + if (bp == NULL) { + mtx_leave(&bq->bufq_mtx); + return (NULL); + } + if (!peeking) + bufq->b_actf = bp->b_actf; + mtx_leave(&bq->bufq_mtx); + + DNPRINTF(BUFQDBG_DISKSORT, "%s: %s buf %p from bufq %p\n", __func__, + peeking ? "peeking at" : "dequeueing", bp, bq); + + return (bp); +} + +int +bufq_disksort_init(struct bufq *bq) +{ + int error = 0; + + bq->bufq_data = malloc(sizeof(struct buf), M_DEVBUF, + M_NOWAIT|M_ZERO); + + if (bq->bufq_data == NULL) + error = ENOMEM; + + return (error); +} + +void +bufq_fifo_queue(struct bufq *bq, struct buf *bp) +{ + struct bufq_fifo_head *head = bq->bufq_data; + + DNPRINTF(BUFQDBG_FIFO, "%s: queueing bp %p in bufq %p\n", + __func__, bp, bq); + + mtx_enter(&bq->bufq_mtx); + TAILQ_INSERT_TAIL(head, bp, b_bufq.bufq_data_fifo.bqf_entries); + mtx_leave(&bq->bufq_mtx); +} + +void +bufq_fifo_requeue(struct bufq *bq, struct buf *bp) +{ + struct bufq_fifo_head *head = bq->bufq_data; + + DNPRINTF(BUFQDBG_FIFO, "%s: requeueing bp % in bufq %p\n", + __func__, bp, bq); + + mtx_enter(&bq->bufq_mtx); + TAILQ_INSERT_HEAD(head, bp, b_bufq.bufq_data_fifo.bqf_entries); + mtx_leave(&bq->bufq_mtx); +} + +struct buf * +bufq_fifo_dequeue(struct bufq *bq, int peeking) +{ + struct bufq_fifo_head *head = bq->bufq_data; + struct buf *bp; + + mtx_enter(&bq->bufq_mtx); + bp = TAILQ_FIRST(head); + if (bp != NULL && !peeking) + TAILQ_REMOVE(head, bp, b_bufq.bufq_data_fifo.bqf_entries); + mtx_leave(&bq->bufq_mtx); + + DNPRINTF(BUFQDBG_FIFO, "%s: %s buf %p from bufq %p\n", __func__, + peeking ? "peeking at" : "dequeueing", bp, bq); + + return (bp); +} + +int +bufq_fifo_init(struct bufq *bq) +{ + struct bufq_fifo_head *head; + + head = malloc(sizeof(*head), M_DEVBUF, M_NOWAIT); + if (head == NULL) + return (ENOMEM); + + TAILQ_INIT(head); + bq->bufq_data = head; + + return (0); +} diff --git a/sys/scsi/sd.c b/sys/scsi/sd.c index d2f67671eb1..d2bc296b27e 100644 --- a/sys/scsi/sd.c +++ b/sys/scsi/sd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sd.c,v 1.190 2010/05/20 00:04:38 krw Exp $ */ +/* $OpenBSD: sd.c,v 1.191 2010/05/26 16:38:20 thib Exp $ */ /* $NetBSD: sd.c,v 1.111 1997/04/02 02:29:41 mycroft Exp $ */ /*- @@ -55,6 +55,7 @@ #include <sys/stat.h> #include <sys/ioctl.h> #include <sys/mtio.h> +#include <sys/mutex.h> #include <sys/buf.h> #include <sys/uio.h> #include <sys/malloc.h> @@ -168,8 +169,6 @@ sdattach(struct device *parent, struct device *self, void *aux) SC_DEBUG(sc_link, SDEV_DB2, ("sdattach:\n")); - mtx_init(&sc->sc_buf_mtx, IPL_BIO); - /* * Store information needed to contact our base driver */ @@ -182,6 +181,7 @@ sdattach(struct device *parent, struct device *self, void *aux) */ sc->sc_dk.dk_driver = &sddkdriver; sc->sc_dk.dk_name = sc->sc_dev.dv_xname; + sc->sc_bufq = bufq_init(BUFQ_DEFAULT); disk_attach(&sc->sc_dk); if ((sc_link->flags & SDEV_ATAPI) && (sc_link->flags & SDEV_REMOVABLE)) @@ -277,7 +277,7 @@ sdactivate(struct device *self, int act) case DVACT_DEACTIVATE: sc->flags |= SDF_DYING; - scsi_buf_killqueue(&sc->sc_buf_queue, &sc->sc_buf_mtx); + bufq_drain(sc->sc_bufq); break; } @@ -291,7 +291,7 @@ sddetach(struct device *self, int flags) struct sd_softc *sc = (struct sd_softc *)self; int bmaj, cmaj, mn; - scsi_buf_killqueue(&sc->sc_buf_queue, &sc->sc_buf_mtx); + bufq_drain(sc->sc_bufq); /* Locate the lowest minor number to be detached. */ mn = DISKMINOR(self->dv_unit, 0); @@ -308,6 +308,7 @@ sddetach(struct device *self, int flags) shutdownhook_disestablish(sc->sc_sdhook); /* Detach disk. */ + bufq_destroy(sc->sc_bufq); disk_detach(&sc->sc_dk); return (0); @@ -561,12 +562,8 @@ sdstrategy(struct buf *bp) (sc->flags & (SDF_WLABEL|SDF_LABELLING)) != 0) <= 0) goto done; - /* - * Place it in the queue of disk activities for this disk - */ - mtx_enter(&sc->sc_buf_mtx); - disksort(&sc->sc_buf_queue, bp); - mtx_leave(&sc->sc_buf_mtx); + /* Place it in the queue of disk activities for this disk. */ + BUFQ_QUEUE(sc->sc_bufq, bp); /* * Tell the device to get going on the transfer if it's @@ -668,12 +665,12 @@ sdstart(struct scsi_xfer *xs) return; } if ((link->flags & SDEV_MEDIA_LOADED) == 0) { - scsi_buf_killqueue(&sc->sc_buf_queue, &sc->sc_buf_mtx); + bufq_drain(sc->sc_bufq); scsi_xs_put(xs); return; } - bp = scsi_buf_dequeue(&sc->sc_buf_queue, &sc->sc_buf_mtx); + bp = BUFQ_DEQUEUE(sc->sc_bufq); if (bp == NULL) { scsi_xs_put(xs); return; @@ -721,7 +718,7 @@ sdstart(struct scsi_xfer *xs) scsi_xs_exec(xs); /* move onto the next io */ - if (scsi_buf_canqueue(&sc->sc_buf_queue, &sc->sc_buf_mtx)) + if (BUFQ_PEEK(sc->sc_bufq) != NULL) scsi_xsh_add(&sc->sc_xsh); } @@ -742,7 +739,7 @@ sd_buf_done(struct scsi_xfer *xs) /* The adapter is busy, requeue the buf and try it later. */ disk_unbusy(&sc->sc_dk, bp->b_bcount - xs->resid, bp->b_flags & B_READ); - scsi_buf_requeue(&sc->sc_buf_queue, bp, &sc->sc_buf_mtx); + BUFQ_REQUEUE(sc->sc_bufq, bp); scsi_xs_put(xs); timeout_add(&sc->sc_timeout, 1); return; diff --git a/sys/scsi/sdvar.h b/sys/scsi/sdvar.h index 4fdefa1d725..396b1cd1da9 100644 --- a/sys/scsi/sdvar.h +++ b/sys/scsi/sdvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sdvar.h,v 1.29 2010/05/19 05:50:50 dlg Exp $ */ +/* $OpenBSD: sdvar.h,v 1.30 2010/05/26 16:38:20 thib Exp $ */ /* $NetBSD: sdvar.h,v 1.7 1998/08/17 00:49:03 mycroft Exp $ */ /*- @@ -51,6 +51,7 @@ struct sd_softc { struct device sc_dev; struct disk sc_dk; + struct bufq *sc_bufq; int flags; #define SDF_LOCKED 0x01 @@ -69,8 +70,6 @@ struct sd_softc { u_long rot_rate; /* rotational rate, in RPM */ daddr64_t disksize; /* total number sectors */ } params; - struct mutex sc_buf_mtx; - struct buf sc_buf_queue; void *sc_sdhook; /* our shutdown hook */ struct timeout sc_timeout; diff --git a/sys/sys/buf.h b/sys/sys/buf.h index 3abc87d19d9..901933befca 100644 --- a/sys/sys/buf.h +++ b/sys/sys/buf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: buf.h,v 1.67 2009/08/02 16:28:40 beck Exp $ */ +/* $OpenBSD: buf.h,v 1.68 2010/05/26 16:38:20 thib Exp $ */ /* $NetBSD: buf.h,v 1.25 1997/04/09 21:12:17 mycroft Exp $ */ /* @@ -41,6 +41,7 @@ #define _SYS_BUF_H_ #include <sys/queue.h> #include <sys/tree.h> +#include <sys/mutex.h> #define NOLIST ((struct buf *)0x87654321) @@ -59,6 +60,61 @@ LIST_HEAD(bufhead, buf); LIST_HEAD(workhead, worklist); /* + * Buffer queues + */ +#define BUFQ_DISKSORT 0 +#define BUFQ_FIFO 1 +#define BUFQ_DEFAULT BUFQ_DISKSORT +#define BUFQ_HOWMANY 2 + +struct bufq { + struct mutex bufq_mtx; + void *bufq_data; + int bufq_type; +}; + +struct buf *bufq_disksort_dequeue(struct bufq *, int); +void bufq_disksort_queue(struct bufq *, struct buf *); +void bufq_disksort_requeue(struct bufq *, struct buf *); +int bufq_disksort_init(struct bufq *); +struct bufq_disksort { + struct buf *bqd_actf; + struct buf **bqd_actb; +}; + +struct buf *bufq_fifo_dequeue(struct bufq *, int); +void bufq_fifo_queue(struct bufq *, struct buf *); +void bufq_fifo_requeue(struct bufq *, struct buf *); +int bufq_fifo_init(struct bufq *); +TAILQ_HEAD(bufq_fifo_head, buf); +struct bufq_fifo { + TAILQ_ENTRY(buf) bqf_entries; +}; + +union bufq_data { + struct bufq_disksort bufq_data_disksort; + struct bufq_fifo bufq_data_fifo; +}; + +extern struct buf *(*bufq_dequeue[BUFQ_HOWMANY])(struct bufq *, int); +extern void (*bufq_queue[BUFQ_HOWMANY])(struct bufq *, struct buf *); +extern void (*bufq_requeue[BUFQ_HOWMANY])(struct bufq *, struct buf *); + +#define BUFQ_QUEUE(_bufq, _bp) \ + bufq_queue[(_bufq)->bufq_type](_bufq, _bp) +#define BUFQ_REQUEUE(_bufq, _bp) \ + bufq_requeue[(_bufq)->bufq_type](_bufq, _bp) +#define BUFQ_DEQUEUE(_bufq) \ + bufq_dequeue[(_bufq)->bufq_type](_bufq, 0) +#define BUFQ_PEEK(_bufq) \ + bufq_dequeue[(_bufq)->bufq_type](_bufq, 1) + +struct bufq *bufq_init(int); +void bufq_destroy(struct bufq *); +void bufq_drain(struct bufq *); + + +/* * These are currently used only by the soft dependency code, hence * are stored once in a global variable. If other subsystems wanted * to use these hooks, a pointer to a set of bio_ops could be added @@ -72,16 +128,17 @@ extern struct bio_ops { int (*io_countdeps)(struct buf *, int, int); } bioops; -/* - * The buffer header describes an I/O operation in the kernel. - */ +/* XXX: disksort(); */ +#define b_actf b_bufq.bufq_data_disksort.bqd_actf +#define b_actb b_bufq.bufq_data_disksort.bqd_actb + +/* The buffer header describes an I/O operation in the kernel. */ struct buf { RB_ENTRY(buf) b_rbbufs; /* vnode "hash" tree */ LIST_ENTRY(buf) b_list; /* All allocated buffers. */ LIST_ENTRY(buf) b_vnbufs; /* Buffer's associated vnode. */ TAILQ_ENTRY(buf) b_freelist; /* Free list position if not active. */ time_t b_synctime; /* Time this buffer should be flushed */ - struct buf *b_actf, **b_actb; /* Device driver queue when active. */ struct proc *b_proc; /* Associated proc; NULL if kernel. */ volatile long b_flags; /* B_* flags. */ int b_error; /* Errno value. */ @@ -94,6 +151,8 @@ struct buf { TAILQ_ENTRY(buf) b_valist; /* LRU of va to reuse. */ + union bufq_data b_bufq; + struct uvm_object *b_pobj; /* Object containing the pages */ off_t b_poffs; /* Offset within object */ |