Search
j0ke.net Open Build Service
>
Projects
>
GFS
>
multipath-tools
> multipath-tools-sp2-update
Sign Up
|
Log In
Username
Password
Cancel
Overview
Repositories
Revisions
Requests
Users
Advanced
Attributes
Meta
File multipath-tools-sp2-update of Package multipath-tools
diff --git a/Makefile.inc b/Makefile.inc index d948e63..a86cd73 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -17,21 +17,26 @@ ifeq ($(strip $(BUILD)),klibc) CC = klcc klibcdir = /usr/lib/klibc libdm = $(klibcdir)/lib/libdevmapper.a - libsysfs = $(klibcdir)/lib/libsysfs.a endif prefix = 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 rcdir = $(prefix)/etc/init.d GZIP = /bin/gzip -9 -c CHECKERSLIB = $(checkersdir)/libcheckers MULTIPATHLIB = $(multipathdir)/libmultipath +LIBPRIO = $(libpriodir)/libprio + +INSTALL_PROGRAM = install -s OPTFLAGS = -pipe -g -Wall -Wunused -Wstrict-prototypes CFLAGS = $(OPTFLAGS) diff --git a/libcheckers/checkers.c b/libcheckers/checkers.c index fd27d3f..4084d0f 100644 --- a/libcheckers/checkers.c +++ b/libcheckers/checkers.c @@ -72,7 +72,6 @@ static struct checker checkers[] = { .init = readsector0_init, .free = readsector0_free }, - { .fd = 0, .sync = 1, @@ -115,7 +114,7 @@ void checker_disable (struct checker * c) struct checker * checker_lookup (char * name) { struct checker * c = &checkers[0]; - + while (c->check) { if (!strncmp(name, c->name, CHECKER_NAME_LEN)) return c; diff --git a/libcheckers/rdac.c b/libcheckers/rdac.c index 3e4f886..f430488 100644 --- a/libcheckers/rdac.c +++ b/libcheckers/rdac.c @@ -24,9 +24,9 @@ #define SG_ERR_DRIVER_SENSE 0x08 #define RECOVERED_ERROR 0x01 -#define MSG_RDAC_UP "rdac checker reports path is up" -#define MSG_RDAC_DOWN "rdac checker reports path is down" -#define MSG_RDAC_GHOST "rdac checker reports path is ghost" +#define MSG_RDAC_UP "rdac checker reports path is up" +#define MSG_RDAC_DOWN "rdac checker reports path is down" +#define MSG_RDAC_GHOST "rdac checker reports path is ghost" struct rdac_checker_context { void * dummy; @@ -69,11 +69,11 @@ do_inq(int sg_fd, unsigned int pg_op, void *resp, int mx_resp_len) /* treat SG_ERR here to get rid of sg_err.[ch] */ io_hdr.status &= 0x7e; if ((0 == io_hdr.status) && (0 == io_hdr.host_status) && - (0 == io_hdr.driver_status)) + (0 == io_hdr.driver_status)) return 0; if ((SCSI_CHECK_CONDITION == io_hdr.status) || - (SCSI_COMMAND_TERMINATED == io_hdr.status) || - (SG_ERR_DRIVER_SENSE == (0xf & io_hdr.driver_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; unsigned char * sense_buffer = io_hdr.sbp; diff --git a/libmultipath/Makefile b/libmultipath/Makefile index 5d8c586..04761e4 100644 --- a/libmultipath/Makefile +++ b/libmultipath/Makefile @@ -6,14 +6,14 @@ 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 \ structs.o discovery.o propsel.o dict.o \ pgpolicies.o debug.o regex.o defaults.o uevent.o \ switchgroup.o uxsock.o print.o alias.o log_pthread.o \ - log.o configure.o structs_vec.o + log.o configure.o structs_vec.o sysfs.o PREVBUILD = $(shell nm debug.o 2> /dev/null|grep log_safe) @@ -25,6 +25,12 @@ else CLEAN = $(shell if [ ! "x$(PREVBUILD)" = "x" ]; then echo clean; fi) endif +LIBDM_API_FLUSH = $(shell objdump -T /lib/libdevmapper.so.* | grep -c dm_task_no_flush) + +ifeq ($(strip $(LIBDM_API_FLUSH)),1) + CFLAGS += -DLIBDM_API_FLUSH +endif + all: $(BUILD) prepare: $(CLEAN) diff --git a/libmultipath/blacklist.c b/libmultipath/blacklist.c index 6587f60..6297516 100644 --- a/libmultipath/blacklist.c +++ b/libmultipath/blacklist.c @@ -136,94 +136,185 @@ setup_default_blist (struct config * conf) } } } - return 0; } int -blacklist_exceptions (vector elist, char * str) +_blacklist_exceptions (vector elist, char * str) { int i; struct blentry * ele; vector_foreach_slot (elist, ele, i) { - if (!regexec(&ele->regex, str, 0, NULL, 0)) { - condlog(3, "%s: exception-listed", str); + if (!regexec(&ele->regex, str, 0, NULL, 0)) return 1; - } } return 0; } int -blacklist (vector blist, vector elist, char * str) +_blacklist (vector blist, char * str) { int i; struct blentry * ble; - if (blacklist_exceptions(elist, str)) - return 0; - vector_foreach_slot (blist, ble, i) { - if (!regexec(&ble->regex, str, 0, NULL, 0)) { - condlog(3, "%s: blacklisted", str); + if (!regexec(&ble->regex, str, 0, NULL, 0)) return 1; - } } return 0; } int -blacklist_exceptions_device(vector elist, char * vendor, char * product) +_blacklist_exceptions_device(vector elist, char * vendor, char * product) { int i; struct blentry_device * ble; vector_foreach_slot (elist, ble, i) { if (!regexec(&ble->vendor_reg, vendor, 0, NULL, 0) && - !regexec(&ble->product_reg, product, 0, NULL, 0)) { - condlog(3, "%s:%s: exception-listed", vendor, product); + !regexec(&ble->product_reg, product, 0, NULL, 0)) return 1; - } } return 0; } int -blacklist_device (vector blist, vector elist, char * vendor, char * product) +_blacklist_device (vector blist, char * vendor, char * product) { int i; struct blentry_device * ble; - if (blacklist_exceptions_device(elist, vendor, product)) - return 0; - vector_foreach_slot (blist, ble, i) { if (!regexec(&ble->vendor_reg, vendor, 0, NULL, 0) && - !regexec(&ble->product_reg, product, 0, NULL, 0)) { - condlog(3, "%s:%s: blacklisted", vendor, product); + !regexec(&ble->product_reg, product, 0, NULL, 0)) return 1; - } } return 0; } +#define LOG_BLIST(M) \ + if (vendor && product) \ + condlog(3, "%s: (%s:%s) %s", dev, vendor, product, (M)); \ + else if (wwid) \ + condlog(3, "%s: (%s) %s", dev, wwid, (M)); \ + else \ + condlog(3, "%s: %s", dev, (M)) + +void +log_filter (char *dev, char *vendor, char *product, char *wwid, int r) +{ + /* + * Try to sort from most likely to least. + */ + switch (r) { + case MATCH_NOTHING: + break; + case MATCH_DEVICE_BLIST: + LOG_BLIST("vendor/product blacklisted"); + break; + case MATCH_WWID_BLIST: + LOG_BLIST("wwid blacklisted"); + break; + case MATCH_DEVNODE_BLIST: + LOG_BLIST("device node name blacklisted"); + break; + case MATCH_DEVICE_BLIST_EXCEPT: + LOG_BLIST("vendor/product whitelisted"); + break; + case MATCH_WWID_BLIST_EXCEPT: + LOG_BLIST("wwid whitelisted"); + break; + case MATCH_DEVNODE_BLIST_EXCEPT: + LOG_BLIST("device node name whitelisted"); + break; + } +} + int -blacklist_path (struct config * conf, struct path * pp) +_filter_device (vector blist, vector elist, char * vendor, char * product) { - if (blacklist(conf->blist_devnode, conf->elist_devnode, pp->dev)) - return 1; + if (!vendor || !product) + return 0; + if (_blacklist_exceptions_device(elist, vendor, product)) + return MATCH_DEVICE_BLIST_EXCEPT; + if (_blacklist_device(blist, vendor, product)) + return MATCH_DEVICE_BLIST; + return 0; +} - if (blacklist(conf->blist_wwid, conf->elist_wwid, pp->wwid)) - return 1; +int +filter_device (vector blist, vector elist, char * vendor, char * product) +{ + int r = _filter_device(blist, elist, vendor, product); + log_filter(NULL, vendor, product, NULL, r); + return r; +} - if (pp->vendor_id && pp->product_id && - blacklist_device(conf->blist_device, conf->elist_device, pp->vendor_id, pp->product_id)) - return 1; +int +_filter_devnode (vector blist, vector elist, char * dev) +{ + if (!dev) + return 0; + if (_blacklist_exceptions(elist, dev)) + return MATCH_DEVNODE_BLIST_EXCEPT; + if (_blacklist(blist, dev)) + return MATCH_DEVNODE_BLIST; + return 0; +} + +int +filter_devnode (vector blist, vector elist, char * dev) +{ + int r = _filter_devnode(blist, elist, dev); + log_filter(dev, NULL, NULL, NULL, r); + return r; +} +int +_filter_wwid (vector blist, vector elist, char * wwid) +{ + if (!wwid) + return 0; + if (_blacklist_exceptions(elist, wwid)) + return MATCH_WWID_BLIST_EXCEPT; + if (_blacklist(blist, wwid)) + return MATCH_WWID_BLIST; return 0; } +int +filter_wwid (vector blist, vector elist, char * wwid) +{ + int r = _filter_wwid(blist, elist, wwid); + log_filter(NULL, NULL, NULL, wwid, r); + return r; +} + +int +_filter_path (struct config * conf, struct path * pp) +{ + int r; + + r = _filter_devnode(conf->blist_devnode, conf->elist_devnode,pp->dev); + if (r > 0) + return r; + r = _filter_device(conf->blist_device, conf->elist_device, + pp->vendor_id, pp->product_id); + if (r > 0) + return r; + r = _filter_wwid(conf->blist_wwid, conf->elist_wwid, pp->wwid); + return r; +} + +int +filter_path (struct config * conf, struct path * pp) +{ + int r=_filter_path(conf, pp); + log_filter(pp->dev, pp->vendor_id, pp->product_id, pp->wwid, r); + return r; +} + void free_blacklist (vector blist) { diff --git a/libmultipath/blacklist.h b/libmultipath/blacklist.h index 99c6fd1..cdbebef 100644 --- a/libmultipath/blacklist.h +++ b/libmultipath/blacklist.h @@ -3,6 +3,14 @@ #include "regex.h" +#define MATCH_NOTHING 0 +#define MATCH_WWID_BLIST 1 +#define MATCH_DEVICE_BLIST 2 +#define MATCH_DEVNODE_BLIST 3 +#define MATCH_WWID_BLIST_EXCEPT -MATCH_WWID_BLIST +#define MATCH_DEVICE_BLIST_EXCEPT -MATCH_DEVICE_BLIST +#define MATCH_DEVNODE_BLIST_EXCEPT -MATCH_DEVNODE_BLIST + struct blentry { char * str; regex_t regex; @@ -19,9 +27,10 @@ struct blentry_device { int setup_default_blist (struct config *); int alloc_ble_device (vector); -int blacklist (vector, vector, char *); -int blacklist_device (vector, vector, char *, char *); -int blacklist_path (struct config *, struct path *); +int filter_devnode (vector, vector, char *); +int filter_wwid (vector, vector, char *); +int filter_device (vector, vector, char *, char *); +int filter_path (struct config *, struct path *); int store_ble (vector, char *, int); int set_ble_device (vector, char *, char *, int); void free_blacklist (vector); diff --git a/libmultipath/config.c b/libmultipath/config.c index 1dfc18c..1433013 100644 --- a/libmultipath/config.c +++ b/libmultipath/config.c @@ -7,6 +7,7 @@ #include <string.h> #include <checkers.h> +#include <libprio.h> #include "memory.h" #include "util.h" @@ -265,9 +266,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 +281,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; @@ -361,6 +360,7 @@ load_config (char * file) conf->dev_type = DEV_NONE; conf->minio = 1000; + conf->max_fds = 0; conf->bindings_file = DEFAULT_BINDINGS_FILE; /* @@ -457,6 +457,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 61e5adf..d6403a8 100644 --- a/libmultipath/config.h +++ b/libmultipath/config.h @@ -21,12 +21,15 @@ struct hwentry { char * hwhandler; char * selector; char * checker_name; + char * prio_name; int pgpolicy; int pgfailback; int rr_weight; int no_path_retry; int minio; + int pg_timeout; + struct prio * prio; struct checker * checker; char * bl_product; }; @@ -35,6 +38,7 @@ struct mpentry { char * wwid; char * alias; char * getuid; + char * getprio; char * selector; int pgpolicy; @@ -42,6 +46,7 @@ struct mpentry { int rr_weight; int no_path_retry; int minio; + int pg_timeout; }; struct config { @@ -51,6 +56,7 @@ struct config { int pgpolicy_flag; int with_sysfs; int pgpolicy; + struct prio * prio; struct checker * checker; enum devtypes dev_type; int minio; @@ -61,8 +67,11 @@ struct config { int rr_weight; int no_path_retry; int user_friendly_names; + int pg_timeout; + int max_fds; char * dev; + char * sysfs_dir; char * udev_dir; char * selector; char * getuid; diff --git a/libmultipath/configure.c b/libmultipath/configure.c index 0d5e405..3b58af7 100644 --- a/libmultipath/configure.c +++ b/libmultipath/configure.c @@ -15,6 +15,7 @@ #include <libdevmapper.h> #include <checkers.h> +#include <libprio.h> #include "vector.h" #include "memory.h" @@ -60,6 +61,7 @@ setup_map (struct multipath * mpp) select_rr_weight(mpp); select_minio(mpp); select_no_path_retry(mpp); + select_pg_timeout(mpp); /* * assign paths to path groups -- start with no groups and all paths @@ -174,8 +176,9 @@ select_action (struct multipath * mpp, vector curmp) mpp->alias); return; } - if (!mpp->no_path_retry && /* let features be handled by the daemon */ - strncmp(cmpp->features, mpp->features, strlen(mpp->features))) { + if (!mpp->no_path_retry && !mpp->pg_timeout && + (strlen(cmpp->features) != strlen(mpp->features) || + strcmp(cmpp->features, mpp->features))) { mpp->action = ACT_RELOAD; condlog(3, "%s: set ACT_RELOAD (features change)", mpp->alias); @@ -323,8 +326,10 @@ domap (struct multipath * mpp) return DOMAP_RETRY; } - if (dm_map_present(mpp->alias)) + if (dm_map_present(mpp->alias)) { + condlog(3, "%s: map already present", mpp->alias); break; + } r = dm_addmap(DM_DEVICE_CREATE, mpp->alias, DEFAULT_TARGET, mpp->params, mpp->size, mpp->wwid); @@ -360,7 +365,7 @@ domap (struct multipath * mpp) if (r) { /* - * DM_DEVICE_CREATE, DM_DEIVCE_RENAME, or DM_DEVICE_RELOAD + * DM_DEVICE_CREATE, DM_DEVICE_RENAME, or DM_DEVICE_RELOAD * succeeded */ #ifndef DAEMON @@ -371,6 +376,10 @@ domap (struct multipath * mpp) mpp->stat_map_loads++; condlog(2, "%s: load table [0 %llu %s %s]", mpp->alias, mpp->size, DEFAULT_TARGET, mpp->params); + /* + * Required action is over, reset for the stateful daemon + */ + mpp->action = ACT_NOTHING; #endif return DOMAP_OK; } @@ -418,7 +427,7 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid) /* 1. if path has no unique id or wwid blacklisted */ if (memcmp(empty_buff, pp1->wwid, WWID_SIZE) == 0 || - blacklist_path(conf, pp1)) + filter_path(conf, pp1) > 0) continue; /* 2. if path already coalesced */ @@ -500,6 +509,12 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid) else dm_queue_if_no_path(mpp->alias, 1); } + if (mpp->pg_timeout != PGTIMEOUT_UNDEF) { + if (mpp->pg_timeout == -PGTIMEOUT_NONE) + dm_set_pg_timeout(mpp->alias, 0); + else + dm_set_pg_timeout(mpp->alias, mpp->pg_timeout); + } if (newmp) { if (mpp->action != ACT_REJECT) { diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h index ab65492..aa2f285 100644 --- a/libmultipath/defaults.h +++ b/libmultipath/defaults.h @@ -4,11 +4,11 @@ #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 #define DEFAULT_NO_PATH_RETRY NO_PATH_RETRY_UNDEF +#define DEFAULT_PGTIMEOUT -PGTIMEOUT_NONE #define DEFAULT_USER_FRIENDLY_NAMES 0 #define DEFAULT_CHECKINT 5 diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c index b04a30f..ef9f4fe 100644 --- a/libmultipath/devmapper.c +++ b/libmultipath/devmapper.c @@ -107,7 +107,7 @@ dm_prereq (char * str, int x, int y, int z) if (!strncmp(str, target->name, strlen(str))) { r--; - + if (target->version[0] >= x && target->version[1] >= y && target->version[2] >= z) @@ -142,6 +142,9 @@ dm_simplecmd (int task, const char *name) { dm_task_no_open_count(dmt); dm_task_skip_lockfs(dmt); /* for DM_DEVICE_RESUME */ +#ifdef LIBDM_API_FLUSH + dm_task_no_flush(dmt); /* for DM_DEVICE_SUSPEND/RESUME */ +#endif r = dm_task_run (dmt); @@ -570,6 +573,16 @@ dm_queue_if_no_path(char *mapname, int enable) return dm_message(mapname, message); } +int +dm_set_pg_timeout(char *mapname, int timeout_val) +{ + char message[24]; + + if (snprintf(message, 24, "set_pg_timeout %d", timeout_val) >= 24) + return 1; + return dm_message(mapname, message); +} + static int dm_groupmsg (char * msg, char * mapname, int index) { @@ -872,7 +885,7 @@ dm_get_info (char * mapname, struct dm_info ** dmi) { int r = 1; struct dm_task *dmt = NULL; - + if (!mapname) return 1; diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h index 1628ba3..c850ace 100644 --- a/libmultipath/devmapper.h +++ b/libmultipath/devmapper.h @@ -12,6 +12,7 @@ int dm_flush_maps (char *); int dm_fail_path(char * mapname, char * path); int dm_reinstate_path(char * mapname, char * path); int dm_queue_if_no_path(char *mapname, int enable); +int dm_set_pg_timeout(char *mapname, int timeout_val); int dm_switchgroup(char * mapname, int index); int dm_enablegroup(char * mapname, int index); int dm_disablegroup(char * mapname, int index); diff --git a/libmultipath/dict.c b/libmultipath/dict.c index c51ab05..2e0e916 100644 --- a/libmultipath/dict.c +++ b/libmultipath/dict.c @@ -5,6 +5,7 @@ * Copyright (c) 2005 Kiyoshi Ueda, NEC */ #include <checkers.h> +#include <libprio.h> #include "vector.h" #include "hwtable.h" @@ -88,13 +89,27 @@ def_prio_callout_handler(vector strvec) if (!conf->getprio) return 1; - + if (strlen(conf->getprio) == 4 && !strcmp(conf->getprio, "none")) { FREE(conf->getprio); conf->getprio = NULL; } - + + return 0; +} + +static int +def_prio_handler(vector strvec) +{ + char * buff; + + buff = set_value(strvec); + if (!buff) + return 1; + + conf->prio = prio_lookup(buff); + FREE(buff); return 0; } @@ -142,6 +157,26 @@ def_minio_handler(vector strvec) } static int +max_fds_handler(vector strvec) +{ + char * buff; + + buff = set_value(strvec); + + if (!buff) + return 1; + + if (strlen(buff) == 9 && + !strcmp(buff, "unlimited")) + conf->max_fds = MAX_FDS_UNLIMITED; + else + conf->max_fds = atoi(buff); + FREE(buff); + + return 0; +} + +static int def_weight_handler(vector strvec) { char * buff; @@ -201,6 +236,32 @@ def_no_path_retry_handler(vector strvec) } static int +def_pg_timeout_handler(vector strvec) +{ + int pg_timeout; + char * buff; + + buff = set_value(strvec); + + if (!buff) + return 1; + + if (strlen(buff) == 4 && !strcmp(buff, "none")) + conf->pg_timeout = -PGTIMEOUT_NONE; + else if (sscanf(buff, "%d", &pg_timeout) == 1 && pg_timeout >= 0) { + if (pg_timeout == 0) + conf->pg_timeout = -PGTIMEOUT_NONE; + else + conf->pg_timeout = pg_timeout; + } + else + conf->pg_timeout = PGTIMEOUT_UNDEF; + + FREE(buff); + return 0; +} + +static int names_handler(vector strvec) { char * buff; @@ -266,12 +327,12 @@ ble_devnode_handler(vector strvec) static int ble_except_devnode_handler(vector strvec) { - char * buff; + char * buff; - buff = set_value(strvec); + buff = set_value(strvec); - if (!buff) - return 1; + if (!buff) + return 1; return store_ble(conf->elist_devnode, buff, ORIGIN_CONFIG); } @@ -548,9 +609,9 @@ static int prio_callout_handler(vector strvec) { struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); - + if (!hwe) - return 1; + return 1; hwe->getprio = set_value(strvec); @@ -566,6 +627,24 @@ prio_callout_handler(vector strvec) } static int +hw_prio_handler(vector strvec) +{ + struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); + char * buff; + + if (!hwe) + return 1; + + buff = set_value(strvec); + if (!buff) + return 1; + + hwe->prio = prio_lookup(buff); + FREE(buff); + return 0; +} + +static int hw_failback_handler(vector strvec) { struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); @@ -656,6 +735,36 @@ hw_minio_handler(vector strvec) return 0; } +static int +hw_pg_timeout_handler(vector strvec) +{ + int pg_timeout; + struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable); + char *buff; + + if (!hwe) + return 1; + + buff = set_value(strvec); + + if (!buff) + return 1; + + if (strlen(buff) == 4 && !strcmp(buff, "none")) + hwe->pg_timeout = -PGTIMEOUT_NONE; + else if (sscanf(buff, "%d", &pg_timeout) == 1 && pg_timeout >= 0) { + if (pg_timeout == 0) + hwe->pg_timeout = -PGTIMEOUT_NONE; + else + hwe->pg_timeout = pg_timeout; + } + else + hwe->pg_timeout = PGTIMEOUT_UNDEF; + + FREE(buff); + return 0; +} + /* * multipaths block handlers */ @@ -848,6 +957,35 @@ mp_minio_handler(vector strvec) return 0; } +static int +mp_pg_timeout_handler(vector strvec) +{ + int pg_timeout; + struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable); + char *buff; + + if (!mpe) + return 1; + + buff = set_value(strvec); + + if (!buff) + return 1; + if (strlen(buff) == 4 && !strcmp(buff, "none")) + mpe->pg_timeout = -PGTIMEOUT_NONE; + else if (sscanf(buff, "%d", &pg_timeout) == 1 && pg_timeout >= 0) { + if (pg_timeout == 0) + mpe->pg_timeout = -PGTIMEOUT_NONE; + else + mpe->pg_timeout = pg_timeout; + } + else + mpe->pg_timeout = PGTIMEOUT_UNDEF; + + FREE(buff); + return 0; +} + /* * config file keywords printing */ @@ -967,6 +1105,22 @@ snprint_mp_rr_min_io (char * buff, int len, void * data) } static int +snprint_mp_pg_timeout (char * buff, int len, void * data) +{ + struct mpentry * mpe = (struct mpentry *)data; + + switch (mpe->pg_timeout) { + case PGTIMEOUT_UNDEF: + break; + case -PGTIMEOUT_NONE: + return snprintf(buff, len, "none"); + default: + return snprintf(buff, len, "%i", mpe->pg_timeout); + } + return 0; +} + +static int snprint_hw_vendor (char * buff, int len, void * data) { struct hwentry * hwe = (struct hwentry *)data; @@ -1034,6 +1188,19 @@ snprint_hw_prio_callout (char * buff, int len, void * data) } static int +snprint_hw_prio (char * buff, int len, void * data) +{ + struct hwentry * hwe = (struct hwentry *)data; + + if (!hwe->prio) + return 0; + if (hwe->prio == conf->prio) + return 0; + + return snprintf(buff, len, "%s", prio_name(hwe->prio)); +} + +static int snprint_hw_features (char * buff, int len, void * data) { struct hwentry * hwe = (struct hwentry *)data; @@ -1168,6 +1335,27 @@ snprint_hw_rr_min_io (char * buff, int len, void * data) } static int +snprint_hw_pg_timeout (char * buff, int len, void * data) +{ + struct hwentry * hwe = (struct hwentry *)data; + + if (!hwe->pg_timeout) + return 0; + if (hwe->pg_timeout == conf->pg_timeout) + return 0; + + switch (hwe->pg_timeout) { + case PGTIMEOUT_UNDEF: + break; + case -PGTIMEOUT_NONE: + return snprintf(buff, len, "none"); + default: + return snprintf(buff, len, "%i", hwe->pg_timeout); + } + return 0; +} + +static int snprint_hw_path_checker (char * buff, int len, void * data) { struct hwentry * hwe = (struct hwentry *)data; @@ -1251,6 +1439,15 @@ snprint_def_getprio_callout (char * buff, int len, void * data) } static int +snprint_def_prio (char * buff, int len, void * data) +{ + if (!conf->prio) + return 0; + + return snprintf(buff, len, "%s", prio_name(conf->prio)); +} + +static int snprint_def_features (char * buff, int len, void * data) { if (!conf->features) @@ -1306,6 +1503,17 @@ snprint_def_rr_min_io (char * buff, int len, void * data) } static int +snprint_max_fds (char * buff, int len, void * data) +{ + if (!conf->max_fds) + return 0; + + if (conf->max_fds < 0) + return snprintf(buff, len, "unlimited"); + return snprintf(buff, len, "%d", conf->max_fds); +} + +static int snprint_def_rr_weight (char * buff, int len, void * data) { if (!conf->rr_weight) @@ -1339,6 +1547,23 @@ snprint_def_no_path_retry (char * buff, int len, void * data) } static int +snprint_def_pg_timeout (char * buff, int len, void * data) +{ + if (conf->pg_timeout == DEFAULT_PGTIMEOUT) + return 0; + + switch (conf->pg_timeout) { + case PGTIMEOUT_UNDEF: + break; + case -PGTIMEOUT_NONE: + return snprintf(buff, len, "none"); + default: + return snprintf(buff, len, "%i", conf->pg_timeout); + } + return 0; +} + +static int snprint_def_user_friendly_names (char * buff, int len, void * data) { if (conf->user_friendly_names == DEFAULT_USER_FRIENDLY_NAMES) @@ -1385,17 +1610,19 @@ init_keywords(void) 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); install_keyword("rr_min_io", &def_minio_handler, &snprint_def_rr_min_io); + install_keyword("max_fds", &max_fds_handler, &snprint_max_fds); install_keyword("rr_weight", &def_weight_handler, &snprint_def_rr_weight); install_keyword("no_path_retry", &def_no_path_retry_handler, &snprint_def_no_path_retry); + install_keyword("pg_timeout", &def_pg_timeout_handler, &snprint_def_pg_timeout); install_keyword("user_friendly_names", &names_handler, &snprint_def_user_friendly_names); __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); @@ -1440,10 +1667,12 @@ init_keywords(void) 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); install_keyword("rr_min_io", &hw_minio_handler, &snprint_hw_rr_min_io); + install_keyword("pg_timeout", &hw_pg_timeout_handler, &snprint_hw_pg_timeout); install_sublevel_end(); install_keyword_root("multipaths", &multipaths_handler); @@ -1457,5 +1686,6 @@ init_keywords(void) install_keyword("rr_weight", &mp_weight_handler, &snprint_mp_rr_weight); install_keyword("no_path_retry", &mp_no_path_retry_handler, &snprint_mp_no_path_retry); install_keyword("rr_min_io", &mp_minio_handler, &snprint_mp_rr_min_io); + install_keyword("pg_timeout", &mp_pg_timeout_handler, &snprint_mp_pg_timeout); install_sublevel_end(); } diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c index 9045d1d..55d40dd 100644 --- a/libmultipath/discovery.c +++ b/libmultipath/discovery.c @@ -8,11 +8,11 @@ #include <fcntl.h> #include <sys/ioctl.h> #include <sys/stat.h> +#include <dirent.h> #include <errno.h> -#include <sysfs/dlist.h> -#include <sysfs/libsysfs.h> #include <checkers.h> +#include <libprio.h> #include "vector.h" #include "memory.h" @@ -24,6 +24,7 @@ #include "debug.h" #include "propsel.h" #include "sg_include.h" +#include "sysfs.h" #include "discovery.h" struct path * @@ -61,7 +62,8 @@ path_discover (vector pathvec, struct config * conf, char * devname, int flag) if (!devname) return 0; - if (blacklist(conf->blist_devnode, conf->elist_devnode, devname)) + if (filter_devnode(conf->blist_devnode, conf->elist_devnode, + devname) > 0) return 0; if(safe_sprintf(path, "%s/block/%s/device", sysfs_path, @@ -88,127 +90,117 @@ path_discover (vector pathvec, struct config * conf, char * devname, int flag) int path_discovery (vector pathvec, struct config * conf, int flag) { - struct dlist * ls; - struct sysfs_class * class; - struct sysfs_class_device * dev; - int r = 1; + DIR *blkdir; + struct dirent *blkdev; + struct stat statbuf; + char devpath[PATH_MAX]; + char *devptr; + int r = 0; - if (!(class = sysfs_open_class("block"))) + if (!(blkdir = opendir("/sys/block"))) return 1; - if (!(ls = sysfs_get_class_devices(class))) - goto out; - - r = 0; - - dlist_for_each_data(ls, dev, struct sysfs_class_device) - r += path_discover(pathvec, conf, dev->name, flag); - -out: - sysfs_close_class(class); - return r; -} + strcpy(devpath,"/sys/block"); + while ((blkdev = readdir(blkdir)) != NULL) { + if ((strcmp(blkdev->d_name,".") == 0) || + (strcmp(blkdev->d_name,"..") == 0)) + continue; -/* - * the daemon can race udev upon path add, - * not multipath(8), ran by udev - */ -#if DAEMON -#define WAIT_MAX_SECONDS 60 -#define WAIT_LOOP_PER_SECOND 5 + devptr = devpath + 10; + *devptr = '\0'; + strcat(devptr,"/"); + strcat(devptr,blkdev->d_name); + if (stat(devpath, &statbuf) < 0) + continue; -static int -wait_for_file (char * filename) -{ - int loop; - struct stat stats; - - loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND; - - while (--loop) { - if (stat(filename, &stats) == 0) - return 0; + if (S_ISDIR(statbuf.st_mode) == 0) + continue; - if (errno != ENOENT) - return 1; + condlog(4, "Discover device %s", devpath); - usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND); + r += path_discover(pathvec, conf, blkdev->d_name, flag); } - return 1; -} -#else -static int -wait_for_file (char * filename) -{ - return 0; + closedir(blkdir); + condlog(4, "Discovery status %d", r); + return r; } -#endif -#define declare_sysfs_get_str(fname, fmt) \ +#define declare_sysfs_get_str(fname) \ extern int \ -sysfs_get_##fname (char * sysfs_path, char * dev, char * buff, int len) \ +sysfs_get_##fname (struct sysfs_device * dev, char * buff, size_t len) \ { \ - struct sysfs_attribute * attr; \ - char attr_path[SYSFS_PATH_SIZE]; \ + char *attr; \ \ - if (safe_sprintf(attr_path, fmt, sysfs_path, dev)) \ + attr = sysfs_attr_get_value(dev->devpath, #fname); \ + if (!attr) \ return 1; \ -\ - if (wait_for_file(attr_path)) \ - return 1; \ -\ - if (!(attr = sysfs_open_attribute(attr_path))) \ - return 1; \ -\ - if (0 > sysfs_read_attribute(attr)) \ - goto out; \ -\ - if (attr->len < 2 || attr->len - 1 > len) \ - goto out; \ -\ - strncpy(buff, attr->value, attr->len - 1); \ + if (strlcpy(buff, attr, len) != strlen(attr)) \ + return 2; \ strchop(buff); \ - sysfs_close_attribute(attr); \ return 0; \ -out: \ - sysfs_close_attribute(attr); \ - return 1; \ } -declare_sysfs_get_str(devtype, "%s/block/%s/device/devtype"); -declare_sysfs_get_str(cutype, "%s/block/%s/device/cutype"); -declare_sysfs_get_str(vendor, "%s/block/%s/device/vendor"); -declare_sysfs_get_str(model, "%s/block/%s/device/model"); -declare_sysfs_get_str(rev, "%s/block/%s/device/rev"); -declare_sysfs_get_str(dev, "%s/block/%s/dev"); +declare_sysfs_get_str(devtype); +declare_sysfs_get_str(cutype); +declare_sysfs_get_str(vendor); +declare_sysfs_get_str(model); +declare_sysfs_get_str(rev); int -sysfs_get_size (char * sysfs_path, char * dev, unsigned long long * size) +sysfs_get_dev (struct sysfs_device * dev, char * buff, size_t len) { - struct sysfs_attribute * attr; - char attr_path[SYSFS_PATH_SIZE]; - int r; + char *attr; - if (safe_sprintf(attr_path, "%s/block/%s/size", sysfs_path, dev)) + attr = sysfs_attr_get_value(dev->devpath, "dev"); + if (!attr) { + condlog(3, "%s: no 'dev' attribute in sysfs", dev->kernel); return 1; + } + if (strlcpy(buff, attr, len) != strlen(attr)) { + condlog(3, "%s: overflow in 'dev' attribute", dev->kernel); + return 2; + } + return 0; +} - attr = sysfs_open_attribute(attr_path); +int +sysfs_get_size (struct sysfs_device * dev, unsigned long long * size) +{ + char *attr; + int r; + attr = sysfs_attr_get_value(dev->devpath, "size"); if (!attr) return 1; - if (0 > sysfs_read_attribute(attr)) - goto out; - - r = sscanf(attr->value, "%llu\n", size); - sysfs_close_attribute(attr); + r = sscanf(attr, "%llu\n", size); if (r != 1) return 1; return 0; -out: - sysfs_close_attribute(attr); +} + +int +sysfs_get_fc_nodename (struct sysfs_device * dev, char * node, + unsigned int host, unsigned int channel, + unsigned int target) +{ + char attr_path[SYSFS_PATH_SIZE], *attr; + + if (safe_sprintf(attr_path, + "/class/fc_transport/target%i:%i:%i", + host, channel, target)) { + condlog(0, "attr_path too small"); + return 1; + } + + attr = sysfs_attr_get_value(attr_path, "node_name"); + if (attr) { + strlcpy(node, attr, strlen(attr)); + return 0; + } + return 1; } @@ -224,18 +216,14 @@ opennode (char * dev, int mode) condlog(0, "devpath too small"); return -1; } - /* Translate '!' into '/' */ + /* + * Translate '!' into '/' + */ ptr = devpath; while ((ptr = strchr(ptr, '!'))) { *ptr = '/'; ptr++; } - - if (wait_for_file(devpath)) { - condlog(3, "failed to open %s", devpath); - return -1; - } - return open(devpath, mode); } @@ -257,7 +245,7 @@ devt2devname (char *devname, char *devt) condlog(0, "Cannot open /proc/partitions"); return 1; } - + while (!feof(fd)) { int r = fscanf(fd,"%u %u %*d %s",&tmpmaj, &tmpmin, dev); if (!r) { @@ -266,6 +254,7 @@ devt2devname (char *devname, char *devt) } if (r != 3) continue; + if ((major == tmpmaj) && (minor == tmpmin)) { sprintf(block_path, "/sys/block/%s", dev); break; @@ -286,71 +275,72 @@ devt2devname (char *devname, char *devt) return 1; } strncpy(devname, dev, FILE_NAME_SIZE); + return 0; } int do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op, - void *resp, int mx_resp_len, int noisy) + void *resp, int mx_resp_len) { - unsigned char inqCmdBlk[INQUIRY_CMDLEN] = - { INQUIRY_CMD, 0, 0, 0, 0, 0 }; - unsigned char sense_b[SENSE_BUFF_LEN]; - struct sg_io_hdr io_hdr; - - if (cmddt) - inqCmdBlk[1] |= 2; - if (evpd) - inqCmdBlk[1] |= 1; - inqCmdBlk[2] = (unsigned char) pg_op; + unsigned char inqCmdBlk[INQUIRY_CMDLEN] = + { INQUIRY_CMD, 0, 0, 0, 0, 0 }; + unsigned char sense_b[SENSE_BUFF_LEN]; + struct sg_io_hdr io_hdr; + + if (cmddt) + inqCmdBlk[1] |= 2; + if (evpd) + inqCmdBlk[1] |= 1; + inqCmdBlk[2] = (unsigned char) pg_op; inqCmdBlk[3] = (unsigned char)((mx_resp_len >> 8) & 0xff); inqCmdBlk[4] = (unsigned char) (mx_resp_len & 0xff); - 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 (sense_b); - io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; - io_hdr.dxfer_len = mx_resp_len; - io_hdr.dxferp = resp; - io_hdr.cmdp = inqCmdBlk; - io_hdr.sbp = sense_b; - io_hdr.timeout = DEF_TIMEOUT; - - if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) - return -1; - - /* treat SG_ERR here to get rid of sg_err.[ch] */ - io_hdr.status &= 0x7e; - if ((0 == io_hdr.status) && (0 == io_hdr.host_status) && - (0 == io_hdr.driver_status)) - return 0; - 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; - unsigned char * sense_buffer = io_hdr.sbp; - if (sense_buffer[0] & 0x2) - sense_key = sense_buffer[1] & 0xf; - else - sense_key = sense_buffer[2] & 0xf; - if(RECOVERED_ERROR == sense_key) - return 0; - } - } - return -1; + 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 (sense_b); + io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + io_hdr.dxfer_len = mx_resp_len; + io_hdr.dxferp = resp; + io_hdr.cmdp = inqCmdBlk; + io_hdr.sbp = sense_b; + io_hdr.timeout = DEF_TIMEOUT; + + if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) + return -1; + + /* treat SG_ERR here to get rid of sg_err.[ch] */ + io_hdr.status &= 0x7e; + if ((0 == io_hdr.status) && (0 == io_hdr.host_status) && + (0 == io_hdr.driver_status)) + return 0; + 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; + unsigned char * sense_buffer = io_hdr.sbp; + if (sense_buffer[0] & 0x2) + sense_key = sense_buffer[1] & 0xf; + else + sense_key = sense_buffer[2] & 0xf; + if(RECOVERED_ERROR == sense_key) + return 0; + } + } + return -1; } static int get_serial (char * str, int maxlen, int fd) { - int len = 0; - char buff[MX_ALLOC_LEN + 1] = {0}; + int len = 0; + char buff[MX_ALLOC_LEN + 1] = {0}; if (fd < 0) - return 1; + return 1; - if (0 == do_inq(fd, 0, 1, 0x80, buff, MX_ALLOC_LEN, 0)) { + if (0 == do_inq(fd, 0, 1, 0x80, buff, MX_ALLOC_LEN)) { len = buff[3]; if (len >= maxlen) return 1; @@ -360,106 +350,55 @@ get_serial (char * str, int maxlen, int fd) } return 0; } - return 1; + return 1; } static int get_inq (char * vendor, char * product, char * rev, int fd) { - int len = 0; char buff[MX_ALLOC_LEN + 1] = {0}; + int len = 0; if (fd < 0) return 1; - if (0 == do_inq(fd, 0, 0, 0, buff, MX_ALLOC_LEN, 0)) { - memcpy(vendor, buff + 8, 8); - vendor[8] = '\0'; - memcpy(product, buff + 16, 16); - product[16] = '\0'; - memcpy(rev, buff + 32, 4); - rev[4] = '\0'; - return 0; - } - return 1; -} - -static int -sysfs_get_bus (char * sysfs_path, struct path * pp) -{ - struct sysfs_device *sdev; - char attr_path[FILE_NAME_SIZE]; - char attr_buff[FILE_NAME_SIZE]; - - pp->bus = SYSFS_BUS_UNDEF; - - /* - * This is ugly : we should be able to do a simple - * get_link("%s/block/%s/device/bus", ...) but it just - * won't work - */ - if(safe_sprintf(attr_path, "%s/block/%s/device", - sysfs_path, pp->dev)) { - condlog(0, "attr_path too small"); + memset(buff, 0, MX_ALLOC_LEN); + if (0 != do_inq(fd, 0, 0, 0, buff, 36)) return 1; - } - - if (0 > sysfs_get_link(attr_path, attr_buff, sizeof(attr_buff))) - return 1; - -#if DAEMON - int loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND; - while (loop--) { - sdev = sysfs_open_device_path(attr_buff); - - if (strlen(sdev->bus)) - break; - - sysfs_close_device(sdev); - usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND); - } -#else - sdev = sysfs_open_device_path(attr_buff); -#endif + len = buff[4] + 4; - if (!strncmp(sdev->bus, "scsi", 4)) - pp->bus = SYSFS_BUS_SCSI; - else if (!strncmp(sdev->bus, "ide", 3)) - pp->bus = SYSFS_BUS_IDE; - else if (!strncmp(sdev->bus, "ccw", 3)) - pp->bus = SYSFS_BUS_CCW; - else if (!strncmp(pp->dev,"cciss", 5)) - pp->bus = SYSFS_BUS_CCISS; - else + if (len < 8) return 1; - sysfs_close_device(sdev); - + memcpy(vendor, buff + 8, 8); + vendor[8] = '\0'; + strchop(vendor); + memcpy(product, buff + 16, 16); + product[16] = '\0'; + strchop(product); + memcpy(rev, buff + 32, 4); + rev[4] = '\0'; + strchop(rev); return 0; } static int -scsi_sysfs_pathinfo (struct path * pp) +scsi_sysfs_pathinfo (struct path * pp, struct sysfs_device * parent) { char attr_path[FILE_NAME_SIZE]; - char attr_buff[FILE_NAME_SIZE]; - struct sysfs_attribute * attr; - if (sysfs_get_vendor(sysfs_path, pp->dev, - pp->vendor_id, SCSI_VENDOR_SIZE)) + if (sysfs_get_vendor(parent, pp->vendor_id, SCSI_VENDOR_SIZE)) return 1; condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); - if (sysfs_get_model(sysfs_path, pp->dev, - pp->product_id, SCSI_PRODUCT_SIZE)) + if (sysfs_get_model(parent, pp->product_id, SCSI_PRODUCT_SIZE)) return 1; condlog(3, "%s: product = %s", pp->dev, pp->product_id); - if (sysfs_get_rev(sysfs_path, pp->dev, - pp->rev, SCSI_REV_SIZE)) + if (sysfs_get_rev(parent, pp->rev, SCSI_REV_SIZE)) return 1; condlog(3, "%s: rev = %s", pp->dev, pp->rev); @@ -472,15 +411,7 @@ scsi_sysfs_pathinfo (struct path * pp) /* * host / bus / target / lun */ - if(safe_sprintf(attr_path, "%s/block/%s/device", - sysfs_path, pp->dev)) { - condlog(0, "attr_path too small"); - return 1; - } - if (0 > sysfs_get_link(attr_path, attr_buff, sizeof(attr_buff))) - return 1; - - basename(attr_buff, attr_path); + basename(parent->devpath, attr_path); sscanf(attr_path, "%i:%i:%i:%i", &pp->sg_id.host_no, @@ -497,35 +428,19 @@ scsi_sysfs_pathinfo (struct path * pp) /* * target node name */ - if(safe_sprintf(attr_path, - "%s/class/fc_transport/target%i:%i:%i/node_name", - sysfs_path, - pp->sg_id.host_no, - pp->sg_id.channel, - pp->sg_id.scsi_id)) { - condlog(0, "attr_path too small"); - return 1; + if(!sysfs_get_fc_nodename(parent, pp->tgt_node_name, + pp->sg_id.host_no, + pp->sg_id.channel, + pp->sg_id.scsi_id)) { + condlog(3, "%s: tgt_node_name = %s", + pp->dev, pp->tgt_node_name); } - if (!(attr = sysfs_open_attribute(attr_path))) - return 0; - - if (sysfs_read_attribute(attr)) - goto err; - - if (attr->len > 0) - strncpy(pp->tgt_node_name, attr->value, attr->len - 1); - - condlog(3, "%s: tgt_node_name = %s", - pp->dev, pp->tgt_node_name); return 0; -err: - sysfs_close_attribute(attr); - return 1; } static int -ccw_sysfs_pathinfo (struct path * pp) +ccw_sysfs_pathinfo (struct path * pp, struct sysfs_device * parent) { char attr_path[FILE_NAME_SIZE]; char attr_buff[FILE_NAME_SIZE]; @@ -534,8 +449,7 @@ ccw_sysfs_pathinfo (struct path * pp) condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); - if (sysfs_get_devtype(sysfs_path, pp->dev, - attr_buff, FILE_NAME_SIZE)) + if (sysfs_get_devtype(parent, attr_buff, FILE_NAME_SIZE)) return 1; if (!strncmp(attr_buff, "3370", 4)) { @@ -556,15 +470,7 @@ ccw_sysfs_pathinfo (struct path * pp) /* * host / bus / target / lun */ - if(safe_sprintf(attr_path, "%s/block/%s/device", - sysfs_path, pp->dev)) { - condlog(0, "attr_path too small"); - return 1; - } - if (0 > sysfs_get_link(attr_path, attr_buff, sizeof(attr_buff))) - return 1; - - basename(attr_buff, attr_path); + basename(parent->devpath, attr_path); pp->sg_id.lun = 0; sscanf(attr_path, "%i.%i.%x", &pp->sg_id.host_no, @@ -581,14 +487,14 @@ ccw_sysfs_pathinfo (struct path * pp) } static int -cciss_sysfs_pathinfo (struct path * pp) +cciss_sysfs_pathinfo (struct path * pp, struct sysfs_device * dev) { char attr_path[FILE_NAME_SIZE]; /* * host / bus / target / lun */ - basename(pp->dev, attr_path); + basename(dev->devpath, attr_path); pp->sg_id.lun = 0; pp->sg_id.channel = 0; sscanf(attr_path, "cciss!c%id%i", @@ -600,25 +506,24 @@ cciss_sysfs_pathinfo (struct path * pp) pp->sg_id.channel, pp->sg_id.scsi_id, pp->sg_id.lun); - return 0; } static int -common_sysfs_pathinfo (struct path * pp) +common_sysfs_pathinfo (struct path * pp, struct sysfs_device *dev) { - if (sysfs_get_bus(sysfs_path, pp)) - return 1; - - condlog(3, "%s: bus = %i", pp->dev, pp->bus); + char *attr; - if (sysfs_get_dev(sysfs_path, pp->dev, - pp->dev_t, BLK_DEV_SIZE)) + attr = sysfs_attr_get_value(dev->devpath, "dev"); + if (!attr) { + condlog(3, "%s: no 'dev' attribute in sysfs", pp->dev); return 1; + } + strlcpy(pp->dev_t, attr, BLK_DEV_SIZE); condlog(3, "%s: dev_t = %s", pp->dev, pp->dev_t); - if (sysfs_get_size(sysfs_path, pp->dev, &pp->size)) + if (sysfs_get_size(dev, &pp->size)) return 1; condlog(3, "%s: size = %llu", pp->dev, pp->size); @@ -626,22 +531,53 @@ common_sysfs_pathinfo (struct path * pp) return 0; } +struct sysfs_device *sysfs_device_from_path(struct path *pp) +{ + char sysdev[FILE_NAME_SIZE]; + + strlcpy(sysdev,"/block/", FILE_NAME_SIZE); + strlcat(sysdev,pp->dev, FILE_NAME_SIZE); + + return sysfs_device_get(sysdev); +} + extern int sysfs_pathinfo(struct path * pp) { - if (common_sysfs_pathinfo(pp)) + struct sysfs_device *parent; + + pp->sysdev = sysfs_device_from_path(pp); + if (!pp->sysdev) { + condlog(1, "%s: failed to get sysfs information", pp->dev); return 1; + } + + if (common_sysfs_pathinfo(pp, pp->sysdev)) + return 1; + + parent = sysfs_device_get_parent(pp->sysdev); + if (!parent) + parent = pp->sysdev; + + condlog(3, "%s: subsystem = %s", pp->dev, parent->subsystem); + + if (!strncmp(parent->subsystem, "scsi",4)) + pp->bus = SYSFS_BUS_SCSI; + if (!strncmp(parent->subsystem, "ccw",3)) + pp->bus = SYSFS_BUS_CCW; + if (!strncmp(pp->dev,"cciss",5)) + pp->bus = SYSFS_BUS_CCISS; if (pp->bus == SYSFS_BUS_UNDEF) return 0; else if (pp->bus == SYSFS_BUS_SCSI) { - if (scsi_sysfs_pathinfo(pp)) + if (scsi_sysfs_pathinfo(pp, parent)) return 1; } else if (pp->bus == SYSFS_BUS_CCW) { - if (ccw_sysfs_pathinfo(pp)) + if (ccw_sysfs_pathinfo(pp, parent)) return 1; } else if (pp->bus == SYSFS_BUS_CCISS) { - if (cciss_sysfs_pathinfo(pp)) + if (cciss_sysfs_pathinfo(pp, pp->sysdev)) return 1; } return 0; @@ -663,6 +599,12 @@ cciss_ioctl_pathinfo (struct path * pp, int mask) { if (mask & DI_SYSFS) { get_inq(pp->vendor_id, pp->product_id, pp->rev, pp->fd); + /* Inquiry returns bogus values if no arrays are configured */ + if (strcmp(pp->vendor_id, "HP")) { + sprintf(pp->vendor_id,"HP"); + sprintf(pp->product_id,"SMART ARRAY"); + memset(pp->rev,0, sizeof(pp->rev)); + } condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); condlog(3, "%s: product = %s", pp->dev, pp->product_id); condlog(3, "%s: revision = %s", pp->dev, pp->rev); @@ -671,8 +613,8 @@ cciss_ioctl_pathinfo (struct path * pp, int mask) */ pp->hwe = find_hwe(conf->hwtable, pp->vendor_id, pp->product_id, pp->rev); - } + } return 0; } @@ -703,27 +645,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; } @@ -782,7 +719,7 @@ pathinfo (struct path *pp, vector hwtable, int mask) goto blank; /* - * Retrieve path priority for even PATH_DOWN paths if it has never + * Retrieve path priority, even for PATH_DOWN paths if it has never * been successfully obtained before. */ if (mask & DI_PRIO && @@ -792,6 +729,10 @@ pathinfo (struct path *pp, vector hwtable, int mask) if (mask & DI_WWID && !strlen(pp->wwid)) get_uid(pp); +#ifndef DAEMON + close(pp->fd); + pp->fd = -1; +#endif return 0; blank: @@ -800,5 +741,11 @@ blank: */ memset(pp->wwid, 0, WWID_SIZE); pp->state = PATH_DOWN; +#ifndef DAEMON + if (pp->fd > 0){ + close(pp->fd); + pp->fd = -1; + } +#endif return 0; } diff --git a/libmultipath/discovery.h b/libmultipath/discovery.h index ab62a59..c7cf7e8 100644 --- a/libmultipath/discovery.h +++ b/libmultipath/discovery.h @@ -24,12 +24,7 @@ #define SCSI_COMMAND_TERMINATED 0x22 #define SG_ERR_DRIVER_SENSE 0x08 -int sysfs_get_vendor (char * sysfs_path, char * dev, char * buff, int len); -int sysfs_get_model (char * sysfs_path, char * dev, char * buff, int len); -int sysfs_get_rev (char * sysfs_path, char * dev, char * buff, int len); -int sysfs_get_dev (char * sysfs_path, char * dev, char * buff, int len); - -int sysfs_get_size (char * sysfs_path, char * dev, unsigned long long *); +int sysfs_get_dev (struct sysfs_device * dev, char * buff, size_t len); int path_discovery (vector pathvec, struct config * conf, int flag); void basename (char *, char *); diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c index 639f30a..c78c4a2 100644 --- a/libmultipath/hwtable.c +++ b/libmultipath/hwtable.c @@ -1,6 +1,7 @@ #include <stdio.h> #include <checkers.h> +#include <libprio.h> #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,14 +58,14 @@ 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 = "mpath_prio_hp_sw /dev/%n", .features = "1 queue_if_no_path", - .hwhandler = "1 hp_sw", + .hwhandler = "1 hp-sw", .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = FAILBACK_UNDEF, @@ -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, @@ -87,16 +87,16 @@ static struct hwentry default_hw[] = { .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, - .checker_name = READSECTOR0, + .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 = "mpath_prio_hp_sw /dev/%n", .features = "1 queue_if_no_path", - .hwhandler = "1 hp_sw", + .hwhandler = "1 hp-sw", .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = FAILBACK_UNDEF, @@ -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 = "mpath_prio_alua /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -120,13 +120,28 @@ 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", + .features = DEFAULT_FEATURES, + .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, + .rr_weight = RR_WEIGHT_NONE, + .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 = "mpath_prio_alua /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -136,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 = "mpath_prio_alua /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -152,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 = "scsi_id -n -g -u -s /block/%n", - .getprio = NULL, + .getuid = "/sbin/scsi_id -n -g -u -s /block/%n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -168,6 +183,7 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, .checker_name = CCISS_TUR, + .prio_name = DEFAULT_PRIO, }, /* * DDN controller family @@ -179,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, @@ -189,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 @@ -199,8 +215,7 @@ static struct hwentry default_hw[] = { { .vendor = "EMC", .product = "SYMMETRIX", - .getuid = "scsi_id -g -u -ppre-spc3-83 -s /block/%n", - .getprio = NULL, + .getuid = "/sbin/scsi_id -g -u -ppre-spc3-83 -s /block/%n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -209,14 +224,14 @@ static struct hwentry default_hw[] = { .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, - .checker_name = READSECTOR0, + .checker_name = DIRECTIO, + .prio_name = DEFAULT_PRIO, }, { .vendor = "DGC", .product = ".*", .bl_product = "LUNZ", .getuid = DEFAULT_GETUID, - .getprio = "mpath_prio_emc /dev/%n", .features = "1 queue_if_no_path", .hwhandler = "1 emc", .selector = DEFAULT_SELECTOR, @@ -226,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 @@ -237,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, @@ -246,7 +261,8 @@ static struct hwentry default_hw[] = { .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, - .checker_name = READSECTOR0, + .checker_name = DIRECTIO, + .prio_name = DEFAULT_PRIO, }, /* * Hitachi controller family @@ -258,8 +274,7 @@ static struct hwentry default_hw[] = { .vendor = "(HITACHI|HP)", .product = "OPEN-.*", .getuid = DEFAULT_GETUID, - .getprio = NULL, - .features = DEFAULT_FEATURES, + .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = MULTIBUS, @@ -268,13 +283,13 @@ 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 = "mpath_prio_hds_modular %d", - .features = DEFAULT_FEATURES, + .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, @@ -282,7 +297,8 @@ static struct hwentry default_hw[] = { .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, - .checker_name = READSECTOR0, + .checker_name = TUR, + .prio_name = PRIO_HDS, }, /* * IBM controller family @@ -294,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, @@ -303,14 +318,14 @@ static struct hwentry default_hw[] = { .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, - .checker_name = READSECTOR0, + .checker_name = DIRECTIO, + .prio_name = DEFAULT_PRIO, }, { /* IBM FAStT 1722-600 */ .vendor = "IBM", .product = "1722-600", .getuid = DEFAULT_GETUID, - .getprio = "mpath_prio_rdac /dev/%n", .features = "1 queue_if_no_path", .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, @@ -320,13 +335,13 @@ static struct hwentry default_hw[] = { .no_path_retry = 300, .minio = DEFAULT_MINIO, .checker_name = RDAC, + .prio_name = PRIO_RDAC, }, { /* IBM DS4400 / FAStT700 */ .vendor = "IBM", .product = "1742", .getuid = DEFAULT_GETUID, - .getprio = "mpath_prio_rdac /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, @@ -336,15 +351,15 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_QUEUE, .minio = DEFAULT_MINIO, .checker_name = RDAC, + .prio_name = PRIO_RDAC, }, { /* IBM DS4700 */ .vendor = "IBM", .product = "1814", .getuid = DEFAULT_GETUID, - .getprio = "mpath_prio_rdac /dev/%n", .features = DEFAULT_FEATURES, - .hwhandler = "2 rdac 0", + .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, @@ -352,13 +367,13 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_QUEUE, .minio = DEFAULT_MINIO, .checker_name = RDAC, + .prio_name = PRIO_RDAC, }, { /* IBM Netfinity Fibre Channel RAID Controller Unit */ .vendor = "IBM", .product = "3526", .getuid = DEFAULT_GETUID, - .getprio = "mpath_prio_rdac /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, @@ -368,13 +383,13 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_QUEUE, .minio = DEFAULT_MINIO, .checker_name = RDAC, + .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, @@ -384,13 +399,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, @@ -400,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 DS6000 */ .vendor = "IBM", .product = "1750500", .getuid = DEFAULT_GETUID, - .getprio = "mpath_prio_alua /dev/%n", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -416,13 +431,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, @@ -432,13 +447,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 = "mpath_prio_alua /dev/%n", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -448,14 +463,14 @@ 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 */ .vendor = "IBM", .product = "S/390 DASD ECKD", .bl_product = "S/390.*", - .getuid = "dasdinfo -u -b %n", - .getprio = NULL, + .getuid = "/sbin/dasdinfo -u -b %n", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -465,8 +480,9 @@ 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 * * Maintainer : Dave Wysochanski @@ -476,7 +492,6 @@ static struct hwentry default_hw[] = { .vendor = "NETAPP", .product = "LUN.*", .getuid = DEFAULT_GETUID, - .getprio = "mpath_prio_ontap /dev/%n", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -485,9 +500,10 @@ static struct hwentry default_hw[] = { .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = 128, - .checker_name = READSECTOR0, + .checker_name = DIRECTIO, + .prio_name = PRIO_NETAPP, }, - /* + /* * IBM NSeries (NETAPP) controller family * * Maintainer : Dave Wysochanski @@ -497,7 +513,6 @@ static struct hwentry default_hw[] = { .vendor = "IBM", .product = "Nseries.*", .getuid = DEFAULT_GETUID, - .getprio = "mpath_prio_ontap /dev/%n", .features = "1 queue_if_no_path", .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -506,7 +521,8 @@ static struct hwentry default_hw[] = { .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = 128, - .checker_name = READSECTOR0, + .checker_name = DIRECTIO, + .prio_name = PRIO_NETAPP, }, /* * Pillar Data controller family @@ -518,7 +534,6 @@ static struct hwentry default_hw[] = { .vendor = "Pillar", .product = "Axiom.*", .getuid = DEFAULT_GETUID, - .getprio = "mpath_prio_alua %n", .features = DEFAULT_FEATURES, .hwhandler = DEFAULT_HWHANDLER, .selector = DEFAULT_SELECTOR, @@ -528,6 +543,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 @@ -539,7 +555,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, @@ -548,13 +563,13 @@ static struct hwentry default_hw[] = { .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, - .checker_name = READSECTOR0, + .checker_name = DIRECTIO, + .prio_name = DEFAULT_PRIO, }, { .vendor = "SGI", .product = "TP9[45]00", .getuid = DEFAULT_GETUID, - .getprio = "mpath_prio_rdac /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, @@ -564,12 +579,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 = "mpath_prio_rdac /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, @@ -579,6 +594,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 @@ -590,7 +606,6 @@ static struct hwentry default_hw[] = { .vendor = "STK", .product = "OPENstorage D280", .getuid = DEFAULT_GETUID, - .getprio = "mpath_prio_rdac /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, @@ -599,7 +614,8 @@ static struct hwentry default_hw[] = { .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, - .checker_name = RDAC, + .checker_name = TUR, + .prio_name = PRIO_RDAC, }, /* * SUN arrays @@ -611,7 +627,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, @@ -620,13 +635,34 @@ static struct hwentry default_hw[] = { .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = DEFAULT_MINIO, - .checker_name = READSECTOR0, + .checker_name = DIRECTIO, + .prio_name = DEFAULT_PRIO, + }, + /* + * Pivot3 RAIGE + * + * Maintainer : Bart Brooks, Pivot3 + * Mail : bartb@pivot3.com + */ + { + .vendor = "PIVOT3", + .product = "RAIGE VOLUME", + .getuid = "/sbin/scsi_id -p 0x80 -g -u -s /block/%n", + .features = "1 queue_if_no_path", + .hwhandler = DEFAULT_HWHANDLER, + .selector = DEFAULT_SELECTOR, + .pgpolicy = MULTIBUS, + .pgfailback = FAILBACK_UNDEF, + .rr_weight = RR_WEIGHT_NONE, + .no_path_retry = NO_PATH_RETRY_UNDEF, + .minio = 100, + .checker_name = TUR, + .prio_name = DEFAULT_PRIO, }, { .vendor = "SUN", .product = "CSM200_R", .getuid = DEFAULT_GETUID, - .getprio = "mpath_prio_rdac /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, @@ -636,13 +672,13 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_QUEUE, .minio = DEFAULT_MINIO, .checker_name = RDAC, + .prio_name = PRIO_RDAC, }, /* SUN/LSI 2540 */ { .vendor = "SUN", .product = "LCSM100_F", .getuid = DEFAULT_GETUID, - .getprio = "mpath_prio_rdac /dev/%n", .features = DEFAULT_FEATURES, .hwhandler = "1 rdac", .selector = DEFAULT_SELECTOR, @@ -652,6 +688,7 @@ static struct hwentry default_hw[] = { .no_path_retry = NO_PATH_RETRY_QUEUE, .minio = DEFAULT_MINIO, .checker_name = RDAC, + .prio_name = PRIO_RDAC, }, /* * EOL @@ -660,7 +697,6 @@ static struct hwentry default_hw[] = { .vendor = NULL, .product = NULL, .getuid = NULL, - .getprio = NULL, .features = NULL, .hwhandler = NULL, .selector = NULL, @@ -670,6 +706,7 @@ static struct hwentry default_hw[] = { .no_path_retry = 0, .minio = 0, .checker_name = NULL, + .prio_name = NULL, }, }; diff --git a/libmultipath/list.h b/libmultipath/list.h new file mode 100644 index 0000000..8626630 --- /dev/null +++ b/libmultipath/list.h @@ -0,0 +1,289 @@ +/* + * Copied from the Linux kernel source tree, version 2.6.0-test1. + * + * Licensed under the GPL v2 as per the whole kernel source tree. + * + */ + +#ifndef _LIST_H +#define _LIST_H + +/** + * container_of - cast a member of a structure out to the containing structure + * + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +/* + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses + * non-initialized list entries. + */ +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(struct list_head *head) +{ + return head->next == head; +} + +static inline void __list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); \ + pos = pos->next) + +/** + * __list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + * + * This variant differs from list_for_each() in that it's the + * simplest possible list iteration code. + * Use this for code that knows the list to be very short (empty + * or 1 entry) most of the time. + */ +#define __list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +#endif /* _LIST_H */ diff --git a/libmultipath/log_pthread.c b/libmultipath/log_pthread.c index 8976bfb..5f1bf12 100644 --- a/libmultipath/log_pthread.c +++ b/libmultipath/log_pthread.c @@ -12,10 +12,12 @@ #include "log_pthread.h" #include "log.h" -void log_safe (int prio, const char * fmt, va_list ap) +void log_safe (int prio, const char * fmt, ...) { + va_list ap; + pthread_mutex_lock(logq_lock); - //va_start(ap, fmt); + va_start(ap, fmt); log_enqueue(prio, fmt, ap); va_end(ap); pthread_mutex_unlock(logq_lock); diff --git a/libmultipath/log_pthread.h b/libmultipath/log_pthread.h index 2b18f59..23ceb84 100644 --- a/libmultipath/log_pthread.h +++ b/libmultipath/log_pthread.h @@ -7,7 +7,7 @@ pthread_mutex_t *logq_lock; pthread_mutex_t *logev_lock; pthread_cond_t *logev_cond; -void log_safe(int prio, const char * fmt, va_list ap); +void log_safe(int prio, const char * fmt, ...); void log_thread_start(void); void log_thread_stop(void); diff --git a/libmultipath/print.c b/libmultipath/print.c index 88d8c84..6f96420 100644 --- a/libmultipath/print.c +++ b/libmultipath/print.c @@ -5,8 +5,8 @@ #include <string.h> #include <libdevmapper.h> #include <stdarg.h> -#include <sysfs/dlist.h> -#include <sysfs/libsysfs.h> +#include <sys/stat.h> +#include <dirent.h> #include <checkers.h> @@ -21,6 +21,7 @@ #include "defaults.h" #include "parser.h" #include "blacklist.h" +#include "switchgroup.h" #define MAX(x,y) (x > y) ? x : y #define TAIL (line + len - 1 - c) @@ -89,8 +90,8 @@ snprint_sysfs (char * buff, size_t len, struct multipath * mpp) { if (mpp->dmi) return snprintf(buff, len, "dm-%i", mpp->dmi->minor); - - return 0; + else + return snprintf(buff, len, "n/a"); } static int @@ -221,17 +222,27 @@ snprint_multipath_uuid (char * buff, size_t len, struct multipath * mpp) } static int +snprint_multipath_vpr (char * buff, size_t len, struct multipath * mpp) +{ + struct path * pp = first_path(mpp); + if (!pp) + return 0; + return snprintf(buff, len, "%s,%s", + pp->vendor_id, pp->product_id); +} + +static int snprint_action (char * buff, size_t len, struct multipath * mpp) { switch (mpp->action) { - case ACT_REJECT: - return snprint_str(buff, len, ACT_REJECT_STR); + case ACT_REJECT: + return snprint_str(buff, len, ACT_REJECT_STR); + case ACT_RENAME: + return snprint_str(buff, len, ACT_RENAME_STR); case ACT_RELOAD: return snprint_str(buff, len, ACT_RELOAD_STR); case ACT_SWITCHPG: return snprint_str(buff, len, ACT_SWITCHPG_STR); - case ACT_RENAME: - return snprint_str(buff, len, ACT_RENAME_STR); case ACT_CREATE: return snprint_str(buff, len, ACT_CREATE_STR); default: @@ -340,6 +351,13 @@ snprint_pg_selector (char * buff, size_t len, struct pathgroup * pgp) static int snprint_pg_pri (char * buff, size_t len, struct pathgroup * pgp) { + /* + * path group priority is not updated for every path prio change, + * but only on switch group code path. + * + * Printing is another reason to update. + */ + path_group_prio_update(pgp); return snprint_int(buff, len, pgp->priority); } @@ -381,6 +399,7 @@ struct multipath_data mpd[] = { {'2', "map_loads", 0, snprint_map_loads}, {'3', "total_q_time", 0, snprint_total_q_time}, {'4', "q_timeouts", 0, snprint_q_timeouts}, + {'s', "vend/prod/rev", 0, snprint_multipath_vpr}, {0, NULL, 0 , NULL} }; @@ -683,10 +702,9 @@ snprint_multipath_topology (char * buff, int len, struct multipath * mpp, c += sprintf(c, "%%n"); if (strncmp(mpp->alias, mpp->wwid, WWID_SIZE)) - c += sprintf(c, " (%%w) "); + c += sprintf(c, " (%%w)"); - c += sprintf(c, "%%d "); - c += snprint_vpr(c, 24, first_path(mpp)); + c += sprintf(c, " %%d %%s"); fwd += snprint_multipath(buff + fwd, len - fwd, style, mpp); if (fwd > len) @@ -1100,42 +1118,59 @@ snprint_blacklist_except (char * buff, int len) extern int snprint_devices (char * buff, int len, struct vectors *vecs) { - struct dlist * ls; - struct sysfs_class * class; - struct sysfs_class_device * dev; + DIR *blkdir; + struct dirent *blkdev; + struct stat statbuf; + char devpath[PATH_MAX]; + char *devptr; int threshold = MAX_LINE_LEN; int fwd = 0; - + int r; struct path * pp; - - - if (!(class = sysfs_open_class("block"))) - return 0; - - if (!(ls = sysfs_get_class_devices(class))) { - sysfs_close_class(class); - return 0; - } + if (!(blkdir = opendir("/sys/block"))) + return 1; if ((len - fwd - threshold) <= 0) return len; fwd += snprintf(buff + fwd, len - fwd, "available block devices:\n"); - dlist_for_each_data(ls, dev, struct sysfs_class_device) { + strcpy(devpath,"/sys/block/"); + while ((blkdev = readdir(blkdir)) != NULL) { + if ((strcmp(blkdev->d_name,".") == 0) || + (strcmp(blkdev->d_name,"..") == 0)) + continue; + + devptr = devpath + 11; + *devptr = '\0'; + strncat(devptr, blkdev->d_name, PATH_MAX-12); + if (stat(devpath, &statbuf) < 0) + continue; + + if (S_ISDIR(statbuf.st_mode) == 0) + continue; + if ((len - fwd - threshold) <= 0) return len; - fwd += snprintf(buff + fwd, len - fwd, " %s ", dev->name); - pp = find_path_by_dev(vecs->pathvec, dev->name); - if (blacklist(conf->blist_devnode, conf->elist_devnode, - dev->name) || pp == NULL) + + fwd += snprintf(buff + fwd, len - fwd, " %s", devptr); + pp = find_path_by_dev(vecs->pathvec, devptr); + if (!pp) { + r = filter_devnode(conf->blist_devnode, + conf->elist_devnode, devptr); + if (r > 0) + fwd += snprintf(buff + fwd, len - fwd, + " devnode blacklisted, unmonitored"); + else if (r < 0) + fwd += snprintf(buff + fwd, len - fwd, + " devnode whitelisted, unmonitored"); + } else fwd += snprintf(buff + fwd, len - fwd, - "(blacklisted)\n"); - else - fwd += snprintf(buff + fwd, len - fwd, "\n"); - } - sysfs_close_class(class); + " devnode whitelisted, monitored"); + fwd += snprintf(buff + fwd, len - fwd, "\n"); + } + closedir(blkdir); if (fwd > len) return len; diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c index 1f0021a..7bc7083 100644 --- a/libmultipath/propsel.c +++ b/libmultipath/propsel.c @@ -6,6 +6,7 @@ #include <stdio.h> #include <checkers.h> +#include <libprio.h> #include "memory.h" #include "vector.h" @@ -257,22 +258,39 @@ select_getuid (struct path * pp) } extern int -select_getprio (struct path * pp) +select_prio (struct path * pp) { + 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 (pp->hwe && pp->hwe->getprio) { pp->getprio = pp->hwe->getprio; - condlog(3, "%s: getprio = %s (controller setting)", - pp->dev, pp->getprio); + pp->prio = prio_lookup(PRIO_COMMAND); + condlog(1, "%s: Using deprecated prio_callout '%s' (controller setting)\n" + "\tPlease fixup /etc/multipath.conf", + pp->dev, pp->hwe->getprio); + return 0; + } + if (conf->prio) { + pp->prio = conf->prio; + condlog(3, "%s: prio = %s (config file default)", + 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); + pp->prio = prio_lookup(PRIO_COMMAND); + condlog(1, "%s: Using deprecated prio_callout '%s' (controller setting)\n" + "\tPlease fixup /etc/multipath.conf", + pp->dev, pp->hwe->getprio); 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; } @@ -330,3 +348,41 @@ select_minio (struct multipath * mp) return 0; } +extern int +select_pg_timeout(struct multipath *mp) +{ + if (mp->mpe && mp->mpe->pg_timeout != PGTIMEOUT_UNDEF) { + mp->pg_timeout = mp->mpe->pg_timeout; + if (mp->pg_timeout > 0) + condlog(3, "%s: pg_timeout = %d (multipath setting)", + mp->alias, mp->pg_timeout); + else + condlog(3, "%s: pg_timeout = NONE (multipath setting)", + mp->alias); + return 0; + } + if (mp->hwe && mp->hwe->pg_timeout != PGTIMEOUT_UNDEF) { + mp->pg_timeout = mp->hwe->pg_timeout; + if (mp->pg_timeout > 0) + condlog(3, "%s: pg_timeout = %d (controller setting)", + mp->alias, mp->pg_timeout); + else + condlog(3, "%s: pg_timeout = NONE (controller setting)", + mp->alias); + return 0; + } + if (conf->pg_timeout != PGTIMEOUT_UNDEF) { + mp->pg_timeout = conf->pg_timeout; + if (mp->pg_timeout > 0) + condlog(3, "%s: pg_timeout = %d (config file default)", + mp->alias, mp->pg_timeout); + else + condlog(3, + "%s: pg_timeout = NONE (config file default)", + mp->alias); + return 0; + } + mp->pg_timeout = PGTIMEOUT_UNDEF; + condlog(3, "pg_timeout = NONE (internal default)"); + return 0; +} diff --git a/libmultipath/propsel.h b/libmultipath/propsel.h index f66a598..62802f8 100644 --- a/libmultipath/propsel.h +++ b/libmultipath/propsel.h @@ -7,6 +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 <libdevmapper.h> #include <checkers.h> +#include <libprio.h> #include "memory.h" #include "vector.h" diff --git a/libmultipath/structs.h b/libmultipath/structs.h index 7cab7ac..6205f59 100644 --- a/libmultipath/structs.h +++ b/libmultipath/structs.h @@ -9,6 +9,9 @@ #define FILE_NAME_SIZE 256 #define CALLOUT_MAX_SIZE 128 #define BLK_DEV_SIZE 33 +#define PATH_SIZE 512 +#define NAME_SIZE 128 + #define SCSI_VENDOR_SIZE 9 #define SCSI_PRODUCT_SIZE 17 @@ -18,8 +21,7 @@ #define NO_PATH_RETRY_FAIL -1 #define NO_PATH_RETRY_QUEUE -2 -#define PRIO_UNDEF -1 -#define PRIO_DEFAULT 1 +#define MAX_FDS_UNLIMITED -1 enum free_path_switch { KEEP_PATHS, @@ -59,6 +61,11 @@ enum pgstates { PGSTATE_ACTIVE }; +enum pgtimeouts { + PGTIMEOUT_UNDEF, + PGTIMEOUT_NONE +}; + struct scsi_idlun { int dev_id; int host_unique_id; @@ -82,9 +89,19 @@ struct scsi_dev { int host_no; }; +struct sysfs_device { + struct sysfs_device *parent; /* parent device */ + char devpath[PATH_SIZE]; + char subsystem[NAME_SIZE]; /* $class, $bus, drivers, module */ + char kernel[NAME_SIZE]; /* device instance name */ + char kernel_number[NAME_SIZE]; + char driver[NAME_SIZE]; /* device driver name */ +}; + struct path { char dev[FILE_NAME_SIZE]; char dev_t[BLK_DEV_SIZE]; + struct sysfs_device *sysdev; struct scsi_idlun scsi_id; struct sg_id sg_id; char wwid[WWID_SIZE]; @@ -104,7 +121,7 @@ struct path { int pgindex; char * getuid; char * getprio; - int getprio_selected; + struct prio * prio; struct checker checker; struct multipath * mpp; int fd; @@ -131,6 +148,7 @@ struct multipath { int no_path_retry; /* number of retries after all paths are down */ int retry_tick; /* remaining times for retries */ int minio; + int pg_timeout; unsigned long long size; vector paths; vector pg; @@ -195,6 +213,6 @@ struct path * first_path (struct multipath * mpp); int pathcountgr (struct pathgroup *, int); int pathcount (struct multipath *, int); -char sysfs_path[FILE_NAME_SIZE]; +extern char sysfs_path[PATH_SIZE]; #endif /* _STRUCTS_H */ diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c index 0f56cb8..26f24cc 100644 --- a/libmultipath/structs_vec.c +++ b/libmultipath/structs_vec.c @@ -3,6 +3,7 @@ #include <unistd.h> #include <checkers.h> +#include <libprio.h> #include "vector.h" #include "defaults.h" @@ -13,6 +14,7 @@ #include "dmparser.h" #include "config.h" #include "propsel.h" +#include "sysfs.h" #include "discovery.h" #include "waiter.h" @@ -80,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); @@ -266,7 +267,7 @@ retry: char new_alias[WWID_SIZE]; /* - * detect an external rename of the multipath device + * detect an external rename of the multipath device */ if (dm_get_name(mpp->wwid, DEFAULT_TARGET, new_alias)) { condlog(3, "%s multipath mapped device name has " @@ -289,6 +290,7 @@ retry: select_rr_weight(mpp); select_pgfailback(mpp); set_no_path_retry(mpp); + select_pg_timeout(mpp); return 0; out: @@ -372,10 +374,10 @@ verify_paths(struct multipath * mpp, struct vectors * vecs, vector rpvec) /* * see if path is in sysfs */ - if (!pp->dev || sysfs_get_dev(sysfs_path, - pp->dev, pp->dev_t, BLK_DEV_SIZE)) { + if (!pp->sysdev || sysfs_get_dev(pp->sysdev, + pp->dev_t, BLK_DEV_SIZE)) { condlog(0, "%s: failed to access path %s", mpp->alias, - pp->dev ? pp->dev : pp->dev_t); + pp->sysdev ? pp->sysdev->devpath : pp->dev_t); count++; vector_del_slot(mpp->paths, i); i--; diff --git a/libmultipath/switchgroup.c b/libmultipath/switchgroup.c index 757543f..e99a297 100644 --- a/libmultipath/switchgroup.c +++ b/libmultipath/switchgroup.c @@ -8,15 +8,31 @@ #include "structs.h" #include "switchgroup.h" +extern void +path_group_prio_update (struct pathgroup * pgp) +{ + int i; + int priority = 0; + struct path * pp; + + if (!pgp->paths) { + pgp->priority = 0; + return; + } + vector_foreach_slot (pgp->paths, pp, i) { + if (pp->state != PATH_DOWN) + priority += pp->priority; + } + pgp->priority = priority; +} + extern int select_path_group (struct multipath * mpp) { - int i, j; + int i; int highest = 0; int bestpg = 1; struct pathgroup * pgp; - struct path * pp; - int priority; if (!mpp->pg) return 1; @@ -25,14 +41,7 @@ select_path_group (struct multipath * mpp) if (!pgp->paths) continue; - priority = 0; - - vector_foreach_slot (pgp->paths, pp, j) { - if (pp->state != PATH_DOWN) - priority += pp->priority; - } - pgp->priority = priority; - + path_group_prio_update(pgp); if (pgp->priority > highest) { highest = pgp->priority; bestpg = i + 1; diff --git a/libmultipath/switchgroup.h b/libmultipath/switchgroup.h index edf6f24..9365e2e 100644 --- a/libmultipath/switchgroup.h +++ b/libmultipath/switchgroup.h @@ -1 +1,2 @@ +void path_group_prio_update (struct pathgroup * pgp); int select_path_group (struct multipath * mpp); diff --git a/libmultipath/sysfs.c b/libmultipath/sysfs.c new file mode 100644 index 0000000..b9621ac --- /dev/null +++ b/libmultipath/sysfs.c @@ -0,0 +1,460 @@ +/* + * Copyright (C) 2005-2006 Kay Sievers <kay.sievers@vrfy.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation version 2 of the License. + * + * 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 for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#include <stdlib.h> +#include <stdio.h> +#include <stddef.h> +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> +#include <errno.h> +#include <sys/stat.h> +#include <string.h> + +#include "checkers.h" +#include "vector.h" +#include "structs.h" +#include "sysfs.h" +#include "list.h" +#include "util.h" + +char sysfs_path[PATH_SIZE]; + +/* attribute value cache */ +static LIST_HEAD(attr_list); +struct sysfs_attr { + struct list_head node; + char path[PATH_SIZE]; + char *value; /* points to value_local if value is cached */ + char value_local[NAME_SIZE]; +}; + +/* list of sysfs devices */ +static LIST_HEAD(sysfs_dev_list); +struct sysfs_dev { + struct list_head node; + struct sysfs_device dev; +}; + +int sysfs_init(char *path, size_t len) +{ + if (path) { + strlcpy(sysfs_path, path, len); + remove_trailing_chars(sysfs_path, '/'); + } else + strlcpy(sysfs_path, "/sys", len); + dbg("sysfs_path='%s'", sysfs_path); + + INIT_LIST_HEAD(&attr_list); + INIT_LIST_HEAD(&sysfs_dev_list); + return 0; +} + +void sysfs_cleanup(void) +{ + struct sysfs_attr *attr_loop; + struct sysfs_attr *attr_temp; + + struct sysfs_dev *sysdev_loop; + struct sysfs_dev *sysdev_temp; + + list_for_each_entry_safe(attr_loop, attr_temp, &attr_list, node) { + list_del(&attr_loop->node); + free(attr_loop); + } + + list_for_each_entry_safe(sysdev_loop, sysdev_temp, &sysfs_dev_list, node) { + free(sysdev_loop); + } +} + +void sysfs_device_set_values(struct sysfs_device *dev, const char *devpath, + const char *subsystem, const char *driver) +{ + char *pos; + + strlcpy(dev->devpath, devpath, sizeof(dev->devpath)); + if (subsystem != NULL) + strlcpy(dev->subsystem, subsystem, sizeof(dev->subsystem)); + if (driver != NULL) + strlcpy(dev->driver, driver, sizeof(dev->driver)); + + /* set kernel name */ + pos = strrchr(dev->devpath, '/'); + if (pos == NULL) + return; + strlcpy(dev->kernel, &pos[1], sizeof(dev->kernel)); + dbg("kernel='%s'", dev->kernel); + + /* some devices have '!' in their name, change that to '/' */ + pos = dev->kernel; + while (pos[0] != '\0') { + if (pos[0] == '!') + pos[0] = '/'; + pos++; + } + + /* get kernel number */ + pos = &dev->kernel[strlen(dev->kernel)]; + while (isdigit(pos[-1])) + pos--; + strlcpy(dev->kernel_number, pos, sizeof(dev->kernel_number)); + dbg("kernel_number='%s'", dev->kernel_number); +} + +int sysfs_resolve_link(char *devpath, size_t size) +{ + char link_path[PATH_SIZE]; + char link_target[PATH_SIZE]; + int len; + int i; + int back; + + strlcpy(link_path, sysfs_path, sizeof(link_path)); + strlcat(link_path, devpath, sizeof(link_path)); + len = readlink(link_path, link_target, sizeof(link_target)); + if (len <= 0) + return -1; + link_target[len] = '\0'; + dbg("path link '%s' points to '%s'", devpath, link_target); + + for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++) + ; + dbg("base '%s', tail '%s', back %i", devpath, &link_target[back * 3], back); + for (i = 0; i <= back; i++) { + char *pos = strrchr(devpath, '/'); + + if (pos == NULL) + return -1; + pos[0] = '\0'; + } + dbg("after moving back '%s'", devpath); + strlcat(devpath, "/", size); + strlcat(devpath, &link_target[back * 3], size); + return 0; +} + +struct sysfs_device *sysfs_device_get(const char *devpath) +{ + char path[PATH_SIZE]; + char devpath_real[PATH_SIZE]; + struct sysfs_device *dev = NULL; + struct sysfs_dev *sysdev_loop, *sysdev; + struct stat statbuf; + char link_path[PATH_SIZE]; + char link_target[PATH_SIZE]; + int len; + char *pos; + + dbg("open '%s'", devpath); + strlcpy(devpath_real, devpath, sizeof(devpath_real)); + remove_trailing_chars(devpath_real, '/'); + + /* if we got a link, resolve it to the real device */ + strlcpy(path, sysfs_path, sizeof(path)); + strlcat(path, devpath_real, sizeof(path)); + if (lstat(path, &statbuf) != 0) { + /* if stat fails look in the cache */ + dbg("stat '%s' failed: %s", path, strerror(errno)); + list_for_each_entry(sysdev_loop, &sysfs_dev_list, node) { + if (strcmp(sysdev_loop->dev.devpath, devpath_real) == 0) { + dbg("found vanished dev in cache '%s'", sysdev_loop->dev.devpath); + return &sysdev_loop->dev; + } + } + return NULL; + } + if (S_ISLNK(statbuf.st_mode)) { + if (sysfs_resolve_link(devpath_real, sizeof(devpath_real)) != 0) + return NULL; + + } + + list_for_each_entry(sysdev_loop, &sysfs_dev_list, node) { + if (strcmp(sysdev_loop->dev.devpath, devpath_real) == 0) { + dbg("found dev in cache '%s'", sysdev_loop->dev.devpath); + dev = &sysdev_loop->dev; + } + } + if(!dev) { + /* it is a new device */ + dbg("new device '%s'", devpath_real); + sysdev = malloc(sizeof(struct sysfs_dev)); + if (sysdev == NULL) + return NULL; + memset(sysdev, 0x00, sizeof(struct sysfs_dev)); + list_add(&sysdev->node, &sysfs_dev_list); + dev = &sysdev->dev; + } + + sysfs_device_set_values(dev, devpath_real, NULL, NULL); + + /* get subsystem name */ + strlcpy(link_path, sysfs_path, sizeof(link_path)); + strlcat(link_path, dev->devpath, sizeof(link_path)); + strlcat(link_path, "/subsystem", sizeof(link_path)); + len = readlink(link_path, link_target, sizeof(link_target)); + if (len > 0) { + /* get subsystem from "subsystem" link */ + link_target[len] = '\0'; + dbg("subsystem link '%s' points to '%s'", link_path, link_target); + pos = strrchr(link_target, '/'); + if (pos != NULL) + strlcpy(dev->subsystem, &pos[1], sizeof(dev->subsystem)); + } else if (strncmp(dev->devpath, "/class/", 7) == 0) { + /* get subsystem from class dir */ + strlcpy(dev->subsystem, &dev->devpath[7], sizeof(dev->subsystem)); + pos = strchr(dev->subsystem, '/'); + if (pos != NULL) + pos[0] = '\0'; + else + dev->subsystem[0] = '\0'; + } else if (strncmp(dev->devpath, "/block/", 7) == 0) { + strlcpy(dev->subsystem, "block", sizeof(dev->subsystem)); + } else if (strncmp(dev->devpath, "/devices/", 9) == 0) { + /* get subsystem from "bus" link */ + strlcpy(link_path, sysfs_path, sizeof(link_path)); + strlcat(link_path, dev->devpath, sizeof(link_path)); + strlcat(link_path, "/bus", sizeof(link_path)); + len = readlink(link_path, link_target, sizeof(link_target)); + if (len > 0) { + link_target[len] = '\0'; + dbg("bus link '%s' points to '%s'", link_path, link_target); + pos = strrchr(link_target, '/'); + if (pos != NULL) + strlcpy(dev->subsystem, &pos[1], sizeof(dev->subsystem)); + } + } else if (strstr(dev->devpath, "/drivers/") != NULL) { + strlcpy(dev->subsystem, "drivers", sizeof(dev->subsystem)); + } else if (strncmp(dev->devpath, "/module/", 8) == 0) { + strlcpy(dev->subsystem, "module", sizeof(dev->subsystem)); + } + + /* get driver name */ + strlcpy(link_path, sysfs_path, sizeof(link_path)); + strlcat(link_path, dev->devpath, sizeof(link_path)); + strlcat(link_path, "/driver", sizeof(link_path)); + len = readlink(link_path, link_target, sizeof(link_target)); + if (len > 0) { + link_target[len] = '\0'; + dbg("driver link '%s' points to '%s'", link_path, link_target); + pos = strrchr(link_target, '/'); + if (pos != NULL) + strlcpy(dev->driver, &pos[1], sizeof(dev->driver)); + } + return dev; +} + +struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev) +{ + char parent_devpath[PATH_SIZE]; + char *pos; + + dbg("open '%s'", dev->devpath); + + /* look if we already know the parent */ + if (dev->parent != NULL) + return dev->parent; + + /* requesting a parent is only valid for devices */ + if ((strncmp(dev->devpath, "/devices/", 9) != 0) && + (strncmp(dev->devpath, "/subsystem/", 11) != 0) && + (strncmp(dev->devpath, "/class/", 7) != 0) && + (strncmp(dev->devpath, "/block/", 7) != 0)) + return NULL; + + strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath)); + dbg("'%s'", parent_devpath); + + /* strip last element */ + pos = strrchr(parent_devpath, '/'); + if (pos == NULL || pos == parent_devpath) + return NULL; + pos[0] = '\0'; + + /* are we at the top level of /devices */ + if (strcmp(parent_devpath, "/devices") == 0) { + dbg("/devices top level"); + return NULL; + } + + /* at the subsystems top level we want to follow the old-style "device" link */ + if (strncmp(parent_devpath, "/subsystem", 10) == 0) { + pos = strrchr(parent_devpath, '/'); + if (pos == &parent_devpath[10] || pos == parent_devpath || strcmp(pos, "/devices") == 0) { + dbg("/subsystem top level, look for device link"); + goto device_link; + } + } + if (strncmp(parent_devpath, "/class", 6) == 0) { + pos = strrchr(parent_devpath, '/'); + if (pos == &parent_devpath[6] || pos == parent_devpath) { + dbg("/class top level, look for device link"); + goto device_link; + } + } + if (strcmp(parent_devpath, "/block") == 0) { + dbg("/block top level, look for device link"); + goto device_link; + } + + /* get parent and remember it */ + dev->parent = sysfs_device_get(parent_devpath); + return dev->parent; + +device_link: + strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath)); + strlcat(parent_devpath, "/device", sizeof(parent_devpath)); + if (sysfs_resolve_link(parent_devpath, sizeof(parent_devpath)) != 0) + return NULL; + + /* get parent and remember it */ + dev->parent = sysfs_device_get(parent_devpath); + return dev->parent; +} + +struct sysfs_device *sysfs_device_get_parent_with_subsystem(struct sysfs_device *dev, const char *subsystem) +{ + struct sysfs_device *dev_parent; + + dev_parent = sysfs_device_get_parent(dev); + while (dev_parent != NULL) { + if (strcmp(dev_parent->subsystem, subsystem) == 0) + return dev_parent; + dev_parent = sysfs_device_get_parent(dev_parent); + } + return NULL; +} + +void sysfs_device_put(struct sysfs_device *dev) +{ + struct sysfs_dev *sysdev_loop; + + list_for_each_entry(sysdev_loop, &sysfs_dev_list, node) { + if (&sysdev_loop->dev == dev) { + dbg("removed dev '%s' from cache", + sysdev_loop->dev.devpath); + list_del(&sysdev_loop->node); + free(sysdev_loop); + return; + } + } + dbg("dev '%s' not found in cache", + sysdev_loop->dev.devpath); + + return; +} + +char *sysfs_attr_get_value(const char *devpath, const char *attr_name) +{ + char path_full[PATH_SIZE]; + const char *path; + char value[NAME_SIZE]; + struct sysfs_attr *attr_loop; + struct sysfs_attr *attr = NULL; + struct stat statbuf; + int fd; + ssize_t size; + size_t sysfs_len; + + dbg("open '%s'/'%s'", devpath, attr_name); + sysfs_len = strlcpy(path_full, sysfs_path, sizeof(path_full)); + path = &path_full[sysfs_len]; + strlcat(path_full, devpath, sizeof(path_full)); + strlcat(path_full, "/", sizeof(path_full)); + strlcat(path_full, attr_name, sizeof(path_full)); + + /* look for attribute in cache */ + list_for_each_entry(attr_loop, &attr_list, node) { + if (strcmp(attr_loop->path, path) == 0) { + dbg("found in cache '%s'", attr_loop->path); + attr = attr_loop; + } + } + if (!attr) { + /* store attribute in cache */ + dbg("new uncached attribute '%s'", path_full); + attr = malloc(sizeof(struct sysfs_attr)); + if (attr == NULL) + return NULL; + memset(attr, 0x00, sizeof(struct sysfs_attr)); + strlcpy(attr->path, path, sizeof(attr->path)); + dbg("add to cache '%s'", path_full); + list_add(&attr->node, &attr_list); + } else { + /* clear old value */ + if(attr->value) + memset(attr->value, 0x00, sizeof(attr->value)); + } + + if (lstat(path_full, &statbuf) != 0) { + dbg("stat '%s' failed: %s", path_full, strerror(errno)); + goto out; + } + + if (S_ISLNK(statbuf.st_mode)) { + /* links return the last element of the target path */ + char link_target[PATH_SIZE]; + int len; + const char *pos; + + len = readlink(path_full, link_target, sizeof(link_target)); + if (len > 0) { + link_target[len] = '\0'; + pos = strrchr(link_target, '/'); + if (pos != NULL) { + dbg("cache '%s' with link value '%s'", path_full, value); + strlcpy(attr->value_local, &pos[1], sizeof(attr->value_local)); + attr->value = attr->value_local; + } + } + goto out; + } + + /* skip directories */ + if (S_ISDIR(statbuf.st_mode)) + goto out; + + /* skip non-readable files */ + if ((statbuf.st_mode & S_IRUSR) == 0) + goto out; + + /* read attribute value */ + fd = open(path_full, O_RDONLY); + if (fd < 0) { + dbg("attribute '%s' does not exist", path_full); + goto out; + } + size = read(fd, value, sizeof(value)); + close(fd); + if (size < 0) + goto out; + if (size == sizeof(value)) + goto out; + + /* got a valid value, store and return it */ + value[size] = '\0'; + remove_trailing_chars(value, '\n'); + dbg("cache '%s' with attribute value '%s'", path_full, value); + strlcpy(attr->value_local, value, sizeof(attr->value_local)); + attr->value = attr->value_local; + +out: + return attr->value; +} diff --git a/libmultipath/sysfs.h b/libmultipath/sysfs.h new file mode 100644 index 0000000..e7fa3e7 --- /dev/null +++ b/libmultipath/sysfs.h @@ -0,0 +1,25 @@ +/* + * sysfs.h + */ + +#ifndef _LIBMULTIPATH_SYSFS_H +#define _LIBMULTIPATH_SYSFS_H + +#ifdef DEBUG +# define dbg(format, args...) printf(format "\n", ##args) +#else +# define dbg(format, args...) do {} while (0) +#endif + +int sysfs_init(char *path, size_t len); +void sysfs_cleanup(void); +void sysfs_device_set_values(struct sysfs_device *dev, const char *devpath, + const char *subsystem, const char *driver); +struct sysfs_device *sysfs_device_get(const char *devpath); +struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev); +struct sysfs_device *sysfs_device_get_parent_with_subsystem(struct sysfs_device *dev, const char *subsystem); +void sysfs_device_put(struct sysfs_device *dev); +char *sysfs_attr_get_value(const char *devpath, const char *attr_name); +int sysfs_resolve_link(char *path, size_t size); + +#endif diff --git a/libmultipath/util.c b/libmultipath/util.c index 911ec55..eaf2266 100644 --- a/libmultipath/util.c +++ b/libmultipath/util.c @@ -103,3 +103,55 @@ get_word (char * sentence, char ** word) return skip + len; } +size_t strlcpy(char *dst, const char *src, size_t size) +{ + size_t bytes = 0; + char *q = dst; + const char *p = src; + char ch; + + while ((ch = *p++)) { + if (bytes+1 < size) + *q++ = ch; + bytes++; + } + + /* If size == 0 there is no space for a final null... */ + if (size) + *q = '\0'; + return bytes; +} + +size_t strlcat(char *dst, const char *src, size_t size) +{ + size_t bytes = 0; + char *q = dst; + const char *p = src; + char ch; + + while (bytes < size && *q) { + q++; + bytes++; + } + if (bytes == size) + return (bytes + strlen(src)); + + while ((ch = *p++)) { + if (bytes+1 < size) + *q++ = ch; + bytes++; + } + + *q = '\0'; + return bytes; +} + +void remove_trailing_chars(char *path, char c) +{ + size_t len; + + len = strlen(path); + while (len > 0 && path[len-1] == c) + path[--len] = '\0'; +} + diff --git a/libmultipath/util.h b/libmultipath/util.h index e86bae2..d0df8aa 100644 --- a/libmultipath/util.h +++ b/libmultipath/util.h @@ -6,7 +6,9 @@ void strchop(char *); void basename (char * src, char * dst); int filepresent (char * run); int get_word (char * sentence, char ** word); - +size_t strlcpy(char *dst, const char *src, size_t size); +size_t strlcat(char *dst, const char *src, size_t size); +void remove_trailing_chars(char *path, char c); #define safe_sprintf(var, format, args...) \ snprintf(var, sizeof(var), format, ##args) >= sizeof(var) diff --git a/libmultipath/vector.h b/libmultipath/vector.h index 294f0b1..993ba79 100644 --- a/libmultipath/vector.h +++ b/libmultipath/vector.h @@ -37,6 +37,8 @@ typedef struct _vector *vector; #define vector_foreach_slot(v,p,i) \ for (i = 0; i < (v)->allocated && ((p) = (v)->slot[i]); i++) +#define vector_foreach_slot_after(v,p,i) \ + for (; i < (v)->allocated && ((p) = (v)->slot[i]); i++) /* Prototypes */ extern vector vector_alloc(void); diff --git a/libmultipath/version.h b/libmultipath/version.h index d577ec9..c4d3266 100644 --- a/libmultipath/version.h +++ b/libmultipath/version.h @@ -20,8 +20,8 @@ #ifndef _VERSION_H #define _VERSION_H -#define VERSION_CODE 0x000407 -#define DATE_CODE 0x030c06 +#define VERSION_CODE 0x000408 +#define DATE_CODE 0x080207 #define PROG "multipath-tools" diff --git a/libmultipath/waiter.c b/libmultipath/waiter.c index d7af0d1..624e282 100644 --- a/libmultipath/waiter.c +++ b/libmultipath/waiter.c @@ -58,7 +58,7 @@ void free_waiter (void *data) void stop_waiter_thread (struct multipath *mpp, struct vectors *vecs) { struct event_thread *wp = (struct event_thread *)mpp->waiter; - + if (!wp) { condlog(3, "%s: no waiter thread", mpp->alias); return; diff --git a/libprio/Makefile b/libprio/Makefile new file mode 100644 index 0000000..127def6 --- /dev/null +++ b/libprio/Makefile @@ -0,0 +1,29 @@ +# Makefile +# +# Copyright (C) 2007 Christophe Varoqui, <christophe.varoqui@free.fr> +# +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 command.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 <shbader@de.ibm.com> + * + * This file is released under the GPL. + */ +#include <stdio.h> + +#include <debug.h> + +#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 <shbader@de.ibm.com> + * + * This file is released under the GPL. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <inttypes.h> + +#define __user +#include <scsi/sg.h> + +#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 <shbader@de.ibm.com> + * + * 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 <shbader@de.ibm.com> + * + * 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/command.c b/libprio/command.c new file mode 100644 index 0000000..1154c59 --- /dev/null +++ b/libprio/command.c @@ -0,0 +1,37 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> + +#include "../libcheckers/checkers.h" + +#include <vector.h> +#include <structs.h> +#include <callout.h> +#include <debug.h> +#include "libprio.h" + +int prio_command(struct path * pp) +{ + char buff[CALLOUT_MAX_SIZE]; + char prio[16], *ptr; + int priority; + + if (!pp->getprio) + return PRIO_UNDEF; + + if (apply_format(pp->getprio, &buff[0], pp)) { + condlog(0, "error formatting prio callout command"); + return PRIO_UNDEF; + } else if (execute_program(buff, prio, 16)) { + condlog(0, "error calling out %s", buff); + return PRIO_UNDEF; + } + + condlog(3, "%s: priority %s", pp->dev, prio); + priority = strtoul(prio, &ptr, 10); + if (prio == ptr) + return PRIO_UNDEF; + + return priority; +} diff --git a/libprio/command.h b/libprio/command.h new file mode 100644 index 0000000..a1af97e --- /dev/null +++ b/libprio/command.h @@ -0,0 +1,7 @@ +#ifndef _COMMAND_H +#define _COMMAND_H + +#define PRIO_COMMAND "command" +int prio_command(struct path * pp); + +#endif 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 <stdio.h> + +#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 <stdio.h> +#include <string.h> +#include <sys/ioctl.h> + +#include <sg_include.h> +#include <debug.h> + +#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 <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <stdlib.h> + +#include <sg_include.h> +#include <debug.h> + +#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[32]; + 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <errno.h> + +#include <sg_include.h> +#include <debug.h> + +#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..bc79fa7 --- /dev/null +++ b/libprio/libprio.c @@ -0,0 +1,74 @@ +#include <stdio.h> +#include <string.h> + +#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 = PRIO_COMMAND, + .getprio = prio_command + }, + { + .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..36d1eff --- /dev/null +++ b/libprio/libprio.h @@ -0,0 +1,44 @@ +#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" +#include "command.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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <assert.h> + +#include <sg_include.h> +#include <debug.h> + +#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; i<size; i++) { + p += snprintf(p, 10*(size-i), "0x%02x ", cdb[i]); + } + condlog(0, "%s", buf); +} + +static void process_sg_error(struct sg_io_hdr *io_hdr) +{ + int i; + char buf[128*5+1]; + char * p = &buf[0]; + + condlog(0, "- masked_status=0x%02x, host_status=0x%02x, " + "driver_status=0x%02x", io_hdr->masked_status, + io_hdr->host_status, io_hdr->driver_status); + if (io_hdr->sb_len_wr > 0) { + condlog(0, "- SCSI sense data: "); + for (i=0; i<io_hdr->sb_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..68441c1 --- /dev/null +++ b/libprio/random.c @@ -0,0 +1,15 @@ +#include <stdio.h> +#include <stdlib.h> +#include <sys/time.h> +#include <time.h> + +#include "libprio.h" + +int prio_random(struct path * pp) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + srand((unsigned int)tv.tv_usec); + return 1+(int) (10.0*rand()/(RAND_MAX+1.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 <stdio.h> +#include <string.h> +#include <sys/ioctl.h> + +#include <sg_include.h> +#include <debug.h> + +#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-tools.spec.in b/multipath-tools.spec.in index 31db0f6..3caede6 100644 --- a/multipath-tools.spec.in +++ b/multipath-tools.spec.in @@ -48,8 +48,8 @@ rm -rf $RPM_BUILD_ROOT %{prefix}/sbin/mpath_prio_emc %{prefix}/sbin/mpath_prio_random %{prefix}/sbin/mpath_prio_balance_units -%{prefix}/sbin/mpath_prio_ontap -%{prefix}/sbin/mpath_prio_tpc +%{prefix}/sbin/mpath_prio_netapp +%{prefix}/sbin/mpath_prio_rdac %{prefix}/sbin/mpath_prio_hds_modular %{prefix}/usr/share/man/man8/devmap_name.8.gz %{prefix}/usr/share/man/man8/multipath.8.gz diff --git a/multipath.conf.annotated b/multipath.conf.annotated index 649ff8c..49c9c5c 100644 --- a/multipath.conf.annotated +++ b/multipath.conf.annotated @@ -11,7 +11,7 @@ # # # # name : udev_dir # # desc : directory where udev creates its device nodes -# # default : /udev +# # default : /dev # # # udev_dir /dev # @@ -38,7 +38,7 @@ # # scope : multipath # # desc : the default path grouping policy to apply to unspecified # # multipaths -# # default : multibus +# # default : failover # # # path_grouping_policy multibus # @@ -47,28 +47,28 @@ # # scope : multipath # # desc : the default program and args to callout to obtain a unique # # path identifier. Absolute path required -# # default : /sbin/scsi_id -g -u -s +# # default : /lib/udev/scsi_id -g -u -s # # -# getuid_callout "/sbin/scsi_id -g -u -s /block/%n" +# 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 # # scope : multipath & multipathd # # desc : the default method used to determine the paths' state # # values : readsector0|tur|emc_clariion|hp_sw|directio -# # default : readsector0 +# # default : directio # # -# #path_checker readsector0 +# #path_checker directio # # # # # name : rr_min_io @@ -80,6 +80,16 @@ # rr_min_io 100 # # # +# # name : max_fds +# # scope : multipathd +# # desc : Sets the maximum number of open file descriptors for the +# # multipathd process. +# # values : unlimited|n > 0 +# # default : None +# # +# max_fds 8192 +# +# # # # name : rr_weight # # scope : multipath # # desc : if set to priorities the multipath configurator will assign @@ -96,9 +106,9 @@ # # 0 means immediate failback, values >0 means deffered failback # # expressed in seconds. # # values : manual|immediate|n > 0 -# # default : immediate +# # default : manual # # -# failback manual +# failback immediate # # # # # name : no_path_retry @@ -198,10 +208,10 @@ # # name : path_checker # # scope : multipathd # # desc : path checking alorithm to use to check path state -# # values : readsector0, tur -# # default : readsector0 +# # values : readsector0|tur|emc_clariion|hp_sw|directio +# # default : directio # # -# # path_checker readsector0 +# # path_checker directio # # # # # name : path_selector @@ -219,9 +229,9 @@ # # 0 means immediate failback, values >0 means deffered failback # # expressed in seconds. # # values : manual|immediate|n > 0 -# # default : immediate +# # default : manual # # -# failback manual +# failback immediate # # # # # name : no_path_retry @@ -291,29 +301,28 @@ # # scope : multipath # # desc : the program and args to callout to obtain a unique # # path identifier. Absolute path required -# # default : /sbin/scsi_id -g -u -s +# # default : /lib/udev/scsi_id -g -u -s # # -# getuid_callout "/sbin/scsi_id -g -u -s /block/%n" +# 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 # # scope : multipathd # # desc : path checking alorithm to use to check path state -# # values : readsector0, tur -# # default : readsector0 +# # values : readsector0|tur|emc_clariion|hp_sw|directio +# # default : directio # # -# path_checker readsector0 +# path_checker directio # # # # # name : path_selector @@ -331,7 +340,7 @@ # # 0 means immediate failback, values >0 means deffered failback # # expressed in seconds. # # values : manual|immediate|n > 0 -# # default : immediate +# # default : manual # # # failback 30 # diff --git a/multipath.conf.synthetic b/multipath.conf.synthetic index e5e6cd0..33f820b 100644 --- a/multipath.conf.synthetic +++ b/multipath.conf.synthetic @@ -7,10 +7,11 @@ # polling_interval 10 # selector "round-robin 0" # path_grouping_policy multibus -# getuid_callout "/sbin/scsi_id -g -u -s /block/%n" -# prio_callout /bin/true -# path_checker readsector0 +# getuid_callout "/lib/udev/scsi_id -g -u -s /block/%n" +# prio const +# path_checker directio # rr_min_io 100 +# max_fds 8192 # rr_weight priorities # failback immediate # no_path_retry fail @@ -35,7 +36,7 @@ # wwid 3600508b4000156d700012000000b0000 # alias yellow # path_grouping_policy multibus -# path_checker readsector0 +# path_checker directio # path_selector "round-robin 0" # failback manual # rr_weight priorities @@ -52,8 +53,8 @@ # vendor "COMPAQ " # product "HSV110 (C)COMPAQ" # path_grouping_policy multibus -# getuid_callout "/sbin/scsi_id -g -u -s /block/%n" -# path_checker readsector0 +# getuid_callout "/lib/udev/scsi_id -g -u -s /block/%n" +# path_checker directio # path_selector "round-robin 0" # hardware_handler "0" # failback 15 diff --git a/multipath/02_multipath b/multipath/02_multipath index 1a5d5a1..067c582 100755 --- a/multipath/02_multipath +++ b/multipath/02_multipath @@ -12,10 +12,10 @@ cp /sbin/kpartx $INITRDDIR/sbin # feed the dependencies too # scsi_id is dynamicaly linked, so store the libs too # -cp /sbin/scsi_id $INITRDDIR/sbin +cp /lib/udev/scsi_id $INITRDDIR/lib/udev/ cp /bin/mountpoint $INITRDDIR/bin -PROGS="/sbin/scsi_id /bin/mountpoint" +PROGS="/lib/udev/scsi_id /bin/mountpoint" LIBS=`ldd $PROGS | grep -v linux-gate.so | sort -u | \ awk '{print $3}'` for i in $LIBS diff --git a/multipath/Makefile b/multipath/Makefile index efbe59f..440dce4 100644 --- a/multipath/Makefile +++ b/multipath/Makefile @@ -5,14 +5,14 @@ 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) ifeq ($(strip $(BUILD)),klibc) - OBJS += $(libdm) $(libsysfs) + OBJS += $(libdm) else - LDFLAGS += -ldevmapper -lsysfs + LDFLAGS += -ldevmapper endif EXEC = multipath @@ -29,6 +29,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) @@ -37,7 +40,7 @@ $(MULTIPATHLIB)-$(BUILD).a: install: install -d $(DESTDIR)$(bindir) - install -m 755 $(EXEC) $(DESTDIR)$(bindir)/ + $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/ install -d $(DESTDIR)/etc/udev/rules.d install -m 644 multipath.rules $(DESTDIR)/etc/udev/rules.d/ install -d $(DESTDIR)$(mandir) @@ -49,6 +52,7 @@ uninstall: rm $(DESTDIR)/etc/udev/rules.d/multipath.rules rm $(DESTDIR)$(bindir)/$(EXEC) rm $(DESTDIR)$(mandir)/$(EXEC).8 + rm $(DESTDIR)$(man5dir)/$(EXEC).conf.5 clean: rm -f core *.o $(EXEC) diff --git a/multipath/main.c b/multipath/main.c index ee51051..49ffa85 100644 --- a/multipath/main.c +++ b/multipath/main.c @@ -25,9 +25,9 @@ #include <stdio.h> #include <unistd.h> #include <ctype.h> -#include <sysfs/libsysfs.h> #include <checkers.h> +#include <libprio.h> #include <vector.h> #include <memory.h> #include <libdevmapper.h> @@ -37,6 +37,7 @@ #include <structs.h> #include <structs_vec.h> #include <dmparser.h> +#include <sysfs.h> #include <config.h> #include <blacklist.h> #include <discovery.h> @@ -225,7 +226,7 @@ configure (void) vecs.mpvec = curmp; /* - * if we have a blacklisted device parameter, exit early + * dev is "/dev/" . "sysfs block dev" */ if (conf->dev) { if (!strncmp(conf->dev, "/dev/", 5) && @@ -235,8 +236,12 @@ configure (void) dev = conf->dev; } - if (dev && blacklist(conf->blist_devnode, conf->elist_devnode, dev)) - goto out; + /* + * if we have a blacklisted device parameter, exit early + */ + if (dev && + (filter_devnode(conf->blist_devnode, conf->elist_devnode, dev) > 0)) + goto out; /* * scope limiting must be translated into a wwid @@ -250,8 +255,8 @@ configure (void) goto out; } condlog(3, "scope limited to %s", refwwid); - - if (blacklist(conf->blist_wwid, conf->elist_wwid, refwwid)) + if (filter_wwid(conf->blist_wwid, conf->elist_wwid, + refwwid) > 0) goto out; } @@ -370,13 +375,13 @@ main (int argc, char *argv[]) if (dm_prereq(DEFAULT_TARGET, 1, 0, 3)) exit(1); - if (sysfs_get_mnt_path(sysfs_path, FILE_NAME_SIZE)) { - condlog(0, "multipath tools need sysfs mounted"); - exit(1); - } if (load_config(DEFAULT_CONFIGFILE)) exit(1); + if (sysfs_init(conf->sysfs_dir, FILE_NAME_SIZE)) { + condlog(0, "multipath tools need sysfs mounted"); + exit(1); + } while ((arg = getopt(argc, argv, ":dhl::FfM:v:p:b:t")) != EOF ) { switch(arg) { case 1: printf("optarg : %s\n",optarg); @@ -418,7 +423,7 @@ main (int argc, char *argv[]) if (conf->pgpolicy_flag == -1) { printf("'%s' is not a valid policy\n", optarg); usage(argv[0]); - } + } break; case 't': dump_config(); @@ -427,14 +432,14 @@ main (int argc, char *argv[]) usage(argv[0]); case ':': fprintf(stderr, "Missing option arguement\n"); - usage(argv[0]); + usage(argv[0]); case '?': fprintf(stderr, "Unknown switch: %s\n", optarg); usage(argv[0]); default: usage(argv[0]); } - } + } if (optind < argc) { conf->dev = MALLOC(FILE_NAME_SIZE); @@ -451,6 +456,7 @@ main (int argc, char *argv[]) conf->dev_type = DEV_DEVMAP; } + dm_init(); if (conf->remove == FLUSH_ONE) { if (conf->dev_type == DEV_DEVMAP) @@ -468,6 +474,7 @@ main (int argc, char *argv[]) condlog(3, "restart multipath configuration process"); out: + sysfs_cleanup(); free_config(conf); dm_lib_release(); dm_lib_exit(); diff --git a/multipath/multipath.rules b/multipath/multipath.rules index 10751ce..9d3579f 100644 --- a/multipath/multipath.rules +++ b/multipath/multipath.rules @@ -1,18 +1,7 @@ # -# multipath and multipath partitions nodes are created in /dev/mapper/ -# this file should be installed in /etc/udev/rules.d +# udev rules for multipathing. +# The persistent symlinks are created with the kpartx rules # -# !! udev must not discard DM events !! -# !! check the other installed rules !! -# - -# lookup the devmap name -#ACTION=="add", SUBSYSTEM=="block", KERNEL=="dm-*", \ -# PROGRAM="/sbin/devmap_name %M %m" -ACTION=="add", SUBSYSTEM=="block", KERNEL=="dm-*", \ - PROGRAM="/sbin/dmsetup -j %M -m %m --noopencount --noheadings -c -o name info" - -# take care of devmap partitioning -ACTION=="add", SUBSYSTEM=="block", KERNEL=="dm-*", \ - RUN+="/sbin/kpartx -a /dev/mapper/%c" +# socket for uevents +RUN+="socket:/org/kernel/dm/multipath_event" diff --git a/multipathd/Makefile b/multipathd/Makefile index f593c13..259a266 100644 --- a/multipathd/Makefile +++ b/multipathd/Makefile @@ -7,7 +7,7 @@ include ../Makefile.inc # basic flags setting # CFLAGS += -DDAEMON -I$(multipathdir) -I$(checkersdir) -LDFLAGS = -lpthread -ldevmapper -lsysfs -lreadline -lncurses +LDFLAGS = -lpthread -ldevmapper -lreadline -lncurses # # debuging stuff @@ -21,6 +21,7 @@ LDFLAGS = -lpthread -ldevmapper -lsysfs -lreadline -lncurses # OBJS = main.o pidfile.o uxlsnr.o uxclnt.o cli.o cli_handlers.o \ $(MULTIPATHLIB)-glibc.a $(CHECKERSLIB)-glibc.a \ + $(LIBPRIO)-glibc.a # @@ -36,6 +37,9 @@ klibc: $(EXEC): clean $(OBJS) $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) +$(LIBPRIO)-glibc.a: + $(MAKE) -C $(libpriodir) BUILD=glibc glibc + $(CHECKERSLIB)-glibc.a: $(MAKE) -C $(checkersdir) BUILD=glibc glibc @@ -44,7 +48,7 @@ $(MULTIPATHLIB)-glibc.a: install: install -d $(DESTDIR)$(bindir) - install -m 755 $(EXEC) $(DESTDIR)$(bindir) + $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir) install -d $(DESTDIR)$(rcdir) install -d $(DESTDIR)$(mandir) install -m 644 $(EXEC).8 $(DESTDIR)$(mandir) diff --git a/multipathd/cli.c b/multipathd/cli.c index 4edf1af..d786eef 100644 --- a/multipathd/cli.c +++ b/multipathd/cli.c @@ -5,6 +5,7 @@ #include <vector.h> #include <util.h> #include <version.h> +#include <readline/readline.h> #include "cli.h" @@ -73,6 +74,30 @@ add_handler (int fp, int (*fn)(void *, char **, int *, void *)) return 0; } +static struct handler * +find_handler (int fp) +{ + int i; + struct handler *h; + + vector_foreach_slot (handlers, h, i) + if (h->fingerprint == fp) + return h; + + return NULL; +} + +int +set_handler_callback (int fp, int (*fn)(void *, char **, int *, void *)) +{ + struct handler * h = find_handler(fp); + + if (!h) + return 1; + h->fn = fn; + return 0; +} + static void free_key (struct key * kw) { @@ -149,27 +174,26 @@ load_keys (void) keys = NULL; return 1; } - return 0; } static struct key * -find_key (char * str) +find_key (const char * str) { int i; int len, klen; struct key * kw = NULL; struct key * foundkw = NULL; - vector_foreach_slot (keys, kw, i) { - len = strlen(str); - klen = strlen(kw->str); + len = strlen(str); + vector_foreach_slot (keys, kw, i) { if (strncmp(kw->str, str, len)) continue; - else if (len == klen) + klen = strlen(kw->str); + if (len == klen) return kw; /* exact match */ - else if (len < klen) { + if (len < klen) { if (!foundkw) foundkw = kw; /* shortcut match */ else @@ -178,24 +202,16 @@ find_key (char * str) } return foundkw; } - -static struct handler * -find_handler (int fp) -{ - int i; - struct handler *h; - - vector_foreach_slot (handlers, h, i) - if (h->fingerprint == fp) - return h; - return NULL; -} +#define E_SYNTAX 1 +#define E_NOPARM 2 +#define E_NOMEM 3 -static vector -get_cmdvec (char * cmd) +static int +get_cmdvec (char * cmd, vector *v) { int fwd = 1; + int r = 0; char * p = cmd; char * buff; struct key * kw = NULL; @@ -203,9 +219,10 @@ get_cmdvec (char * cmd) vector cmdvec; cmdvec = vector_alloc(); + *v = cmdvec; if (!cmdvec) - return NULL; + return E_NOMEM; while (fwd) { fwd = get_word(p, &buff); @@ -218,18 +235,19 @@ get_cmdvec (char * cmd) FREE(buff); if (!kw) - goto out; /* synthax error */ + return E_SYNTAX; cmdkw = alloc_key(); - if (!cmdkw) + if (!cmdkw) { + r = E_NOMEM; goto out; - + } if (!vector_alloc_slot(cmdvec)) { FREE(cmdkw); + r = E_NOMEM; goto out; } - vector_set_slot(cmdvec, cmdkw); cmdkw->code = kw->code; cmdkw->has_param = kw->has_param; @@ -241,18 +259,18 @@ get_cmdvec (char * cmd) fwd = get_word(p, &buff); if (!buff) - goto out; + return E_NOPARM; p += fwd; cmdkw->param = buff; } } - - return cmdvec; + return 0; out: free_keys(cmdvec); - return NULL; + *v = NULL; + return r; } static int @@ -262,6 +280,9 @@ fingerprint(vector vec) int fp = 0; struct key * kw; + if (!vec) + return 0; + vector_foreach_slot(vec, kw, i) fp += kw->code; @@ -334,9 +355,13 @@ parse_cmd (char * cmd, char ** reply, int * len, void * data) { int r; struct handler * h; - vector cmdvec = get_cmdvec(cmd); + vector cmdvec; + + r = get_cmdvec(cmd, &cmdvec); - if (!cmdvec) { + if (r) { + if (cmdvec) + free_keys(cmdvec); *reply = genhelp_handler(); *len = strlen(*reply) + 1; return 0; @@ -371,3 +396,141 @@ get_keyparam (vector v, int code) return NULL; } + +int +cli_init (void) { + if (load_keys()) + return 1; + + if (alloc_handlers()) + return 1; + + add_handler(LIST+PATHS, NULL); + add_handler(LIST+MAPS, NULL); + add_handler(LIST+MAPS+STATUS, NULL); + add_handler(LIST+MAPS+STATS, NULL); + add_handler(LIST+MAPS+TOPOLOGY, NULL); + add_handler(LIST+TOPOLOGY, NULL); + add_handler(LIST+MAP+TOPOLOGY, NULL); + add_handler(LIST+CONFIG, NULL); + add_handler(LIST+BLACKLIST, NULL); + add_handler(LIST+DEVICES, NULL); + add_handler(ADD+PATH, NULL); + add_handler(DEL+PATH, NULL); + add_handler(ADD+MAP, NULL); + add_handler(DEL+MAP, NULL); + add_handler(SWITCH+MAP+GROUP, NULL); + add_handler(RECONFIGURE, NULL); + add_handler(SUSPEND+MAP, NULL); + add_handler(RESUME+MAP, NULL); + add_handler(REINSTATE+PATH, NULL); + add_handler(FAIL+PATH, NULL); + + return 0; +} + +static int +key_match_fingerprint (struct key * kw, int fp) +{ + if (!fp) + return 0; + + return ((fp & kw->code) == kw->code); +} + +/* + * This is the readline completion handler + */ +char * +key_generator (const char * str, int state) +{ + static int index, len, rlfp, has_param; + struct key * kw; + int i; + struct handler *h; + vector v; + + if (!state) { + index = 0; + has_param = 0; + rlfp = 0; + len = strlen(str); + int r = get_cmdvec(rl_line_buffer, &v); + /* + * If a word completion is in progess, we don't want + * to take an exact keyword match in the fingerprint. + * For ex "show map[tab]" would validate "map" and discard + * "maps" as a valid candidate. + */ + if (v && len) + vector_del_slot(v, VECTOR_SIZE(v) - 1); + /* + * Clean up the mess if we dropped the last slot of a 1-slot + * vector + */ + if (v && !VECTOR_SIZE(v)) { + vector_free(v); + v = NULL; + } + /* + * If last keyword takes a param, don't even try to guess + */ + if (r == E_NOPARM) { + has_param = 1; + return (strdup("(value)")); + } + /* + * Compute a command fingerprint to find out possible completions. + * Once done, the vector is useless. Free it. + */ + if (v) { + rlfp = fingerprint(v); + free_keys(v); + } + } + /* + * No more completions for parameter placeholder. + * Brave souls might try to add parameter completion by walking paths and + * multipaths vectors. + */ + if (has_param) + return ((char *)NULL); + /* + * Loop through keywords for completion candidates + */ + vector_foreach_slot_after (keys, kw, index) { + if (!strncmp(kw->str, str, len)) { + /* + * Discard keywords already in the command line + */ + if (key_match_fingerprint(kw, rlfp)) { + struct key * curkw = find_key(str); + if (!curkw || (curkw != kw)) + continue; + } + /* + * Discard keywords making syntax errors. + * + * nfp is the candidate fingerprint we try to + * validate against all known command fingerprints. + */ + int nfp = rlfp | kw->code; + vector_foreach_slot(handlers, h, i) { + if (!rlfp || ((h->fingerprint & nfp) == nfp)) { + /* + * At least one full command is + * possible with this keyword : + * Consider it validated + */ + index++; + return (strdup(kw->str)); + } + } + } + } + /* + * No more candidates + */ + return ((char *)NULL); +} + diff --git a/multipathd/cli.h b/multipathd/cli.h index 99dfcbd..a2397df 100644 --- a/multipathd/cli.h +++ b/multipathd/cli.h @@ -61,8 +61,11 @@ vector handlers; int alloc_handlers (void); int add_handler (int fp, int (*fn)(void *, char **, int *, void *)); +int set_handler_callback (int fp, int (*fn)(void *, char **, int *, void *)); int parse_cmd (char * cmd, char ** reply, int * len, void *); int load_keys (void); char * get_keyparam (vector v, int code); void free_keys (vector vec); void free_handlers (vector vec); +int cli_init (void); +char * key_generator (const char * str, int state); diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c index b01a60a..f71dca0 100644 --- a/multipathd/cli_handlers.c +++ b/multipathd/cli_handlers.c @@ -13,6 +13,7 @@ #include <blacklist.h> #include <debug.h> #include <print.h> +#include <sysfs.h> #include "main.h" #include "cli.h" @@ -286,8 +287,8 @@ cli_add_path (void * v, char ** reply, int * len, void * data) condlog(2, "%s: add path (operator)", param); - if (blacklist(conf->blist_devnode, conf->elist_devnode, param) || - (r = ev_add_path(param, vecs)) == 2) { + if (filter_devnode(conf->blist_devnode, conf->elist_devnode, + param) > 0 || (r = ev_add_path(param, vecs)) == 2) { *reply = strdup("blacklisted"); *len = strlen(*reply) + 1; condlog(2, "%s: path blacklisted", param); @@ -312,16 +313,30 @@ cli_add_map (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); + int minor; + char dev_path[PATH_SIZE]; + struct sysfs_device *sysdev; condlog(2, "%s: add map (operator)", param); - if (blacklist(conf->blist_wwid, conf->elist_wwid, param)) { + if (filter_wwid(conf->blist_wwid, conf->elist_wwid, param) > 0) { *reply = strdup("blacklisted"); *len = strlen(*reply) + 1; condlog(2, "%s: map blacklisted", param); return 0; } - return ev_add_map(param, vecs); + minor = dm_get_minor(param); + if (minor < 0) { + condlog(2, "%s: not a device mapper table", param); + return 0; + } + sprintf(dev_path,"/block/dm-%d", minor); + sysdev = sysfs_device_get(dev_path); + if (!sysdev) { + condlog(2, "%s: not found in sysfs", param); + return 0; + } + return ev_add_map(sysdev, vecs); } int diff --git a/multipathd/main.c b/multipathd/main.c index 4c99ce6..6441760 100644 --- a/multipathd/main.c +++ b/multipathd/main.c @@ -12,12 +12,8 @@ #include <sys/types.h> #include <fcntl.h> #include <errno.h> - -/* - * libsysfs - */ -#include <sysfs/libsysfs.h> -#include <sysfs/dlist.h> +#include <sys/time.h> +#include <sys/resource.h> /* * libcheckers @@ -40,6 +36,7 @@ #include <structs_vec.h> #include <dmparser.h> #include <devmapper.h> +#include <sysfs.h> #include <dict.h> #include <discovery.h> #include <debug.h> @@ -151,7 +148,7 @@ static void sync_map_state(struct multipath *mpp) { struct pathgroup *pgp; - struct path *pp; + struct path *pp; unsigned int i, j; vector_foreach_slot (mpp->pg, pgp, i){ @@ -177,7 +174,7 @@ sync_maps_state(vector mpvec) unsigned int i; struct multipath *mpp; - vector_foreach_slot (mpvec, mpp, i) + vector_foreach_slot (mpvec, mpp, i) sync_map_state(mpp); } @@ -208,54 +205,50 @@ flush_map(struct multipath * mpp, struct vectors * vecs) } static int -uev_add_map (char * devname, struct vectors * vecs) +uev_add_map (struct sysfs_device * dev, struct vectors * vecs) { - condlog(2, "%s: add map (uevent)", devname); - return ev_add_map(devname, vecs); + condlog(2, "%s: add map (uevent)", dev->kernel); + return ev_add_map(dev, vecs); } int -ev_add_map (char * devname, struct vectors * vecs) +ev_add_map (struct sysfs_device * dev, struct vectors * vecs) { - int major, minor; - char dev_t[BLK_DEV_SIZE]; char * alias; + char *dev_t; + int major, minor; char * refwwid; struct multipath * mpp; int map_present; int r = 1; - /* libsysfs seems to forget to terminate the string... */ - memset(dev_t, 0, BLK_DEV_SIZE); - if (sscanf(devname, "dm-%d", &minor) == 1 && - !sysfs_get_dev(sysfs_path, devname, dev_t, BLK_DEV_SIZE) && - sscanf(dev_t, "%d:%d", &major, &minor) == 2) - alias = dm_mapname(major, minor); - else - alias = STRDUP(devname); - + dev_t = sysfs_attr_get_value(dev->devpath, "dev"); + + if (!dev_t || sscanf(dev_t, "%d:%d", &major, &minor) != 2) + return 1; + + alias = dm_mapname(major, minor); + if (!alias) return 1; - + map_present = dm_map_present(alias); if (map_present && dm_type(alias, DEFAULT_TARGET) <= 0) { condlog(4, "%s: not a multipath map", alias); - FREE(alias); return 0; } mpp = find_mp_by_alias(vecs->mpvec, alias); if (mpp) { - /* + /* * Not really an error -- we generate our own uevent * if we create a multipath mapped device as a result * of uev_add_path */ condlog(0, "%s: devmap already registered", - devname); - FREE(alias); + dev->kernel); return 0; } @@ -265,31 +258,30 @@ ev_add_map (char * devname, struct vectors * vecs) if (map_present && (mpp = add_map_without_path(vecs, minor, alias, start_waiter_thread))) { sync_map_state(mpp); - condlog(3, "%s: devmap %s added", alias, devname); + condlog(3, "%s: devmap %s added", alias, dev->kernel); return 0; } - refwwid = get_refwwid(devname, DEV_DEVMAP, vecs->pathvec); + refwwid = get_refwwid(dev->kernel, DEV_DEVMAP, vecs->pathvec); if (refwwid) { r = coalesce_paths(vecs, NULL, refwwid); dm_lib_release(); } - + if (!r) - condlog(3, "%s: devmap %s added", alias, devname); + condlog(3, "%s: devmap %s added", alias, dev->kernel); else - condlog(0, "%s: uev_add_map %s failed", alias, devname); + condlog(0, "%s: uev_add_map %s failed", alias, dev->kernel); FREE(refwwid); - FREE(alias); return r; } static int -uev_remove_map (char * devname, struct vectors * vecs) +uev_remove_map (struct sysfs_device * dev, struct vectors * vecs) { - condlog(2, "%s: remove map (uevent)", devname); - return ev_remove_map(devname, vecs); + condlog(2, "%s: remove map (uevent)", dev->kernel); + return ev_remove_map(dev->kernel, vecs); } int @@ -310,13 +302,13 @@ ev_remove_map (char * devname, struct vectors * vecs) } static int -uev_umount_map (char * devname, struct vectors * vecs) +uev_umount_map (struct sysfs_device * dev, struct vectors * vecs) { struct multipath * mpp; - condlog(2, "%s: umount map (uevent)", devname); + condlog(2, "%s: umount map (uevent)", dev->kernel); - mpp = find_mp_by_str(vecs->mpvec, devname); + mpp = find_mp_by_str(vecs->mpvec, dev->kernel); if (!mpp) return 0; @@ -329,12 +321,12 @@ uev_umount_map (char * devname, struct vectors * vecs) return 0; } - + static int -uev_add_path (char * devname, struct vectors * vecs) +uev_add_path (struct sysfs_device * dev, struct vectors * vecs) { - condlog(2, "%s: add path (uevent)", devname); - return (ev_add_path(devname, vecs) != 1)? 0 : 1; + condlog(2, "%s: add path (uevent)", dev->kernel); + return (ev_add_path(dev->kernel, vecs) != 1)? 0 : 1; } @@ -378,13 +370,13 @@ ev_add_path (char * devname, struct vectors * vecs) condlog(0, "%s: failed to get path uid", devname); return 1; /* leave path added to pathvec */ } - if (blacklist_path(conf, pp)){ + if (filter_path(conf, pp) > 0){ int i = find_slot(vecs->pathvec, (void *)pp); if (i != -1) vector_del_slot(vecs->pathvec, i); free_path(pp); return 2; - } + } mpp = pp->mpp = find_mp_by_wwid(vecs->mpvec, pp->wwid); rescan: if (mpp) { @@ -416,8 +408,8 @@ rescan: condlog(0, "%s: failed in domap for addition of new " "path %s", mpp->alias, devname); /* - * deal with asynchronous uevents :(( - */ + * deal with asynchronous uevents :(( + */ if (mpp->action == ACT_RELOAD) { condlog(0, "%s: uev_add_path sleep", mpp->alias); sleep(1); @@ -450,10 +442,16 @@ out: } static int -uev_remove_path (char * devname, struct vectors * vecs) +uev_remove_path (struct sysfs_device * dev, struct vectors * vecs) { - condlog(2, "%s: remove path (uevent)", devname); - return ev_remove_path(devname, vecs); + int retval; + + condlog(2, "%s: remove path (uevent)", dev->kernel); + retval = ev_remove_path(dev->kernel, vecs); + if (!retval) + sysfs_device_put(dev); + + return retval; } int @@ -483,9 +481,9 @@ ev_remove_path (char * devname, struct vectors * vecs) vector rpvec = vector_alloc(); /* - * transform the mp->pg vector of vectors of paths - * into a mp->params string to feed the device-mapper - */ + * transform the mp->pg vector of vectors of paths + * into a mp->params string to feed the device-mapper + */ update_mpp_paths(mpp, vecs->pathvec); if ((i = find_slot(mpp->paths, (void *)pp)) != -1) vector_del_slot(mpp->paths, i); @@ -511,8 +509,8 @@ ev_remove_path (char * devname, struct vectors * vecs) goto out; } /* - * reload the map - */ + * reload the map + */ mpp->action = ACT_RELOAD; if (domap(mpp) <= 0) { condlog(0, "%s: failed in domap for " @@ -590,7 +588,7 @@ uxsock_trigger (char * str, char ** reply, int * len, void * trigger_data) { struct vectors * vecs; int r; - + *reply = NULL; *len = 0; vecs = (struct vectors *)trigger_data; @@ -632,11 +630,11 @@ uev_discard(char * devpath) return 0; } -int +int uev_trigger (struct uevent * uev, void * trigger_data) { int r = 0; - char devname[32]; + struct sysfs_device *sysdev; struct vectors * vecs; vecs = (struct vectors *)trigger_data; @@ -644,7 +642,10 @@ uev_trigger (struct uevent * uev, void * trigger_data) if (uev_discard(uev->devpath)) return 0; - basename(uev->devpath, devname); + sysdev = sysfs_device_get(uev->devpath); + if(!sysdev) + return 0; + lock(vecs->lock); /* @@ -655,34 +656,35 @@ uev_trigger (struct uevent * uev, void * trigger_data) * Device-mapper generated tons of them and * they don't carry additional information. */ - if (!strncmp(devname, "dm-", 3)) { + if (!strncmp(sysdev->kernel, "dm-", 3)) { if (!strncmp(uev->action, "online", 6)) { - r = uev_add_map(devname, vecs); + r = uev_add_map(sysdev, vecs); goto out; } if (!strncmp(uev->action, "remove", 6)) { - r = uev_remove_map(devname, vecs); + r = uev_remove_map(sysdev, vecs); goto out; } if (!strncmp(uev->action, "umount", 6)) { - r = uev_umount_map(devname, vecs); + r = uev_umount_map(sysdev, vecs); goto out; } goto out; } - + /* * path add/remove event */ - if (blacklist(conf->blist_devnode, conf->elist_devnode, devname)) + if (filter_devnode(conf->blist_devnode, conf->elist_devnode, + sysdev->kernel) > 0) goto out; if (!strncmp(uev->action, "add", 3)) { - r = uev_add_path(devname, vecs); + r = uev_add_path(sysdev, vecs); goto out; } if (!strncmp(uev->action, "remove", 6)) { - r = uev_remove_path(devname, vecs); + r = uev_remove_path(sysdev, vecs); goto out; } @@ -696,39 +698,36 @@ ueventloop (void * ap) { if (uevent_listen(&uev_trigger, ap)) fprintf(stderr, "error starting uevent listener"); - + return NULL; } static void * uxlsnrloop (void * ap) { - if (load_keys()) - return NULL; - - if (alloc_handlers()) + if (cli_init()) return NULL; - add_handler(LIST+PATHS, cli_list_paths); - add_handler(LIST+MAPS, cli_list_maps); - add_handler(LIST+MAPS+STATUS, cli_list_maps_status); - add_handler(LIST+MAPS+STATS, cli_list_maps_stats); - add_handler(LIST+MAPS+TOPOLOGY, cli_list_maps_topology); - add_handler(LIST+TOPOLOGY, cli_list_maps_topology); - add_handler(LIST+MAP+TOPOLOGY, cli_list_map_topology); - add_handler(LIST+CONFIG, cli_list_config); - add_handler(LIST+BLACKLIST, cli_list_blacklist); - add_handler(LIST+DEVICES, cli_list_devices); - add_handler(ADD+PATH, cli_add_path); - add_handler(DEL+PATH, cli_del_path); - add_handler(ADD+MAP, cli_add_map); - add_handler(DEL+MAP, cli_del_map); - add_handler(SWITCH+MAP+GROUP, cli_switch_group); - add_handler(RECONFIGURE, cli_reconfigure); - add_handler(SUSPEND+MAP, cli_suspend); - add_handler(RESUME+MAP, cli_resume); - add_handler(REINSTATE+PATH, cli_reinstate); - add_handler(FAIL+PATH, cli_fail); + set_handler_callback(LIST+PATHS, cli_list_paths); + set_handler_callback(LIST+MAPS, cli_list_maps); + set_handler_callback(LIST+MAPS+STATUS, cli_list_maps_status); + set_handler_callback(LIST+MAPS+STATS, cli_list_maps_stats); + set_handler_callback(LIST+MAPS+TOPOLOGY, cli_list_maps_topology); + set_handler_callback(LIST+TOPOLOGY, cli_list_maps_topology); + set_handler_callback(LIST+MAP+TOPOLOGY, cli_list_map_topology); + set_handler_callback(LIST+CONFIG, cli_list_config); + set_handler_callback(LIST+BLACKLIST, cli_list_blacklist); + set_handler_callback(LIST+DEVICES, cli_list_devices); + set_handler_callback(ADD+PATH, cli_add_path); + set_handler_callback(DEL+PATH, cli_del_path); + set_handler_callback(ADD+MAP, cli_add_map); + set_handler_callback(DEL+MAP, cli_del_map); + set_handler_callback(SWITCH+MAP+GROUP, cli_switch_group); + set_handler_callback(RECONFIGURE, cli_reconfigure); + set_handler_callback(SUSPEND+MAP, cli_suspend); + set_handler_callback(RESUME+MAP, cli_resume); + set_handler_callback(REINSTATE+PATH, cli_reinstate); + set_handler_callback(FAIL+PATH, cli_fail); uxsock_listen(&uxsock_trigger, ap); @@ -794,12 +793,12 @@ enable_group(struct path * pp) * * we can safely return here, because upon map reload, all * PG will be enabled. - */ + */ if (!pp->mpp->pg || !pp->pgindex) return; pgp = VECTOR_SLOT(pp->mpp->pg, pp->pgindex - 1); - + if (pgp->status == PGSTATE_DISABLED) { condlog(2, "%s: enable group #%i", pp->mpp->alias, pp->pgindex); dm_enablegroup(pp->mpp->alias, pp->pgindex); @@ -858,167 +857,181 @@ retry_count_tick(vector mpvec) } } -static void * -checkerloop (void *ap) +void +check_path (struct vectors * vecs, struct path * pp) { - struct vectors *vecs; - struct path *pp; - int count = 0; int newstate; - unsigned int i; - mlockall(MCL_CURRENT | MCL_FUTURE); - vecs = (struct vectors *)ap; - condlog(2, "path checkers start up"); + if (!pp->mpp) + return; + + if (pp->tick && --pp->tick) + return; /* don't check this path yet */ /* - * init the path check interval + * provision a next check soonest, + * in case we exit abnormaly from here */ - vector_foreach_slot (vecs->pathvec, pp, i) { - pp->checkint = conf->checkint; + pp->tick = conf->checkint; + + if (!checker_selected(&pp->checker)) { + pathinfo(pp, conf->hwtable, DI_SYSFS); + select_checker(pp); } + if (!checker_selected(&pp->checker)) { + condlog(0, "%s: checker is not set", pp->dev); + return; + } + /* + * Set checker in async mode. + * Honored only by checker implementing the said mode. + */ + checker_set_async(&pp->checker); - while (1) { - pthread_cleanup_push(cleanup_lock, vecs->lock); - lock(vecs->lock); - condlog(4, "tick"); + newstate = checker_check(&pp->checker); - vector_foreach_slot (vecs->pathvec, pp, i) { - if (!pp->mpp) - continue; + if (newstate < 0) { + condlog(2, "%s: unusable path", pp->dev); + pathinfo(pp, conf->hwtable, 0); + return; + } + /* + * Async IO in flight. Keep the previous path state + * and reschedule as soon as possible + */ + if (newstate == PATH_PENDING) { + pp->tick = 1; + return; + } + if (newstate != pp->state) { + int oldstate = pp->state; + pp->state = newstate; + LOG_MSG(1, checker_message(&pp->checker)); - if (pp->tick && --pp->tick) - continue; /* don't check this path yet */ + /* + * upon state change, reset the checkint + * to the shortest delay + */ + pp->checkint = conf->checkint; + if (newstate == PATH_DOWN || newstate == PATH_SHAKY || + update_multipath_strings(pp->mpp, vecs->pathvec)) { /* - * provision a next check soonest, - * in case we exit abnormaly from here + * proactively fail path in the DM */ - pp->tick = conf->checkint; - - if (!checker_selected(&pp->checker)) { - pathinfo(pp, conf->hwtable, DI_SYSFS); - select_checker(pp); - } - if (!checker_selected(&pp->checker)) { - condlog(0, "%s: checker is not set", pp->dev); - continue; - } + if (oldstate == PATH_UP || + oldstate == PATH_GHOST) + fail_path(pp, 1); + else + fail_path(pp, 0); + /* - * Set checker in async mode. - * Honored only by checker implementing the said mode. + * cancel scheduled failback */ - checker_set_async(&pp->checker); + pp->mpp->failback_tick = 0; - newstate = checker_check(&pp->checker); - - if (newstate < 0) { - condlog(2, "%s: unusable path", pp->dev); - pathinfo(pp, conf->hwtable, 0); - continue; - } + pp->mpp->stat_path_failures++; + return; + } + + /* + * reinstate this path + */ + if (oldstate != PATH_UP && + oldstate != PATH_GHOST) + reinstate_path(pp, 1); + else + reinstate_path(pp, 0); + + /* + * schedule [defered] failback + */ + if (pp->mpp->pgfailback > 0) + pp->mpp->failback_tick = + pp->mpp->pgfailback + 1; + else if (pp->mpp->pgfailback == -FAILBACK_IMMEDIATE && + need_switch_pathgroup(pp->mpp, 1)) + switch_pathgroup(pp->mpp); + + /* + * if at least one path is up in a group, and + * the group is disabled, re-enable it + */ + if (newstate == PATH_UP) + enable_group(pp); + } + else if (newstate == PATH_UP || newstate == PATH_GHOST) { + if (pp->dmstate == PSTATE_FAILED || + pp->dmstate == PSTATE_UNDEF) { + /* Clear IO errors */ + reinstate_path(pp, 0); + } else { + LOG_MSG(4, checker_message(&pp->checker)); /* - * Async IO in flight. Keep the previous path state - * and reschedule as soon as possible + * double the next check delay. + * max at conf->max_checkint */ - if (newstate == PATH_PENDING) { - pp->tick = 1; - continue; - } - if (newstate != pp->state) { - int oldstate = pp->state; - pp->state = newstate; - LOG_MSG(1, checker_message(&pp->checker)); - - /* - * upon state change, reset the checkint - * to the shortest delay - */ - pp->checkint = conf->checkint; + if (pp->checkint < (conf->max_checkint / 2)) + pp->checkint = 2 * pp->checkint; + else + pp->checkint = conf->max_checkint; - if (newstate == PATH_DOWN || - newstate == PATH_SHAKY || - update_multipath_strings(pp->mpp, - vecs->pathvec)) { - /* - * proactively fail path in the DM - */ - if (oldstate == PATH_UP || - oldstate == PATH_GHOST) - fail_path(pp, 1); - else - fail_path(pp, 0); + pp->tick = pp->checkint; + condlog(4, "%s: delay next check %is", + pp->dev_t, pp->tick); + } + } + else if (newstate == PATH_DOWN) + LOG_MSG(2, checker_message(&pp->checker)); - /* - * cancel scheduled failback - */ - pp->mpp->failback_tick = 0; + pp->state = newstate; - pp->mpp->stat_path_failures++; - continue; - } + /* + * path prio refreshing + */ + condlog(4, "path prio refresh"); + pathinfo(pp, conf->hwtable, DI_PRIO); - /* - * reinstate this path - */ - if (oldstate != PATH_UP && - oldstate != PATH_GHOST) - reinstate_path(pp, 1); - else - reinstate_path(pp, 0); + /* + * pathgroup failback policy + */ + if (need_switch_pathgroup(pp->mpp, 0)) { + if (pp->mpp->pgfailback > 0 && + pp->mpp->failback_tick <= 0) + pp->mpp->failback_tick = + pp->mpp->pgfailback + 1; + else if (pp->mpp->pgfailback == + -FAILBACK_IMMEDIATE) + switch_pathgroup(pp->mpp); + } +} - /* - * schedule [defered] failback - */ - if (pp->mpp->pgfailback > 0) - pp->mpp->failback_tick = - pp->mpp->pgfailback + 1; - else if (pp->mpp->pgfailback == -FAILBACK_IMMEDIATE && - need_switch_pathgroup(pp->mpp, 1)) - switch_pathgroup(pp->mpp); +static void * +checkerloop (void *ap) +{ + struct vectors *vecs; + struct path *pp; + int count = 0; + unsigned int i; - /* - * if at least one path is up in a group, and - * the group is disabled, re-enable it - */ - if (newstate == PATH_UP) - enable_group(pp); - } - else if (newstate == PATH_UP || newstate == PATH_GHOST) { - LOG_MSG(4, checker_message(&pp->checker)); - /* - * double the next check delay. - * max at conf->max_checkint - */ - if (pp->checkint < (conf->max_checkint / 2)) - pp->checkint = 2 * pp->checkint; - else - pp->checkint = conf->max_checkint; + mlockall(MCL_CURRENT | MCL_FUTURE); + vecs = (struct vectors *)ap; + condlog(2, "path checkers start up"); - pp->tick = pp->checkint; - condlog(4, "%s: delay next check %is", - pp->dev_t, pp->tick); - } - else if (newstate == PATH_DOWN) - LOG_MSG(2, checker_message(&pp->checker)); + /* + * init the path check interval + */ + vector_foreach_slot (vecs->pathvec, pp, i) { + pp->checkint = conf->checkint; + } - pp->state = newstate; + while (1) { + pthread_cleanup_push(cleanup_lock, vecs->lock); + lock(vecs->lock); + condlog(4, "tick"); - /* - * path prio refreshing - */ - condlog(4, "path prio refresh"); - pathinfo(pp, conf->hwtable, DI_PRIO); - - if (need_switch_pathgroup(pp->mpp, 0)) { - if (pp->mpp->pgfailback > 0 && - pp->mpp->failback_tick <= 0) - pp->mpp->failback_tick = - pp->mpp->pgfailback + 1; - else if (pp->mpp->pgfailback == - -FAILBACK_IMMEDIATE) - switch_pathgroup(pp->mpp); - } + vector_foreach_slot (vecs->pathvec, pp, i) { + check_path(vecs, pp); } defered_failback_tick(vecs->mpvec); retry_count_tick(vecs->mpvec); @@ -1030,7 +1043,7 @@ checkerloop (void *ap) mpvec_garbage_collector(vecs); count = MAPGCINT; } - + lock_cleanup_pop(vecs->lock); sleep(1); } @@ -1047,10 +1060,10 @@ configure (struct vectors * vecs, int start_waiters) if (!vecs->pathvec && !(vecs->pathvec = vector_alloc())) return 1; - + if (!vecs->mpvec && !(vecs->mpvec = vector_alloc())) return 1; - + if (!(mpvec = vector_alloc())) return 1; @@ -1060,11 +1073,11 @@ configure (struct vectors * vecs, int start_waiters) path_discovery(vecs->pathvec, conf, DI_ALL); vector_foreach_slot (vecs->pathvec, pp, i){ - if (blacklist_path(conf, pp)){ + if (filter_path(conf, pp) > 0){ vector_del_slot(vecs->pathvec, i); free_path(pp); i--; - } + } else pp->checkint = conf->checkint; } @@ -1088,10 +1101,6 @@ configure (struct vectors * vecs, int start_waiters) sync_maps_state(mpvec); - if (conf->verbosity > 2) - vector_foreach_slot(mpvec, mpp, i) - print_map(mpp); - /* * purge dm of old maps */ @@ -1157,7 +1166,7 @@ init_vecs (void) if (!vecs) return NULL; - vecs->lock = + vecs->lock = (pthread_mutex_t *)MALLOC(sizeof(pthread_mutex_t)); if (!vecs->lock) @@ -1231,15 +1240,15 @@ signal_init(void) static void setscheduler (void) { - int res; + int res; static struct sched_param sched_param = { .sched_priority = 99 }; - res = sched_setscheduler (0, SCHED_RR, &sched_param); + res = sched_setscheduler (0, SCHED_RR, &sched_param); - if (res == -1) - condlog(LOG_WARNING, "Could not set SCHED_RR at priority 99"); + if (res == -1) + condlog(LOG_WARNING, "Could not set SCHED_RR at priority 99"); return; } @@ -1256,7 +1265,7 @@ set_oom_adj (int val) fprintf(fp, "%i", val); fclose(fp); } - + static int child (void * param) { @@ -1285,6 +1294,21 @@ child (void * param) conf->max_checkint = MAX_CHECKINT(conf->checkint); } + if (conf->max_fds) { + struct rlimit fd_limit; + if (conf->max_fds > 0) { + fd_limit.rlim_cur = conf->max_fds; + fd_limit.rlim_max = conf->max_fds; + } + else { + fd_limit.rlim_cur = RLIM_INFINITY; + fd_limit.rlim_max = RLIM_INFINITY; + } + if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0) + condlog(0, "can't set open fds limit to %d : %s\n", + conf->max_fds, strerror(errno)); + } + if (pidfile_create(DEFAULT_PIDFILE, getpid())) { if (logsink) log_thread_stop(); @@ -1299,7 +1323,7 @@ child (void * param) if (!vecs) exit(1); - if (sysfs_get_mnt_path(sysfs_path, FILE_NAME_SIZE)) { + if (sysfs_init(conf->sysfs_dir, FILE_NAME_SIZE)) { condlog(0, "can not find sysfs mount point"); exit(1); } @@ -1318,7 +1342,7 @@ child (void * param) pthread_attr_init(&attr); pthread_attr_setstacksize(&attr, 64 * 1024); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - + pthread_create(&check_thr, &attr, checkerloop, vecs); pthread_create(&uevent_thr, &attr, ueventloop, vecs); pthread_create(&uxlsnr_thr, &attr, uxlsnrloop, vecs); @@ -1328,20 +1352,22 @@ child (void * param) /* * exit path */ - lock(vecs->lock); remove_maps(vecs, stop_waiter_thread); - free_pathvec(vecs->pathvec, FREE_PATHS); pthread_cancel(check_thr); pthread_cancel(uevent_thr); pthread_cancel(uxlsnr_thr); + sysfs_cleanup(); + free_keys(keys); keys = NULL; free_handlers(handlers); handlers = NULL; free_polls(); + lock(vecs->lock); + free_pathvec(vecs->pathvec, FREE_PATHS); unlock(vecs->lock); pthread_mutex_destroy(vecs->lock); FREE(vecs->lock); @@ -1352,7 +1378,7 @@ child (void * param) conf = NULL; condlog(2, "--------shut down-------"); - + if (logsink) log_thread_stop(); @@ -1420,7 +1446,7 @@ main (int argc, char *argv[]) extern int optind; int arg; int err; - + logsink = 1; dm_init(); @@ -1463,7 +1489,7 @@ main (int argc, char *argv[]) err = 0; else err = daemonize(); - + if (err < 0) /* error */ exit(1); diff --git a/multipathd/main.h b/multipathd/main.h index d0cce3a..1a6dc55 100644 --- a/multipathd/main.h +++ b/multipathd/main.h @@ -7,7 +7,7 @@ int reconfigure (struct vectors *); int ev_add_path (char *, struct vectors *); int ev_remove_path (char *, struct vectors *); -int ev_add_map (char *, struct vectors *); +int ev_add_map (struct sysfs_device *, struct vectors *); int ev_remove_map (char *, struct vectors *); #endif /* MAIN_H */ diff --git a/multipathd/multipathd.init.suse b/multipathd/multipathd.init.suse new file mode 100644 index 0000000..d851354 --- /dev/null +++ b/multipathd/multipathd.init.suse @@ -0,0 +1,132 @@ +#! /bin/sh +# Copyright (c) 1995-2001 SuSE GmbH Nuernberg, Germany. +# +# Author: Hannes Reinecke <feedback@suse.de> +# +# init.d/routed +# +# and symbolic its link +# +# /usr/sbin/rcrouted +# +### BEGIN INIT INFO +# Provides: multipathd +# Required-Start: $syslog +# Required-Stop: +# Default-Start: 3 5 +# Default-Stop: 0 1 2 4 6 +# Description: Starts multipath daemon +### END INIT INFO + +PATH=/bin:/usr/bin:/sbin:/usr/sbin +DAEMON=/sbin/multipathd +PIDFILE=/var/run/multipathd.pid + +# Set the maximum number of open files +MAX_OPEN_FDS=4096 + +test -x $DAEMON || exit 5 + +. /etc/rc.status + +# First reset status of this service +rc_reset + +case "$1" in + start) + echo -n "Starting multipathd" + + modprobe dm-multipath + + # Set the maximum number of open files + if [ -n "$MAX_OPEN_FDS" ] ; then + ulimit -n $MAX_OPEN_FDS + fi + + if [ -f $PIDFILE ]; then + PID="$(cat $PIDFILE)" + PROCNAME="$(ps -o cmd --no-headers $PID)" + fi + + if [ "$PROCNAME" != "$DAEMON" ]; then + $DAEMON + fi + + # Remember status and be verbose + rc_status -v + sleep 1 + ;; + stop) + echo -n "Shutting down multipathd" + # Because of the way how multipathd sets up its own namespace + # and chroots to it, killproc cannot be used with this process. + # So implement a cruder version: + if [ -f $PIDFILE ]; then + PID="$(cat $PIDFILE)" + PROCNAME="$(ps -o cmd --no-headers $PID)" + fi + + if [ "$PROCNAME" == "$DAEMON" ]; then + kill -TERM $PID + fi + + # Remember status and be verbose + rc_status -v + ;; + try-restart) + ## Stop the service and if this succeeds (i.e. the + ## service was running before), start it again. + $0 status >/dev/null && $0 restart + + # Remember status and be quiet + rc_status + ;; + restart|force-reload) + ## Stop the service and regardless of whether it was + ## running or not, start it again. + $0 stop + $0 start + + # Remember status and be quiet + rc_status + ;; + reload) + ## Like force-reload, but if daemon does not support + ## signalling, do nothing (!) + + # If it does not support reload: + exit 3 + ;; + status) + echo -n "Checking for multipathd: " + + # Status has a slightly different for the status command: + # 0 - service running + # 1 - service dead, but /var/run/ pid file exists + # 2 - service dead, but /var/lock/ lock file exists + # 3 - service not running + + if [ -f $PIDFILE ]; then + PID="$(cat $PIDFILE)" + PROCNAME="$(ps -o cmd --no-headers $PID)" + if [ "$PROCNAME" == "$DAEMON" ]; then + (exit 0) + else + (exit 1) + fi + else + (exit 3) + fi + + rc_status -v + ;; + probe) + ## Optional: Probe for the necessity of a reload, + ## give out the argument which is required for a reload. + ;; + *) + echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload|probe}" + exit 1 + ;; +esac +rc_exit diff --git a/multipathd/uxclnt.c b/multipathd/uxclnt.c index ff7b578..009e5cb 100644 --- a/multipathd/uxclnt.c +++ b/multipathd/uxclnt.c @@ -21,6 +21,9 @@ #include <memory.h> #include <defaults.h> +#include <vector.h> +#include "cli.h" + /* * process the client */ @@ -29,6 +32,9 @@ static void process(int fd) char *line; char *reply; + cli_init(); + rl_readline_name = "multipathd"; + rl_completion_entry_function = key_generator; while ((line = readline("multipathd> "))) { size_t len; size_t llen = strlen(line); diff --git a/path_priority/pp_balance_units/Makefile b/path_priority/pp_balance_units/Makefile index b0fee28..43a0fc2 100644 --- a/path_priority/pp_balance_units/Makefile +++ b/path_priority/pp_balance_units/Makefile @@ -35,7 +35,7 @@ $(MULTIPATHLIB)-$(BUILD).a: install: install -d $(DESTDIR)$(bindir) - install -m 755 $(EXEC) $(DESTDIR)$(bindir)/ + $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/ uninstall: rm $(DESTDIR)$(bindir)/$(EXEC) diff --git a/path_priority/pp_emc/Makefile b/path_priority/pp_emc/Makefile index 8c6e922..93e6075 100644 --- a/path_priority/pp_emc/Makefile +++ b/path_priority/pp_emc/Makefile @@ -14,7 +14,7 @@ klibc: $(OBJS) $(CC) -static -o $(EXEC) $(OBJS) install: $(EXEC) - install -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) + $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) uninstall: rm $(DESTDIR)$(bindir)/$(EXEC) diff --git a/path_priority/pp_hds_modular/Makefile b/path_priority/pp_hds_modular/Makefile index 1c276e6..ca00ca7 100644 --- a/path_priority/pp_hds_modular/Makefile +++ b/path_priority/pp_hds_modular/Makefile @@ -14,7 +14,7 @@ klibc: $(OBJS) $(CC) -static -o $(EXEC) $(OBJS) install: $(EXEC) - install -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) + $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) uninstall: rm $(DESTDIR)$(bindir)/$(EXEC) diff --git a/path_priority/pp_hds_modular/pp_hds_modular.c b/path_priority/pp_hds_modular/pp_hds_modular.c index f38ebcf..7411508 100644 --- a/path_priority/pp_hds_modular/pp_hds_modular.c +++ b/path_priority/pp_hds_modular/pp_hds_modular.c @@ -2,40 +2,40 @@ * (C) Copyright HDS GmbH 2006. All Rights Reserved. * * pp_hds_modular.c - * Version 1.12 + * Version 2.00 * - * Prioritizer for multipath tools device mapper and HDS Storage + * 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 + * 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 + * 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 commands the represented LDEV and - * Controller number and gives back a priority followed by this scheme : + * + * 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 + * 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 + * 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. + * 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 : + * You can run the prioritizer manually in verbose mode: * # pp_hds_modular -v 8:224 * VENDOR: HITACHI * PRODUCT: DF600F-CM @@ -45,31 +45,21 @@ * PORT: B * CTRL ODD, LDEV EVEN, PRIO 0 * - * The items VENDOR and PRODUCT helps you to make the correct entries in file - * /etc/multipath.conf : - * # cat /etc/multipath.conf - * ... - * devices { - * device { - * vendor "HITACHI" - * product "DF600F" - * path_grouping_policy group_by_prio - * prio_callout "/sbin/pp_hds_modular %d" - * path_checker readsector0 - * getuid_callout "/sbin/scsi_id -g -u -s /block/%n" - * failback immediate - * } - * device { - * vendor "HITACHI" - * product "DF600F-CM" - * path_grouping_policy group_by_prio - * prio_callout "/sbin/pp_hds_modular %d" - * path_checker readsector0 - * getuid_callout "/sbin/scsi_id -g -u -s /block/%n" - * failback immediate - * + * To compile this source please execute # cc pp_hds_modular.c -o /sbin/mpath_prio_hds_modular * - * Author: Matthias Rudolph <matthias.rudolph@hds.com> + * 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. * @@ -86,167 +76,136 @@ #include <memory.h> #include <sys/types.h> #include <sys/stat.h> -#include <scsi/sg.h> /* take care: fetches glibc's /usr/include/scsi/sg.h */ +#include <scsi/sg.h> #define INQ_REPLY_LEN 255 #define INQ_CMD_CODE 0x12 #define INQ_CMD_LEN 6 #define FILE_NAME_SIZE 255 -#define safe_sprintf(var, format, args...) \ - snprintf(var, sizeof(var), format, ##args) >= sizeof(var) -#define safe_snprintf(var, size, format, args...) \ - snprintf(var, size, format, ##args) >= size -int verbose; +int verbose=0; + +void print_help (void); +int hds_modular_prio (const char *); + +int main (int argc, char **argv) +{ + int prio; + if (argc == 2) + { + if (strcmp (argv[1], "-h") == 0) + { + print_help (); + exit (0); + } + else + { + verbose = 0; + prio = hds_modular_prio (argv[1]); + printf ("%d\n", prio); + exit (0); + } + } + if ((argc == 3) && (strcmp (argv[1], "-v")) == 0) + { + verbose = 1; + prio = hds_modular_prio (argv[2]); + printf ("%d\n", prio); + exit (0); + } + print_help (); + exit (1); +} -int hds_modular_prio(char * major_minor) +int hds_modular_prio (const char *dev) { - int sg_fd, k, i; - char vendor[32]; + int sg_fd, k; + char vendor[32]; char product[32]; char serial[32]; char ldev[32]; char ctrl[32]; char port[32]; - char devpath[FILE_NAME_SIZE]; - unsigned int major; - unsigned int minor; - unsigned char inqCmdBlk[INQ_CMD_LEN] = - {INQ_CMD_CODE, 0, 0, 0, INQ_REPLY_LEN, 0}; + 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; - sscanf(major_minor, "%u:%u", &major, &minor); - memset(devpath, 0, FILE_NAME_SIZE); + if ((sg_fd = open (dev, O_RDONLY)) < 0) exit (1); + if ((ioctl (sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) exit (1); - if (safe_sprintf(devpath, "/tmp/.pp_balance.%u.%u.devnode", - major, minor)) - exit(1); - - unlink (devpath); - mknod(devpath, S_IFBLK|S_IRUSR|S_IWUSR, makedev(major, minor)); - - if ((sg_fd = open(devpath, O_RDONLY)) < 0) exit(1); - if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) - exit(1); - - memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); + 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.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(sg_fd, SG_IO, &io_hdr) < 0) exit(1); - if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK) exit(1); - - for (i = 0; i < 8 ; i++) vendor[i] = inqBuff[i+8]; - vendor[8] = 0; - for (i = 0; i < 16 ; i++) product[i] = inqBuff[i+16]; - product[16] = 0; - for (i = 0; i < 4 ; i++) serial[i] = inqBuff[i+40]; - serial[4] = 0; - for (i = 0; i < 4 ; i++) ldev[i] = inqBuff[i+44]; - ldev[4] = 0; - ctrl[0] = inqBuff[49]; - ctrl[1] = 0; - port[0] = inqBuff[50]; - port[1] = 0; - - close(sg_fd); - - if (1 == verbose) { - printf("VENDOR: %s\n", vendor); - printf("PRODUCT: %s\n", product); - printf("SERIAL: 0x%s\n", serial); - printf("LDEV: 0x%s\n", ldev); - printf("CTRL: %s\n", ctrl); - printf("PORT: %s\n", port); + io_hdr.timeout = 2000; /* TimeOut = 2 seconds */ + + if (ioctl (sg_fd, SG_IO, &io_hdr) < 0) exit (1); + if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK) exit (1); + + snprintf (vendor, 9, "%.8s\n", 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); + + close (sg_fd); + + if (verbose) + { + printf ("VENDOR: %s\n", vendor); + printf ("PRODUCT: %s\n", product); + printf ("SERIAL: 0x%s\n", serial); + printf ("LDEV: 0x%s\n", ldev); + printf ("CTRL: %s\n", ctrl); + printf ("PORT: %s\n", 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': - if (1 == verbose) - printf("CTRL EVEN, LDEV EVEN, " - "PRIO 1\n"); - return 1; - break; - case '1': case '3': case '5': case '7': - case '9': case 'B': case 'D': case 'F': - if (1 == verbose) - printf("CTRL EVEN, LDEV ODD, " - "PRIO 0\n"); - 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': - if (1 == verbose) - printf("CTRL ODD, LDEV EVEN, " - "PRIO 0\n"); - return 0; - break; - case '1': case '3': case '5': case '7': - case '9': case 'B': case 'D': case 'F': - if (1 == verbose) - printf("CTRL ODD, LDEV ODD, " - "PRIO 1\n"); - return 1; - break; - } + 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': + if (1 == verbose) printf("CTRL EVEN, LDEV EVEN, PRIO 1\n"); + return 1; + break; + case '1': case '3': case '5': case '7': case '9': case 'B': case 'D': case 'F': + if (1 == verbose) printf("CTRL EVEN, LDEV ODD, PRIO 0\n"); + 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': + if (1 == verbose) printf("CTRL ODD, LDEV EVEN, PRIO 0\n"); + return 0; + break; + case '1': case '3': case '5': case '7': case '9': case 'B': case 'D': case 'F': + if (1 == verbose) printf("CTRL ODD, LDEV ODD, PRIO 1\n"); + return 1; + break; + } } - exit(1); + return 0; } -void print_help(void) +void print_help (void) { - printf("Usage: " - "pp_hds_modular [-v] <device_major:device_minor>\n"); - printf("Option: " - "-v verbose mode\n"); - printf("Description: " - "Prioritizer for Multipath Tools and HDS Storage\n"); - printf("Version: " - "1.12\n"); - printf("Author: " - "Matthias Rudolph <matthias.rudolph@hds.com>\n"); + printf ("\n"); + printf ("Usage: pp_hds_modular [-v] <device>\n"); + printf ("Option: -v verbose mode\n"); + printf ("Description: Prioritizer for Device Mapper Multipath and HDS Storage\n"); + printf ("Version: 2.00\n"); + printf ("Author: Matthias Rudolph <matthias.rudolph@hds.com>\n"); + printf ("Remarks: Prioritizer CTRL#1 corresponds to hardware CTRL#0\n"); + printf (" Prioritizer CTRL#2 corresponds to hardware CTRL#1\n"); + printf ("\n"); return; } -int main(int argc, char * argv[]) -{ - int prio; - - if (2 == argc) { - if (0 == strcmp(argv[1], "-h")) { - print_help(); - exit(0); - } - else { - verbose = 0; - prio = hds_modular_prio(argv[1]); - printf("%d\n", prio); - exit(0); - } - } - - if ((3 == argc) && (0 == strcmp(argv[1], "-v"))) { - verbose = 1; - prio = hds_modular_prio(argv[2]); - printf("%d\n", prio); - exit(0); - } - print_help(); - exit(1); -} - diff --git a/path_priority/pp_ontap/Makefile b/path_priority/pp_ontap/Makefile index 8b8f901..2c364a8 100644 --- a/path_priority/pp_ontap/Makefile +++ b/path_priority/pp_ontap/Makefile @@ -14,7 +14,7 @@ klibc: $(OBJS) $(CC) -static -o $(EXEC) $(OBJS) install: $(EXEC) - install -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) + $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) uninstall: rm $(DESTDIR)$(bindir)/$(EXEC) diff --git a/path_priority/pp_random/Makefile b/path_priority/pp_random/Makefile index ca7974e..85d7c2f 100644 --- a/path_priority/pp_random/Makefile +++ b/path_priority/pp_random/Makefile @@ -14,7 +14,7 @@ klibc: $(OBJS) $(CC) -static -o $(EXEC) $(OBJS) install: $(EXEC) - install -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) + $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) uninstall: rm $(DESTDIR)$(bindir)/$(EXEC) diff --git a/path_priority/pp_rdac/Makefile b/path_priority/pp_rdac/Makefile new file mode 100644 index 0000000..64ed4c3 --- /dev/null +++ b/path_priority/pp_rdac/Makefile @@ -0,0 +1,22 @@ +EXEC = mpath_prio_rdac +BUILD = glibc +OBJS = pp_rdac.o + +TOPDIR = ../.. +include $(TOPDIR)/Makefile.inc + +all: $(BUILD) + +glibc: $(OBJS) + $(CC) -o $(EXEC) $(OBJS) $(LDFLAGS) + +klibc: $(OBJS) + $(CC) -static -o $(EXEC) $(OBJS) + +install: $(EXEC) + $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) + +uninstall: + rm $(DESTDIR)$(bindir)/$(EXEC) +clean: + rm -f *.o $(EXEC) diff --git a/path_priority/pp_rdac/pp_rdac.c b/path_priority/pp_rdac/pp_rdac.c new file mode 100644 index 0000000..49a13cf --- /dev/null +++ b/path_priority/pp_rdac/pp_rdac.c @@ -0,0 +1,112 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <errno.h> + +#include "../../libmultipath/sg_include.h" + +#define INQUIRY_CMD 0x12 +#define INQUIRY_CMDLEN 6 + +int rdac_prio(const char *dev) +{ + 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; + int fd; + + fd = open(dev, O_RDWR|O_NONBLOCK); + + if (fd <= 0) { + fprintf(stderr, "opening of the device failed.\n"); + goto out; + } + + 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) { + fprintf(stderr, "sending inquiry command failed\n"); + goto out; + } + if (io_hdr.info & SG_INFO_OK_MASK) { + fprintf(stderr, "inquiry command indicates error"); + goto out; + } + + close(fd); + + 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' ) { + fprintf(stderr, "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 +main (int argc, char **argv) +{ + int prio; + if (argc != 2) { + fprintf(stderr, "Wrong number of arguments.\n"); + fprintf(stderr, "Usage: %s device\n", argv[0]); + prio = 0; + } else + prio = rdac_prio(argv[1]); + + printf("%d\n", prio); + exit(0); +} diff --git a/path_priority/pp_tpc/Makefile b/path_priority/pp_tpc/Makefile deleted file mode 100644 index 624f76d..0000000 --- a/path_priority/pp_tpc/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -EXEC = mpath_prio_tpc -BUILD = glibc -OBJS = pp_tpc.o - -TOPDIR = ../.. -include $(TOPDIR)/Makefile.inc - -all: $(BUILD) - -glibc: $(OBJS) - $(CC) -o $(EXEC) $(OBJS) $(LDFLAGS) - -klibc: $(OBJS) - $(CC) -static -o $(EXEC) $(OBJS) - -install: $(EXEC) - install -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) - -uninstall: - rm $(DESTDIR)$(bindir)/$(EXEC) -clean: - rm -f *.o $(EXEC) diff --git a/path_priority/pp_tpc/pp_tpc.c b/path_priority/pp_tpc/pp_tpc.c deleted file mode 100644 index a7ed7ad..0000000 --- a/path_priority/pp_tpc/pp_tpc.c +++ /dev/null @@ -1,112 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/ioctl.h> -#include <errno.h> - -#include "../../libmultipath/sg_include.h" - -#define INQUIRY_CMD 0x12 -#define INQUIRY_CMDLEN 6 - -int sgi_tpc_prio(const char *dev) -{ - 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; - int fd; - - fd = open(dev, O_RDWR|O_NONBLOCK); - - if (fd <= 0) { - fprintf(stderr, "opening of the device failed.\n"); - goto out; - } - - 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) { - fprintf(stderr, "sending inquiry command failed\n"); - goto out; - } - if (io_hdr.info & SG_INFO_OK_MASK) { - fprintf(stderr, "inquiry command indicates error"); - goto out; - } - - close(fd); - - 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' ) { - fprintf(stderr, "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 -main (int argc, char **argv) -{ - int prio; - if (argc != 2) { - fprintf(stderr, "Wrong number of arguments.\n"); - fprintf(stderr, "Usage: %s device\n", argv[0]); - prio = 0; - } else - prio = sgi_tpc_prio(argv[1]); - - printf("%d\n", prio); - exit(0); -}