Index: sbin/geom/class/multipath/geom_multipath.c =================================================================== --- sbin/geom/class/multipath/geom_multipath.c +++ sbin/geom/class/multipath/geom_multipath.c @@ -57,27 +57,31 @@ { { 'A', "active_active", NULL, G_TYPE_BOOL }, { 'R', "active_read", NULL, G_TYPE_BOOL }, + { 'L', "logicalblock", 0, G_TYPE_NUMBER }, G_OPT_SENTINEL }, - "[-vAR] name prov ..." + "[-vAR] [-L logicalblock] name prov ..." }, { "label", G_FLAG_VERBOSE | G_FLAG_LOADKLD, mp_main, { { 'A', "active_active", NULL, G_TYPE_BOOL }, { 'R', "active_read", NULL, G_TYPE_BOOL }, + { 'L', "logicalblock", 0, G_TYPE_NUMBER }, G_OPT_SENTINEL }, - "[-vAR] name prov ..." + "[-vAR] [-L logicalblock] name prov ..." }, - { "configure", G_FLAG_VERBOSE, NULL, + { + "configure", G_FLAG_VERBOSE, NULL, { { 'A', "active_active", NULL, G_TYPE_BOOL }, { 'P', "active_passive", NULL, G_TYPE_BOOL }, { 'R', "active_read", NULL, G_TYPE_BOOL }, + { 'L', "logicalblock", 0, G_TYPE_NUMBER }, G_OPT_SENTINEL }, - "[-vAPR] name" + "[-vAPR] [-L logicalblock] name" }, { "add", G_FLAG_VERBOSE, NULL, G_NULL_OPTS, @@ -148,7 +152,7 @@ { struct g_multipath_metadata md; off_t disksize = 0, msize; - uint8_t *sector, *rsector; + uint8_t *sector, *rsector, val; char *ptr; uuid_t uuid; ssize_t secsize = 0, ssize; @@ -213,10 +217,20 @@ return; } strlcpy(md.md_uuid, ptr, sizeof (md.md_uuid)); + free(ptr); + md.md_active_active = gctl_get_int(req, "active_active"); if (gctl_get_int(req, "active_read")) md.md_active_active = 2; - free(ptr); + val = gctl_get_int(req, "logicalblock"); + if (val == 0 || (val > 8 && val < 32)) + md.md_logicalblock = val; + else { + gctl_error(req, "Invalid logicalblock bitshift value: %u. " + "Must be a value between 9 and 31, or 0 to disable.", + val); + return; + } /* * Allocate a sector to write as metadata. Index: sbin/geom/class/multipath/gmultipath.8 =================================================================== --- sbin/geom/class/multipath/gmultipath.8 +++ sbin/geom/class/multipath/gmultipath.8 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd July 18, 2015 +.Dd August 8, 2015 .Dt GMULTIPATH 8 .Os .Sh NAME @@ -34,16 +34,19 @@ .Nm .Cm create .Op Fl ARv +.Op Fl L Ar logicalblock .Ar name .Ar prov ... .Nm .Cm label .Op Fl ARv +.Op Fl L Ar logicalblock .Ar name .Ar prov ... .Nm .Cm configure .Op Fl APRv +.Op Fl L Ar logicalblock .Ar name .Nm .Cm add @@ -134,6 +137,12 @@ .Fl R option enables Active/Read mode, otherwise Active/Passive mode is used by default. +.Pp +.Fl L +option enables Logical Block mode, the device is divided into stripes of +.No 2** Ns Ar logicalblock +bytes, and all requests to a stripe are served via the same path. +This can improve performance by taking advantage of read-ahead and caching. .It Cm label Create multipath device with .Dq automatic @@ -149,6 +158,12 @@ .Fl R option enables Active/Read mode, otherwise Active/Passive mode is used by default. +.Pp +.Fl L +option enables Logical Block mode, the device is divided into stripes of +.No 2** Ns Ar logicalblock +bytes, and all requests to a stripe are served via the same path. +This can improve performance by taking advantage of read-ahead and caching. .It Cm configure Configure the given multipath device. .Pp @@ -158,6 +173,12 @@ option enables Active/Passive mode, .Fl R option enables Active/Read mode. +.Pp +.Fl L +option enables Logical Block mode, the device is divided into stripes of +.No 2** Ns Ar logicalblock +bytes, and all requests to a stripe are served via the same path. +This can improve performance by taking advantage of read-ahead and caching. .It Cm add Add the given provider as a path to the given multipath device. Should normally be used only for devices created with Index: sys/geom/multipath/g_multipath.h =================================================================== --- sys/geom/multipath/g_multipath.h +++ sys/geom/multipath/g_multipath.h @@ -35,9 +35,19 @@ #define _G_MULTIPATH_H_ #define G_MULTIPATH_CLASS_NAME "MULTIPATH" -#define G_MULTIPATH_VERSION 1 #define G_MULTIPATH_MAGIC "GEOM::MULTIPATH" +/* + * Version history: + * 1 - Initial version number. + * 2 - Added Logical Block mode, selects the same path for reads and writes to + * nearby sectors, improving cache hit ratios on the controller. + */ +#define G_MULTIPATH_VERSION_01 1 +#define G_MULTIPATH_VERSION_02 2 +#define G_MULTIPATH_VERSION G_MULTIPATH_VERSION_02 + + #include #ifdef _KERNEL @@ -53,6 +63,7 @@ int sc_stopping; int sc_ndisks; int sc_active_active; /* Active/Active mode */ + int sc_logicalblock; /* logical block mode */ }; #endif /* _KERNEL */ @@ -64,16 +75,25 @@ uint32_t md_sectorsize; /* sectorsize of provider */ uint64_t md_size; /* absolute size of provider */ uint8_t md_active_active; /* Active/Active mode */ + uint16_t md_logicalblock; /* Logical Block mode */ }; static __inline void +multipath_metadata_encode_v1(const struct g_multipath_metadata *, u_char *); +static __inline void +multipath_metadata_encode_v2(const struct g_multipath_metadata *, u_char *); +static __inline void multipath_metadata_encode(const struct g_multipath_metadata *, u_char *); static __inline void +multipath_metadata_decode_v1(u_char *, struct g_multipath_metadata *); +static __inline void +multipath_metadata_decode_v2(u_char *, struct g_multipath_metadata *); +static __inline int multipath_metadata_decode(u_char *, struct g_multipath_metadata *); static __inline void -multipath_metadata_encode(const struct g_multipath_metadata *md, u_char *data) +multipath_metadata_encode_v1(const struct g_multipath_metadata *md, u_char *data) { bcopy(md->md_magic, data, sizeof(md->md_magic)); data += sizeof(md->md_magic); @@ -91,7 +111,27 @@ } static __inline void -multipath_metadata_decode(u_char *data, struct g_multipath_metadata *md) +multipath_metadata_encode_v2(const struct g_multipath_metadata *md, u_char *data) +{ + bcopy(md->md_magic, data, sizeof(md->md_magic)); + data += sizeof(md->md_magic); + bcopy(md->md_uuid, data, sizeof(md->md_uuid)); + data += sizeof(md->md_uuid); + bcopy(md->md_name, data, sizeof(md->md_name)); + data += sizeof(md->md_name); + le32enc(data, md->md_version); + data += sizeof(md->md_version); + le32enc(data, md->md_sectorsize); + data += sizeof(md->md_sectorsize); + le64enc(data, md->md_size); + data += sizeof(md->md_size); + *data = md->md_active_active; + data += sizeof(md->md_active_active); + le16enc(data, md->md_logicalblock); +} + +static __inline void +multipath_metadata_decode_v1(u_char *data, struct g_multipath_metadata *md) { bcopy(data, md->md_magic, sizeof(md->md_magic)); data += sizeof(md->md_magic); @@ -107,4 +147,80 @@ data += sizeof(md->md_size); md->md_active_active = *data; } + +static __inline void +multipath_metadata_decode_v2(u_char *data, struct g_multipath_metadata *md) +{ + bcopy(data, md->md_magic, sizeof(md->md_magic)); + data += sizeof(md->md_magic); + bcopy(data, md->md_uuid, sizeof(md->md_uuid)); + data += sizeof(md->md_uuid); + bcopy(data, md->md_name, sizeof(md->md_name)); + data += sizeof(md->md_name); + md->md_version = le32dec(data); + data += sizeof(md->md_version); + md->md_sectorsize = le32dec(data); + data += sizeof(md->md_sectorsize); + md->md_size = le64dec(data); + data += sizeof(md->md_size); + md->md_active_active = *data; + data += sizeof(md->md_active_active); + md->md_logicalblock = le16dec(data); +} + +static __inline void +multipath_metadata_encode(const struct g_multipath_metadata *md, u_char *data) +{ + u_char *p; + + p = data; + p += sizeof(md->md_magic); + p += sizeof(md->md_uuid); + p += sizeof(md->md_name); + le32enc(p, md->md_version); + switch (md->md_version) { + case G_MULTIPATH_VERSION_01: + multipath_metadata_encode_v1(md, data); + break; + case G_MULTIPATH_VERSION_02: + multipath_metadata_encode_v2(md, data); + break; + default: +#ifdef _KERNEL + panic("%s: Unsupported version %u.", __func__, + (u_int)md->md_version); +#else + assert(!"Unsupported metadata version."); +#endif + } +} + +static __inline int +multipath_metadata_decode(u_char *data, struct g_multipath_metadata *md) +{ + int error; + u_char *p; + + error = 0; + p = data; + bcopy(data, md->md_magic, sizeof(md->md_magic)); + if (strcmp(md->md_magic, G_MULTIPATH_MAGIC) != 0) + return (EINVAL); + p += sizeof(md->md_magic); + p += sizeof(md->md_uuid); + p += sizeof(md->md_name); + md->md_version = le32dec(p); + switch (md->md_version) { + case G_MULTIPATH_VERSION_01: + multipath_metadata_decode_v1(data, md); + break; + case G_MULTIPATH_VERSION_02: + multipath_metadata_decode_v2(data, md); + break; + default: + error = EOPNOTSUPP; + break; + } + return (error); +} #endif /* _G_MULTIPATH_H_ */ Index: sys/geom/multipath/g_multipath.c =================================================================== --- sys/geom/multipath/g_multipath.c +++ sys/geom/multipath/g_multipath.c @@ -170,12 +170,42 @@ { struct g_multipath_softc *sc; struct g_consumer *best, *cp; + int lbselect, cidx; sc = gp->softc; + /* + * If mode is Active/Passive, use the active component. + * If mode is Active/Read and the request is not a read, use the active + * component. + */ if (sc->sc_active_active == 0 || (sc->sc_active_active == 2 && bp->bio_cmd != BIO_READ)) return (sc->sc_active); + /* + * If Mode is Active/Active of Active/Read and Logical Block mode is + * enabled, choose the component based on which Logical Block is being + * requested + */ best = NULL; + if (sc->sc_logicalblock > 0) { + lbselect = (bp->bio_offset >> sc->sc_logicalblock) + % sc->sc_ndisks; + + cidx = 0; + LIST_FOREACH(cp, &gp->consumer, consumer) { + if (cp->index & MP_BAD) { + cidx++; + continue; + } + if (lbselect == cidx) + return (cp); + cidx++; + } + /* + * If the consumer we wanted to use is not available, + * fallthrough to the usual algorithm + */ + } LIST_FOREACH(cp, &gp->consumer, consumer) { if (cp->index & MP_BAD) continue; @@ -311,6 +341,7 @@ md.md_size = size; md.md_sectorsize = ssize; md.md_active_active = sc->sc_active_active; + md.md_logicalblock = sc->sc_logicalblock; error = g_multipath_write_metadata(cp, &md); if (error != 0) printf("GEOM_MULTIPATH: Can't update metadata on %s " @@ -531,6 +562,7 @@ memcpy(sc->sc_uuid, md->md_uuid, sizeof (sc->sc_uuid)); memcpy(sc->sc_name, md->md_name, sizeof (sc->sc_name)); sc->sc_active_active = md->md_active_active; + sc->sc_logicalblock = md->md_logicalblock; sc->sc_size = md->md_size; gp->softc = sc; gp->start = g_multipath_start; @@ -758,9 +790,9 @@ g_access(cp, -1, 0, 0); if (buf == NULL) return (error); - multipath_metadata_decode(buf, md); + error = multipath_metadata_decode(buf, md); g_free(buf); - return (0); + return (error); } static int @@ -817,7 +849,7 @@ printf("%s is not MULTIPATH\n", pp->name); return (NULL); } - if (md.md_version != G_MULTIPATH_VERSION) { + if (md.md_version > G_MULTIPATH_VERSION) { printf("%s has version %d multipath id- this module is version " " %d: rejecting\n", pp->name, md.md_version, G_MULTIPATH_VERSION); @@ -1120,6 +1152,17 @@ val = gctl_get_paraml(req, "active_read", sizeof(*val)); if (val != NULL && *val != 0) md.md_active_active = 2; + val = gctl_get_paraml(req, "logicalblock", sizeof(*val)); + if (val == NULL) + md.md_logicalblock = 0; + else if (*val == 0 || (*val > 8 && *val < 32)) + md.md_logicalblock = *val; + else { + gctl_error(req, "Invalid logicalblock bitshift value: %u. " + "Must be a value between 9 and 31, or 0 to disable.", + *val); + return; + } gp = g_multipath_create(mp, &md); if (gp == NULL) { gctl_error(req, "GEOM_MULTIPATH: cannot create geom %s/%s\n", @@ -1171,6 +1214,17 @@ val = gctl_get_paraml(req, "active_passive", sizeof(*val)); if (val != NULL && *val != 0) sc->sc_active_active = 0; + val = gctl_get_paraml(req, "logicalblock", sizeof(*val)); + if (val == NULL) + sc->sc_logicalblock = 0; + else if (*val == 0 || (*val > 8 && *val < 32)) + sc->sc_logicalblock = *val; + else { + gctl_error(req, "Invalid logicalblock bitshift value: %u. " + "Must be a value between 9 and 31, or 0 to disable.", + *val); + return; + } if (sc->sc_uuid[0] != 0 && sc->sc_active != NULL) { cp = sc->sc_active; pp = cp->provider; @@ -1181,6 +1235,7 @@ md.md_size = pp->mediasize; md.md_sectorsize = pp->sectorsize; md.md_active_active = sc->sc_active_active; + md.md_logicalblock = sc->sc_logicalblock; error = g_multipath_write_metadata(cp, &md); if (error != 0) gctl_error(req, "Can't update metadata on %s (%d)",