summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/kern/kern_bufq.c244
-rw-r--r--sys/scsi/sd.c27
-rw-r--r--sys/scsi/sdvar.h5
-rw-r--r--sys/sys/buf.h69
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 */