From owner-svn-src-user@FreeBSD.ORG Wed Jan 14 13:46:01 2009 Return-Path: Delivered-To: svn-src-user@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 7C663106567B; Wed, 14 Jan 2009 13:46:01 +0000 (UTC) (envelope-from luigi@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 69E188FC26; Wed, 14 Jan 2009 13:46:01 +0000 (UTC) (envelope-from luigi@FreeBSD.org) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id n0EDk1sS044797; Wed, 14 Jan 2009 13:46:01 GMT (envelope-from luigi@svn.freebsd.org) Received: (from luigi@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id n0EDk1R0044792; Wed, 14 Jan 2009 13:46:01 GMT (envelope-from luigi@svn.freebsd.org) Message-Id: <200901141346.n0EDk1R0044792@svn.freebsd.org> From: Luigi Rizzo Date: Wed, 14 Jan 2009 13:46:01 +0000 (UTC) To: src-committers@freebsd.org, svn-src-user@freebsd.org X-SVN-Group: user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r187220 - user/luigi/geom_sched/sys/geom/sched X-BeenThere: svn-src-user@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the experimental " user" src tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 14 Jan 2009 13:46:02 -0000 Author: luigi Date: Wed Jan 14 13:46:01 2009 New Revision: 187220 URL: http://svn.freebsd.org/changeset/base/187220 Log: sync with private tree Added: user/luigi/geom_sched/sys/geom/sched/gs_scheduler.h (contents, props changed) - copied, changed from r186940, user/luigi/geom_sched/sys/geom/sched/g_gsched.h Deleted: user/luigi/geom_sched/sys/geom/sched/g_gsched.h Modified: user/luigi/geom_sched/sys/geom/sched/g_sched.c user/luigi/geom_sched/sys/geom/sched/g_sched.h user/luigi/geom_sched/sys/geom/sched/gs_as.c user/luigi/geom_sched/sys/geom/sched/gs_rr.c Modified: user/luigi/geom_sched/sys/geom/sched/g_sched.c ============================================================================== --- user/luigi/geom_sched/sys/geom/sched/g_sched.c Wed Jan 14 13:44:59 2009 (r187219) +++ user/luigi/geom_sched/sys/geom/sched/g_sched.c Wed Jan 14 13:46:01 2009 (r187220) @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2007 Fabio Checconi + * Copyright (c) 2009 Fabio Checconi * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,6 +24,9 @@ * SUCH DAMAGE. */ +/* + * The main control module for geom-based disk schedulers + */ #include #include @@ -37,20 +40,16 @@ #include #include /* we access curthread */ #include -#include "g_gsched.h" -#include "g_sched.h" - -SYSCTL_DECL(_kern_geom); -SYSCTL_NODE(_kern_geom, OID_AUTO, sched, CTLFLAG_RW, 0, "GEOM_SCHED stuff"); -static u_int g_sched_debug = 0; -SYSCTL_UINT(_kern_geom_sched, OID_AUTO, debug, CTLFLAG_RW, &g_sched_debug, 0, - "Debug level"); +#include "gs_scheduler.h" +#include "g_sched.h" /* geom hooks */ static int g_sched_destroy(struct g_geom *gp, boolean_t force); -static int g_sched_destroy_geom(struct gctl_req *req, struct g_class *mp, - struct g_geom *gp); +static int g_sched_destroy_geom(struct gctl_req *req, + struct g_class *mp, struct g_geom *gp); static void g_sched_config(struct gctl_req *req, struct g_class *mp, const char *verb); +static struct g_geom * +g_sched_taste(struct g_class *mp, struct g_provider *pp, int flags __unused); static void g_sched_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, struct g_consumer *cp, struct g_provider *pp); static void g_sched_init(struct g_class *mp); @@ -60,15 +59,44 @@ struct g_class g_sched_class = { .name = G_SCHED_CLASS_NAME, .version = G_VERSION, .ctlreq = g_sched_config, + .taste = g_sched_taste, .destroy_geom = g_sched_destroy_geom, .init = g_sched_init, .fini = g_sched_fini }; -static struct mtx g_gsched_mtx; -LIST_HEAD(gsched_list, g_gsched); -static struct gsched_list gsched_list; +MALLOC_DEFINE(M_GEOM_SCHED, "GEOM_SCHED", "Geom schedulers data structures"); + +/* + * Global variables describing the state of the geom_sched module. + */ +LIST_HEAD(gs_list, g_gsched); /* type, link field */ +struct geom_sched_vars { + struct mtx gs_mtx; + struct gs_list gs_scheds; /* list of schedulers */ + int gs_sched_count; /* how many schedulers ? */ + u_int gs_debug; + int gs_patched; /* g_io_request was patched */ + char gs_names[256]; /* names of schedulers */ +}; + +static struct geom_sched_vars me; + +SYSCTL_DECL(_kern_geom); +SYSCTL_NODE(_kern_geom, OID_AUTO, sched, CTLFLAG_RW, 0, + "GEOM_SCHED stuff"); +SYSCTL_UINT(_kern_geom_sched, OID_AUTO, debug, CTLFLAG_RW, + &me.gs_debug, 0, "Debug level"); +SYSCTL_UINT(_kern_geom_sched, OID_AUTO, sched_count, CTLFLAG_RD, + &me.gs_sched_count, 0, "Number of schedulers"); +SYSCTL_STRING(_kern_geom_sched, OID_AUTO, schedulers, CTLFLAG_RD, + &me.gs_names, 0, "Scheduler names"); +/* + * This module calls the scheduler algorithms with this lock held. + * The functions are exposed so the scheduler algorithms can also + * protect themselves e.g. when running a callout handler. + */ void g_sched_lock(struct g_geom *gp) { @@ -85,6 +113,10 @@ g_sched_unlock(struct g_geom *gp) mtx_unlock(&sc->sc_mtx); } +/* + * Handle references to the module, which are coming from devices + * using this scheduler. + */ static inline void g_gsched_ref(struct g_gsched *gsp) { @@ -96,51 +128,85 @@ static inline void g_gsched_unref(struct g_gsched *gsp) { - /* - * The last reference to a gsp before releasing it is the one - * of the gsched_list. Elements are not released nor removed - * from the list until there is an external reference to them. - */ atomic_add_int(&gsp->gs_refs, -1); } +void +g_sched_dispatch(struct g_geom *gp) +{ + struct g_sched_softc *sc = gp->softc; + struct g_gsched *gsp = sc->sc_gsched; + struct bio *bp; + + mtx_assert(&sc->sc_mtx, MTX_OWNED); + while ((bp = gsp->gs_next(sc->sc_data)) != NULL) + g_io_request(bp, LIST_FIRST(&gp->consumer)); +} + static struct g_gsched * g_gsched_find(const char *name) { struct g_gsched *gsp = NULL; - mtx_lock(&g_gsched_mtx); - LIST_FOREACH(gsp, &gsched_list, glist) + mtx_lock(&me.gs_mtx); + LIST_FOREACH(gsp, &me.gs_scheds, glist) if (strcmp(name, gsp->gs_name) == 0) { g_gsched_ref(gsp); break; } - mtx_unlock(&g_gsched_mtx); + mtx_unlock(&me.gs_mtx); return gsp; } -static int -g_gsched_register(struct g_gsched *gsp) +/* + * rebuild the list of scheduler names. + * To be called with lock held. + */ +static void +g_gsched_build_names(struct g_gsched *gsp) { - struct g_gsched *tmp; - int error; + int pos, l; + struct g_gsched *cur; - error = 0; - mtx_lock(&g_gsched_mtx); - LIST_FOREACH(tmp, &gsched_list, glist) - if (strcmp(gsp->gs_name, tmp->gs_name) == 0) { - G_SCHED_DEBUG(0, "A scheduler named %s already" - "exists.", gsp->gs_name); - error = EEXIST; - goto out; + pos = 0; + LIST_FOREACH(cur, &me.gs_scheds, glist) { + l = strlen(cur->gs_name); + if (l + pos + 1 + 1 < sizeof(me.gs_names)) { + if (pos != 0) + me.gs_names[pos++] = ' '; + strcpy(me.gs_names + pos, cur->gs_name); + pos += l; } + } + me.gs_names[pos] = '\0'; +} - LIST_INSERT_HEAD(&gsched_list, gsp, glist); - gsp->gs_refs = 1; +/* + * Register or unregister individual scheduling algorithms. + */ +static int +g_gsched_register(struct g_gsched *gsp) +{ + struct g_gsched *cur; + int error = 0; -out: - mtx_unlock(&g_gsched_mtx); + mtx_lock(&me.gs_mtx); + LIST_FOREACH(cur, &me.gs_scheds, glist) { + if (strcmp(gsp->gs_name, cur->gs_name) == 0) + break; + } + if (cur != NULL) { + G_SCHED_DEBUG(0, "A scheduler named %s already" + "exists.", gsp->gs_name); + error = EEXIST; + } else { + LIST_INSERT_HEAD(&me.gs_scheds, gsp, glist); + gsp->gs_refs = 1; + me.gs_sched_count++; + g_gsched_build_names(gsp); + } + mtx_unlock(&me.gs_mtx); return (error); } @@ -149,29 +215,46 @@ static int g_gsched_unregister(struct g_gsched *gsp) { struct g_gsched *cur, *tmp; - int error; + int error = 0; + struct g_geom *gp, *gp_tmp; error = 0; - mtx_lock(&g_gsched_mtx); - LIST_FOREACH_SAFE(cur, &gsched_list, glist, tmp) { - if (cur == gsp && gsp->gs_refs != 1) { + mtx_lock(&me.gs_mtx); + + /* scan stuff attached here ? */ + printf("%s, scan attached providers\n", __FUNCTION__); + LIST_FOREACH_SAFE(gp, &g_sched_class.geom, geom, gp_tmp) { + if (gp->class != &g_sched_class) + continue; /* should not happen */ + g_sched_destroy(gp, 0); + } + + + LIST_FOREACH_SAFE(cur, &me.gs_scheds, glist, tmp) { + if (cur != gsp) + continue; + if (gsp->gs_refs != 1) { G_SCHED_DEBUG(0, "%s still in use.", gsp->gs_name); error = EBUSY; - goto out; - } else if (cur == gsp && gsp->gs_refs == 1) { + } else { LIST_REMOVE(gsp, glist); - goto out; + me.gs_sched_count--; + g_gsched_build_names(gsp); } + break; } + if (cur == NULL) + G_SCHED_DEBUG(0, "%s not registered.", gsp->gs_name); - G_SCHED_DEBUG(0, "%s not registered.", gsp->gs_name); - -out: - mtx_unlock(&g_gsched_mtx); + mtx_unlock(&me.gs_mtx); return (error); } +/* + * Module event called when a scheduling algorithm module is loaded or + * unloaded. + */ int g_gsched_modevent(module_t mod, int cmd, void *arg) { @@ -182,23 +265,44 @@ g_gsched_modevent(module_t mod, int cmd, switch (cmd) { case MOD_LOAD: error = g_gsched_register(gsp); + printf("loaded module %s error %d\n", gsp->gs_name, error); + if (error == 0) + g_retaste(&g_sched_class); break; case MOD_UNLOAD: error = g_gsched_unregister(gsp); + printf("unloading for scheduler %s error %d\n", + gsp->gs_name, error); break; }; return (error); } -static void -g_sched_orphan(struct g_consumer *cp) +/* + * Lookup the identity of the issuer of the original request. + * In the current implementation we use the curthread of the + * issuer, but different mechanisms may be implemented later + * so we do not make assumptions on the return value which for + * us is just an opaque identifier. + */ +u_long +g_sched_classify(struct bio *bp) { - g_topology_assert(); - g_sched_destroy(cp->geom, 1); + if (bp == NULL) { + printf("g_sched_classify: NULL bio\n"); + return (0); /* as good as anything */ + } + while (bp->bio_parent != NULL) + bp = bp->bio_parent; + return ((u_long)(bp->bio_caller1)); } +/* + * g_sched_done() and g_sched_start() dispatch the geom requests to + * the scheduling algorithm in use. + */ static void g_sched_done(struct bio *bio) { @@ -238,10 +342,29 @@ g_sched_start(struct bio *bp) cbp->bio_to = pp; G_SCHED_LOGREQ(cbp, "Sending request."); gsp = sc->sc_gsched; - gsp->gs_start(sc->sc_data, cbp); + /* + * Call the algorithm's gs_start to queue the request in the + * scheduler. If gs_start fails then pass the request down, + * otherwise call g_sched_dispatch() which tries to push + * one or more requests down. + */ + if (gsp->gs_start(sc->sc_data, cbp)) + g_io_request(cbp, LIST_FIRST(&gp->consumer)); + g_sched_dispatch(gp); g_sched_unlock(gp); } +/* + * The next few functions are the geom glue + */ +static void +g_sched_orphan(struct g_consumer *cp) +{ + + g_topology_assert(); + g_sched_destroy(cp->geom, 1); +} + static int g_sched_access(struct g_provider *pp, int dr, int dw, int de) { @@ -256,23 +379,23 @@ g_sched_access(struct g_provider *pp, in return (error); } +/* + * Create a geom node for the device passed as *pp. + * If successful, add a reference to this gsp. + */ static int g_sched_create(struct gctl_req *req, struct g_class *mp, struct g_provider *pp, struct g_gsched *gsp) { struct g_sched_softc *sc; struct g_geom *gp; - struct g_provider *newpp; - struct g_consumer *cp; + struct g_provider *newpp = NULL; + struct g_consumer *cp = NULL; char name[64]; int error; g_topology_assert(); - gp = NULL; - newpp = NULL; - cp = NULL; - snprintf(name, sizeof(name), "%s%s", pp->name, G_SCHED_SUFFIX); LIST_FOREACH(gp, &mp->geom, geom) { if (strcmp(gp->name, name) == 0) { @@ -284,7 +407,8 @@ g_sched_create(struct gctl_req *req, str gp = g_new_geomf(mp, name); if (gp == NULL) { gctl_error(req, "Cannot create geom %s.", name); - return (ENOMEM); + error = ENOMEM; + goto fail; } sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO); @@ -365,14 +489,14 @@ g_sched_destroy(struct g_geom *gp, boole return (ENXIO); pp = LIST_FIRST(&gp->provider); if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) { - if (force) { - G_SCHED_DEBUG(0, "Device %s is still open, so it " - "can't be definitely removed.", pp->name); - } else { - G_SCHED_DEBUG(1, "Device %s is still open (r%dw%de%d).", - pp->name, pp->acr, pp->acw, pp->ace); + const char *msg = force ? + "but we force removal" : "cannot remove"; + + G_SCHED_DEBUG( (force ? 0 : 1) , + "Device %s is still open (r%dw%de%d), %s.", + pp->name, pp->acr, pp->acw, pp->ace, msg); + if (!force) return (EBUSY); - } } else { G_SCHED_DEBUG(0, "Device %s removed.", gp->name); } @@ -398,23 +522,44 @@ g_sched_destroy_geom(struct gctl_req *re } /* - * The code below patches g_io_request() to call g_new_io_request() first. + * Functions related to the classification of requests. + * In principle, we need to store in the 'struct bio' a reference + * to the issuer of the request and all info that can be useful for + * classification, accounting and so on. + * In the final version this should be done by adding some extra + * field(s) to struct bio, and marking the bio as soon as it is + * posted to the geom queue (but not later, as requests are managed + * by the g_down thread afterwards). + * + * XXX TEMPORARY SOLUTION: + * The 'struct bio' in 7.x and 6.x does not have a field for storing + * the classification info, so we abuse the caller1 field in the + * root element of the bio tree. The marking is done at the beginning + * of g_io_request() and only if we find that the field is NULL. + * + * To avoid rebuilding the kernel, this module will patch the + * initial part of g_io_request() so it jumps to a trampoline code + * that calls the marking function ( g_new_io_request() ) and then + * executes the original body of g_io_request(). + * THIS IS A HACK THAT WILL GO AWAY IN THE FINAL VERSION. + * * We must be careful with the compiler, as it may clobber the * parameters on the stack so they are not preserved for the * continuation of the original function. * Ideally we should write everything in assembler: - mov 0x8(%esp), %edx // load bp + mov 0x8(%esp), %eax // load bp + 2: mov %eax, %edx mov 0x64(%edx),%eax // load bp->bio_parent test %eax,%eax - jne 1f - mov 0x30(%edx),%eax // load bp->bio_caller1 + jne 2b // follow the pointer + mov 0x30(%edx),%eax // load bp->bio_caller1 test %eax,%eax - jne 1f + jne 1f // already set, never mind mov %fs:0x0,%eax // pcpu pointer mov 0x34(%eax),%eax // curthread mov %eax,0x30(%edx) // store in bp->bio_caller1 - 1: // old function + 1: // header of the old function push %ebp mov %esp, %ebp push %edi @@ -423,6 +568,11 @@ g_sched_destroy_geom(struct gctl_req *re */ +#if !defined(__i386__) +#error please add the code in g_new_io_request() to the beginning of \ + /sys/geom/geom_io.c::g_io_request(), and remove this line. +#else +/* i386-only code, trampoline + patching support */ static unsigned char g_io_trampoline[] = { 0xe8, 0x00, 0x00, 0x00, 0x00, /* call foo */ @@ -438,11 +588,12 @@ g_new_io_request(const char *ret, struct { /* - * Scheduler support: if this is the first element in the geom - * chain (we know from bp->bio_parent == NULL), store - * the thread that originated the request in bp->bio_caller1, - * which should be unused in this particular entry (at least - * with the code in 7.1/8.0). + * bio classification: if bio_caller1 is available in the + * root of the 'struct bio' tree, store there the thread id + * of the thread that originated the request. + * More sophisticated classification schemes can be used. + * XXX do not change this code without making sure that + * the compiler does not clobber the arguments. */ struct bio *top = bp; if (top) { @@ -451,19 +602,19 @@ g_new_io_request(const char *ret, struct if (top->bio_caller1 == NULL) top->bio_caller1 = (void *)curthread->td_tid; } - return (bp != top); /* prevent compiler from clobbering bp */ + return (bp != top); /* prevent compiler from clobbering bp */ } -static int g_io_patched = 0; static int g_io_patch(void *f, void *p, void *new_f) { int found = bcmp(f, (const char *)p + 5, 5); + printf("match result %d\n", found); if (found == 0) { int ofs; - printf("patching function\n"); + printf("patching g_io_request\n"); /* link the trampoline to the new function */ ofs = (int)new_f - ((int)p + 5); bcopy(&ofs, (char *)p + 1, 4); @@ -474,34 +625,141 @@ g_io_patch(void *f, void *p, void *new_f *(unsigned char *)f = 0xe9; /* jump opcode */ ofs = (int)p - ((int)f + 5); bcopy(&ofs, (char *)f + 1, 4); - g_io_patched = 1; + me.gs_patched = 1; } return 0; } +#endif /* __i386__ */ static void g_sched_init(struct g_class *mp) { - mtx_init(&g_gsched_mtx, "gsched", NULL, MTX_DEF); - LIST_INIT(&gsched_list); + mtx_init(&me.gs_mtx, "gsched", NULL, MTX_DEF); + LIST_INIT(&me.gs_scheds); printf("%s loading...\n", __FUNCTION__); +#if defined(__i386__) /* patch g_io_request to set the thread */ g_io_patch(g_io_request, g_io_trampoline, g_new_io_request); +#endif } static void g_sched_fini(struct g_class *mp) { - if (g_io_patched) { - /* restore the original g_io_request */ +#if defined(__i386__) + if (me.gs_patched) { + printf("/* restore the original g_io_request */\n"); bcopy(g_io_trampoline + 5, (char *)g_io_request, 5); } +#endif printf("%s unloading...\n", __FUNCTION__); - KASSERT(LIST_EMPTY(&gsched_list), ("still registered schedulers")); - mtx_destroy(&g_gsched_mtx); + KASSERT(LIST_EMPTY(&gs_scheds), ("still registered schedulers")); + mtx_destroy(&me.gs_mtx); +} + +/* + * We accept a "/dev/" prefix on device names, we want the + * provider name that is after that. + */ +static const char *dev_prefix = "/dev/"; + +/* + * read the i-th argument for a request + */ +static const char * +g_sched_argi(struct gctl_req *req, int i) +{ + const char *name; + char param[16]; + int l = strlen(dev_prefix); + + snprintf(param, sizeof(param), "arg%d", i); + name = gctl_get_asciiparam(req, param); + if (name == NULL) { + gctl_error(req, "No 'arg%d' argument", i); + return NULL; + } + if (strncmp(name, dev_prefix, l) == 0) + name += l; + return name; +} + +/* + * fetch nargs and do appropriate checks. + */ +static int +g_sched_get_nargs(struct gctl_req *req) +{ + int *nargs; + + nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); + if (nargs == NULL) { + gctl_error(req, "No 'nargs' argument"); + return 0; + } + if (*nargs <= 0) + gctl_error(req, "Missing device(s)."); + return *nargs; +} + +/* + * Check whether we should add the class on certain volumes when + * this geom is created. Right now this is under control of a kenv + * variable containing the names of all devices that we care about. + */ +static struct g_geom * +g_sched_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) +{ + struct g_gsched *gsp = NULL; /* the sched. algorithm we want */ + const char *s; /* generic string pointer */ + const char *taste_names; /* devices we like */ + int l; + + g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name); + g_topology_assert(); + + G_SCHED_DEBUG(2, "Tasting %s.", pp->name); + + do { + /* do not allow taste on ourselves */ + if (strcmp(pp->geom->class->name, mp->name) == 0) + break; + + taste_names = getenv("geom.sched.taste"); + if (taste_names == NULL) + break; + + l = strlen(pp->name); + for (s = taste_names; *s && + (s = strstr(s, pp->name)); s++) { + /* further checks for an exact match */ + if ( (s == taste_names || s[-1] == ' ') && + (s[l] == '\0' || s[l] == ' ') ) + break; + } + if (s == NULL) + break; + printf("attach device %s match [%s]\n", + pp->name, s); + + /* look up the provider name in the list */ + s = getenv("geom.sched.algo"); + if (s == NULL) + s = "rr"; + + gsp = g_gsched_find(s); /* also get a reference */ + if (gsp == NULL) { + printf("Bad '%s' algorithm\n", s); + break; + } + + g_sched_create(NULL, mp, pp, gsp); + g_gsched_unref(gsp); + } while (0); + return NULL; } static void @@ -510,8 +768,7 @@ g_sched_ctl_create(struct gctl_req *req, struct g_provider *pp; struct g_gsched *gsp; const char *name; - char param[16]; - int i, *nargs; + int i, nargs; g_topology_assert(); @@ -521,43 +778,32 @@ g_sched_ctl_create(struct gctl_req *req, return; } - gsp = g_gsched_find(name); + gsp = g_gsched_find(name); /* also get a reference */ if (gsp == NULL) { gctl_error(req, "Bad '%s' argument", "sched"); return; } - nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); - if (nargs == NULL) { - gctl_error(req, "No '%s' argument", "nargs"); - goto out; - } - - if (*nargs <= 0) { - gctl_error(req, "Missing device(s)."); - goto out; - } + nargs = g_sched_get_nargs(req); - for (i = 0; i < *nargs; i++) { - snprintf(param, sizeof(param), "arg%d", i); - name = gctl_get_asciiparam(req, param); - if (name == NULL) { - gctl_error(req, "No 'arg%d' argument", i); - goto out; - } - if (strncmp(name, "/dev/", strlen("/dev/")) == 0) - name += strlen("/dev/"); + /* + * Run on the arguments, and break on any error. + * We look for a device name, but skip the /dev/ prefix if any. + */ + for (i = 0; i < nargs; i++) { + name = g_sched_argi(req, i); + if (name == NULL) + break; pp = g_provider_by_name(name); if (pp == NULL) { G_SCHED_DEBUG(1, "Provider %s is invalid.", name); gctl_error(req, "Provider %s is invalid.", name); - goto out; + break; } if (g_sched_create(req, mp, pp, gsp) != 0) break; } -out: g_gsched_unref(gsp); } @@ -567,43 +813,27 @@ g_sched_ctl_configure(struct gctl_req *r struct g_sched_softc *sc; struct g_provider *pp; const char *name; - char param[16]; - int i, *nargs; + int i, nargs; g_topology_assert(); - nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); - if (nargs == NULL) { - gctl_error(req, "No '%s' argument", "nargs"); - return; - } - - if (*nargs <= 0) { - gctl_error(req, "Missing device(s)."); - return; - } - - for (i = 0; i < *nargs; i++) { - snprintf(param, sizeof(param), "arg%d", i); + nargs = g_sched_get_nargs(req); - name = gctl_get_asciiparam(req, param); - if (name == NULL) { - gctl_error(req, "No 'arg%d' argument", i); - return; - } - - if (strncmp(name, "/dev/", strlen("/dev/")) == 0) - name += strlen("/dev/"); + for (i = 0; i < nargs; i++) { + name = g_sched_argi(req, i); + if (name == NULL) + break; pp = g_provider_by_name(name); if (pp == NULL || pp->geom->class != mp) { G_SCHED_DEBUG(1, "Provider %s is invalid.", name); gctl_error(req, "Provider %s is invalid.", name); - return; + break; } sc = pp->geom->softc; + /* still unimplemented, so we exit! */ gctl_error(req, "Reconfiguration not supported yet."); - return; + break; } } @@ -622,23 +852,13 @@ g_sched_find_geom(struct g_class *mp, co static void g_sched_ctl_destroy(struct gctl_req *req, struct g_class *mp) { - int *nargs, *force, error, i; + int nargs, *force, error, i; struct g_geom *gp; const char *name; - char param[16]; g_topology_assert(); - nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); - if (nargs == NULL) { - gctl_error(req, "No '%s' argument", "nargs"); - return; - } - - if (*nargs <= 0) { - gctl_error(req, "Missing device(s)."); - return; - } + nargs = g_sched_get_nargs(req); force = gctl_get_paraml(req, "force", sizeof(*force)); if (force == NULL) { @@ -646,29 +866,23 @@ g_sched_ctl_destroy(struct gctl_req *req return; } - for (i = 0; i < *nargs; i++) { - snprintf(param, sizeof(param), "arg%d", i); - name = gctl_get_asciiparam(req, param); - if (name == NULL) { - gctl_error(req, "No 'arg%d' argument", i); - return; - } - - if (strncmp(name, "/dev/", strlen("/dev/")) == 0) - name += strlen("/dev/"); + for (i = 0; i < nargs; i++) { + name = g_sched_argi(req, i); + if (name == NULL) + break; gp = g_sched_find_geom(mp, name); if (gp == NULL) { G_SCHED_DEBUG(1, "Device %s is invalid.", name); gctl_error(req, "Device %s is invalid.", name); - return; + break; } error = g_sched_destroy(gp, *force); if (error != 0) { gctl_error(req, "Cannot destroy device %s (error=%d).", gp->name, error); - return; + break; } } } @@ -713,5 +927,4 @@ g_sched_dumpconf(struct sbuf *sb, const } DECLARE_GEOM_CLASS(g_sched_class, g_sched); -MODULE_VERSION(g_sched, 0); - +MODULE_VERSION(geom_sched, 0); Modified: user/luigi/geom_sched/sys/geom/sched/g_sched.h ============================================================================== --- user/luigi/geom_sched/sys/geom/sched/g_sched.h Wed Jan 14 13:44:59 2009 (r187219) +++ user/luigi/geom_sched/sys/geom/sched/g_sched.h Wed Jan 14 13:46:01 2009 (r187220) @@ -27,15 +27,19 @@ #ifndef _G_SCHED_H_ #define _G_SCHED_H_ +/* + * header for the geom_sched class (userland library and kernel part) + */ + #define G_SCHED_CLASS_NAME "SCHED" #define G_SCHED_VERSION 0 -#define G_SCHED_SUFFIX ".sched." +#define G_SCHED_SUFFIX "-sched-" #ifdef _KERNEL #define G_SCHED_DEBUG(lvl, ...) do { \ - if (g_sched_debug >= (lvl)) { \ + if (me.gs_debug >= (lvl)) { \ printf("GEOM_SCHED"); \ - if (g_sched_debug > 0) \ + if (me.gs_debug > 0) \ printf("[%u]", lvl); \ printf(": "); \ printf(__VA_ARGS__); \ @@ -44,7 +48,7 @@ } while (0) #define G_SCHED_LOGREQ(bp, ...) do { \ - if (g_sched_debug >= 2) { \ + if (me.gs_debug >= 2) { \ printf("GEOM_SCHED[2]: "); \ printf(__VA_ARGS__); \ printf(" "); \ @@ -53,9 +57,6 @@ } \ } while (0) -struct mtx; -struct g_gsched; - struct g_sched_softc { void *sc_data; /* scheduler private data */ struct mtx sc_mtx; Modified: user/luigi/geom_sched/sys/geom/sched/gs_as.c ============================================================================== --- user/luigi/geom_sched/sys/geom/sched/gs_as.c Wed Jan 14 13:44:59 2009 (r187219) +++ user/luigi/geom_sched/sys/geom/sched/gs_as.c Wed Jan 14 13:46:01 2009 (r187220) @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2008 Fabio Checconi + * Copyright (c) 2009 Fabio Checconi * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,6 +24,13 @@ * SUCH DAMAGE. */ +/* + * This code implements the algorithm for Anticipatory disk Scheduler (AS). + * This version does not track process state or behaviour, and is + * just a proof of concept to show how non work-conserving policies + * can be implemented within this framework. + */ + #include #include @@ -37,168 +44,173 @@ #include #include -#include -#include +#include "gs_scheduler.h" /* * Status values for AS. */ -#define G_AS_NOWAIT 0 /* Not wating at all. */ -#define G_AS_WAITREQ 1 /* Waiting a request to complete. */ -#define G_AS_WAITING 2 /* Waiting a new request. */ - -struct gs_as_softc { - struct disk *sc_disk; +enum g_as_status { + G_AS_NOWAIT = 0, /* Not waiting at all. */ + G_AS_WAITREQ, /* Waiting a request to complete. */ + G_AS_WAITING /* Waiting a new request. */ +}; +struct g_as_softc { + struct g_geom *sc_geom; u_long sc_curkey; - int sc_status; + enum g_as_status 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 + * Return 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, by g_sched_dispatch(). */ static struct bio * -gs_as_next(void *data, int force) +g_as_next(void *data) { - struct gs_as_softc *sc; + struct g_as_softc *sc = data; struct bio *bio; + u_long head_key; - sc = data; - - if (sc->sc_status != G_AS_NOWAIT && force == 0) - return (NULL); + if (sc->sc_status != G_AS_NOWAIT) + return NULL; /* - * 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 do anticipation if this request is from a new client, + * or if the current client has not yet exhausted its budget. + * Otherwise, we will serve the next request immediately. */ - sc->sc_curkey = NULL; bio = bioq_takefirst(&sc->sc_bioq); *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***