From: Christophe Varoqui Date: Mon, 19 Nov 2007 00:12:23 +0000 (+0100) Subject: [libprio] initial commit X-Git-Tag: 0.4.9~248 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=0c858203d190690ad46beaa7d9a187260533f7d3;p=platform%2Fupstream%2Fmultipath-tools.git [libprio] initial commit Priority callouts are nice, but have drawbacks. 1) multipathd holds a fd per path, but callouts have to open another for themselves. Which may fail. 2) callouts stored on a multipahed filesystem may hang multipathed on paging operation when there are no active path for the hosting device (getprio is in the salvation codepath). The window is small but Netapp fault injection tool manage to triggers the bug reliably. This patch merges to priority methods in multipathd. --- diff --git a/Makefile.inc b/Makefile.inc index 7e2d4e6..1b07ab0 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -24,6 +24,7 @@ exec_prefix = $(prefix) bindir = $(exec_prefix)/sbin libudevdir = ${prefix}/lib/udev checkersdir = $(TOPDIR)/libcheckers +libpriodir = $(TOPDIR)/libprio multipathdir = $(TOPDIR)/libmultipath mandir = $(prefix)/usr/share/man/man8 man5dir = $(prefix)/usr/share/man/man5 @@ -33,6 +34,7 @@ GZIP = /bin/gzip -9 -c CHECKERSLIB = $(checkersdir)/libcheckers MULTIPATHLIB = $(multipathdir)/libmultipath +LIBPRIO = $(libpriodir)/libprio INSTALL_PROGRAM = install -s diff --git a/libmultipath/Makefile b/libmultipath/Makefile index 511f5ad..04761e4 100644 --- a/libmultipath/Makefile +++ b/libmultipath/Makefile @@ -6,7 +6,7 @@ BUILD = glibc include ../Makefile.inc -CFLAGS += -I$(checkersdir) +CFLAGS += -I$(checkersdir) -I$(libpriodir) OBJS = memory.o parser.o vector.o devmapper.o callout.o \ hwtable.o blacklist.o util.o dmparser.o config.o \ diff --git a/libmultipath/config.c b/libmultipath/config.c index a39af8a..619b6e7 100644 --- a/libmultipath/config.c +++ b/libmultipath/config.c @@ -7,6 +7,7 @@ #include #include +#include #include "memory.h" #include "util.h" @@ -135,9 +136,6 @@ free_hwe (struct hwentry * hwe) if (hwe->getuid) FREE(hwe->getuid); - if (hwe->getprio) - FREE(hwe->getprio); - if (hwe->features) FREE(hwe->features); @@ -265,9 +263,6 @@ store_hwe (vector hwtable, struct hwentry * dhwe) if (dhwe->getuid && !(hwe->getuid = set_param_str(dhwe->getuid))) goto out; - if (dhwe->getprio && !(hwe->getprio = set_param_str(dhwe->getprio))) - goto out; - if (dhwe->features && !(hwe->features = set_param_str(dhwe->features))) goto out; @@ -283,6 +278,7 @@ store_hwe (vector hwtable, struct hwentry * dhwe) hwe->no_path_retry = dhwe->no_path_retry; hwe->minio = dhwe->minio; hwe->checker = dhwe->checker; + hwe->prio = dhwe->prio; if (dhwe->bl_product && !(hwe->bl_product = set_param_str(dhwe->bl_product))) goto out; @@ -321,9 +317,6 @@ free_config (struct config * conf) if (conf->getuid) FREE(conf->getuid); - if (conf->getprio) - FREE(conf->getprio); - if (conf->features) FREE(conf->features); @@ -454,6 +447,9 @@ load_config (char * file) !conf->hwhandler) goto out; + if (!conf->prio) + conf->prio = prio_default(); + if (!conf->checker) conf->checker = checker_lookup(DEFAULT_CHECKER); diff --git a/libmultipath/config.h b/libmultipath/config.h index a25b3ad..7f7b2a9 100644 --- a/libmultipath/config.h +++ b/libmultipath/config.h @@ -16,11 +16,11 @@ struct hwentry { char * product; char * revision; char * getuid; - char * getprio; char * features; char * hwhandler; char * selector; char * checker_name; + char * prio_name; int pgpolicy; int pgfailback; @@ -28,6 +28,7 @@ struct hwentry { int no_path_retry; int minio; int pg_timeout; + struct prio * prio; struct checker * checker; char * bl_product; }; @@ -53,6 +54,7 @@ struct config { int pgpolicy_flag; int with_sysfs; int pgpolicy; + struct prio * prio; struct checker * checker; enum devtypes dev_type; int minio; @@ -70,7 +72,6 @@ struct config { char * udev_dir; char * selector; char * getuid; - char * getprio; char * features; char * hwhandler; char * bindings_file; diff --git a/libmultipath/configure.c b/libmultipath/configure.c index 3cd6041..4a1966c 100644 --- a/libmultipath/configure.c +++ b/libmultipath/configure.c @@ -15,6 +15,7 @@ #include #include +#include #include "vector.h" #include "memory.h" diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h index df7d971..97a3dd7 100644 --- a/libmultipath/defaults.h +++ b/libmultipath/defaults.h @@ -4,7 +4,6 @@ #define DEFAULT_FEATURES "0" #define DEFAULT_HWHANDLER "0" #define DEFAULT_MINIO 1000 -#define DEFAULT_GETPRIO NULL #define DEFAULT_PGPOLICY FAILOVER #define DEFAULT_FAILBACK -FAILBACK_MANUAL #define DEFAULT_RR_WEIGHT RR_WEIGHT_NONE diff --git a/libmultipath/dict.c b/libmultipath/dict.c index 4572a7d..6ed5cfd 100644 --- a/libmultipath/dict.c +++ b/libmultipath/dict.c @@ -5,6 +5,7 @@ * Copyright (c) 2005 Kiyoshi Ueda, NEC */ #include +#include #include "vector.h" #include "hwtable.h" @@ -82,19 +83,16 @@ def_getuid_callout_handler(vector strvec) } static int -def_prio_callout_handler(vector strvec) +def_prio_handler(vector strvec) { - conf->getprio = set_value(strvec); + char * buff; - if (!conf->getprio) + buff = set_value(strvec); + if (!buff) return 1; - - if (strlen(conf->getprio) == 4 && - !strcmp(conf->getprio, "none")) { - FREE(conf->getprio); - conf->getprio = NULL; - } - + + conf->prio = prio_lookup(buff); + FREE(buff); return 0; } @@ -571,23 +569,20 @@ hw_handler_handler(vector strvec) } static int -prio_callout_handler(vector strvec) +hw_prio_handler(vector strvec) { struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); + char * buff; if (!hwe) return 1; - hwe->getprio = set_value(strvec); - - if (!hwe->getprio) + buff = set_value(strvec); + if (!buff) return 1; - - if (strlen(hwe->getprio) == 4 && !strcmp(hwe->getprio, "none")) { - FREE(hwe->getprio); - hwe->getprio = NULL; - } - + + hwe->prio = prio_lookup(buff); + FREE(buff); return 0; } @@ -1115,23 +1110,16 @@ snprint_hw_getuid_callout (char * buff, int len, void * data) } static int -snprint_hw_prio_callout (char * buff, int len, void * data) +snprint_hw_prio (char * buff, int len, void * data) { struct hwentry * hwe = (struct hwentry *)data; - if (!conf->getprio && !hwe->getprio) + if (!hwe->prio) return 0; - if (!conf->getprio && hwe->getprio) - return snprintf(buff, len, "%s", hwe->getprio); - if (conf->getprio && !hwe->getprio) - return snprintf(buff, len, "none"); - - /* conf->getprio && hwe->getprio */ - if (strlen(hwe->getprio) == strlen(conf->getprio) && - !strcmp(hwe->getprio, conf->getprio)) + if (hwe->prio == conf->prio) return 0; - - return snprintf(buff, len, "%s", hwe->getprio); + + return snprintf(buff, len, "%s", prio_name(hwe->prio)); } static int @@ -1364,12 +1352,12 @@ snprint_def_getuid_callout (char * buff, int len, void * data) } static int -snprint_def_getprio_callout (char * buff, int len, void * data) +snprint_def_prio (char * buff, int len, void * data) { - if (!conf->getprio) + if (!conf->prio) return 0; - return snprintf(buff, len, "%s", conf->getprio); + return snprintf(buff, len, "%s", prio_name(conf->prio)); } static int @@ -1523,7 +1511,7 @@ init_keywords(void) install_keyword("selector", &def_selector_handler, &snprint_def_selector); install_keyword("path_grouping_policy", &def_pgpolicy_handler, &snprint_def_path_grouping_policy); install_keyword("getuid_callout", &def_getuid_callout_handler, &snprint_def_getuid_callout); - install_keyword("prio_callout", &def_prio_callout_handler, &snprint_def_getprio_callout); + install_keyword("prio", &def_prio_handler, &snprint_def_prio); install_keyword("features", &def_features_handler, &snprint_def_features); install_keyword("path_checker", &def_path_checker_handler, &snprint_def_path_checker); install_keyword("failback", &default_failback_handler, &snprint_def_failback); @@ -1535,7 +1523,6 @@ init_keywords(void) __deprecated install_keyword("default_selector", &def_selector_handler, NULL); __deprecated install_keyword("default_path_grouping_policy", &def_pgpolicy_handler, NULL); __deprecated install_keyword("default_getuid_callout", &def_getuid_callout_handler, NULL); - __deprecated install_keyword("default_prio_callout", &def_prio_callout_handler, NULL); __deprecated install_keyword("default_features", &def_features_handler, NULL); __deprecated install_keyword("default_path_checker", &def_path_checker_handler, NULL); @@ -1579,7 +1566,7 @@ init_keywords(void) install_keyword("path_checker", &hw_path_checker_handler, &snprint_hw_path_checker); install_keyword("features", &hw_features_handler, &snprint_hw_features); install_keyword("hardware_handler", &hw_handler_handler, &snprint_hw_hardware_handler); - install_keyword("prio_callout", &prio_callout_handler, &snprint_hw_prio_callout); + install_keyword("prio", &hw_prio_handler, &snprint_hw_prio); install_keyword("failback", &hw_failback_handler, &snprint_hw_failback); install_keyword("rr_weight", &hw_weight_handler, &snprint_hw_rr_weight); install_keyword("no_path_retry", &hw_no_path_retry_handler, &snprint_hw_no_path_retry); diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c index c842eb0..aaa13fb 100644 --- a/libmultipath/discovery.c +++ b/libmultipath/discovery.c @@ -12,6 +12,7 @@ #include #include +#include #include "vector.h" #include "memory.h" @@ -626,27 +627,22 @@ get_state (struct path * pp) static int get_prio (struct path * pp) { - char buff[CALLOUT_MAX_SIZE]; - char prio[16]; + if (!pp) + return 0; - if (!pp->getprio_selected) { - select_getprio(pp); - pp->getprio_selected = 1; + if (!pp->prio) { + select_prio(pp); + if (!pp->prio) + return 1; } - if (!pp->getprio) { - pp->priority = PRIO_DEFAULT; - } else if (apply_format(pp->getprio, &buff[0], pp)) { - condlog(0, "error formatting prio callout command"); + pp->priority = prio_getprio(pp->prio, pp); + if (pp->priority < 0) { + condlog(0, "%s: %s prio error", pp->dev, prio_name(pp->prio)); pp->priority = PRIO_UNDEF; return 1; - } else if (execute_program(buff, prio, 16)) { - condlog(0, "error calling out %s", buff); - pp->priority = PRIO_UNDEF; - return 1; - } else - pp->priority = atoi(prio); - - condlog(3, "%s: prio = %u", pp->dev, pp->priority); + } + condlog(3, "%s: %s prio = %u", + pp->dev, prio_name(pp->prio), pp->priority); return 0; } diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c index f126f13..80b8dd2 100644 --- a/libmultipath/hwtable.c +++ b/libmultipath/hwtable.c @@ -1,6 +1,7 @@ #include #include +#include #include "vector.h" #include "defaults.h" @@ -27,7 +28,6 @@ static struct hwentry default_hw[] = { .vendor = "APPLE*", .product = "Xserve RAID ", .getuid = DEFAULT_GETUID, - .getprio = NULL, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -37,6 +37,7 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = DEFAULT_CHECKER, + .prio_name = DEFAULT_PRIO, }, /* * StorageWorks controller family @@ -48,7 +49,6 @@ static struct hwentry default_hw[] = { .vendor = "3PARdata", .product = "VV", .getuid = DEFAULT_GETUID, - .getprio = NULL, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -58,12 +58,12 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = DEFAULT_CHECKER, + .prio_name = DEFAULT_PRIO, }, { .vendor = "DEC", .product = "HSG80", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_hp_sw /dev/%n", .features = "1 queue_if_no_path", .hwhandler = "1 hp-sw", .selector = DEFAULT_SELECTOR, @@ -73,12 +73,12 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = HP_SW, + .prio_name = PRIO_HP_SW, }, { .vendor = "HP", .product = "A6189A", .getuid = DEFAULT_GETUID, - .getprio = NULL, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -88,13 +88,13 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = DIRECTIO, + .prio_name = DEFAULT_PRIO, }, { /* MSA 1000/MSA1500 EVA 3000/5000 with old firmware */ .vendor = "(COMPAQ|HP)", .product = "(MSA|HSV)1.0.*", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_hp_sw /dev/%n", .features = "1 queue_if_no_path", .hwhandler = "1 hp-sw", .selector = DEFAULT_SELECTOR, @@ -104,13 +104,13 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = HP_SW, + .prio_name = PRIO_HP_SW, }, { /* MSA 1000/1500 with new firmware */ .vendor = "HP", .product = "MSA VOLUME", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_alua /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -120,12 +120,12 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, + .prio_name = PRIO_ALUA, }, { .vendor = "HP", .product = "MSA2000s*", .getuid = "/sbin/cciss_id %n", - .getprio = NULL, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -135,13 +135,13 @@ static struct hwentry default_hw[] = { .no_path_retry = 12, .minio = DEFAULT_MINIO, .checker_name = TUR, + .prio_name = DEFAULT_PRIO, }, { /* EVA 3000/5000 with new firmware */ .vendor = "(COMPAQ|HP)", .product = "(MSA|HSV)1.1.*", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_alua /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -151,13 +151,13 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, + .prio_name = PRIO_ALUA, }, { /* EVA 4000/6000/8000 */ .vendor = "HP", .product = "HSV2.*", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_alua /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -167,13 +167,13 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, + .prio_name = PRIO_ALUA, }, { /* HP Smart Array */ .vendor = "HP", .product = "LOGICAL VOLUME.*", .getuid = "/lib/udev/scsi_id -n -g -u -s /block/%n", - .getprio = NULL, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -183,6 +183,7 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, + .prio_name = DEFAULT_PRIO, }, /* * DDN controller family @@ -194,7 +195,6 @@ static struct hwentry default_hw[] = { .vendor = "DDN", .product = "SAN DataDirector", .getuid = DEFAULT_GETUID, - .getprio = NULL, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -204,6 +204,7 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = DIRECTIO, + .prio_name = DEFAULT_PRIO, }, /* * EMC / Clariion controller family @@ -215,7 +216,6 @@ static struct hwentry default_hw[] = { .vendor = "EMC", .product = "SYMMETRIX", .getuid = "/lib/udev/scsi_id -g -u -ppre-spc3-83 -s /block/%n", - .getprio = NULL, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -225,13 +225,13 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = DIRECTIO, + .prio_name = DEFAULT_PRIO, }, { .vendor = "DGC", .product = ".*", .bl_product = "LUNZ", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_emc /dev/%n", .features = "1 queue_if_no_path", .hwhandler = "1 emc", .selector = DEFAULT_SELECTOR, @@ -241,6 +241,7 @@ static struct hwentry default_hw[] = { .no_path_retry = (300 / DEFAULT_CHECKINT), .minio = DEFAULT_MINIO, .checker_name = EMC_CLARIION, + .prio_name = PRIO_EMC, }, /* * Fujitsu controller family @@ -252,7 +253,6 @@ static struct hwentry default_hw[] = { .vendor = "FSC", .product = "CentricStor", .getuid = DEFAULT_GETUID, - .getprio = NULL, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -262,6 +262,7 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = DIRECTIO, + .prio_name = DEFAULT_PRIO, }, /* * Hitachi controller family @@ -273,7 +274,6 @@ static struct hwentry default_hw[] = { .vendor = "(HITACHI|HP)", .product = "OPEN-.*", .getuid = DEFAULT_GETUID, - .getprio = NULL, .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -283,12 +283,12 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, + .prio_name = DEFAULT_PRIO, }, { .vendor = "HITACHI", .product = "DF.*", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_hds_modular /dev/%n", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -298,6 +298,7 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, + .prio_name = PRIO_HDS, }, /* * IBM controller family @@ -309,7 +310,6 @@ static struct hwentry default_hw[] = { .vendor = "IBM", .product = "ProFibre 4000R", .getuid = DEFAULT_GETUID, - .getprio = NULL, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -319,13 +319,13 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = DIRECTIO, + .prio_name = DEFAULT_PRIO, }, { /* IBM DS4100 / FAStT100 */ .vendor = "IBM", .product = "1742", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_rdac /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -335,13 +335,13 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, + .prio_name = PRIO_RDAC, }, { /* IBM Netfinity Fibre Channel RAID Controller Unit */ .vendor = "IBM", .product = "3526", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_rdac /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -351,13 +351,13 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, + .prio_name = PRIO_RDAC, }, { /* IBM DS4200 / FAStT200 */ .vendor = "IBM", .product = "3542", .getuid = DEFAULT_GETUID, - .getprio = NULL, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -367,13 +367,13 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, + .prio_name = DEFAULT_PRIO, }, { /* IBM ESS F20 aka Shark */ .vendor = "IBM", .product = "2105(800|F20)", .getuid = DEFAULT_GETUID, - .getprio = NULL, .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -383,13 +383,13 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, + .prio_name = DEFAULT_PRIO, }, { /* IBM DS6000 */ .vendor = "IBM", .product = "1750500", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_alua /dev/%n", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -399,13 +399,13 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, + .prio_name = PRIO_ALUA, }, { /* IBM DS8000 */ .vendor = "IBM", .product = "2107900", .getuid = DEFAULT_GETUID, - .getprio = NULL, .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -415,13 +415,13 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, + .prio_name = DEFAULT_PRIO, }, { /* IBM SAN Volume Controller */ .vendor = "IBM", .product = "2145", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_alua /dev/%n", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -431,6 +431,7 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, + .prio_name = PRIO_ALUA, }, { /* IBM S/390 ECKD DASD */ @@ -438,7 +439,6 @@ static struct hwentry default_hw[] = { .product = "S/390 DASD ECKD", .bl_product = "S/390.*", .getuid = "/sbin/dasdinfo -u -b %n", - .getprio = NULL, .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -448,6 +448,7 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = DIRECTIO, + .prio_name = DEFAULT_PRIO, }, /* * NETAPP controller family @@ -459,7 +460,6 @@ static struct hwentry default_hw[] = { .vendor = "NETAPP", .product = "LUN.*", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_netapp /dev/%n", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -469,6 +469,7 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = 128, .checker_name = DIRECTIO, + .prio_name = PRIO_NETAPP, }, /* * IBM NSeries (NETAPP) controller family @@ -480,7 +481,6 @@ static struct hwentry default_hw[] = { .vendor = "IBM", .product = "Nseries.*", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_netapp /dev/%n", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -490,6 +490,7 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = 128, .checker_name = DIRECTIO, + .prio_name = PRIO_NETAPP, }, /* * Pillar Data controller family @@ -501,7 +502,6 @@ static struct hwentry default_hw[] = { .vendor = "Pillar", .product = "Axiom.*", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_alua %n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -511,6 +511,7 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, + .prio_name = PRIO_ALUA, }, /* * SGI arrays @@ -522,7 +523,6 @@ static struct hwentry default_hw[] = { .vendor = "SGI", .product = "TP9[13]00", .getuid = DEFAULT_GETUID, - .getprio = NULL, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -532,12 +532,12 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = DIRECTIO, + .prio_name = DEFAULT_PRIO, }, { .vendor = "SGI", .product = "TP9[45]00", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_rdac /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -547,12 +547,12 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_QUEUE, .minio = DEFAULT_MINIO, .checker_name = RDAC, + .prio_name = PRIO_RDAC, }, { .vendor = "SGI", .product = "IS.*", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_rdac /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -562,6 +562,7 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_QUEUE, .minio = DEFAULT_MINIO, .checker_name = RDAC, + .prio_name = PRIO_RDAC, }, /* * STK arrays @@ -573,7 +574,6 @@ static struct hwentry default_hw[] = { .vendor = "STK", .product = "OPENstorage D280", .getuid = DEFAULT_GETUID, - .getprio = "/sbin/mpath_prio_rdac /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -583,6 +583,7 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = TUR, + .prio_name = PRIO_RDAC, }, /* * SUN arrays @@ -594,7 +595,6 @@ static struct hwentry default_hw[] = { .vendor = "SUN", .product = "(StorEdge 3510|T4)", .getuid = DEFAULT_GETUID, - .getprio = NULL, .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -604,6 +604,7 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = DIRECTIO, + .prio_name = DEFAULT_PRIO, }, /* * Pivot3 RAIGE @@ -615,7 +616,6 @@ static struct hwentry default_hw[] = { .vendor = "PIVOT3", .product = "RAIGE VOLUME", .getuid = "/sbin/scsi_id -p 0x80 -g -u -s /block/%n", - .getprio = NULL, .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -625,6 +625,7 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = 100, .checker_name = TUR, + .prio_name = DEFAULT_PRIO, }, /* * EOL @@ -633,7 +634,6 @@ static struct hwentry default_hw[] = { .vendor = NULL, .product = NULL, .getuid = NULL, - .getprio = NULL, .features = NULL, .hwhandler = NULL, .selector = NULL, @@ -643,6 +643,7 @@ static struct hwentry default_hw[] = { .no_path_retry = 0, .minio = 0, .checker_name = NULL, + .prio_name = NULL, }, }; diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c index 45a3728..bb2b2b7 100644 --- a/libmultipath/propsel.c +++ b/libmultipath/propsel.c @@ -6,6 +6,7 @@ #include #include +#include #include "memory.h" #include "vector.h" @@ -257,22 +258,23 @@ select_getuid (struct path * pp) } extern int -select_getprio (struct path * pp) +select_prio (struct path * pp) { - if (pp->hwe && pp->hwe->getprio) { - pp->getprio = pp->hwe->getprio; - condlog(3, "%s: getprio = %s (controller setting)", - pp->dev, pp->getprio); + if (pp->hwe && pp->hwe->prio) { + pp->prio = pp->hwe->prio; + condlog(3, "%s: prio = %s (controller setting)", + pp->dev, prio_name(pp->prio)); return 0; } - if (conf->getprio) { - pp->getprio = conf->getprio; - condlog(3, "%s: getprio = %s (config file default)", - pp->dev, pp->getprio); + if (conf->prio) { + pp->prio = conf->prio; + condlog(3, "%s: prio = %s (config file default)", + pp->dev, prio_name(pp->prio)); return 0; } - pp->getprio = DEFAULT_GETPRIO; - condlog(3, "%s: getprio = NULL (internal default)", pp->dev); + pp->prio = prio_default(); + condlog(3, "%s: prio = %s (internal default)", + pp->dev, prio_name(pp->prio)); return 0; } diff --git a/libmultipath/propsel.h b/libmultipath/propsel.h index afd1f88..62802f8 100644 --- a/libmultipath/propsel.h +++ b/libmultipath/propsel.h @@ -7,7 +7,7 @@ int select_features (struct multipath * mp); int select_hwhandler (struct multipath * mp); int select_checker(struct path *pp); int select_getuid (struct path * pp); -int select_getprio (struct path * pp); +int select_prio (struct path * pp); int select_no_path_retry(struct multipath *mp); int select_pg_timeout(struct multipath *mp); int select_minio(struct multipath *mp); diff --git a/libmultipath/structs.c b/libmultipath/structs.c index d36eaef..c9b446b 100644 --- a/libmultipath/structs.c +++ b/libmultipath/structs.c @@ -7,6 +7,7 @@ #include #include +#include #include "memory.h" #include "vector.h" diff --git a/libmultipath/structs.h b/libmultipath/structs.h index f821f87..8e9ac86 100644 --- a/libmultipath/structs.h +++ b/libmultipath/structs.h @@ -21,9 +21,6 @@ #define NO_PATH_RETRY_FAIL -1 #define NO_PATH_RETRY_QUEUE -2 -#define PRIO_UNDEF -1 -#define PRIO_DEFAULT 1 - enum free_path_switch { KEEP_PATHS, FREE_PATHS @@ -121,8 +118,7 @@ struct path { int priority; int pgindex; char * getuid; - char * getprio; - int getprio_selected; + struct prio * prio; struct checker checker; struct multipath * mpp; int fd; diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c index 1cc6028..807d7ab 100644 --- a/libmultipath/structs_vec.c +++ b/libmultipath/structs_vec.c @@ -3,6 +3,7 @@ #include #include +#include #include "vector.h" #include "defaults.h" @@ -81,8 +82,7 @@ orphan_path (struct path * pp) pp->mpp = NULL; pp->dmstate = PSTATE_UNDEF; pp->getuid = NULL; - pp->getprio = NULL; - pp->getprio_selected = 0; + pp->prio = NULL; checker_put(&pp->checker); if (pp->fd >= 0) close(pp->fd); diff --git a/libprio/Makefile b/libprio/Makefile new file mode 100644 index 0000000..177a67b --- /dev/null +++ b/libprio/Makefile @@ -0,0 +1,29 @@ +# Makefile +# +# Copyright (C) 2007 Christophe Varoqui, +# +BUILD = glibc + +include ../Makefile.inc + +OBJS = libprio.o random.o const.o hp_sw.o emc.o rdac.o alua.o alua_rtpg.o netapp.o hds.o + +CFLAGS += -I$(multipathdir) + +all: $(BUILD) + +prepare: + @file *-$(BUILD).a >/dev/null 2>&1 || rm -f core *.o *.gz + +klibc: prepare $(OBJS) + ar rs libprio-klibc.a *.o + +glibc: prepare $(OBJS) + ar rs libprio-glibc.a *.o + +install: + +uninstall: + +clean: + rm -f core *.a *.o *.gz diff --git a/libprio/alua.c b/libprio/alua.c new file mode 100644 index 0000000..64f41f7 --- /dev/null +++ b/libprio/alua.c @@ -0,0 +1,90 @@ +/* + * (C) Copyright IBM Corp. 2004, 2005 All Rights Reserved. + * + * main.c + * + * Tool to make use of a SCSI-feature called Asymmetric Logical Unit Access. + * It determines the ALUA state of a device and prints a priority value to + * stdout. + * + * Author(s): Jan Kunigk + * S. Bader + * + * This file is released under the GPL. + */ +#include + +#include + +#include "libprio.h" + +#define ALUA_PRIO_NOT_SUPPORTED 1 +#define ALUA_PRIO_RTPG_FAILED 2 +#define ALUA_PRIO_GETAAS_FAILED 3 + +int +get_alua_info(int fd) +{ + char * aas_string[] = { + [AAS_OPTIMIZED] = "active/optimized", + [AAS_NON_OPTIMIZED] = "active/non-optimized", + [AAS_STANDBY] = "standby", + [AAS_UNAVAILABLE] = "unavailable", + [AAS_TRANSITIONING] = "transitioning between states", + }; + int rc; + int tpg; + + rc = get_target_port_group_support(fd); + if (rc < 0) + return rc; + + if (rc == TPGS_NONE) + return -ALUA_PRIO_NOT_SUPPORTED; + + tpg = get_target_port_group(fd); + if (tpg < 0) + return -ALUA_PRIO_RTPG_FAILED; + + condlog(3, "reported target port group is %i", tpg); + rc = get_asymmetric_access_state(fd, tpg); + if (rc < 0) + return -ALUA_PRIO_GETAAS_FAILED; + + condlog(3, "aas = [%s]", + (aas_string[rc]) ? aas_string[rc] : "invalid/reserved"); + return rc; +} + +int prio_alua(struct path * pp) +{ + int rc = get_alua_info(pp->fd); + if (rc >= 0) { + switch(rc) { + case AAS_OPTIMIZED: + rc = 50; + break; + case AAS_NON_OPTIMIZED: + rc = 10; + break; + case AAS_STANDBY: + rc = 1; + break; + default: + rc = 0; + } + } else { + switch(-rc) { + case ALUA_PRIO_NOT_SUPPORTED: + condlog(0, "%s: alua not supported", pp->dev); + break; + case ALUA_PRIO_RTPG_FAILED: + condlog(0, "%s: couldn't get target port group", pp->dev); + break; + case ALUA_PRIO_GETAAS_FAILED: + condlog(0, "%s: couln't get asymmetric access state", pp->dev); + break; + } + } + return rc; +} diff --git a/libprio/alua.h b/libprio/alua.h new file mode 100644 index 0000000..78a3d15 --- /dev/null +++ b/libprio/alua.h @@ -0,0 +1,9 @@ +#ifndef _ALUA_H +#define _ALUA_H + +#include "alua_rtpg.h" + +#define PRIO_ALUA "alua" +int prio_alua(struct path * pp); + +#endif diff --git a/libprio/alua_rtpg.c b/libprio/alua_rtpg.c new file mode 100644 index 0000000..c5528c5 --- /dev/null +++ b/libprio/alua_rtpg.c @@ -0,0 +1,301 @@ +/* + * (C) Copyright IBM Corp. 2004, 2005 All Rights Reserved. + * + * rtpg.c + * + * Tool to make use of a SCSI-feature called Asymmetric Logical Unit Access. + * It determines the ALUA state of a device and prints a priority value to + * stdout. + * + * Author(s): Jan Kunigk + * S. Bader + * + * This file is released under the GPL. + */ +#include +#include +#include +#include +#include + +#define __user +#include + +#include "alua_rtpg.h" + +#define SENSE_BUFF_LEN 32 +#define DEF_TIMEOUT 300000 + +/* + * Macro used to print debug messaged. + */ +#if DEBUG > 0 +#define PRINT_DEBUG(f, a...) \ + fprintf(stderr, "DEBUG: " f, ##a) +#else +#define PRINT_DEBUG(f, a...) +#endif + +/* + * Optionally print the commands sent and the data received a hex dump. + */ +#if DEBUG > 0 +#if DEBUG_DUMPHEX > 0 +#define PRINT_HEX(p, l) print_hex(p, l) +void +print_hex(unsigned char *p, unsigned long len) +{ + int i; + + for(i = 0; i < len; i++) { + if (i % 16 == 0) + printf("%04x: ", i); + printf("%02x%s", p[i], (((i + 1) % 16) == 0) ? "\n" : " "); + } + printf("\n"); +} +#else +#define PRINT_HEX(p, l) +#endif +#else +#define PRINT_HEX(p, l) +#endif + +/* + * Returns 0 if the SCSI command either was successful or if the an error was + * recovered, otherwise 1. (definitions taken from sg_err.h) + */ +#define SCSI_CHECK_CONDITION 0x2 +#define SCSI_COMMAND_TERMINATED 0x22 +#define SG_ERR_DRIVER_SENSE 0x08 +#define RECOVERED_ERROR 0x01 + +static int +scsi_error(struct sg_io_hdr *hdr) +{ + /* Treat SG_ERR here to get rid of sg_err.[ch] */ + hdr->status &= 0x7e; + + if ( + (hdr->status == 0) && + (hdr->host_status == 0) && + (hdr->driver_status == 0) + ) { + return 0; + } + + if ( + (hdr->status == SCSI_CHECK_CONDITION) || + (hdr->status == SCSI_COMMAND_TERMINATED) || + ((hdr->driver_status & 0xf) == SG_ERR_DRIVER_SENSE) + ) { + if (hdr->sbp && (hdr->sb_len_wr > 2)) { + int sense_key; + unsigned char * sense_buffer = hdr->sbp; + + if (sense_buffer[0] & 0x2) + sense_key = sense_buffer[1] & 0xf; + else + sense_key = sense_buffer[2] & 0xf; + + if (sense_key == RECOVERED_ERROR) + return 0; + } + } + + return 1; +} + +/* + * Helper function to setup and run a SCSI inquiry command. + */ +int +do_inquiry(int fd, int evpd, unsigned int codepage, void *resp, int resplen) +{ + struct inquiry_command cmd; + struct sg_io_hdr hdr; + unsigned char sense[SENSE_BUFF_LEN]; + + memset(&cmd, 0, sizeof(cmd)); + cmd.op = OPERATION_CODE_INQUIRY; + if (evpd) { + inquiry_command_set_evpd(&cmd); + cmd.page = codepage; + } + set_uint16(cmd.length, resplen); + PRINT_HEX((unsigned char *) &cmd, sizeof(cmd)); + + memset(&hdr, 0, sizeof(hdr)); + hdr.interface_id = 'S'; + hdr.cmdp = (unsigned char *) &cmd; + hdr.cmd_len = sizeof(cmd); + hdr.dxfer_direction = SG_DXFER_FROM_DEV; + hdr.dxferp = resp; + hdr.dxfer_len = resplen; + hdr.sbp = sense; + hdr.mx_sb_len = sizeof(sense); + hdr.timeout = DEF_TIMEOUT; + + if (ioctl(fd, SG_IO, &hdr) < 0) { + PRINT_DEBUG("do_inquiry: IOCTL failed!\n"); + return -RTPG_INQUIRY_FAILED; + } + + if (scsi_error(&hdr)) { + PRINT_DEBUG("do_inquiry: SCSI error!\n"); + return -RTPG_INQUIRY_FAILED; + } + PRINT_HEX((unsigned char *) resp, resplen); + + return 0; +} + +/* + * This function returns the support for target port groups by evaluating the + * data returned by the standard inquiry command. + */ +int +get_target_port_group_support(int fd) +{ + struct inquiry_data inq; + int rc; + + rc = do_inquiry(fd, 0, 0x00, &inq, sizeof(inq)); + if (!rc) { + rc = inquiry_data_get_tpgs(&inq); + } + + return rc; +} + +int +get_target_port_group(int fd) +{ + unsigned char buf[128]; + struct vpd83_data * vpd83; + struct vpd83_dscr * dscr; + int rc; + + rc = do_inquiry(fd, 1, 0x83, buf, sizeof(buf)); + if (!rc) { + vpd83 = (struct vpd83_data *) buf; + + rc = -RTPG_NO_TPG_IDENTIFIER; + FOR_EACH_VPD83_DSCR(vpd83, dscr) { + if ((((char *) dscr) - ((char *) vpd83)) > sizeof(buf)) + break; + + if (vpd83_dscr_istype(dscr, IDTYPE_TARGET_PORT_GROUP)) { + struct vpd83_tpg_dscr * p; + + if (rc != -RTPG_NO_TPG_IDENTIFIER) { + PRINT_DEBUG("get_target_port_group: " + "more than one TPG identifier " + "found!\n"); + continue; + } + + p = (struct vpd83_tpg_dscr *) dscr->data; + rc = get_uint16(p->tpg); + } + } + if (rc == -RTPG_NO_TPG_IDENTIFIER) { + PRINT_DEBUG("get_target_port_group: " + "no TPG identifier found!\n"); + } + } + + return rc; +} + +int +do_rtpg(int fd, void* resp, long resplen) +{ + struct rtpg_command cmd; + struct sg_io_hdr hdr; + unsigned char sense[SENSE_BUFF_LEN]; + + memset(&cmd, 0, sizeof(cmd)); + cmd.op = OPERATION_CODE_RTPG; + rtpg_command_set_service_action(&cmd); + set_uint32(cmd.length, resplen); + PRINT_HEX((unsigned char *) &cmd, sizeof(cmd)); + + memset(&hdr, 0, sizeof(hdr)); + hdr.interface_id = 'S'; + hdr.cmdp = (unsigned char *) &cmd; + hdr.cmd_len = sizeof(cmd); + hdr.dxfer_direction = SG_DXFER_FROM_DEV; + hdr.dxferp = resp; + hdr.dxfer_len = resplen; + hdr.mx_sb_len = sizeof(sense); + hdr.sbp = sense; + hdr.timeout = DEF_TIMEOUT; + + if (ioctl(fd, SG_IO, &hdr) < 0) + return -RTPG_RTPG_FAILED; + + if (scsi_error(&hdr)) { + PRINT_DEBUG("do_rtpg: SCSI error!\n"); + return -RTPG_RTPG_FAILED; + } + PRINT_HEX(resp, resplen); + + return 0; +} + +int +get_asymmetric_access_state(int fd, unsigned int tpg) +{ + unsigned char *buf; + struct rtpg_data * tpgd; + struct rtpg_tpg_dscr * dscr; + int rc; + int buflen; + uint32_t scsi_buflen; + + buflen = 128; /* Initial value from old code */ + buf = (unsigned char *)malloc(buflen); + if (!buf) { + PRINT_DEBUG ("malloc failed: could not allocate" + "%u bytes\n", buflen); + return -RTPG_RTPG_FAILED; + } + rc = do_rtpg(fd, buf, buflen); + if (rc < 0) + return rc; + scsi_buflen = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; + if (buflen < (scsi_buflen + 4)) { + free(buf); + buf = (unsigned char *)malloc(scsi_buflen); + if (!buf) { + PRINT_DEBUG ("malloc failed: could not allocate" + "%u bytes\n", scsi_buflen); + return -RTPG_RTPG_FAILED; + } + buflen = scsi_buflen; + rc = do_rtpg(fd, buf, buflen); + if (rc < 0) + goto out; + } + + + tpgd = (struct rtpg_data *) buf; + rc = -RTPG_TPG_NOT_FOUND; + RTPG_FOR_EACH_PORT_GROUP(tpgd, dscr) { + if (get_uint16(dscr->tpg) == tpg) { + if (rc != -RTPG_TPG_NOT_FOUND) { + PRINT_DEBUG("get_asymmetric_access_state: " + "more than one entry with same port " + "group.\n"); + } else { + PRINT_DEBUG("pref=%i\n", dscr->pref); + rc = rtpg_tpg_dscr_get_aas(dscr); + } + } + } +out: + free(buf); + return rc; +} + diff --git a/libprio/alua_rtpg.h b/libprio/alua_rtpg.h new file mode 100644 index 0000000..c43e0a9 --- /dev/null +++ b/libprio/alua_rtpg.h @@ -0,0 +1,30 @@ +/* + * (C) Copyright IBM Corp. 2004, 2005 All Rights Reserved. + * + * rtpg.h + * + * Tool to make use of a SCSI-feature called Asymmetric Logical Unit Access. + * It determines the ALUA state of a device and prints a priority value to + * stdout. + * + * Author(s): Jan Kunigk + * S. Bader + * + * This file is released under the GPL. + */ +#ifndef __RTPG_H__ +#define __RTPG_H__ +#include "alua_spc3.h" + +#define RTPG_SUCCESS 0 +#define RTPG_INQUIRY_FAILED 1 +#define RTPG_NO_TPG_IDENTIFIER 2 +#define RTPG_RTPG_FAILED 3 +#define RTPG_TPG_NOT_FOUND 4 + +int get_target_port_group_support(int fd); +int get_target_port_group(int fd); +int get_asymmetric_access_state(int fd, unsigned int tpg); + +#endif /* __RTPG_H__ */ + diff --git a/libprio/alua_spc3.h b/libprio/alua_spc3.h new file mode 100644 index 0000000..bddbbdd --- /dev/null +++ b/libprio/alua_spc3.h @@ -0,0 +1,322 @@ +/* + * (C) Copyright IBM Corp. 2004, 2005 All Rights Reserved. + * + * spc3.h + * + * Tool to make use of a SCSI-feature called Asymmetric Logical Unit Access. + * It determines the ALUA state of a device and prints a priority value to + * stdout. + * + * Author(s): Jan Kunigk + * S. Bader + * + * This file is released under the GPL. + */ +#ifndef __SPC3_H__ +#define __SPC3_H__ +/*============================================================================= + * Some helper functions for getting and setting 16 and 32 bit values. + *============================================================================= + */ +static inline unsigned short +get_uint16(unsigned char *p) +{ + return (p[0] << 8) + p[1]; +} + +static inline void +set_uint16(unsigned char *p, unsigned short v) +{ + p[0] = (v >> 8) & 0xff; + p[1] = v & 0xff; +} + +static inline unsigned int +get_uint32(unsigned char *p) +{ + return (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3]; +} + +static inline void +set_uint32(unsigned char *p, unsigned int v) +{ + p[0] = (v >> 24) & 0xff; + p[1] = (v >> 16) & 0xff; + p[2] = (v >> 8) & 0xff; + p[3] = v & 0xff; +} + +/*============================================================================= + * Definitions to support the standard inquiry command as defined in SPC-3. + * If the evpd (enable vital product data) bit is set the data that will be + * returned is selected by the page field. This field must be 0 if the evpd + * bit is not set. + *============================================================================= + */ +#define OPERATION_CODE_INQUIRY 0x12 + +struct inquiry_command { + unsigned char op; + unsigned char b1; /* xxxxxx.. = reserved */ + /* ......x. = obsolete */ + /* .......x = evpd */ + unsigned char page; + unsigned char length[2]; + unsigned char control; +} __attribute__((packed)); + +static inline void +inquiry_command_set_evpd(struct inquiry_command *ic) +{ + ic->b1 |= 1; +} + +/*----------------------------------------------------------------------------- + * Data returned by the standard inquiry command. + *----------------------------------------------------------------------------- + * + * Peripheral qualifier codes. + */ +#define PQ_CONNECTED 0x0 +#define PQ_DISCONNECTED 0x1 +#define PQ_UNSUPPORTED 0x3 + +/* Defined peripheral device types. */ +#define PDT_DIRECT_ACCESS 0x00 +#define PDT_SEQUENTIAL_ACCESS 0x01 +#define PDT_PRINTER 0x02 +#define PDT_PROCESSOR 0x03 +#define PDT_WRITE_ONCE 0x04 +#define PDT_CD_DVD 0x05 +#define PDT_SCANNER 0x06 +#define PDT_OPTICAL_MEMORY 0x07 +#define PDT_MEDIUM_CHANGER 0x08 +#define PDT_COMMUNICATIONS 0x09 +#define PDT_STORAGE_ARRAY_CONTROLLER 0x0c +#define PDT_ENCLOSURE_SERVICES 0x0d +#define PDT_SIMPLIFIED_DIRECT_ACCESS 0x0e +#define PDT_OPTICAL_CARD_READER_WRITER 0x0f +#define PDT_BRIDGE_CONTROLLER 0x10 +#define PDT_OBJECT_BASED 0x11 +#define PDT_AUTOMATION_INTERFACE 0x12 +#define PDT_LUN 0x1e +#define PDT_UNKNOWN 0x1f + +/* Defined version codes. */ +#define VERSION_NONE 0x00 +#define VERSION_SPC 0x03 +#define VERSION_SPC2 0x04 +#define VERSION_SPC3 0x05 + +/* Defined TPGS field values. */ +#define TPGS_NONE 0x0 +#define TPGS_IMPLICIT 0x1 +#define TPGS_EXPLICIT 0x2 +#define TPGS_BOTH 0x3 + +struct inquiry_data { + unsigned char b0; /* xxx..... = peripheral_qualifier */ + /* ...xxxxx = peripheral_device_type */ + unsigned char b1; /* x....... = removable medium */ + /* .xxxxxxx = reserverd */ + unsigned char version; + unsigned char b3; /* xx...... = obsolete */ + /* ..x..... = normal aca supported */ + /* ...x.... = hirarchichal lun supp. */ + /* ....xxxx = response format */ + /* 2 is spc-3 format */ + unsigned char length; + unsigned char b5; /* x....... = storage controller */ + /* component supported */ + /* .x...... = access controls coord. */ + /* ..xx.... = target port group supp.*/ + /* ....x... = third party copy supp. */ + /* .....xx. = reserved */ + /* .......x = protection info supp. */ + unsigned char b6; /* x....... = bque */ + /* .x...... = enclosure services sup.*/ + /* ..x..... = vs1 */ + /* ...x.... = multiport support */ + /* ....x... = medium changer */ + /* .....xx. = obsolete */ + /* .......x = add16 */ + unsigned char b7; /* xx...... = obsolete */ + /* ..x..... = wbus16 */ + /* ...x.... = sync */ + /* ....x... = linked commands supp. */ + /* .....x.. = obsolete */ + /* ......x. = command queue support */ + /* .......x = vs2 */ + unsigned char vendor_identification[8]; + unsigned char product_identification[16]; + unsigned char product_revision[4]; + unsigned char vendor_specific[20]; + unsigned char b56; /* xxxx.... = reserved */ + /* ....xx.. = clocking */ + /* ......x. = qas */ + /* .......x = ius */ + unsigned char reserved4; + unsigned char version_descriptor[8][2]; + unsigned char reserved5[22]; + unsigned char vendor_parameters[0]; +} __attribute__((packed)); + +static inline int +inquiry_data_get_tpgs(struct inquiry_data *id) +{ + return (id->b5 >> 4) & 3; +} + +/*----------------------------------------------------------------------------- + * Inquiry data returned when requesting vital product data page 0x83. + *----------------------------------------------------------------------------- + */ +#define CODESET_BINARY 0x1 +#define CODESET_ACSII 0x2 +#define CODESET_UTF8 0x3 + +#define ASSOCIATION_UNIT 0x0 +#define ASSOCIATION_PORT 0x1 +#define ASSOCIATION_DEVICE 0x2 + +#define IDTYPE_VENDOR_SPECIFIC 0x0 +#define IDTYPE_T10_VENDOR_ID 0x1 +#define IDTYPE_EUI64 0x2 +#define IDTYPE_NAA 0x3 +#define IDTYPE_RELATIVE_TPG_ID 0x4 +#define IDTYPE_TARGET_PORT_GROUP 0x5 +#define IDTYPE_LUN_GROUP 0x6 +#define IDTYPE_MD5_LUN_ID 0x7 +#define IDTYPE_SCSI_NAME_STRING 0x8 + +struct vpd83_tpg_dscr { + unsigned char reserved1[2]; + unsigned char tpg[2]; +} __attribute__((packed)); + +struct vpd83_dscr { + unsigned char b0; /* xxxx.... = protocol id */ + /* ....xxxx = codeset */ + unsigned char b1; /* x....... = protocol id valid */ + /* .x...... = reserved */ + /* ..xx.... = association */ + /* ....xxxx = id type */ + unsigned char reserved2; + unsigned char length; /* size-4 */ + unsigned char data[0]; +} __attribute__((packed)); + +static inline int +vpd83_dscr_istype(struct vpd83_dscr *d, unsigned char type) +{ + return ((d->b1 & 7) == type); +} + +struct vpd83_data { + unsigned char b0; /* xxx..... = peripheral_qualifier */ + /* ...xxxxx = peripheral_device_type */ + unsigned char page_code; /* 0x83 */ + unsigned char length[2]; /* size-4 */ + struct vpd83_dscr data[0]; +} __attribute__((packed)); + +/*----------------------------------------------------------------------------- + * This macro should be used to walk through all identification descriptors + * defined in the code page 0x83. + * The argument p is a pointer to the code page 0x83 data and d is used to + * point to the current descriptor. + *----------------------------------------------------------------------------- + */ +#define FOR_EACH_VPD83_DSCR(p, d) \ + for( \ + d = p->data; \ + (((char *) d) - ((char *) p)) < \ + get_uint16(p->length); \ + d = (struct vpd83_dscr *) \ + ((char *) d + d->length + 4) \ + ) + +/*============================================================================= + * The following stuctures and macros are used to call the report target port + * groups command defined in SPC-3. + * This command is used to get information about the target port groups (which + * states are supported, which ports belong to this group, and so on) and the + * current state of each target port group. + *============================================================================= + */ +#define OPERATION_CODE_RTPG 0xa3 +#define SERVICE_ACTION_RTPG 0x0a + +struct rtpg_command { + unsigned char op; /* 0xa3 */ + unsigned char b1; /* xxx..... = reserved */ + /* ...xxxxx = service action (0x0a) */ + unsigned char reserved2[4]; + unsigned char length[4]; + unsigned char reserved3; + unsigned char control; +} __attribute__((packed)); + +static inline void +rtpg_command_set_service_action(struct rtpg_command *cmd) +{ + cmd->b1 = (cmd->b1 & 0xe0) | SERVICE_ACTION_RTPG; +} + +struct rtpg_tp_dscr { + unsigned char obsolete1[2]; + /* The Relative Target Port Identifier of a target port. */ + unsigned char rtpi[2]; +} __attribute__((packed)); + +#define AAS_OPTIMIZED 0x0 +#define AAS_NON_OPTIMIZED 0x1 +#define AAS_STANDBY 0x2 +#define AAS_UNAVAILABLE 0x3 +#define AAS_TRANSITIONING 0xf + +#define TPG_STATUS_NONE 0x0 +#define TPG_STATUS_SET 0x1 +#define TPG_STATUS_IMPLICIT_CHANGE 0x2 + +struct rtpg_tpg_dscr { + unsigned char b0; /* x....... = pref(ered) port */ + /* .xxx.... = reserved */ + /* ....xxxx = asymetric access state */ + unsigned char b1; /* xxxx.... = reserved */ + /* ....x... = unavailable support */ + /* .....x.. = standby support */ + /* ......x. = non-optimized support */ + /* .......x = optimized support */ + unsigned char tpg[2]; + unsigned char reserved3; + unsigned char status; + unsigned char vendor_unique; + unsigned char port_count; + struct rtpg_tp_dscr data[0]; +} __attribute__((packed)); + +static inline int +rtpg_tpg_dscr_get_aas(struct rtpg_tpg_dscr *d) +{ + return (d->b0 & 0x0f); +} + +struct rtpg_data { + unsigned char length[4]; /* size-4 */ + struct rtpg_tpg_dscr data[0]; +} __attribute__((packed)); + +#define RTPG_FOR_EACH_PORT_GROUP(p, g) \ + for( \ + g = &(p->data[0]); \ + (((char *) g) - ((char *) p)) < get_uint32(p->length); \ + g = (struct rtpg_tpg_dscr *) ( \ + ((char *) g) + \ + sizeof(struct rtpg_tpg_dscr) + \ + g->port_count * sizeof(struct rtpg_tp_dscr) \ + ) \ + ) + +#endif /* __SPC3_H__ */ + diff --git a/libprio/const.c b/libprio/const.c new file mode 100644 index 0000000..5e2ef23 --- /dev/null +++ b/libprio/const.c @@ -0,0 +1,8 @@ +#include + +#include "libprio.h" + +int prio_const(struct path * pp) +{ + return 1; +} diff --git a/libprio/const.h b/libprio/const.h new file mode 100644 index 0000000..220db54 --- /dev/null +++ b/libprio/const.h @@ -0,0 +1,7 @@ +#ifndef _CONST_H +#define _CONST_H + +#define PRIO_CONST "const" +int prio_const(struct path * pp); + +#endif diff --git a/libprio/emc.c b/libprio/emc.c new file mode 100644 index 0000000..047269d --- /dev/null +++ b/libprio/emc.c @@ -0,0 +1,80 @@ +#include +#include +#include + +#include +#include + +#include "libprio.h" + +#define INQUIRY_CMD 0x12 +#define INQUIRY_CMDLEN 6 + +#define pp_emc_log(prio, msg) condlog(prio, "%s: emc prio: " msg, dev) + +int emc_clariion_prio(const char *dev, int fd) +{ + unsigned char sense_buffer[256]; + unsigned char sb[128]; + unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC0, 0, + sizeof(sb), 0}; + struct sg_io_hdr io_hdr; + int ret = 0; + + memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = sizeof (inqCmdBlk); + io_hdr.mx_sb_len = sizeof (sb); + io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + io_hdr.dxfer_len = sizeof (sense_buffer); + io_hdr.dxferp = sense_buffer; + io_hdr.cmdp = inqCmdBlk; + io_hdr.sbp = sb; + io_hdr.timeout = 60000; + io_hdr.pack_id = 0; + if (ioctl(fd, SG_IO, &io_hdr) < 0) { + pp_emc_log(0, "sending query command failed"); + goto out; + } + if (io_hdr.info & SG_INFO_OK_MASK) { + pp_emc_log(0, "query command indicates error"); + goto out; + } + + if (/* Verify the code page - right page & revision */ + sense_buffer[1] != 0xc0 || sense_buffer[9] != 0x00) { + pp_emc_log(0, "path unit report page in unknown format"); + goto out; + } + + if ( /* Effective initiator type */ + sense_buffer[27] != 0x03 + /* + * Failover mode should be set to 1 (PNR failover mode) + * or 4 (ALUA failover mode). + */ + || (((sense_buffer[28] & 0x07) != 0x04) && + ((sense_buffer[28] & 0x07) != 0x06)) + /* Arraycommpath should be set to 1 */ + || (sense_buffer[30] & 0x04) != 0x04) { + pp_emc_log(0, "path not correctly configured for failover"); + } + + if ( /* LUN operations should indicate normal operations */ + sense_buffer[48] != 0x00) { + pp_emc_log(0, "path not available for normal operations"); + } + + /* Is the default owner equal to this path? */ + /* Note this will switch to the default priority group, even if + * it is not the currently active one. */ + ret = (sense_buffer[5] == sense_buffer[8]) ? 1 : 0; + +out: + return(ret); +} + +int prio_emc(struct path * pp) +{ + return emc_clariion_prio(pp->dev, pp->fd); +} diff --git a/libprio/emc.h b/libprio/emc.h new file mode 100644 index 0000000..0ab0bc5 --- /dev/null +++ b/libprio/emc.h @@ -0,0 +1,7 @@ +#ifndef _EMC_H +#define _EMC_H + +#define PRIO_EMC "emc" +int prio_emc(struct path * pp); + +#endif diff --git a/libprio/hds.c b/libprio/hds.c new file mode 100644 index 0000000..d79e514 --- /dev/null +++ b/libprio/hds.c @@ -0,0 +1,171 @@ +/* + * (C) Copyright HDS GmbH 2006. All Rights Reserved. + * + * pp_hds_modular.c + * Version 2.00 + * + * Prioritizer for Device Mapper Multipath and HDS Storage + * + * Hitachis Modular Storage contains two controllers for redundancy. The + * Storage internal LUN (LDEV) will normally allocated via two pathes to the + * server (one path per controller). For performance reasons should the server + * access to a LDEV only via one controller. The other path to the other + * controller is stand-by. It is also possible to allocate more as one path + * for a LDEV per controller. Here is active/active access allowed. The other + * pathes via the other controller are stand-by. + * + * This prioritizer checks with inquiry command the represented LDEV and + * Controller number and gives back a priority followed by this scheme: + * + * CONTROLLER ODD and LDEV ODD: PRIORITY 1 + * CONTROLLER ODD and LDEV EVEN: PRIORITY 0 + * CONTROLLER EVEN and LDEV ODD: PRIORITY 0 + * CONTROLLER EVEN and LDEV EVEN: PRIORITY 1 + * + * In the storage you can define for each LDEV a owner controller. If the + * server makes IOs via the other controller the storage will switch the + * ownership automatically. In this case you can see in the storage that the + * current controller is different from the default controller, but this is + * absolutely no problem. + * + * With this prioritizer it is possible to establish a static load balancing. + * Half of the LUNs are accessed via one HBA/storage controller and the other + * half via the other HBA/storage controller. + * + * In cluster environmemnts (RAC) it also guarantees that all cluster nodes have + * access to the LDEVs via the same controller. + * + * You can run the prioritizer manually in verbose mode: + * # pp_hds_modular -v 8:224 + * VENDOR: HITACHI + * PRODUCT: DF600F-CM + * SERIAL: 0x0105 + * LDEV: 0x00C6 + * CTRL: 1 + * PORT: B + * CTRL ODD, LDEV EVEN, PRIO 0 + * + * To compile this source please execute # cc pp_hds_modular.c -o /sbin/mpath_prio_hds_modular + * + * Changes 2006-07-16: + * - Changed to forward declaration of functions + * - The switch-statement was changed to a logical expression + * - unlinking of the devpath now also occurs at the end of + * hds_modular_prio to avoid old /tmp/.pp_balance.%u.%u.devnode + * entries in /tmp-Directory + * - The for-statements for passing variables where changed to + * snprintf-commands in verbose mode + * Changes 2006-08-10: + * - Back to the old switch statements because the regular expression does + * not work under RHEL4 U3 i386 + * Changes 2007-06-27: + * - switched from major:minor argument to device node argument + * + * This file is released under the GPL. + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "libprio.h" + +#define INQ_REPLY_LEN 255 +#define INQ_CMD_CODE 0x12 +#define INQ_CMD_LEN 6 + +#define pp_hds_log(prio, fmt, args...) \ + condlog(prio, "%s: hds prio: " fmt, dev, ##args) + +int hds_modular_prio (const char *dev, int fd) +{ + int k; + char vendor[8]; + char product[32]; + char serial[32]; + char ldev[32]; + char ctrl[32]; + char port[32]; + unsigned char inqCmdBlk[INQ_CMD_LEN] = { INQ_CMD_CODE, 0, 0, 0, INQ_REPLY_LEN, 0 }; + unsigned char inqBuff[INQ_REPLY_LEN]; + unsigned char *inqBuffp = inqBuff; + unsigned char sense_buffer[32]; + sg_io_hdr_t io_hdr; + + if ((ioctl (fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) { + pp_hds_log(0, "can't use SG ioctl interface"); + return -1; + } + + memset (&io_hdr, 0, sizeof (sg_io_hdr_t)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = sizeof (inqCmdBlk); + io_hdr.mx_sb_len = sizeof (sense_buffer); + io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + io_hdr.dxfer_len = INQ_REPLY_LEN; + io_hdr.dxferp = inqBuff; + io_hdr.cmdp = inqCmdBlk; + io_hdr.sbp = sense_buffer; + io_hdr.timeout = 2000; /* TimeOut = 2 seconds */ + + if (ioctl (fd, SG_IO, &io_hdr) < 0) { + pp_hds_log(0, "SG_IO error"); + return -1; + } + if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK) { + pp_hds_log(0, "SCSI error"); + return -1; + } + + snprintf (vendor, 9, "%.8s", inqBuffp + 8); + snprintf (product, 17, "%.16s", inqBuffp + 16); + snprintf (serial, 5, "%.4s", inqBuffp + 40); + snprintf (ldev, 5, "%.4s", inqBuffp + 44); + snprintf (ctrl, 2, "%.1s", inqBuffp + 49); + snprintf (port, 2, "%.1s", inqBuffp + 50); + + pp_hds_log(4, "VENDOR: %s", vendor); + pp_hds_log(4, "PRODUCT: %s", product); + pp_hds_log(4, "SERIAL: 0x%s", serial); + pp_hds_log(4, "LDEV: 0x%s", ldev); + pp_hds_log(4, "CTRL: %s", ctrl); + pp_hds_log(4, "PORT: %s", port); + + switch (ctrl[0]) { + case '0': case '2': case '4': case '6': case '8': + switch (ldev[3]) { + case '0': case '2': case '4': case '6': case '8': case 'A': case 'C': case 'E': + pp_hds_log(4, "CTRL EVEN, LDEV EVEN, PRIO 1"); + return 1; + break; + case '1': case '3': case '5': case '7': case '9': case 'B': case 'D': case 'F': + pp_hds_log(4, "CTRL EVEN, LDEV ODD, PRIO 0"); + return 0; + break; + } + case '1': case '3': case '5': case '7': case '9': + switch (ldev[3]) { + case '0': case '2': case '4': case '6': case '8': case 'A': case 'C': case 'E': + pp_hds_log(4, "CTRL ODD, LDEV EVEN, PRIO 0"); + return 0; + break; + case '1': case '3': case '5': case '7': case '9': case 'B': case 'D': case 'F': + pp_hds_log(4, "CTRL ODD, LDEV ODD, PRIO 1"); + return 1; + break; + } + } + return -1; +} + +int prio_hds(struct path * pp) +{ + return hds_modular_prio(pp->dev, pp->fd); +} diff --git a/libprio/hds.h b/libprio/hds.h new file mode 100644 index 0000000..084d77c --- /dev/null +++ b/libprio/hds.h @@ -0,0 +1,7 @@ +#ifndef _HDS_H +#define _HDS_H + +#define PRIO_HDS "hds" +int prio_hds(struct path * pp); + +#endif diff --git a/libprio/hp_sw.c b/libprio/hp_sw.c new file mode 100644 index 0000000..f46a1cf --- /dev/null +++ b/libprio/hp_sw.c @@ -0,0 +1,101 @@ +/* + * Path priority checker for HP active/standby controller + * + * Check the path state and sort them into groups. + * There is actually a preferred path in the controller; + * we should ask HP on how to retrieve that information. + */ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "libprio.h" + +#define TUR_CMD_LEN 6 +#define SCSI_CHECK_CONDITION 0x2 +#define SCSI_COMMAND_TERMINATED 0x22 +#define SG_ERR_DRIVER_SENSE 0x08 +#define RECOVERED_ERROR 0x01 +#define NOT_READY 0x02 +#define UNIT_ATTENTION 0x06 + +#define HP_PATH_ACTIVE 0x04 +#define HP_PATH_STANDBY 0x02 +#define HP_PATH_FAILED 0x00 + +#define pp_hp_sw_log(prio, fmt, args...) \ + condlog(prio, "%s: hp_sw prio: " fmt, dev, ##args) + +int hp_sw_prio(const char *dev, int fd) +{ + unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 }; + unsigned char sb[128]; + struct sg_io_hdr io_hdr; + int ret = HP_PATH_FAILED; + + memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = sizeof (turCmdBlk); + io_hdr.mx_sb_len = sizeof (sb); + io_hdr.dxfer_direction = SG_DXFER_NONE; + io_hdr.cmdp = turCmdBlk; + io_hdr.sbp = sb; + io_hdr.timeout = 60000; + io_hdr.pack_id = 0; + retry: + if (ioctl(fd, SG_IO, &io_hdr) < 0) { + pp_hp_sw_log(0, "sending tur command failed"); + goto out; + } + io_hdr.status &= 0x7e; + if ((0 == io_hdr.status) && (0 == io_hdr.host_status) && + (0 == io_hdr.driver_status)) { + /* Command completed normally, path is active */ + ret = HP_PATH_ACTIVE; + } + + if ((SCSI_CHECK_CONDITION == io_hdr.status) || + (SCSI_COMMAND_TERMINATED == io_hdr.status) || + (SG_ERR_DRIVER_SENSE == (0xf & io_hdr.driver_status))) { + if (io_hdr.sbp && (io_hdr.sb_len_wr > 2)) { + int sense_key, asc, asq; + unsigned char * sense_buffer = io_hdr.sbp; + if (sense_buffer[0] & 0x2) { + sense_key = sense_buffer[1] & 0xf; + asc = sense_buffer[2]; + asq = sense_buffer[3]; + } else { + sense_key = sense_buffer[2] & 0xf; + asc = sense_buffer[12]; + asq = sense_buffer[13]; + } + if(RECOVERED_ERROR == sense_key) + ret = HP_PATH_ACTIVE; + if(NOT_READY == sense_key) { + if (asc == 0x04 && asq == 0x02) { + /* This is a standby path */ + ret = HP_PATH_STANDBY; + } + } + if(UNIT_ATTENTION == sense_key) { + if (asc == 0x29) { + /* Retry for device reset */ + goto retry; + } + } + } + } +out: + return(ret); +} + +int prio_hp_sw(struct path * pp) +{ + return hp_sw_prio(pp->dev, pp->fd); +} diff --git a/libprio/hp_sw.h b/libprio/hp_sw.h new file mode 100644 index 0000000..2fea486 --- /dev/null +++ b/libprio/hp_sw.h @@ -0,0 +1,7 @@ +#ifndef _HP_SW_H +#define _HP_SW_H + +#define PRIO_HP_SW "hp_sw" +int prio_hp_sw(struct path * pp); + +#endif diff --git a/libprio/libprio.c b/libprio/libprio.c new file mode 100644 index 0000000..0ff33dc --- /dev/null +++ b/libprio/libprio.c @@ -0,0 +1,70 @@ +#include +#include + +#include "libprio.h" + +static struct prio prioritizers[] = { + { + .name = PRIO_CONST, + .getprio = prio_const + }, + { + .name = PRIO_RANDOM, + .getprio = prio_random + }, + { + .name = PRIO_ALUA, + .getprio = prio_alua + }, + { + .name = PRIO_EMC, + .getprio = prio_emc + }, + { + .name = PRIO_RDAC, + .getprio = prio_rdac + }, + { + .name = PRIO_NETAPP, + .getprio = prio_netapp + }, + { + .name = PRIO_HDS, + .getprio = prio_hds + }, + { + .name = PRIO_HP_SW, + .getprio = prio_hp_sw + }, + { + .name = "", + .getprio = NULL + }, +}; + +struct prio * prio_lookup (char * name) +{ + struct prio * p = &prioritizers[0]; + + while (p->getprio) { + if (!strncmp(name, p->name, PRIO_NAME_LEN)) + return p; + p++; + } + return prio_default(); +} + +int prio_getprio (struct prio * p, struct path * pp) +{ + return p->getprio(pp); +} + +char * prio_name (struct prio * p) +{ + return p->name; +} + +struct prio * prio_default (void) +{ + return prio_lookup(DEFAULT_PRIO); +} diff --git a/libprio/libprio.h b/libprio/libprio.h new file mode 100644 index 0000000..978e14e --- /dev/null +++ b/libprio/libprio.h @@ -0,0 +1,43 @@ +#ifndef _LIBPRIO_H +#define _LIBPRIO_H + +/* + * knowing about path struct gives flexibility to prioritizers + */ +#include "../libcheckers/checkers.h" +#include "../libmultipath/vector.h" +#include "../libmultipath/structs.h" + +#include "const.h" +#include "random.h" +#include "hp_sw.h" +#include "alua.h" +#include "emc.h" +#include "netapp.h" +#include "hds.h" +#include "rdac.h" + +#define DEFAULT_PRIO PRIO_CONST + +/* + * Value used to mark the fact prio was not defined + */ +#define PRIO_UNDEF -1 + +/* + * strings lengths + */ +#define PRIO_NAME_LEN 16 +#define PRIO_DEV_LEN 256 + +struct prio { + char name[PRIO_NAME_LEN]; + int (*getprio)(struct path *); +}; + +struct prio * prio_lookup (char *); +int prio_getprio (struct prio *, struct path *); +char * prio_name (struct prio *); +struct prio * prio_default (void); + +#endif /* _LIBPRIO_H */ diff --git a/libprio/netapp.c b/libprio/netapp.c new file mode 100644 index 0000000..f1da1f2 --- /dev/null +++ b/libprio/netapp.c @@ -0,0 +1,244 @@ +/* + * Copyright 2005 Network Appliance, Inc., All Rights Reserved + * Author: David Wysochanski available at davidw@netapp.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License v2 for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "libprio.h" + +#define INQUIRY_CMD 0x12 +#define INQUIRY_CMDLEN 6 +#define DEFAULT_PRIOVAL 10 +#define RESULTS_MAX 256 +#define SG_TIMEOUT 30000 + +#define pp_netapp_log(prio, fmt, args...) \ + condlog(prio, "%s: netapp prio: " fmt, dev, ##args) + +static void dump_cdb(unsigned char *cdb, int size) +{ + int i; + char buf[10*5+1]; + char * p = &buf[0]; + + condlog(0, "- SCSI CDB: "); + for (i=0; imasked_status, + io_hdr->host_status, io_hdr->driver_status); + if (io_hdr->sb_len_wr > 0) { + condlog(0, "- SCSI sense data: "); + for (i=0; isb_len_wr; i++) { + p += snprintf(p, 128*(io_hdr->sb_len_wr-i), "0x%02x ", + io_hdr->sbp[i]); + } + condlog(0, "%s", buf); + } +} + +/* + * Returns: + * -1: error, errno set + * 0: success + */ +static int send_gva(const char *dev, int fd, unsigned char pg, + unsigned char *results, int *results_size) +{ + unsigned char sb[128]; + unsigned char cdb[10] = {0xc0, 0, 0x1, 0xa, 0x98, 0xa, + pg, sizeof(sb), 0, 0}; + struct sg_io_hdr io_hdr; + int ret = -1; + + memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = sizeof (cdb); + io_hdr.mx_sb_len = sizeof (sb); + io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + io_hdr.dxfer_len = *results_size; + io_hdr.dxferp = results; + io_hdr.cmdp = cdb; + io_hdr.sbp = sb; + io_hdr.timeout = SG_TIMEOUT; + io_hdr.pack_id = 0; + if (ioctl(fd, SG_IO, &io_hdr) < 0) { + pp_netapp_log(0, "SG_IO ioctl failed, errno=%d", errno); + dump_cdb(cdb, sizeof(cdb)); + goto out; + } + if (io_hdr.info & SG_INFO_OK_MASK) { + pp_netapp_log(0, "SCSI error"); + dump_cdb(cdb, sizeof(cdb)); + process_sg_error(&io_hdr); + goto out; + } + + if (results[4] != 0x0a || results[5] != 0x98 || + results[6] != 0x0a ||results[7] != 0x01) { + dump_cdb(cdb, sizeof(cdb)); + pp_netapp_log(0, "GVA return wrong format "); + pp_netapp_log(0, "results[4-7] = 0x%02x 0x%02x 0x%02x 0x%02x", + results[4], results[5], results[6], results[7]); + goto out; + } + ret = 0; + out: + return(ret); +} + +/* + * Retuns: + * -1: Unable to obtain proxy info + * 0: Device _not_ proxy path + * 1: Device _is_ proxy path + */ +static int get_proxy(const char *dev, int fd) +{ + unsigned char results[256]; + unsigned char sb[128]; + unsigned char cdb[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xc1, 0, + sizeof(sb), 0}; + struct sg_io_hdr io_hdr; + int ret = -1; + + memset(&results, 0, sizeof (results)); + memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = sizeof (cdb); + io_hdr.mx_sb_len = sizeof (sb); + io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + io_hdr.dxfer_len = sizeof (results); + io_hdr.dxferp = results; + io_hdr.cmdp = cdb; + io_hdr.sbp = sb; + io_hdr.timeout = SG_TIMEOUT; + io_hdr.pack_id = 0; + if (ioctl(fd, SG_IO, &io_hdr) < 0) { + pp_netapp_log(0, "ioctl sending inquiry command failed, " + "errno=%d", errno); + dump_cdb(cdb, sizeof(cdb)); + goto out; + } + if (io_hdr.info & SG_INFO_OK_MASK) { + pp_netapp_log(0, "SCSI error"); + dump_cdb(cdb, sizeof(cdb)); + process_sg_error(&io_hdr); + goto out; + } + + if (results[1] != 0xc1 || results[8] != 0x0a || + results[9] != 0x98 || results[10] != 0x0a || + results[11] != 0x0 || results[12] != 0xc1 || + results[13] != 0x0) { + pp_netapp_log(0,"proxy info page in unknown format - "); + pp_netapp_log(0,"results[8-13]=0x%02x 0x%02x 0x%02x 0x%02x " + "0x%02x 0x%02x", + results[8], results[9], results[10], + results[11], results[12], results[13]); + dump_cdb(cdb, sizeof(cdb)); + goto out; + } + ret = (results[19] & 0x02) >> 1; + + out: + return(ret); +} + +/* + * Returns priority of device based on device info. + * + * 4: FCP non-proxy, FCP proxy unknown, or unable to determine protocol + * 3: iSCSI HBA + * 2: iSCSI software + * 1: FCP proxy + */ +static int netapp_prio(const char *dev, int fd) +{ + unsigned char results[RESULTS_MAX]; + int results_size=RESULTS_MAX; + int rc; + int is_proxy; + int is_iscsi_software; + int is_iscsi_hardware; + int tot_len; + + is_iscsi_software = is_iscsi_hardware = is_proxy = 0; + + memset(&results, 0, sizeof (results)); + rc = send_gva(dev, fd, 0x41, results, &results_size); + if (rc == 0) { + tot_len = results[0] << 24 | results[1] << 16 | + results[2] << 8 | results[3]; + if (tot_len <= 8) { + goto try_fcp_proxy; + } + if (results[8] != 0x41) { + pp_netapp_log(0, "GVA page 0x41 error - " + "results[8] = 0x%x", results[8]); + goto try_fcp_proxy; + } + if ((strncmp((char *)&results[12], "ism_sw", 6) == 0) || + (strncmp((char *)&results[12], "iswt", 4) == 0)) { + is_iscsi_software = 1; + goto prio_select; + } + else if (strncmp((char *)&results[12], "ism_sn", 6) == 0) { + is_iscsi_hardware = 1; + goto prio_select; + } + } + + try_fcp_proxy: + rc = get_proxy(dev, fd); + if (rc >= 0) { + is_proxy = rc; + } + + prio_select: + if (is_iscsi_hardware) { + return 3; + } else if (is_iscsi_software) { + return 2; + } else { + if (is_proxy) { + return 1; + } else { + /* Either non-proxy, or couldn't get proxy info */ + return 4; + } + } +} + +int prio_netapp(struct path * pp) +{ + return netapp_prio(pp->dev, pp->fd); +} diff --git a/libprio/netapp.h b/libprio/netapp.h new file mode 100644 index 0000000..ec38821 --- /dev/null +++ b/libprio/netapp.h @@ -0,0 +1,7 @@ +#ifndef _NETAPP_H +#define _NETAPP_H + +#define PRIO_NETAPP "netapp" +int prio_netapp(struct path * pp); + +#endif diff --git a/libprio/random.c b/libprio/random.c new file mode 100644 index 0000000..0ad9e26 --- /dev/null +++ b/libprio/random.c @@ -0,0 +1,16 @@ +#include +#include +#include +#include + +#include "libprio.h" + +int prio_random(struct path * pp) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + srand((unsigned int)tv.tv_usec); + printf("%i\n", 1+(int) (10.0*rand()/(RAND_MAX+1.0))); + return 0; +} diff --git a/libprio/random.h b/libprio/random.h new file mode 100644 index 0000000..b9dae69 --- /dev/null +++ b/libprio/random.h @@ -0,0 +1,7 @@ +#ifndef _RANDOM_H +#define _RANDOM_H + +#define PRIO_RANDOM "random" +int prio_random(struct path * pp); + +#endif diff --git a/libprio/rdac.c b/libprio/rdac.c new file mode 100644 index 0000000..d7c19a9 --- /dev/null +++ b/libprio/rdac.c @@ -0,0 +1,91 @@ +#include +#include +#include + +#include +#include + +#include "libprio.h" + +#define INQUIRY_CMD 0x12 +#define INQUIRY_CMDLEN 6 + +#define pp_rdac_log(prio, msg) condlog(prio, "%s: rdac prio: " msg, dev) + +int rdac_prio(const char *dev, int fd) +{ + unsigned char sense_buffer[256]; + unsigned char sb[128]; + unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC9, 0, + sizeof(sb), 0}; + struct sg_io_hdr io_hdr; + int ret = 0; + + memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = sizeof (inqCmdBlk); + io_hdr.mx_sb_len = sizeof (sb); + io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + io_hdr.dxfer_len = sizeof (sense_buffer); + io_hdr.dxferp = sense_buffer; + io_hdr.cmdp = inqCmdBlk; + io_hdr.sbp = sb; + io_hdr.timeout = 60000; + io_hdr.pack_id = 0; + if (ioctl(fd, SG_IO, &io_hdr) < 0) { + pp_rdac_log(0, "sending inquiry command failed"); + goto out; + } + if (io_hdr.info & SG_INFO_OK_MASK) { + pp_rdac_log(0, "inquiry command indicates error"); + goto out; + } + + if (/* Verify the code page - right page & page identifier */ + sense_buffer[1] != 0xc9 || + sense_buffer[3] != 0x2c || + sense_buffer[4] != 'v' || + sense_buffer[5] != 'a' || + sense_buffer[6] != 'c' ) { + pp_rdac_log(0, "volume access control page in unknown format"); + goto out; + } + + if ( /* Current Volume Path Bit */ + ( sense_buffer[8] & 0x01) == 0x01 ) { + /* + * This volume was owned by the controller receiving + * the inquiry command. + */ + ret |= 0x01; + } + + /* Volume Preferred Path Priority */ + switch ( sense_buffer[9] & 0x0F ) { + case 0x01: + /* + * Access to this volume is most preferred through + * this path and other paths with this value. + */ + ret |= 0x02; + break; + case 0x02: + /* + * Access to this volume through this path is to be used + * as a secondary path. Typically this path would be used + * for fail-over situations. + */ + /* Fallthrough */ + default: + /* Reserved values */ + break; + } + +out: + return(ret); +} + +int prio_rdac(struct path * pp) +{ + return rdac_prio(pp->dev, pp->fd); +} diff --git a/libprio/rdac.h b/libprio/rdac.h new file mode 100644 index 0000000..a5b7f8e --- /dev/null +++ b/libprio/rdac.h @@ -0,0 +1,7 @@ +#ifndef _RDAC_H +#define _RDAC_H + +#define PRIO_RDAC "rdac" +int prio_rdac(struct path * pp); + +#endif diff --git a/multipath.conf.annotated b/multipath.conf.annotated index e6cfe9a..f617876 100644 --- a/multipath.conf.annotated +++ b/multipath.conf.annotated @@ -52,14 +52,14 @@ # getuid_callout "/lib/udev/scsi_id -g -u -s /block/%n" # # # -# # name : prio_callout +# # name : prio # # scope : multipath -# # desc : the default program and args to callout to obtain a path +# # desc : the default function to call to obtain a path # # priority value. The ALUA bits in SPC-3 provide an -# # exploitable prio value for example. "none" is a valid value +# # exploitable prio value for example. # # default : (null) # # -# #prio_callout "/bin/true" +# #prio "alua" # # # # # name : path_checker @@ -296,15 +296,14 @@ # getuid_callout "/lib/udev/scsi_id -g -u -s /block/%n" # # # -# # name : prio_callout +# # name : prio # # scope : multipath -# # desc : the program and args to callout to obtain a path +# # desc : the function to call to obtain a path # # weight. Weights are summed for each path group to # # determine the next PG to use case of failure. -# # "none" is a valid value. # # default : no callout, all paths equals # # -# prio_callout "/sbin/mpath_prio_balance_units %d" +# prio "hp_sw" # # # # # name : path_checker diff --git a/multipath.conf.synthetic b/multipath.conf.synthetic index 633d625..0c05b4c 100644 --- a/multipath.conf.synthetic +++ b/multipath.conf.synthetic @@ -8,7 +8,7 @@ # selector "round-robin 0" # path_grouping_policy multibus # getuid_callout "/lib/udev/scsi_id -g -u -s /block/%n" -# prio_callout /bin/true +# prio const # path_checker directio # rr_min_io 100 # rr_weight priorities diff --git a/multipath/Makefile b/multipath/Makefile index 4923b2f..e7e557e 100644 --- a/multipath/Makefile +++ b/multipath/Makefile @@ -5,9 +5,9 @@ BUILD = glibc include ../Makefile.inc -OBJS = main.o $(MULTIPATHLIB)-$(BUILD).a $(CHECKERSLIB)-$(BUILD).a +OBJS = main.o $(MULTIPATHLIB)-$(BUILD).a $(CHECKERSLIB)-$(BUILD).a $(LIBPRIO)-$(BUILD).a -CFLAGS += -I$(multipathdir) -I$(checkersdir) +CFLAGS += -I$(multipathdir) -I$(checkersdir) -I$(libpriodir) LDFLAGS += -laio ifeq ($(strip $(BUILD)),klibc) @@ -32,6 +32,9 @@ glibc: prepare $(OBJS) klibc: prepare $(OBJS) $(CC) -static -o $(EXEC) $(CRT0) $(OBJS) $(KLIBC) $(LIBGCC) +$(LIBPRIO)-$(BUILD).a: + make -C $(libpriodir) BUILD=$(BUILD) $(BUILD) + $(CHECKERSLIB)-$(BUILD).a: make -C $(checkersdir) BUILD=$(BUILD) $(BUILD) diff --git a/multipath/main.c b/multipath/main.c index 815c307..922e0f9 100644 --- a/multipath/main.c +++ b/multipath/main.c @@ -27,6 +27,7 @@ #include #include +#include #include #include #include diff --git a/multipathd/Makefile b/multipathd/Makefile index b430b94..b82f159 100644 --- a/multipathd/Makefile +++ b/multipathd/Makefile @@ -21,6 +21,7 @@ LDFLAGS = -lpthread -ldevmapper -lreadline -lncurses -laio # OBJS = main.o pidfile.o uxlsnr.o uxclnt.o cli.o cli_handlers.o \ $(MULTIPATHLIB)-glibc.a $(CHECKERSLIB)-glibc.a \ + $(LIBPRIO)-glibc.a # @@ -37,6 +38,9 @@ $(EXEC): clean $(OBJS) $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) $(GZIP) $(EXEC).8 > $(EXEC).8.gz +$(LIBPRIO)-glibc.a: + $(MAKE) -C $(libpriodir) BUILD=glibc glibc + $(CHECKERSLIB)-glibc.a: $(MAKE) -C $(checkersdir) BUILD=glibc glibc