X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=extras%2Fmultipath%2Fmain.c;h=dfaa6867dda84ed34a56504fa83d1a21ddd74499;hb=e41016d3547ef704c0785ba197d36ef69de51260;hp=c57cd4fe7eb703873df47dbdbb1428637242921d;hpb=4081da7fe560f0ad173a9836589d6839d9dff9df;p=elogind.git diff --git a/extras/multipath/main.c b/extras/multipath/main.c index c57cd4fe7..dfaa6867d 100644 --- a/extras/multipath/main.c +++ b/extras/multipath/main.c @@ -25,146 +25,27 @@ #include #include #include -#include +#include #include "libdevmapper/libdevmapper.h" #include "main.h" - -static int -do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op, - void *resp, int mx_resp_len, int noisy) -{ - 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[4] = (unsigned char) mx_resp_len; - 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) { - perror("SG_IO (inquiry) error"); - 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 void -sprint_wwid(char * buff, const char * str) -{ - int i; - const char *p; - char *cursor; - unsigned char c; - - p = str; - cursor = buff; - for (i = 0; i <= WWID_SIZE / 2 - 1; i++) { - c = *p++; - sprintf(cursor, "%.2x", (int) (unsigned char) c); - cursor += 2; - } - buff[WWID_SIZE - 1] = '\0'; -} - -static int -get_lun_strings(int fd, struct path * mypath) -{ - char buff[36]; - - if (0 == do_inq(fd, 0, 0, 0, buff, 36, 1)) { - memcpy(mypath->vendor_id, &buff[8], 8); - memcpy(mypath->product_id, &buff[16], 16); - memcpy(mypath->rev, &buff[32], 4); - return 1; - } - return 0; -} - -/* -static int -get_serial (int fd, char * str) -{ - char buff[MX_ALLOC_LEN + 1]; - int len; - - if (0 == do_inq(fd, 0, 1, 0x80, buff, MX_ALLOC_LEN, 0)) { - len = buff[3]; - if (len > 0) { - memcpy(str, buff + 4, len); - buff[len] = '\0'; - } - return 1; - } - return 0; -} -*/ - -/* hardware vendor specifics : add support for new models below */ - -/* this one get EVPD page 0x83 off 8 */ -/* tested ok with StorageWorks */ -static int -get_evpd_wwid(int fd, char *str) -{ - char buff[64]; - - if (0 == do_inq(fd, 0, 1, 0x83, buff, sizeof (buff), 1)) { - sprint_wwid(str, &buff[8]); - return 1; - } - return 0; -} +#include "devinfo.h" /* White list switch */ static int -get_unique_id(int fd, struct path * mypath) +get_unique_id(struct path * mypath) { int i; static struct { char * vendor; char * product; int iopolicy; - int (*getuid) (int fd, char * wwid); + int (*getuid) (char *, char *); } wlist[] = { - {"COMPAQ ", "HSV110 (C)COMPAQ", MULTIBUS, &get_evpd_wwid}, - {"COMPAQ ", "MSA1000 ", MULTIBUS, &get_evpd_wwid}, - {"COMPAQ ", "MSA1000 VOLUME ", MULTIBUS, &get_evpd_wwid}, - {"DEC ", "HSG80 ", MULTIBUS, &get_evpd_wwid}, - {"HP ", "HSV100 ", MULTIBUS, &get_evpd_wwid}, + {"COMPAQ ", "HSV110 (C)COMPAQ", GROUP_BY_SERIAL, &get_evpd_wwid}, + {"COMPAQ ", "MSA1000 ", GROUP_BY_SERIAL, &get_evpd_wwid}, + {"COMPAQ ", "MSA1000 VOLUME ", GROUP_BY_SERIAL, &get_evpd_wwid}, + {"DEC ", "HSG80 ", GROUP_BY_SERIAL, &get_evpd_wwid}, + {"HP ", "HSV100 ", GROUP_BY_SERIAL, &get_evpd_wwid}, {"HP ", "A6189A ", MULTIBUS, &get_evpd_wwid}, {"HP ", "OPEN- ", MULTIBUS, &get_evpd_wwid}, {"DDN ", "SAN DataDirector", MULTIBUS, &get_evpd_wwid}, @@ -183,24 +64,14 @@ get_unique_id(int fd, struct path * mypath) for (i = 0; wlist[i].vendor; i++) { if (strncmp(mypath->vendor_id, wlist[i].vendor, 8) == 0 && strncmp(mypath->product_id, wlist[i].product, 16) == 0) { - wlist[i].getuid(fd, mypath->wwid); mypath->iopolicy = wlist[i].iopolicy; - return 0; + if (!wlist[i].getuid(mypath->sg_dev, mypath->wwid)) + return 0; } } return 1; } -static void -basename(char * str1, char * str2) -{ - char *p = str1 + (strlen(str1) - 1); - - while (*--p != '/') - continue; - strcpy(str2, ++p); -} - static int blacklist (char * dev) { int i; @@ -230,7 +101,6 @@ static int get_all_paths_sysfs(struct env * conf, struct path * all_paths) { int k=0; - int sg_fd; struct sysfs_directory * sdir; struct sysfs_directory * devp; struct sysfs_link * linkp; @@ -240,7 +110,7 @@ get_all_paths_sysfs(struct env * conf, struct path * all_paths) char path[FILE_NAME_SIZE]; struct path curpath; - /* if called from hotplug, only consider the paths that relate to */ + /* if called from hotplug, only consider the paths that relate */ /* to the device pointed by conf.hotplugdev */ memset(empty_buff, 0, WWID_SIZE); memset(refwwid, 0, WWID_SIZE); @@ -248,15 +118,21 @@ get_all_paths_sysfs(struct env * conf, struct path * all_paths) sprintf(buff, "%s%s/block", conf->sysfs_path, conf->hotplugdev); memset(conf->hotplugdev, 0, FILE_NAME_SIZE); - readlink(buff, conf->hotplugdev, FILE_NAME_SIZE); + + /* if called from hotplug but with no block, leave */ + if (0 > readlink(buff, conf->hotplugdev, FILE_NAME_SIZE)) + return 0; + basename(conf->hotplugdev, buff); sprintf(curpath.sg_dev, "/dev/%s", buff); - if ((sg_fd = open(curpath.sg_dev, O_RDONLY)) < 0) - exit(1); - - get_lun_strings(sg_fd, &curpath); - get_unique_id(sg_fd, &curpath); + get_lun_strings(curpath.vendor_id, + curpath.product_id, + curpath.rev, + curpath.sg_dev); + get_serial(curpath.serial, curpath.sg_dev); + if (!get_unique_id(&curpath)) + return 0; strcpy(refwwid, curpath.wwid); memset(&curpath, 0, sizeof(path)); } @@ -286,16 +162,16 @@ get_all_paths_sysfs(struct env * conf, struct path * all_paths) basename(devp->path, buff); sprintf(curpath.sg_dev, "/dev/%s", buff); - if ((sg_fd = open(curpath.sg_dev, O_RDONLY)) < 0) { - if (conf->verbose) - fprintf(stderr, "can't open %s\n", - curpath.sg_dev); + get_lun_strings(curpath.vendor_id, + curpath.product_id, + curpath.rev, + curpath.sg_dev); + get_serial(curpath.serial, curpath.sg_dev); + if(!get_unique_id(&curpath)) { + memset(&curpath, 0, sizeof(path)); continue; } - get_lun_strings(sg_fd, &curpath); - get_unique_id(sg_fd, &curpath); - if (memcmp(empty_buff, refwwid, WWID_SIZE) != 0 && strncmp(curpath.wwid, refwwid, WWID_SIZE) != 0) { memset(&curpath, 0, sizeof(path)); @@ -312,7 +188,6 @@ get_all_paths_sysfs(struct env * conf, struct path * all_paths) /* done with curpath, zero for reuse */ memset(&curpath, 0, sizeof(path)); - close(sg_fd); basename(linkp->target, buff); sscanf(buff, "%i:%i:%i:%i", &all_paths[k].sg_id.host_no, @@ -329,7 +204,7 @@ static int get_all_paths_nosysfs(struct env * conf, struct path * all_paths, struct scsi_dev * all_scsi_ids) { - int k, i, sg_fd; + int k, i, fd; char buff[FILE_NAME_SIZE]; char file_name[FILE_NAME_SIZE]; @@ -338,19 +213,23 @@ get_all_paths_nosysfs(struct env * conf, struct path * all_paths, sprintf(buff, "%d", k); strncat(file_name, buff, FILE_NAME_SIZE); strcpy(all_paths[k].sg_dev, file_name); - if ((sg_fd = open(file_name, O_RDONLY)) < 0) { - if (conf->verbose) - fprintf(stderr, "can't open %s. mknod ?", - file_name); + + get_lun_strings(all_paths[k].vendor_id, + all_paths[k].product_id, + all_paths[k].rev, + all_paths[k].sg_dev); + get_serial(all_paths[k].serial, all_paths[k].sg_dev); + if (!get_unique_id(&all_paths[k])) continue; - } - get_lun_strings(sg_fd, &all_paths[k]); - get_unique_id(sg_fd, &all_paths[k]); - if (0 > ioctl(sg_fd, SG_GET_SCSI_ID, &(all_paths[k].sg_id))) + + if ((fd = open(all_paths[k].sg_dev, O_RDONLY)) < 0) + return 0; + + if (0 > ioctl(fd, SG_GET_SCSI_ID, &(all_paths[k].sg_id))) printf("device %s failed on sg ioctl, skip\n", file_name); - close(sg_fd); + close(fd); for (i = 0; i < conf->max_devs; i++) { if ((all_paths[k].sg_id.host_no == @@ -474,32 +353,6 @@ print_all_mp(struct path * all_paths, struct multipath * mp, int nmp) } } -static long -get_disk_size (struct env * conf, char * dev) { - long size; - int fd; - char attr_path[FILE_NAME_SIZE]; - char buff[FILE_NAME_SIZE]; - char basedev[FILE_NAME_SIZE]; - - if(conf->with_sysfs) { - basename(dev, basedev); - sprintf(attr_path, "%s/block/%s/size", - conf->sysfs_path, basedev); - if (0 > sysfs_read_attribute_value(attr_path, buff, - FILE_NAME_SIZE * sizeof(char))) - return -1; - size = atoi(buff); - return size; - } else { - if ((fd = open(dev, O_RDONLY)) < 0) - return -1; - if(!ioctl(fd, BLKGETSIZE, &size)) - return size; - } - return -1; -} - static int coalesce_paths(struct env * conf, struct multipath * mp, struct path * all_paths) @@ -518,7 +371,7 @@ coalesce_paths(struct env * conf, struct multipath * mp, if (memcmp(empty_buff, all_paths[k].wwid, WWID_SIZE) == 0) continue; - /* 2. mp with this uid already instanciated */ + /* 2. if mp with this uid already instanciated */ for (i = 0; i <= nmp; i++) { if (0 == strcmp(mp[i].wwid, all_paths[k].wwid)) already_done = 1; @@ -535,7 +388,7 @@ coalesce_paths(struct env * conf, struct multipath * mp, PINDEX(nmp,np) = k; if (mp[nmp].size == 0) - mp[nmp].size = get_disk_size(conf, all_paths[k].dev); + mp[nmp].size = get_disk_size(all_paths[k].dev); for (i = k + 1; i < conf->max_devs; i++) { if (0 == strcmp(all_paths[k].wwid, all_paths[i].wwid)) { @@ -548,53 +401,95 @@ coalesce_paths(struct env * conf, struct multipath * mp, return nmp; } +static void +group_by_serial(struct multipath * mp, struct path * all_paths, char * str) { + int path_count, pg_count = 0; + int i, k; + int * bitmap; + char path_buff[FILE_NAME_SIZE]; + char pg_buff[FILE_NAME_SIZE]; + char * path_buff_p = &path_buff[0]; + char * pg_buff_p = &pg_buff[0]; + + /* init the bitmap */ + bitmap = malloc((mp->npaths + 1) * sizeof(int)); + memset(bitmap, 0, (mp->npaths + 1) * sizeof(int)); + + for (i = 0; i <= mp->npaths; i++) { + if (bitmap[i]) + continue; + + /* here, we really got a new pg */ + pg_count++; + path_count = 1; + memset(&path_buff, 0, FILE_NAME_SIZE * sizeof(char)); + path_buff_p = &path_buff[0]; + + path_buff_p += sprintf(path_buff_p, " %s", all_paths[mp->pindex[i]].dev); + bitmap[i] = 1; + + for (k = i + 1; k <= mp->npaths; k++) { + if (bitmap[k]) + continue; + if (0 == strcmp(all_paths[mp->pindex[i]].serial, + all_paths[mp->pindex[k]].serial)) { + path_buff_p += sprintf(path_buff_p, " %s", all_paths[mp->pindex[k]].dev); + bitmap[k] = 1; + path_count++; + } + } + pg_buff_p += sprintf(pg_buff_p, " 1 round-robin %i 0%s", + path_count, path_buff); + } + sprintf(str, " %i%s", pg_count, pg_buff); + free(bitmap); +} + static int -make_dm_node(char * str) -{ +dm_simplecmd(int task, const char *name) { int r = 0; - char buff[FILE_NAME_SIZE]; - struct dm_names * names; - unsigned next = 0; struct dm_task *dmt; - if (!(dmt = dm_task_create(DM_DEVICE_LIST))) + if (!(dmt = dm_task_create(task))) return 0; - if (!dm_task_run(dmt)) + if (!dm_task_set_name(dmt, name)) goto out; - if (!(names = dm_task_get_names(dmt))) - goto out; + r = dm_task_run(dmt); - if (!names->dev) { - r = 1; - goto out; - } + out: + dm_task_destroy(dmt); + return r; +} - do { - if (0 == strcmp(names->name, str)) - break; - next = names->next; - names = (void *) names + next; - } while (next); +static int +dm_addmap(int task, const char *name, const char *params, long size) { + struct dm_task *dmt; - sprintf(buff, "/dev/mapper/%s", str); - unlink(buff); - mknod(buff, 0600 | S_IFBLK, names->dev); + if (!(dmt = dm_task_create(task))) + return 0; - out: - dm_task_destroy(dmt); - return r; + if (!dm_task_set_name(dmt, name)) + goto addout; + + if (!dm_task_add_target(dmt, 0, size, DM_TARGET, params)) + goto addout; + if (!dm_task_run(dmt)) + goto addout; + + addout: + dm_task_destroy(dmt); + return 1; } static int -add_map(struct env * conf, struct path * all_paths, +setup_map(struct env * conf, struct path * all_paths, struct multipath * mp, int index, int op) { char params[255]; char * params_p; - struct dm_task *dmt; int i, np; /* defaults for multipath target */ @@ -602,11 +497,6 @@ add_map(struct env * conf, struct path * all_paths, char * dm_ps_name = "round-robin"; int dm_ps_nr_args = 0; - if (!(dmt = dm_task_create(op))) - return 0; - - if (!dm_task_set_name(dmt, mp[index].wwid)) - goto addout; params_p = ¶ms[0]; np = 0; @@ -614,17 +504,13 @@ add_map(struct env * conf, struct path * all_paths, if (0 == all_paths[PINDEX(index,i)].sg_id.scsi_type) np++; } - if (np == 0) - goto addout; if (np < 1) - goto addout; - - params_p += sprintf(params_p, "%i", conf->dm_path_test_int); + return 0; - if (all_paths[PINDEX(index,0)].iopolicy == MULTIBUS && - !conf->forcedfailover ) { - params_p += sprintf(params_p, " %i %s %i %i", + if ((all_paths[PINDEX(index,0)].iopolicy == MULTIBUS && + conf->iopolicy == -1) || conf->iopolicy == MULTIBUS) { + params_p += sprintf(params_p, " 1 %i %s %i %i", dm_pg_prio, dm_ps_name, np, dm_ps_nr_args); for (i=0; i<=mp[index].npaths; i++) { @@ -635,8 +521,9 @@ add_map(struct env * conf, struct path * all_paths, } } - if (all_paths[PINDEX(index,0)].iopolicy == FAILOVER || - conf->forcedfailover) { + if ((all_paths[PINDEX(index,0)].iopolicy == FAILOVER && + conf->iopolicy == -1) || conf->iopolicy == FAILOVER) { + params_p += sprintf(params_p, " %i", mp[index].npaths + 1); for (i=0; i<=mp[index].npaths; i++) { if (0 != all_paths[PINDEX(index,i)].sg_id.scsi_type) continue; @@ -649,8 +536,13 @@ add_map(struct env * conf, struct path * all_paths, } } + if ((all_paths[PINDEX(index,0)].iopolicy == GROUP_BY_SERIAL && + conf->iopolicy == -1) || conf->iopolicy == GROUP_BY_SERIAL) { + group_by_serial(&mp[index], all_paths, params_p); + } + if (mp[index].size < 0) - goto addout; + return 0; if (!conf->quiet) { if (op == DM_DEVICE_RELOAD) @@ -661,17 +553,14 @@ add_map(struct env * conf, struct path * all_paths, mp[index].wwid, mp[index].size, DM_TARGET, params); } - if (!dm_task_add_target(dmt, 0, mp[index].size, DM_TARGET, params)) - goto addout; - - if (!dm_task_run(dmt)) - goto addout; + if (op == DM_DEVICE_RELOAD) + dm_simplecmd(DM_DEVICE_SUSPEND, mp[index].wwid); + dm_addmap(op, mp[index].wwid, params, mp[index].size); - make_dm_node(mp[index].wwid); + if (op == DM_DEVICE_RELOAD) + dm_simplecmd(DM_DEVICE_RESUME, mp[index].wwid); - addout: - dm_task_destroy(dmt); return 1; } @@ -712,14 +601,19 @@ static void usage(char * progname) { fprintf(stderr, VERSION_STRING); - fprintf(stderr, "Usage: %s [-v|-q] [-d] [-i int] [-m max_devs]\n", + fprintf(stderr, "Usage: %s [-v|-q] [-d] [-m max_devs]", progname); + fprintf(stderr, "[-p failover|multibus|group_by_serial] [device]\n"); + fprintf(stderr, "\t-v\t\tverbose, print all paths and multipaths\n"); + fprintf(stderr, "\t-q\t\tquiet, no output at all\n"); fprintf(stderr, "\t-d\t\tdry run, do not create or update devmaps\n"); - fprintf(stderr, "\t-f\t\tforce maps to failover mode (1 path/pg)\n"); - fprintf(stderr, "\t-i\t\tmultipath target param : polling interval\n"); fprintf(stderr, "\t-m max_devs\tscan {max_devs} devices at most\n"); - fprintf(stderr, "\t-q\t\tquiet, no output at all\n"); - fprintf(stderr, "\t-v\t\tverbose, print all paths and multipaths\n"); + fprintf(stderr, "\t-p policy\tforce maps to specified policy :\n"); + fprintf(stderr, "\t failover\t\t- 1 path per priority group\n"); + fprintf(stderr, "\t multibus\t\t- all paths in 1 priority group\n"); + fprintf(stderr, "\t group_by_serial\t- 1 priority group per serial\n"); + fprintf(stderr, "\tdevice\t\tlimit scope to the device's multipath\n"); + fprintf(stderr, "\t\t\t(hotplug-style $DEVPATH reference)\n"); exit(1); } @@ -737,12 +631,7 @@ main(int argc, char *argv[]) conf.dry_run = 0; /* 1 == Do not Create/Update devmaps */ conf.verbose = 0; /* 1 == Print all_paths and mp */ conf.quiet = 0; /* 1 == Do not even print devmaps */ - conf.with_sysfs = 0; /* Default to compat / suboptimal behaviour */ - conf.dm_path_test_int = 10; - - /* kindly provided by libsysfs */ - if (0 == sysfs_get_mnt_path(conf.sysfs_path, FILE_NAME_SIZE)) - conf.with_sysfs = 1; + conf.iopolicy = -1; /* Apply the defaults in get_unique_id() */ for (i = 1; i < argc; ++i) { if (0 == strcmp("-v", argv[i])) { @@ -759,20 +648,19 @@ main(int argc, char *argv[]) conf.quiet = 1; } else if (0 == strcmp("-d", argv[i])) conf.dry_run = 1; - else if (0 == strcmp("-f", argv[i])) - conf.forcedfailover = 1; - else if (0 == strcmp("-i", argv[i])) - conf.dm_path_test_int = atoi(argv[++i]); - else if (0 == strcmp("scsi", argv[i])) - strcpy(conf.hotplugdev, argv[++i]); - else if (*argv[i] == '-') { + else if (0 == strcmp("-p", argv[i])) { + i++; + if (!strcmp(argv[i], "failover")) + conf.iopolicy = FAILOVER; + if (!strcmp(argv[i], "multibus")) + conf.iopolicy = MULTIBUS; + if (!strcmp(argv[i], "group_by_serial")) + conf.iopolicy = GROUP_BY_SERIAL; + } else if (*argv[i] == '-') { fprintf(stderr, "Unknown switch: %s\n", argv[i]); usage(argv[0]); - } else if (*argv[i] != '-') { - fprintf(stderr, "Unknown argument\n"); - usage(argv[0]); - } - + } else + strncpy(conf.hotplugdev, argv[i], FILE_NAME_SIZE); } /* dynamic allocations */ @@ -782,7 +670,7 @@ main(int argc, char *argv[]) if (mp == NULL || all_paths == NULL || all_scsi_ids == NULL) exit(1); - if (!conf.with_sysfs) { + if (sysfs_get_mnt_path(conf.sysfs_path, FILE_NAME_SIZE)) { get_all_scsi_ids(&conf, all_scsi_ids); get_all_paths_nosysfs(&conf, all_paths, all_scsi_ids); } else { @@ -802,9 +690,9 @@ main(int argc, char *argv[]) for (k=0; k<=nmp; k++) { if (map_present(mp[k].wwid)) { - add_map(&conf, all_paths, mp, k, DM_DEVICE_RELOAD); + setup_map(&conf, all_paths, mp, k, DM_DEVICE_RELOAD); } else { - add_map(&conf, all_paths, mp, k, DM_DEVICE_CREATE); + setup_map(&conf, all_paths, mp, k, DM_DEVICE_CREATE); } }