diff options
author | greg@kroah.com <greg@kroah.com> | 2004-03-10 22:40:39 -0800 |
---|---|---|
committer | Greg KH <gregkh@suse.de> | 2005-04-26 21:35:09 -0700 |
commit | a3b37a073d52ff01d4ef023a10f13316da4c9966 (patch) | |
tree | 349bfb87361de4b6d1990b9d5a9283d445b7ea37 /extras/multipath-tools/multipath | |
parent | 49f9acf3844aa5c004b5794e919bd54166e53227 (diff) |
[PATCH] Added multipath-tools 0.1.1 release
Diffstat (limited to 'extras/multipath-tools/multipath')
-rw-r--r-- | extras/multipath-tools/multipath/Makefile | 60 | ||||
-rw-r--r-- | extras/multipath-tools/multipath/devinfo.c | 240 | ||||
-rw-r--r-- | extras/multipath-tools/multipath/devinfo.h | 21 | ||||
-rw-r--r-- | extras/multipath-tools/multipath/main.c | 895 | ||||
-rw-r--r-- | extras/multipath-tools/multipath/main.h | 131 | ||||
-rw-r--r-- | extras/multipath-tools/multipath/multipath.8 | 66 | ||||
-rw-r--r-- | extras/multipath-tools/multipath/multipath.hotplug | 7 | ||||
-rw-r--r-- | extras/multipath-tools/multipath/sg_err.h | 162 | ||||
-rw-r--r-- | extras/multipath-tools/multipath/sg_include.h | 43 | ||||
-rw-r--r-- | extras/multipath-tools/multipath/unused.c | 95 |
10 files changed, 1720 insertions, 0 deletions
diff --git a/extras/multipath-tools/multipath/Makefile b/extras/multipath-tools/multipath/Makefile new file mode 100644 index 0000000000..4121f4fc3b --- /dev/null +++ b/extras/multipath-tools/multipath/Makefile @@ -0,0 +1,60 @@ +# Makefile +# +# Copyright (C) 2003 Christophe Varoqui, <christophe.varoqui@free.fr> + +EXEC = multipath + +prefix = +exec_prefix = ${prefix} +bindir = ${exec_prefix}/sbin +udevdir = ../../.. +klibcdir = $(udevdir)/klibc +sysfsdir = $(udevdir)/libsysfs +mandir = /usr/share/man/man8 +libdmdir = ../libdevmapper +arch = i386 +klibcarch = $(klibcdir)/klibc/arch/$(arch)/include + +CC = gcc +GZIP = /bin/gzip -9 -c + +GCCINCDIR := ${shell $(CC) -print-search-dirs | sed -ne "s/install: \(.*\)/\1include/gp"} +KERNEL_DIR = /lib/modules/${shell uname -r}/build +CFLAGS = -pipe -g -O2 -Wall -Wunused -Wstrict-prototypes -nostdinc \ + -I$(klibcdir)/klibc/include -I$(klibcdir)/klibc/include/bits32 \ + -I$(GCCINCDIR) -I$(KERNEL_DIR)/include -I$(sysfsdir) -I. -I$(klibcarch) + +OBJS = devinfo.o main.o +CRT0 = $(klibcdir)/klibc/crt0.o +LIB = $(klibcdir)/klibc/libc.a +LIBGCC := $(shell $(CC) -print-libgcc-file-name ) + +DMOBJS = $(libdmdir)/libdm-common.o $(libdmdir)/ioctl/libdevmapper.o +SYSFSOBJS = $(sysfsdir)/dlist.o $(sysfsdir)/sysfs_bus.o \ + $(sysfsdir)/sysfs_class.o $(sysfsdir)/sysfs_device.o \ + $(sysfsdir)/sysfs_dir.o $(sysfsdir)/sysfs_driver.o \ + $(sysfsdir)/sysfs_utils.o + +$(EXEC): $(OBJS) + $(LD) -o $(EXEC) $(CRT0) $(OBJS) $(SYSFSOBJS) $(DMOBJS) $(LIB) $(LIBGCC) + strip $(EXEC) + $(GZIP) $(EXEC).8 > $(EXEC).8.gz + +clean: + rm -f core *.o $(EXEC) *.gz + +install: + install -d $(DESTDIR)$(bindir) + install -m 755 $(EXEC) $(DESTDIR)$(bindir)/ + install -d $(DESTDIR)/etc/hotplug.d/scsi/ + install -m 755 multipath.hotplug $(DESTDIR)/etc/hotplug.d/scsi/ + install -d $(DESTDIR)$(mandir) + install -m 644 multipath.8.gz $(DESTDIR)$(mandir) + +uninstall: + rm $(DESTDIR)/etc/hotplug.d/scsi/multipath.hotplug + rm $(DESTDIR)$(bindir)/$(EXEC) + rm $(DESTDIR)$(mandir)/multipath.8.gz + +# Code dependencies +main.o: main.c main.h sg_include.h devinfo.h diff --git a/extras/multipath-tools/multipath/devinfo.c b/extras/multipath-tools/multipath/devinfo.c new file mode 100644 index 0000000000..21890621e0 --- /dev/null +++ b/extras/multipath-tools/multipath/devinfo.c @@ -0,0 +1,240 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sysfs/libsysfs.h> +#include "devinfo.h" +#include "sg_include.h" + +#define FILE_NAME_SIZE 255 + +void +basename(char * str1, char * str2) +{ + char *p = str1 + (strlen(str1) - 1); + + while (*--p != '/') + continue; + strcpy(str2, ++p); +} + +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; +} + +int +get_serial (char * str, char * devname) +{ + int fd; + int len; + char buff[MX_ALLOC_LEN + 1]; + + if ((fd = open(devname, O_RDONLY)) < 0) + return 0; + + 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'; + } + close(fd); + return 1; + } + close(fd); + return 0; +} + +int +get_lun_strings(char * vendor_id, char * product_id, char * rev, char * devname) +{ + int fd; + char buff[36]; + char attr_path[FILE_NAME_SIZE]; + char sysfs_path[FILE_NAME_SIZE]; + char basedev[FILE_NAME_SIZE]; + + if (0 == sysfs_get_mnt_path(sysfs_path, FILE_NAME_SIZE)) { + /* sysfs style */ + basename(devname, basedev); + + sprintf(attr_path, "%s/block/%s/device/vendor", + sysfs_path, basedev); + if (0 > sysfs_read_attribute_value(attr_path, + vendor_id, 8)) return 0; + + sprintf(attr_path, "%s/block/%s/device/model", + sysfs_path, basedev); + if (0 > sysfs_read_attribute_value(attr_path, + product_id, 16)) return 0; + + sprintf(attr_path, "%s/block/%s/device/rev", + sysfs_path, basedev); + if (0 > sysfs_read_attribute_value(attr_path, + rev, 4)) return 0; + } else { + /* ioctl style */ + if ((fd = open(devname, O_RDONLY)) < 0) + return 0; + if (0 != do_inq(fd, 0, 0, 0, buff, 36, 1)) + return 0; + memcpy(vendor_id, &buff[8], 8); + memcpy(product_id, &buff[16], 16); + memcpy(rev, &buff[32], 4); + close(fd); + return 1; + } + return 0; +} + +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'; +} + +/* get EVPD page 0x83 off 8 */ +/* tested ok with StorageWorks */ +int +get_evpd_wwid(char * devname, char * wwid) +{ + int fd; + char buff[MX_ALLOC_LEN + 1]; + + if ((fd = open(devname, O_RDONLY)) < 0) + return 0; + + if (0 == do_inq(fd, 0, 1, 0x83, buff, MX_ALLOC_LEN, 1)) { + sprint_wwid(wwid, &buff[8]); + close(fd); + return 1; /* success */ + } + + close(fd); + return 0; /* not good */ +} + +long +get_disk_size (char * devname) { + long size; + int fd; + char attr_path[FILE_NAME_SIZE]; + char sysfs_path[FILE_NAME_SIZE]; + char buff[FILE_NAME_SIZE]; + char basedev[FILE_NAME_SIZE]; + + if (0 == sysfs_get_mnt_path(sysfs_path, FILE_NAME_SIZE)) { + basename(devname, basedev); + sprintf(attr_path, "%s/block/%s/size", + 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(devname, O_RDONLY)) < 0) + return -1; + if(!ioctl(fd, BLKGETSIZE, &size)) + return size; + } + return -1; +} + +int +do_tur(char *dev) +{ + unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 }; + struct sg_io_hdr io_hdr; + unsigned char sense_buffer[32]; + int fd; + + fd = open(dev, O_RDONLY); + + if (fd < 0) + return 0; + + 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 (sense_buffer); + io_hdr.dxfer_direction = SG_DXFER_NONE; + io_hdr.cmdp = turCmdBlk; + io_hdr.sbp = sense_buffer; + io_hdr.timeout = 20000; + io_hdr.pack_id = 0; + + if (ioctl(fd, SG_IO, &io_hdr) < 0) { + close(fd); + return 0; + } + + close(fd); + + if (io_hdr.info & SG_INFO_OK_MASK) + return 0; + + return 1; +} diff --git a/extras/multipath-tools/multipath/devinfo.h b/extras/multipath-tools/multipath/devinfo.h new file mode 100644 index 0000000000..ee17aba923 --- /dev/null +++ b/extras/multipath-tools/multipath/devinfo.h @@ -0,0 +1,21 @@ +#define INQUIRY_CMDLEN 6 +#define INQUIRY_CMD 0x12 +#define SENSE_BUFF_LEN 32 +#define DEF_TIMEOUT 60000 +#define RECOVERED_ERROR 0x01 +#define MX_ALLOC_LEN 255 +#define WWID_SIZE 33 +#define BLKGETSIZE _IO(0x12,96) +#define TUR_CMD_LEN 6 + +/* exerpt from "sg_err.h" */ +#define SCSI_CHECK_CONDITION 0x2 +#define SCSI_COMMAND_TERMINATED 0x22 +#define SG_ERR_DRIVER_SENSE 0x08 + +void basename (char *, char *); +int get_serial (char *, char *); +int get_lun_strings (char *, char *, char *, char *); +int get_evpd_wwid(char *, char *); +long get_disk_size (char *); +int do_tur (char *); diff --git a/extras/multipath-tools/multipath/main.c b/extras/multipath-tools/multipath/main.c new file mode 100644 index 0000000000..8f06a8f94c --- /dev/null +++ b/extras/multipath-tools/multipath/main.c @@ -0,0 +1,895 @@ +/* + * Soft: multipath device mapper target autoconfig + * + * Version: $Id: main.h,v 0.0.1 2003/09/18 15:13:38 cvaroqui Exp $ + * + * Author: Copyright (C) 2003 Christophe Varoqui + * + * 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. + * + * 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; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <linux/kdev_t.h> +#include <string.h> +#include <signal.h> +#include <sys/ioctl.h> +#include <dlist.h> +#include <sysfs/libsysfs.h> +#include "../libdevmapper/libdevmapper.h" +#include "main.h" +#include "devinfo.h" + +/* White list switch */ +static int +get_unique_id(struct path * mypath) +{ + int i; + static struct { + char * vendor; + char * product; + int iopolicy; + int (*getuid) (char *, char *); + } wlist[] = { + {"COMPAQ ", "HSV110 (C)COMPAQ", GROUP_BY_TUR, &get_evpd_wwid}, + {"COMPAQ ", "MSA1000 ", GROUP_BY_TUR, &get_evpd_wwid}, + {"COMPAQ ", "MSA1000 VOLUME ", GROUP_BY_TUR, &get_evpd_wwid}, + {"DEC ", "HSG80 ", GROUP_BY_TUR, &get_evpd_wwid}, + {"HP ", "HSV100 ", GROUP_BY_TUR, &get_evpd_wwid}, + {"HP ", "A6189A ", MULTIBUS, &get_evpd_wwid}, + {"HP ", "OPEN- ", MULTIBUS, &get_evpd_wwid}, + {"DDN ", "SAN DataDirector", MULTIBUS, &get_evpd_wwid}, + {"FSC ", "CentricStor ", MULTIBUS, &get_evpd_wwid}, + {"HITACHI ", "DF400 ", MULTIBUS, &get_evpd_wwid}, + {"HITACHI ", "DF500 ", MULTIBUS, &get_evpd_wwid}, + {"HITACHI ", "DF600 ", MULTIBUS, &get_evpd_wwid}, + {"IBM ", "ProFibre 4000R ", MULTIBUS, &get_evpd_wwid}, + {"SGI ", "TP9100 ", MULTIBUS, &get_evpd_wwid}, + {"SGI ", "TP9300 ", MULTIBUS, &get_evpd_wwid}, + {"SGI ", "TP9400 ", MULTIBUS, &get_evpd_wwid}, + {"SGI ", "TP9500 ", MULTIBUS, &get_evpd_wwid}, + {NULL, NULL, 0, NULL}, + }; + + 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) { + mypath->iopolicy = wlist[i].iopolicy; + if (!wlist[i].getuid(mypath->sg_dev, mypath->wwid)) + return 0; + } + } + return 1; +} + +static int +sysfsdevice2devname (char *devname, char *device) +{ + char sysfs_path[FILE_NAME_SIZE]; + char block_path[FILE_NAME_SIZE]; + char link_path[FILE_NAME_SIZE]; + int r; + + if (sysfs_get_mnt_path(sysfs_path, FILE_NAME_SIZE)) { + fprintf(stderr, "[device] feature available with sysfs only\n"); + exit (1); + } + + sprintf(link_path, "%s%s/block", sysfs_path, device); + + r = sysfs_get_link(link_path, block_path, FILE_NAME_SIZE); + + if (r != 0) + return 1; + + sysfs_get_name_from_path(block_path, devname, FILE_NAME_SIZE); + + return 0; +} + + +static int +devt2devname (char *devname, int major, int minor) +{ + struct sysfs_directory * sdir; + struct sysfs_directory * devp; + char sysfs_path[FILE_NAME_SIZE]; + char block_path[FILE_NAME_SIZE]; + char attr_path[FILE_NAME_SIZE]; + char attr_value[16]; + char attr_ref_value[16]; + + if (sysfs_get_mnt_path(sysfs_path, FILE_NAME_SIZE)) { + fprintf(stderr, "-D feature available with sysfs only\n"); + exit (1); + } + + sprintf(attr_ref_value, "%i:%i\n", major, minor); + sprintf(block_path, "%s/block", sysfs_path); + sdir = sysfs_open_directory(block_path); + sysfs_read_directory(sdir); + + dlist_for_each_data(sdir->subdirs, devp, struct sysfs_directory) { + sprintf(attr_path, "%s/%s/dev", block_path, devp->name); + sysfs_read_attribute_value(attr_path, attr_value, 16); + + if (!strcmp(attr_value, attr_ref_value)) { + sprintf(attr_path, "%s/%s", block_path, devp->name); + sysfs_get_name_from_path(attr_path, devname, FILE_NAME_SIZE); + break; + } + } + + sysfs_close_directory(sdir); + + return 0; +} + +static int +blacklist (char * dev) { + int i; + static struct { + char * headstr; + int lengh; + } blist[] = { + {"cciss", 5}, + {"hd", 2}, + {"md", 2}, + {"dm", 2}, + {"sr", 2}, + {"scd", 3}, + {"ram", 3}, + {"raw", 3}, + {NULL, 0}, + }; + + for (i = 0; blist[i].lengh; i++) { + if (strncmp(dev, blist[i].headstr, blist[i].lengh) == 0) + return 1; + } + return 0; +} + +static int +devinfo (struct path *curpath) +{ + get_lun_strings(curpath->vendor_id, + curpath->product_id, + curpath->rev, + curpath->sg_dev); + get_serial(curpath->serial, curpath->sg_dev); + curpath->tur = do_tur(curpath->sg_dev); + if (!get_unique_id(curpath)) + return 1; + + return 0; +} + + +static int +get_all_paths_sysfs(struct env * conf, struct path * all_paths) +{ + int k=0; + struct sysfs_directory * sdir; + struct sysfs_directory * devp; + struct sysfs_link * linkp; + char refwwid[WWID_SIZE]; + char empty_buff[WWID_SIZE]; + char buff[FILE_NAME_SIZE]; + char path[FILE_NAME_SIZE]; + struct path curpath; + + memset(empty_buff, 0, WWID_SIZE); + memset(refwwid, 0, WWID_SIZE); + + /* if called from hotplug, only consider the paths that relate + to the device pointed by conf.hotplugdev */ + + if (strncmp("/devices", conf->hotplugdev, 8) == 0) { + if (sysfsdevice2devname (buff, conf->hotplugdev)) + return 0; + + sprintf(curpath.sg_dev, "/dev/%s", buff); + + if (devinfo(&curpath)) + return 0; + + strcpy(refwwid, curpath.wwid); + memset(&curpath, 0, sizeof(path)); + } + + /* if major/minor specified on the cmd line, + only consider affiliated paths */ + + if (conf->major >= 0 && conf->minor >= 0) { + if (devt2devname(buff, conf->major, conf->minor)) + return 0; + + sprintf(curpath.sg_dev, "/dev/%s", buff); + + if (devinfo(&curpath)) + return 0; + + strcpy(refwwid, curpath.wwid); + memset(&curpath, 0, sizeof(path)); + } + + + sprintf(path, "%s/block", conf->sysfs_path); + sdir = sysfs_open_directory(path); + sysfs_read_directory(sdir); + + dlist_for_each_data(sdir->subdirs, devp, struct sysfs_directory) { + if (blacklist(devp->name)) + continue; + + sysfs_read_directory(devp); + + if(devp->links == NULL) + continue; + + dlist_for_each_data(devp->links, linkp, struct sysfs_link) { + if (!strncmp(linkp->name, "device", 6)) + break; + } + + if (linkp == NULL) { + continue; + } + + basename(devp->path, buff); + sprintf(curpath.sg_dev, "/dev/%s", buff); + + if(devinfo(&curpath)) { + memset(&curpath, 0, sizeof(path)); + continue; + } + + if (memcmp(empty_buff, refwwid, WWID_SIZE) != 0 && + strncmp(curpath.wwid, refwwid, WWID_SIZE) != 0) { + memset(&curpath, 0, sizeof(path)); + continue; + } + + strcpy(all_paths[k].sg_dev, curpath.sg_dev); + strcpy(all_paths[k].dev, curpath.sg_dev); + strcpy(all_paths[k].wwid, curpath.wwid); + strcpy(all_paths[k].vendor_id, curpath.vendor_id); + strcpy(all_paths[k].product_id, curpath.product_id); + all_paths[k].iopolicy = curpath.iopolicy; + all_paths[k].tur = curpath.tur; + + /* done with curpath, zero for reuse */ + memset(&curpath, 0, sizeof(path)); + + basename(linkp->target, buff); + sscanf(buff, "%i:%i:%i:%i", + &all_paths[k].sg_id.host_no, + &all_paths[k].sg_id.channel, + &all_paths[k].sg_id.scsi_id, + &all_paths[k].sg_id.lun); + k++; + } + sysfs_close_directory(sdir); + return 0; +} + +static int +get_all_paths_nosysfs(struct env * conf, struct path * all_paths, + struct scsi_dev * all_scsi_ids) +{ + int k, i, fd; + char buff[FILE_NAME_SIZE]; + char file_name[FILE_NAME_SIZE]; + + for (k = 0; k < conf->max_devs; k++) { + strcpy(file_name, "/dev/sg"); + sprintf(buff, "%d", k); + strncat(file_name, buff, FILE_NAME_SIZE); + strcpy(all_paths[k].sg_dev, 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; + + 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(fd); + + for (i = 0; i < conf->max_devs; i++) { + if ((all_paths[k].sg_id.host_no == + all_scsi_ids[i].host_no) + && (all_paths[k].sg_id.scsi_id == + (all_scsi_ids[i].scsi_id.dev_id & 0xff)) + && (all_paths[k].sg_id.lun == + ((all_scsi_ids[i].scsi_id.dev_id >> 8) & 0xff)) + && (all_paths[k].sg_id.channel == + ((all_scsi_ids[i].scsi_id. + dev_id >> 16) & 0xff))) { + strcpy(all_paths[k].dev, all_scsi_ids[i].dev); + break; + } + } + } + return 0; +} + +static int +get_all_scsi_ids(struct env * conf, struct scsi_dev * all_scsi_ids) +{ + int k, big, little, res, host_no, fd; + char buff[64]; + char fname[FILE_NAME_SIZE]; + struct scsi_idlun my_scsi_id; + + for (k = 0; k < conf->max_devs; k++) { + strcpy(fname, "/dev/sd"); + if (k < 26) { + buff[0] = 'a' + (char) k; + buff[1] = '\0'; + strcat(fname, buff); + } else if (k <= 255) { + /* assumes sequence goes x,y,z,aa,ab,ac etc */ + big = k / 26; + little = k - (26 * big); + big = big - 1; + + buff[0] = 'a' + (char) big; + buff[1] = 'a' + (char) little; + buff[2] = '\0'; + strcat(fname, buff); + } else + strcat(fname, "xxxx"); + + if ((fd = open(fname, O_RDONLY)) < 0) { + if (conf->verbose) + fprintf(stderr, "can't open %s. mknod ?", + fname); + continue; + } + + res = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &my_scsi_id); + if (res < 0) { + close(fd); + printf("Could not get scsi idlun\n"); + continue; + } + + res = ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &host_no); + if (res < 0) { + close(fd); + printf("Could not get host_no\n"); + continue; + } + + close(fd); + + strcpy(all_scsi_ids[k].dev, fname); + all_scsi_ids[k].scsi_id = my_scsi_id; + all_scsi_ids[k].host_no = host_no; + } + return 0; +} + +/* print_path style */ +#define ALL 0 +#define NOWWID 1 + +static void +print_path(struct path * all_paths, int k, int style) +{ + if (style != NOWWID) + printf("%s ", all_paths[k].wwid); + else + printf(" \\_"); + printf("(%i %i %i %i) ", + all_paths[k].sg_id.host_no, + all_paths[k].sg_id.channel, + all_paths[k].sg_id.scsi_id, all_paths[k].sg_id.lun); + if(0 != strcmp(all_paths[k].sg_dev, all_paths[k].dev)) + printf("%s ", all_paths[k].sg_dev); + printf("%s ", all_paths[k].dev); + printf("[%.16s]\n", all_paths[k].product_id); +} + +static void +print_all_path(struct env * conf, struct path * all_paths) +{ + int k; + char empty_buff[WWID_SIZE]; + + memset(empty_buff, 0, WWID_SIZE); + for (k = 0; k < conf->max_devs; k++) { + if (memcmp(empty_buff, all_paths[k].wwid, WWID_SIZE) == 0) + continue; + print_path(all_paths, k, ALL); + } +} + +static void +print_all_mp(struct path * all_paths, struct multipath * mp, int nmp) +{ + int k, i; + + for (k = 0; k <= nmp; k++) { + printf("%s\n", mp[k].wwid); + for (i = 0; i <= mp[k].npaths; i++) + print_path(all_paths, PINDEX(k,i), NOWWID); + } +} + +static int +coalesce_paths(struct env * conf, struct multipath * mp, + struct path * all_paths) +{ + int k, i, nmp, np, already_done; + char empty_buff[WWID_SIZE]; + + nmp = -1; + already_done = 0; + memset(empty_buff, 0, WWID_SIZE); + + for (k = 0; k < conf->max_devs - 1; k++) { + /* skip this path for some reason */ + + /* 1. if path has no unique id */ + if (memcmp(empty_buff, all_paths[k].wwid, WWID_SIZE) == 0) + continue; + + /* 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; + } + if (already_done) { + already_done = 0; + continue; + } + + /* at this point, we know we really got a new mp */ + np = 0; + nmp++; + strcpy(mp[nmp].wwid, all_paths[k].wwid); + PINDEX(nmp,np) = k; + + if (mp[nmp].size == 0) + 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)) { + np++; + PINDEX(nmp,np) = i; + mp[nmp].npaths = np; + } + } + } + return nmp; +} + +static void +group_by_tur(struct multipath * mp, struct path * all_paths, char * str) { + int left_path_count = 0; + int right_path_count = 0; + int i; + char left_path_buff[FILE_NAME_SIZE], right_path_buff[FILE_NAME_SIZE]; + char * left_path_buff_p = &left_path_buff[0]; + char * right_path_buff_p = &right_path_buff[0]; + + for (i = 0; i <= mp->npaths; i++) { + if (all_paths[mp->pindex[i]].tur) { + left_path_buff_p += sprintf(left_path_buff_p, " %s", all_paths[mp->pindex[i]].dev); + left_path_count++; + } else { + right_path_buff_p += sprintf(right_path_buff_p, " %s", all_paths[mp->pindex[i]].dev); + right_path_count++; + } + } + if (!left_path_count) + sprintf(str, " 1 round-robin %i 0 %s", right_path_count, right_path_buff); + else if (!right_path_count) + sprintf(str, " 1 round-robin %i 0 %s", left_path_count, left_path_buff); + else + sprintf(str, " 2 round-robin %i 0 %s round-robin %i 0 %s", + left_path_count, left_path_buff, + right_path_count, right_path_buff); +} + +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, " round-robin %i 0%s", + path_count, path_buff); + } + sprintf(str, " %i%s", pg_count, pg_buff); + free(bitmap); +} + +static int +dm_simplecmd(int task, const char *name) { + int r = 0; + struct dm_task *dmt; + + if (!(dmt = dm_task_create(task))) + return 0; + + if (!dm_task_set_name(dmt, name)) + goto out; + + r = dm_task_run(dmt); + + out: + dm_task_destroy(dmt); + return r; +} + +static int +dm_addmap(int task, const char *name, const char *params, long size) { + struct dm_task *dmt; + + if (!(dmt = dm_task_create(task))) + return 0; + + 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 +setup_map(struct env * conf, struct path * all_paths, + struct multipath * mp, int index, int op) +{ + char params[255]; + char * params_p; + int i, np; + + /* defaults for multipath target */ + char * dm_ps_name = "round-robin"; + int dm_ps_nr_args = 0; + + params_p = ¶ms[0]; + + np = 0; + for (i=0; i<=mp[index].npaths; i++) { + if (0 == all_paths[PINDEX(index,i)].sg_id.scsi_type) + np++; + } + + if (np < 1) + return 0; + + if ((all_paths[PINDEX(index,0)].iopolicy == MULTIBUS && + conf->iopolicy == -1) || conf->iopolicy == MULTIBUS) { + params_p += sprintf(params_p, " 1 %s %i %i", + dm_ps_name, np, dm_ps_nr_args); + + for (i=0; i<=mp[index].npaths; i++) { + if (0 != all_paths[PINDEX(index,i)].sg_id.scsi_type) + continue; + params_p += sprintf(params_p, " %s", + all_paths[PINDEX(index,i)].dev); + } + } + + 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; + params_p += sprintf(params_p, " %s ", + dm_ps_name); + params_p += sprintf(params_p, "1 %i", + dm_ps_nr_args); + params_p += sprintf(params_p, " %s", + all_paths[PINDEX(index,i)].dev); + } + } + + 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 ((all_paths[PINDEX(index,0)].iopolicy == GROUP_BY_TUR && + conf->iopolicy == -1) || conf->iopolicy == GROUP_BY_TUR) { + group_by_tur(&mp[index], all_paths, params_p); + } + + if (mp[index].size < 0) + return 0; + + if (!conf->quiet) { + if (op == DM_DEVICE_RELOAD) + printf("U:"); + if (op == DM_DEVICE_CREATE) + printf("N:"); + printf("%s:0 %li %s %s\n", + mp[index].wwid, mp[index].size, DM_TARGET, params); + } + + if (op == DM_DEVICE_RELOAD) + dm_simplecmd(DM_DEVICE_SUSPEND, mp[index].wwid); + + dm_addmap(op, mp[index].wwid, params, mp[index].size); + + if (op == DM_DEVICE_RELOAD) + dm_simplecmd(DM_DEVICE_RESUME, mp[index].wwid); + + return 1; +} + +static int +map_present(char * str) +{ + int r = 0; + struct dm_task *dmt; + struct dm_names *names; + unsigned next = 0; + + if (!(dmt = dm_task_create(DM_DEVICE_LIST))) + return 0; + + if (!dm_task_run(dmt)) + goto out; + + if (!(names = dm_task_get_names(dmt))) + goto out; + + if (!names->dev) { + goto out; + } + + do { + if (0 == strcmp(names->name, str)) + r = 1; + next = names->next; + names = (void *) names + next; + } while (next); + + out: + dm_task_destroy(dmt); + return r; +} + +static void +signal_daemon (void) +{ + FILE *file; + pid_t pid; + char *buf; + + buf = malloc (8); + + file = fopen (PIDFILE, "r"); + + if (!file) { + fprintf(stderr, "cannot signal daemon, pidfile not found\n"); + return; + } + + buf = fgets (buf, 8, file); + fclose (file); + + pid = (pid_t) atol (buf); + free (buf); + + kill(pid, SIGHUP); +} + +static int +filepresent(char * run) { + struct stat buf; + + if(!stat(run, &buf)) + return 1; + return 0; +} + +static void +usage(char * progname) +{ + fprintf(stderr, VERSION_STRING); + fprintf(stderr, "Usage: %s [-v|-q] [-d] [-m max_devs]\n", + progname); + fprintf(stderr, " [-p failover|multibus|group_by_serial]\n" \ + " [device]\n" \ + "\n" \ + "\t-v\t\tverbose, print all paths and multipaths\n" \ + "\t-q\t\tquiet, no output at all\n" \ + "\t-d\t\tdry run, do not create or update devmaps\n" \ + "\t-m max_devs\tscan {max_devs} devices at most\n" \ + "\n" \ + "\t-p policy\tforce maps to specified policy :\n" \ + "\t failover\t\t- 1 path per priority group\n" \ + "\t multibus\t\t- all paths in 1 priority group\n" \ + "\t group_by_serial\t- 1 priority group per serial\n" \ + "\t group_by_tur\t\t- 1 priority group per TUR state\n" \ + "\n" \ + "\t-D maj min\tlimit scope to the device's multipath\n" \ + "\t\t\t(major minor device reference)\n" + "\tdevice\t\tlimit scope to the device's multipath\n" \ + "\t\t\t(hotplug-style $DEVPATH reference)\n" + ); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + struct multipath * mp; + struct path * all_paths; + struct scsi_dev * all_scsi_ids; + struct env conf; + int i, k, nmp; + int try = 0; + + /* Don't run in parallel */ + while (filepresent(RUN) && try++ < MAXTRY) + usleep(100000); + + if (filepresent(RUN)) { + fprintf(stderr, "waited for to long. exiting\n"); + exit (1); + } + + /* Our turn */ + if (!open(RUN, O_CREAT)) { + fprintf(stderr, "can't create runfile\n"); + exit (1); + } + + /* Default behaviour */ + conf.max_devs = MAX_DEVS; + 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.iopolicy = -1; /* Apply the defaults in get_unique_id() */ + conf.major = -1; + conf.minor = -1; + + for (i = 1; i < argc; ++i) { + if (0 == strcmp("-v", argv[i])) { + if (conf.quiet == 1) + usage(argv[0]); + conf.verbose = 1; + } else if (0 == strcmp("-m", argv[i])) { + conf.max_devs = atoi(argv[++i]); + if (conf.max_devs < 2) + usage(argv[0]); + } else if (0 == strcmp("-D", argv[i])) { + conf.major = atoi(argv[++i]); + conf.minor = atoi(argv[++i]); + } else if (0 == strcmp("-q", argv[i])) { + if (conf.verbose == 1) + usage(argv[0]); + conf.quiet = 1; + } else if (0 == strcmp("-d", argv[i])) + conf.dry_run = 1; + 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; + if (!strcmp(argv[i], "group_by_tur")) + conf.iopolicy = GROUP_BY_TUR; + } else if (*argv[i] == '-') { + fprintf(stderr, "Unknown switch: %s\n", argv[i]); + usage(argv[0]); + } else + strncpy(conf.hotplugdev, argv[i], FILE_NAME_SIZE); + } + + /* dynamic allocations */ + mp = malloc(conf.max_devs * sizeof(struct multipath)); + all_paths = malloc(conf.max_devs * sizeof(struct path)); + all_scsi_ids = malloc(conf.max_devs * sizeof(struct scsi_dev)); + if (mp == NULL || all_paths == NULL || all_scsi_ids == NULL) + exit(1); + + 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 { + get_all_paths_sysfs(&conf, all_paths); + } + + nmp = coalesce_paths(&conf, mp, all_paths); + + if (conf.verbose) { + print_all_path(&conf, all_paths); + fprintf(stdout, "\n"); + print_all_mp(all_paths, mp, nmp); + fprintf(stdout, "\n"); + } + + if (conf.dry_run) + exit(0); + + for (k=0; k<=nmp; k++) { + if (map_present(mp[k].wwid)) { + setup_map(&conf, all_paths, mp, k, DM_DEVICE_RELOAD); + } else { + setup_map(&conf, all_paths, mp, k, DM_DEVICE_CREATE); + } + } + + /* signal multipathd that new devmaps may have come up */ + signal_daemon(); + + /* free allocs */ + free(mp); + free(all_paths); + free(all_scsi_ids); + + /* release runfile */ + unlink(RUN); + + exit(0); +} diff --git a/extras/multipath-tools/multipath/main.h b/extras/multipath-tools/multipath/main.h new file mode 100644 index 0000000000..ada7a7394b --- /dev/null +++ b/extras/multipath-tools/multipath/main.h @@ -0,0 +1,131 @@ +/* + * Soft: Description here... + * + * Version: $Id: main.h,v 0.0.1 2003/09/18 15:13:38 cvaroqui Exp $ + * + * Author: Copyright (C) 2003 Christophe Varoqui + * + * 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. + * + * 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; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _MAIN_H +#define _MAIN_H + +/* local includes */ +#include "sg_include.h" + +/* exerpt from "sg_err.h" */ +#define SCSI_CHECK_CONDITION 0x2 +#define SCSI_COMMAND_TERMINATED 0x22 +#define SG_ERR_DRIVER_SENSE 0x08 + +/* exerpt from "scsi.h" */ +#define SCSI_IOCTL_GET_IDLUN 0x5382 +#define SCSI_IOCTL_GET_BUS_NUMBER 0x5386 + +/* global defs */ +#define WWID_SIZE 33 +#define SERIAL_SIZE 14 +#define MAX_DEVS 128 +#define MAX_MP MAX_DEVS / 2 +#define MAX_MP_PATHS MAX_DEVS / 4 +#define FILE_NAME_SIZE 256 +#define DEF_TIMEOUT 60000 +#define EBUFF_SZ 256 +#define TUR_CMD_LEN 6 +#define DM_TARGET "multipath" +#define PIDFILE "/var/run/multipathd.pid" +#define RUN "/var/run/multipath.run" +#define MAXTRY 50 + +/* Storage controlers cpabilities */ +#define FAILOVER 0 +#define MULTIBUS 1 +#define GROUP_BY_SERIAL 2 +#define GROUP_BY_TUR 4 + +#define PINDEX(x,y) mp[(x)].pindex[(y)] + +/* global types */ +struct scsi_idlun { + int dev_id; + int host_unique_id; + int host_no; +}; + +struct sg_id { + int host_no; + int channel; + int scsi_id; + int lun; + int scsi_type; + short h_cmd_per_lun; + short d_queue_depth; + int unused1; + int unused2; +}; + +struct scsi_dev { + char dev[FILE_NAME_SIZE]; + struct scsi_idlun scsi_id; + int host_no; +}; + +struct path { + char dev[FILE_NAME_SIZE]; + char sg_dev[FILE_NAME_SIZE]; + struct scsi_idlun scsi_id; + struct sg_id sg_id; + char wwid[WWID_SIZE]; + char vendor_id[8]; + char product_id[16]; + char rev[4]; + char serial[SERIAL_SIZE]; + int iopolicy; + int tur; +}; + +struct multipath { + char wwid[WWID_SIZE]; + int npaths; + long size; + int pindex[MAX_MP_PATHS]; +}; + +struct env { + int max_devs; + int verbose; + int quiet; + int dry_run; + int iopolicy; + int with_sysfs; + int major; + int minor; + char sysfs_path[FILE_NAME_SIZE]; + char hotplugdev[FILE_NAME_SIZE]; +}; + +/* Build version */ +#define PROG "multipath" + +#define VERSION_CODE 0x000012 +#define DATE_CODE 0x021504 + +#define MULTIPATH_VERSION(version) \ + (version >> 16) & 0xFF, \ + (version >> 8) & 0xFF, \ + version & 0xFF + +#define VERSION_STRING PROG" v%d.%d.%d (%.2d/%.2d, 20%.2d)\n", \ + MULTIPATH_VERSION(VERSION_CODE), \ + MULTIPATH_VERSION(DATE_CODE) + +#endif diff --git a/extras/multipath-tools/multipath/multipath.8 b/extras/multipath-tools/multipath/multipath.8 new file mode 100644 index 0000000000..0856ca8dfc --- /dev/null +++ b/extras/multipath-tools/multipath/multipath.8 @@ -0,0 +1,66 @@ +.TH MULTIPATH 8 "February 2004" "" "Linux Administrator's Manual" +.SH NAME +multipath \- Device mapper target autoconfig +.SH SYNOPSIS +.B multipath +.RB [\| \-v | \-q \|] +.RB [\| \-d \|] +.RB [\| \-i\ \c +.IR int \|] +.RB [\| \-m\ \c +.IR max_devs \|] +.RB [\| \-p\ \c +.BR failover | multibus | group_by_serial \|] +.RB [\| device \|] +.SH DESCRIPTION +.B multipath +is used to detect multiple paths to devices for fail-over or performance reasons and coalesces them. +.SH OPTIONS +.TP +.B \-v +verbose, print all paths and multipaths +.TP +.B \-q +quiet, no output at all +.TP +.B \-d +dry run, do not create or update devmaps +.TP +.BI \-i " int" +multipath target param: polling interval +.TP +.BI \-m " max_devs" +scan +.I max_devs +devices at most +.TP +.BI \-D " major minor" +update only the devmap the path pointed by +.I major minor +is in +.TP +.BI \-p " policy" +force maps to specified policy: +.RS 1.2i +.TP 1.2i +.B failover +1 path per priority group +.TP +.B multibus +all paths in 1 priority group +.TP +.B group_by_serial +1 priority group per serial +.RE +.TP +.BI device +update only the devmap the path pointed by +.I device +is in +.SH "SEE ALSO" +.BR udev (8), +.BR dmsetup (8) +.BR hotplug (8) +.SH AUTHORS +.B multipath +was developed by Christophe Varoqui, <christophe.varoqui@free.fr> and others. diff --git a/extras/multipath-tools/multipath/multipath.hotplug b/extras/multipath-tools/multipath/multipath.hotplug new file mode 100644 index 0000000000..5c411d1edf --- /dev/null +++ b/extras/multipath-tools/multipath/multipath.hotplug @@ -0,0 +1,7 @@ +#!/bin/sh +. /etc/hotplug/hotplug.functions + +# wait for sysfs +sleep 1 + +mesg `/sbin/multipath $DEVPATH` diff --git a/extras/multipath-tools/multipath/sg_err.h b/extras/multipath-tools/multipath/sg_err.h new file mode 100644 index 0000000000..ef57b5ce38 --- /dev/null +++ b/extras/multipath-tools/multipath/sg_err.h @@ -0,0 +1,162 @@ +#ifndef SG_ERR_H +#define SG_ERR_H + +/* Feel free to copy and modify this GPL-ed code into your applications. */ + +/* Version 0.90 (20030519) +*/ + + +/* Some of the following error/status codes are exchanged between the + various layers of the SCSI sub-system in Linux and should never + reach the user. They are placed here for completeness. What appears + here is copied from drivers/scsi/scsi.h which is not visible in + the user space. */ + +#ifndef SCSI_CHECK_CONDITION +/* Following are the "true" SCSI status codes. Linux has traditionally + used a 1 bit right and masked version of these. So now CHECK_CONDITION + and friends (in <scsi/scsi.h>) are deprecated. */ +#define SCSI_CHECK_CONDITION 0x2 +#define SCSI_CONDITION_MET 0x4 +#define SCSI_BUSY 0x8 +#define SCSI_IMMEDIATE 0x10 +#define SCSI_IMMEDIATE_CONDITION_MET 0x14 +#define SCSI_RESERVATION_CONFLICT 0x18 +#define SCSI_COMMAND_TERMINATED 0x22 +#define SCSI_TASK_SET_FULL 0x28 +#define SCSI_ACA_ACTIVE 0x30 +#define SCSI_TASK_ABORTED 0x40 +#endif + +/* The following are 'host_status' codes */ +#ifndef DID_OK +#define DID_OK 0x00 +#endif +#ifndef DID_NO_CONNECT +#define DID_NO_CONNECT 0x01 /* Unable to connect before timeout */ +#define DID_BUS_BUSY 0x02 /* Bus remain busy until timeout */ +#define DID_TIME_OUT 0x03 /* Timed out for some other reason */ +#define DID_BAD_TARGET 0x04 /* Bad target (id?) */ +#define DID_ABORT 0x05 /* Told to abort for some other reason */ +#define DID_PARITY 0x06 /* Parity error (on SCSI bus) */ +#define DID_ERROR 0x07 /* Internal error */ +#define DID_RESET 0x08 /* Reset by somebody */ +#define DID_BAD_INTR 0x09 /* Received an unexpected interrupt */ +#define DID_PASSTHROUGH 0x0a /* Force command past mid-level */ +#define DID_SOFT_ERROR 0x0b /* The low-level driver wants a retry */ +#endif + +/* These defines are to isolate applictaions from kernel define changes */ +#define SG_ERR_DID_OK DID_OK +#define SG_ERR_DID_NO_CONNECT DID_NO_CONNECT +#define SG_ERR_DID_BUS_BUSY DID_BUS_BUSY +#define SG_ERR_DID_TIME_OUT DID_TIME_OUT +#define SG_ERR_DID_BAD_TARGET DID_BAD_TARGET +#define SG_ERR_DID_ABORT DID_ABORT +#define SG_ERR_DID_PARITY DID_PARITY +#define SG_ERR_DID_ERROR DID_ERROR +#define SG_ERR_DID_RESET DID_RESET +#define SG_ERR_DID_BAD_INTR DID_BAD_INTR +#define SG_ERR_DID_PASSTHROUGH DID_PASSTHROUGH +#define SG_ERR_DID_SOFT_ERROR DID_SOFT_ERROR + +/* The following are 'driver_status' codes */ +#ifndef DRIVER_OK +#define DRIVER_OK 0x00 +#endif +#ifndef DRIVER_BUSY +#define DRIVER_BUSY 0x01 +#define DRIVER_SOFT 0x02 +#define DRIVER_MEDIA 0x03 +#define DRIVER_ERROR 0x04 +#define DRIVER_INVALID 0x05 +#define DRIVER_TIMEOUT 0x06 +#define DRIVER_HARD 0x07 +#define DRIVER_SENSE 0x08 /* Sense_buffer has been set */ + +/* Following "suggests" are "or-ed" with one of previous 8 entries */ +#define SUGGEST_RETRY 0x10 +#define SUGGEST_ABORT 0x20 +#define SUGGEST_REMAP 0x30 +#define SUGGEST_DIE 0x40 +#define SUGGEST_SENSE 0x80 +#define SUGGEST_IS_OK 0xff +#endif +#ifndef DRIVER_MASK +#define DRIVER_MASK 0x0f +#endif +#ifndef SUGGEST_MASK +#define SUGGEST_MASK 0xf0 +#endif + +/* These defines are to isolate applictaions from kernel define changes */ +#define SG_ERR_DRIVER_OK DRIVER_OK +#define SG_ERR_DRIVER_BUSY DRIVER_BUSY +#define SG_ERR_DRIVER_SOFT DRIVER_SOFT +#define SG_ERR_DRIVER_MEDIA DRIVER_MEDIA +#define SG_ERR_DRIVER_ERROR DRIVER_ERROR +#define SG_ERR_DRIVER_INVALID DRIVER_INVALID +#define SG_ERR_DRIVER_TIMEOUT DRIVER_TIMEOUT +#define SG_ERR_DRIVER_HARD DRIVER_HARD +#define SG_ERR_DRIVER_SENSE DRIVER_SENSE +#define SG_ERR_SUGGEST_RETRY SUGGEST_RETRY +#define SG_ERR_SUGGEST_ABORT SUGGEST_ABORT +#define SG_ERR_SUGGEST_REMAP SUGGEST_REMAP +#define SG_ERR_SUGGEST_DIE SUGGEST_DIE +#define SG_ERR_SUGGEST_SENSE SUGGEST_SENSE +#define SG_ERR_SUGGEST_IS_OK SUGGEST_IS_OK +#define SG_ERR_DRIVER_MASK DRIVER_MASK +#define SG_ERR_SUGGEST_MASK SUGGEST_MASK + + + +/* The following "print" functions send ACSII to stdout */ +extern void sg_print_command(const unsigned char * command); +extern void sg_print_sense(const char * leadin, + const unsigned char * sense_buffer, int sb_len); +extern void sg_print_status(int masked_status); +extern void sg_print_scsi_status(int scsi_status); +extern void sg_print_host_status(int host_status); +extern void sg_print_driver_status(int driver_status); + +/* sg_chk_n_print() returns 1 quietly if there are no errors/warnings + else it prints to standard output and returns 0. */ +extern int sg_chk_n_print(const char * leadin, int masked_status, + int host_status, int driver_status, + const unsigned char * sense_buffer, int sb_len); + +/* The following function declaration is for the sg version 3 driver. + Only version 3 sg_err.c defines it. */ +struct sg_io_hdr; +extern int sg_chk_n_print3(const char * leadin, struct sg_io_hdr * hp); + + +/* The following "category" function returns one of the following */ +#define SG_ERR_CAT_CLEAN 0 /* No errors or other information */ +#define SG_ERR_CAT_MEDIA_CHANGED 1 /* interpreted from sense buffer */ +#define SG_ERR_CAT_RESET 2 /* interpreted from sense buffer */ +#define SG_ERR_CAT_TIMEOUT 3 +#define SG_ERR_CAT_RECOVERED 4 /* Successful command after recovered err */ +#define SG_ERR_CAT_SENSE 98 /* Something else is in the sense buffer */ +#define SG_ERR_CAT_OTHER 99 /* Some other error/warning has occurred */ + +extern int sg_err_category(int masked_status, int host_status, + int driver_status, const unsigned char * sense_buffer, + int sb_len); + +extern int sg_err_category_new(int scsi_status, int host_status, + int driver_status, const unsigned char * sense_buffer, + int sb_len); + +/* The following function declaration is for the sg version 3 driver. + Only version 3 sg_err.c defines it. */ +extern int sg_err_category3(struct sg_io_hdr * hp); + +/* Returns length of SCSI command given the opcode (first byte) */ +extern int sg_get_command_size(unsigned char opcode); + +extern void sg_get_command_name(unsigned char opcode, int buff_len, + char * buff); + +#endif diff --git a/extras/multipath-tools/multipath/sg_include.h b/extras/multipath-tools/multipath/sg_include.h new file mode 100644 index 0000000000..460506826e --- /dev/null +++ b/extras/multipath-tools/multipath/sg_include.h @@ -0,0 +1,43 @@ +#ifdef SG_KERNEL_INCLUDES + #define __user + typedef unsigned char u8; + #include "/usr/src/linux/include/scsi/sg.h" + #include "/usr/src/linux/include/scsi/scsi.h" +#else + #ifdef SG_TRICK_GNU_INCLUDES + #include <linux/../scsi/sg.h> + #include <linux/../scsi/scsi.h> + #else + #include <scsi/sg.h> + #endif +#endif + +/* + Getting the correct include files for the sg interface can be an ordeal. + In a perfect world, one would just write: + #include <scsi/sg.h> + #include <scsi/scsi.h> + This would include the files found in the /usr/include/scsi directory. + Those files are maintained with the GNU library which may or may not + agree with the kernel and version of sg driver that is running. Any + many cases this will not matter. However in some it might, for example + glibc 2.1's include files match the sg driver found in the lk 2.2 + series. Hence if glibc 2.1 is used with lk 2.4 then the additional + sg v3 interface will not be visible. + If this is a problem then defining SG_KERNEL_INCLUDES will access the + kernel supplied header files (assuming they are in the normal place). + The GNU library maintainers and various kernel people don't like + this approach (but it does work). + The technique selected by defining SG_TRICK_GNU_INCLUDES worked (and + was used) prior to glibc 2.2 . Prior to that version /usr/include/linux + was a symbolic link to /usr/src/linux/include/linux . + + There are other approaches if this include "mixup" causes pain. These + would involve include files being copied or symbolic links being + introduced. + + Sorry about the inconvenience. Typically neither SG_KERNEL_INCLUDES + nor SG_TRICK_GNU_INCLUDES is defined. + + dpg 20010415, 20030522 +*/ diff --git a/extras/multipath-tools/multipath/unused.c b/extras/multipath-tools/multipath/unused.c new file mode 100644 index 0000000000..08144cf717 --- /dev/null +++ b/extras/multipath-tools/multipath/unused.c @@ -0,0 +1,95 @@ +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; +} + +static int +do_tur(int fd) +{ + unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 }; + struct sg_io_hdr io_hdr; + unsigned char sense_buffer[32]; + + 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 (sense_buffer); + io_hdr.dxfer_direction = SG_DXFER_NONE; + io_hdr.cmdp = turCmdBlk; + io_hdr.sbp = sense_buffer; + io_hdr.timeout = 20000; + io_hdr.pack_id = 0; + if (ioctl(fd, SG_IO, &io_hdr) < 0) { + close(fd); + return 0; + } + if (io_hdr.info & SG_INFO_OK_MASK) { + return 0; + } + return 1; +} + +static int +del_map(char * str) { + struct dm_task *dmt; + + if (!(dmt = dm_task_create(DM_DEVICE_REMOVE))) + return 0; + if (!dm_task_set_name(dmt, str)) + goto delout; + if (!dm_task_run(dmt)) + goto delout; + + printf("Deleted device map : %s\n", str); + + delout: + dm_task_destroy(dmt); + return 1; +} + +get_table(const char * str) +{ + int r = 0; + struct dm_task *dmt; + void *next = NULL; + uint64_t start, length; + char *target_type = NULL; + char *params; + + if (!(dmt = dm_task_create(DM_DEVICE_TABLE))) + return 0; + + if (!dm_task_set_name(dmt, str)) + goto out; + + if (!dm_task_run(dmt)) + goto out; + + do { + next = dm_get_next_target(dmt, next, &start, &length, + &target_type, ¶ms); + if (target_type) { + printf("%" PRIu64 " %" PRIu64 " %s %s\n", + start, length, target_type, params); + } + } while (next); + + r = 1; + + out: + dm_task_destroy(dmt); + return r; + +} |