svn commit: r186944 - user/luigi/geom_sched/sys/geom/sched
Luigi Rizzo
luigi at FreeBSD.org
Fri Jan 9 03:47:15 PST 2009
Author: luigi
Date: Fri Jan 9 11:47:14 2009
New Revision: 186944
URL: http://svn.freebsd.org/changeset/base/186944
Log:
document and small reorganization of variables
Modified:
user/luigi/geom_sched/sys/geom/sched/g_as.c
Modified: user/luigi/geom_sched/sys/geom/sched/g_as.c
==============================================================================
--- user/luigi/geom_sched/sys/geom/sched/g_as.c Fri Jan 9 11:46:23 2009 (r186943)
+++ user/luigi/geom_sched/sys/geom/sched/g_as.c Fri Jan 9 11:47:14 2009 (r186944)
@@ -24,6 +24,13 @@
* SUCH DAMAGE.
*/
+/*
+ * This code implements a GEOM-based anticipatory disk scheduler.
+ * This version does not track process state or behaviour and it is
+ * just a proof of concept to show how non work-conserving policies
+ * can be implemented within this framework.
+ */
+
#include <sys/cdefs.h>
#include <sys/param.h>
@@ -52,17 +59,18 @@ struct g_as_softc {
u_long sc_curkey;
int sc_status;
long sc_batch;
+ int sc_wait_ticks;
+ int sc_max_batch;
struct callout sc_wait;
struct bio_queue_head sc_bioq;
};
-#define G_AS_WAIT_EXPIRE (hz/200 > 0 ? hz/200 : 2)
-#define G_AS_MAX_BATCH 0x00800000
-
/*
- * Dispatch the first queued request. Here we also update the status
+ * Dispatch the first queued request, and update the status
* according to the dispatched request.
+ * This is called as a result of a start, on a timeout, or on
+ * a completion event.
*/
static void
g_as_dispatch(struct g_as_softc *sc)
@@ -70,37 +78,43 @@ g_as_dispatch(struct g_as_softc *sc)
struct bio *bio;
/*
- * Batching means just don't serve too many requests waiting
- * for sequential ones, it is not really coupled with the
- * threads being served. Its only purpose is to let not the
- * scheduler starve other threads while an aggressive one
- * is making continuously new requests.
+ * Serve the requests at the head of the queue, if any,
+ * and decide whether or not to do anticipatory scheduling
+ * for the next round. We anticipate if this request is from
+ * a new client or the current client has not yet exhausted
+ * its budget. Otherwise, we will serve the next request
+ * immediately.
*/
- sc->sc_curkey = 0;
bio = bioq_takefirst(&sc->sc_bioq);
- if (bio != NULL) {
- sc->sc_batch += bio->bio_length;
- if (sc->sc_batch > G_AS_MAX_BATCH) {
- /*
- * Too many requests served here, don't wait
- * for the next.
- */
+ if (bio == NULL) {
+ /* stray call or timeout */
+ sc->sc_curkey = 0;
+ sc->sc_batch = 0;
+ sc->sc_status = G_AS_NOWAIT;
+ } else {
+ u_long head_key = g_sched_classify(bio);
+
+ /* pass down the current request */
+ g_io_request(bio, LIST_FIRST(&sc->sc_geom->consumer));
+
+ /*
+ * Now decide what to do next:
+ * reset budget if client has changed,
+ * store the identity of the current client,
+ * and anticipate if and only if the current
+ * client is below its allowed budget.
+ */
+ if (head_key != sc->sc_curkey)
sc->sc_batch = 0;
+ sc->sc_curkey = head_key;
+ if (sc->sc_batch > sc->sc_max_batch) {
sc->sc_status = G_AS_NOWAIT;
} else {
- /*
- * When this request will be served we'll wait
- * for a new one from the same thread.
- * Of course we are anticipating everything
- * here, even writes or asynchronous requests,
- * but this is only a prototype.
- */
+ sc->sc_batch += bio->bio_length;
sc->sc_status = G_AS_WAITREQ;
}
- g_io_request(bio, LIST_FIRST(&sc->sc_geom->consumer));
- } else
- sc->sc_status = G_AS_NOWAIT;
+ }
}
static void
@@ -118,17 +132,31 @@ g_as_wait_timeout(void *data)
g_sched_unlock(sc->sc_geom);
}
+/*
+ * This function is called when I have a real disk I/O request coming
+ * from a client (only for schedulable requests).
+ * Queue the request and possibly dispatch it. If not dispatched now,
+ * surely there a timeout or a completion event that will keep things
+ * running.
+ */
static void
g_as_start(void *data, struct bio *bio)
{
struct g_as_softc *sc = data;
- bioq_disksort(&sc->sc_bioq, bio);
-
/*
- * If the request being submitted is the one we were waiting for
- * stop the timer and dispatch it, otherwise do nothing.
+ * This is an approximated implementation: we do an immediate
+ * dispatch if the current request is coming from the same client
+ * who was served last (who was the "privileged" one in terms
+ * of the scheduling policy). However, the dispatch may actually
+ * serve a different request if the incoming request is not
+ * sequential (hence it would cause a seek anyways).
+ * For this reason, we do an unconditional disksort, and
+ * then decide to dispatch or wait using the identity
+ * of the client issuing the request.
*/
+ bioq_disksort(&sc->sc_bioq, bio);
+
if (sc->sc_status == G_AS_NOWAIT ||
g_sched_classify(bio) == sc->sc_curkey) {
callout_stop(&sc->sc_wait);
@@ -136,25 +164,17 @@ g_as_start(void *data, struct bio *bio)
}
}
+/*
+ * callback when a request is complete.
+ */
static void
g_as_done(void *data, struct bio *bio)
{
struct g_as_softc *sc = data;
- struct bio *bp2;
-
- bp2 = bio->bio_parent;
-
- /* Don't wait when fragments are completed. */
- if (bp2->bio_children != bp2->bio_inbed + 1)
- return;
if (sc->sc_status == G_AS_WAITREQ) {
- /*
- * Start waiting for a new request from curthread.
- */
- sc->sc_curkey = g_sched_classify(bio);
- sc->sc_status = G_AS_WAITING;
- callout_reset(&sc->sc_wait, G_AS_WAIT_EXPIRE,
+ sc->sc_status = G_AS_WAITING; /* have pending timer */
+ callout_reset(&sc->sc_wait, sc->sc_wait_ticks,
g_as_wait_timeout, sc);
} else {
/*
@@ -165,6 +185,11 @@ g_as_done(void *data, struct bio *bio)
}
}
+/*
+ * Geom glue. When a new geom is attached, allocate and init a
+ * descriptor. We use a callout queue for timeouts, and a bioq
+ * to store pending requests.
+ */
static void *
g_as_init(struct g_geom *geom)
{
@@ -174,6 +199,8 @@ g_as_init(struct g_geom *geom)
sc->sc_geom = geom;
sc->sc_curkey = 0;
sc->sc_status = G_AS_NOWAIT;
+ sc->sc_wait_ticks = (hz >= 400) ? hz/200 : 2;
+ sc->sc_max_batch = 0x00800000; /* 8 MB */
callout_init(&sc->sc_wait, CALLOUT_MPSAFE);
bioq_init(&sc->sc_bioq);
@@ -186,6 +213,11 @@ g_as_fini(void *data)
{
struct g_as_softc *sc = data;
+ /*
+ * geom should guarantee that _fini is only called when there
+ * are no more bio's active (GEOM does not know about the queue,
+ * but it can count existing bio's associated to the geom).
+ */
KASSERT(bioq_first(&sc->sc_bioq) == NULL,
("Still requests pending."));
callout_drain(&sc->sc_wait);
More information about the svn-src-user
mailing list