[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

Re: [dm-devel] RHEL 4.3 to Sun 6320 storage



Andrew Elwell wrote:

> but the error log is filling up with 
>  kernel: Device sde not ready.
>  kernel: end_request: I/O error, dev sde, sector 2181037632
>  multipathd: 8:64: mark as failed
>  multipathd: cab2a01t2: remaining active paths: 1
>  multipathd: 8:64: tur checker reports path is up
>  multipathd: 8:64: reinstated
>  multipathd: cab2a01t2: remaining active paths: 2
>  kernel: Device sde not ready.
>  kernel: end_request: I/O error, dev sde, sector 3187670215
>  kernel: device-mapper: dm-multipath: Failing path 8:64.

You'll get a device not ready on the secondary/ghost path if you have
MPxIO enabled on the array.


> 
> etc. I guess I should be using something other than the default for
> prio_callout, but what?

Nothing included with the standard kernel and multipath-tools will
work if you have the 6320 array set in MPxIO mode.  You'll either
have to set the array into 'rw' mode, which I would not recommend if
you are sharing LUNs between hosts, or you can try the patches
for the kernel and multipath-tools that I've been working on.
I've attached the patches.

The first patch is to add a hardware handler to the kernel so it
can send the command to the array to enable the secondary path to
become active if there is a problem with the primary path.  Be
sure to enable the hardware handler in the config after applying
the patch and before recompiling your kernel.

The kernel patch requires the bio-sense-data.patch be applied first.
I would also recommend applying the dm-mpath-hw-handler-sense-data
patch also.  Hopefully someone comes along and produces an alternative
to the bio-sense-data patch that can get included into the standard
kernel, but until then you have to apply it yourself.


The second patch is to the multipath-tools to add priority checker
to make sure device-mapper uses the primary path if that is available.
It also adds an entry to the hardware table so that it defaults are
set correctly for a Sun T3/T4 array. There is also a path checker
included, but readsector0 might also work too.


Comments and suggestions on the patches are welcomed.

-- 
Jim


> 
> Please can someone give me hints on the correct incantation for
> path_checker, path_grouping_policy, prio_callout
> 
> Many thanks
> 
> Andrew
> 
> --
> dm-devel mailing list
> dm-devel redhat com
> https://www.redhat.com/mailman/listinfo/dm-devel

diff -uNr multipath-tools-0.4.7.orig/libcheckers/Makefile multipath-tools-0.4.7/libcheckers/Makefile
--- multipath-tools-0.4.7.orig/libcheckers/Makefile	2006-03-13 06:07:45.000000000 -0500
+++ multipath-tools-0.4.7/libcheckers/Makefile	2006-06-09 11:20:14.748802000 -0400
@@ -6,7 +6,7 @@
 
 include ../Makefile.inc
 
-OBJS = checkers.o readsector0.o tur.o directio.o emc_clariion.o hp_sw.o
+OBJS = checkers.o readsector0.o tur.o directio.o emc_clariion.o hp_sw.o sun_tx.o
 
 all: $(BUILD)
 
diff -uNr multipath-tools-0.4.7.orig/libcheckers/checkers.c multipath-tools-0.4.7/libcheckers/checkers.c
--- multipath-tools-0.4.7.orig/libcheckers/checkers.c	2006-03-13 06:07:45.000000000 -0500
+++ multipath-tools-0.4.7/libcheckers/checkers.c	2006-06-09 11:08:08.131391250 -0400
@@ -8,6 +8,7 @@
 #include "hp_sw.h"
 #include "emc_clariion.h"
 #include "readsector0.h"
+#include "sun_tx.h"
 
 static struct checker checkers[] = {
 	{
@@ -55,6 +56,15 @@
 		.init       = readsector0_init,
 		.free       = readsector0_free
 	},
+	{
+		.fd         = 0,
+		.name       = SUN_TX,
+		.message    = "",
+		.context    = NULL,
+		.check      = sun_tx,
+		.init       = sun_tx_init,
+		.free       = sun_tx_free
+	},
 	{0, "", "", NULL, NULL, NULL, NULL},
 };
 
diff -uNr multipath-tools-0.4.7.orig/libcheckers/checkers.h multipath-tools-0.4.7/libcheckers/checkers.h
--- multipath-tools-0.4.7.orig/libcheckers/checkers.h	2006-03-13 06:07:45.000000000 -0500
+++ multipath-tools-0.4.7/libcheckers/checkers.h	2006-06-09 11:07:56.878688000 -0400
@@ -16,6 +16,7 @@
 #define HP_SW        "hp_sw"
 #define EMC_CLARIION "emc_clariion"
 #define READSECTOR0  "readsector0"
+#define SUN_TX       "sun_tx"
 
 #define DEFAULT_CHECKER READSECTOR0
 
diff -uNr multipath-tools-0.4.7.orig/libcheckers/sun_tx.c multipath-tools-0.4.7/libcheckers/sun_tx.c
--- multipath-tools-0.4.7.orig/libcheckers/sun_tx.c	1969-12-31 19:00:00.000000000 -0500
+++ multipath-tools-0.4.7/libcheckers/sun_tx.c	2006-06-09 11:23:40.461658250 -0400
@@ -0,0 +1,110 @@
+#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 "checkers.h"
+
+#include "../libmultipath/sg_include.h"
+
+#define INQUIRY_CMD		0x12
+#define INQUIRY_CMDLEN		6
+#define SENSE_BUFF_LEN		32
+#define DEF_TIMEOUT		60000
+#define SCSI_CHECK_CONDITION	0x2
+#define SCSI_COMMAND_TERMINATED 0x22
+#define SG_ERR_DRIVER_SENSE     0x08
+#define RECOVERED_ERROR         0x01
+#define MX_ALLOC_LEN            255
+
+#define MSG_SUN_TX_UP    "sun_tx checker reports path is up"
+#define MSG_SUN_TX_DOWN  "sun_tx checker reports path is down"
+#define MSG_SUN_TX_GHOST "sun_tx checker reports path is ghost"
+
+static int
+do_inq(int sg_fd, int evpd, unsigned int pg_op,
+       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 (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;
+}
+
+
+extern int
+sun_tx (struct checker * c)
+{
+	char buff[255];
+
+	if (0 != do_inq(c->fd, 0, 0, buff, sizeof(buff))) {
+		MSG(c, MSG_SUN_TX_DOWN);
+		return PATH_DOWN;
+	}
+
+	if (0x20 & buff[6]) {
+		MSG(c, MSG_SUN_TX_GHOST);
+		return PATH_GHOST;
+	}
+
+	MSG(c, MSG_SUN_TX_UP);
+	return PATH_UP;
+}
+
+extern int
+sun_tx_init (struct checker * c)
+{
+	return 0;
+}
+
+void
+sun_tx_free (struct checker * c)
+{
+	return;
+}
+
diff -uNr multipath-tools-0.4.7.orig/libcheckers/sun_tx.h multipath-tools-0.4.7/libcheckers/sun_tx.h
--- multipath-tools-0.4.7.orig/libcheckers/sun_tx.h	1969-12-31 19:00:00.000000000 -0500
+++ multipath-tools-0.4.7/libcheckers/sun_tx.h	2006-06-09 11:08:48.561918000 -0400
@@ -0,0 +1,9 @@
+
+#ifndef _SUN_TX_H
+#define _SUN_TX_H
+
+int sun_tx (struct checker *);
+int sun_tx_init (struct checker *);
+void sun_tx_free (struct checker *);
+
+#endif /* _SUN_TX_H */
diff -uNr multipath-tools-0.4.7.orig/libmultipath/hwtable.c multipath-tools-0.4.7/libmultipath/hwtable.c
--- multipath-tools-0.4.7.orig/libmultipath/hwtable.c	2006-03-13 06:07:45.000000000 -0500
+++ multipath-tools-0.4.7/libmultipath/hwtable.c	2006-06-12 14:56:00.671262250 -0400
@@ -422,18 +422,18 @@
 	 */
 	{
 		.vendor        = "SUN",
-		.product       = "{StorEdge 3510,T4}",
+		.product       = "(StorEdge 3510|T[34])",
 		.getuid        = DEFAULT_GETUID,
-		.getprio       = NULL,
+		.getprio       = "/sbin/mpath_prio_sun_tx /dev/%n",
 		.features      = DEFAULT_FEATURES,
-		.hwhandler     = DEFAULT_HWHANDLER,
+		.hwhandler     = "1 sun_tx",
 		.selector      = DEFAULT_SELECTOR,
-		.pgpolicy      = MULTIBUS,
+		.pgpolicy      = GROUP_BY_PRIO,
 		.pgfailback    = FAILBACK_UNDEF,
 		.rr_weight     = RR_WEIGHT_NONE,
 		.no_path_retry = NO_PATH_RETRY_UNDEF,
 		.minio         = DEFAULT_MINIO,
-		.checker_name  = READSECTOR0,
+		.checker_name  = SUN_TX,
 	},
 	/*
 	 * EOL
diff -uNr multipath-tools-0.4.7.orig/path_priority/pp_sun_tx/Makefile multipath-tools-0.4.7/path_priority/pp_sun_tx/Makefile
--- multipath-tools-0.4.7.orig/path_priority/pp_sun_tx/Makefile	1969-12-31 19:00:00.000000000 -0500
+++ multipath-tools-0.4.7/path_priority/pp_sun_tx/Makefile	2006-06-09 11:31:42.435779750 -0400
@@ -0,0 +1,32 @@
+
+EXEC           = mpath_prio_sun_tx
+OBJS           = main.o
+BUILD          = glibc
+INSTALL        = install -D
+
+TOPDIR	= ../..
+
+ifneq ($(shell ls $(TOPDIR)/Makefile.inc 2> /dev/null),)
+include $(TOPDIR)/Makefile.inc
+endif
+
+CFLAGS += -Wall -O0 -g
+
+all: $(BUILD)
+
+glibc: $(OBJS)
+	$(CC) -o $(EXEC) $(OBJS) $(LDFLAGS)
+
+klibc: $(OBJS)
+	$(CC) -static -o $(EXEC) $(OBJS)
+
+install: $(BUILD)
+	$(INSTALL) -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC)
+
+uninstall:
+	rm $(DESTDIR)$(bindir)/$(EXEC)
+
+clean:
+	rm -f *.o $(EXEC)
+
+main.o: main.c scsi3.h sun_tx.h Makefile
diff -uNr multipath-tools-0.4.7.orig/path_priority/pp_sun_tx/main.c multipath-tools-0.4.7/path_priority/pp_sun_tx/main.c
--- multipath-tools-0.4.7.orig/path_priority/pp_sun_tx/main.c	1969-12-31 19:00:00.000000000 -0500
+++ multipath-tools-0.4.7/path_priority/pp_sun_tx/main.c	2006-06-09 10:56:34.916068000 -0400
@@ -0,0 +1,90 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "sun_tx.h"
+
+#define SUN_TX_PRIO_SUCCESS			0
+#define SUN_TX_PRIO_INVALID_COMMANDLINE	1
+#define SUN_TX_PRIO_OPEN_FAILED		2
+
+static void
+print_help(char *program)
+{
+	printf("Usage: %s <device>\n", program);
+}
+
+static int
+open_block_device(char *name)
+{
+	int		fd;
+	struct stat	st;
+
+        if (stat(name, &st) != 0) {
+                fprintf(stderr, "Cannot get file status from %s (errno = %i)!\n",
+                        name, errno);
+                return -SUN_TX_PRIO_OPEN_FAILED;
+        }
+        if (!S_ISBLK(st.st_mode)) {
+                fprintf(stderr, "%s is not a block device!\n", name);
+                return -SUN_TX_PRIO_OPEN_FAILED;
+        }
+        fd = open(name, O_RDONLY);
+        if (fd < 0) {
+                fprintf(stderr, "Couldn't open %s (errno = %i)!\n", name, errno);
+                return -SUN_TX_PRIO_OPEN_FAILED;
+        }
+        return fd;
+}
+
+static int
+close_block_device(int fd)
+{
+        return close(fd);
+}
+
+int
+main(int argc, char **argv)
+{
+	int		fd;
+	char		device[256];
+
+	if (argc != 2) {
+		print_help(argv[0]);
+		return -1;
+	}
+
+	if (argv[1][0] == '/') {
+		strncpy( device, argv[1], sizeof(device));
+	} else {
+		strncpy( device, "/dev/", sizeof(device));
+		strncat( device, argv[1], sizeof(device)-5);
+	}
+
+	fd = open_block_device(device);
+
+	if (fd < 0)
+		return -fd;
+
+	{
+		scsi3_inquiry_t	resp;
+
+		if (scsi3_inquiry(fd, 0, 0, (unsigned char *)&resp, sizeof(resp)) != 0)
+			return -2;
+
+		if (resp.vendor1 == 0)
+			puts("50");
+		else
+			puts("1");
+	}
+
+	close_block_device(fd);
+	return SUN_TX_PRIO_SUCCESS;
+}
diff -uNr multipath-tools-0.4.7.orig/path_priority/pp_sun_tx/scsi3.h multipath-tools-0.4.7/path_priority/pp_sun_tx/scsi3.h
--- multipath-tools-0.4.7.orig/path_priority/pp_sun_tx/scsi3.h	1969-12-31 19:00:00.000000000 -0500
+++ multipath-tools-0.4.7/path_priority/pp_sun_tx/scsi3.h	2006-06-09 10:20:03.035084000 -0400
@@ -0,0 +1,446 @@
+
+#ifndef __SCSI3_H__
+#define __SCSI3_H__
+
+#include <endian.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#define __user
+#include  <scsi/sg.h>
+
+
+#define SCSI3_CMD_INQUIRY	0x12
+
+#define SCSI3_PQ_CONNECTED		0x0
+#define SCSI3_PQ_DISCONNECTED		0x1
+#define SCSI3_PQ_UNSUPPORTED		0x3
+
+#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
+
+#define VERSION_NONE                                    0x00
+#define VERSION_SPC                                     0x03
+#define VERSION_SPC2                                    0x04
+#define VERSION_SPC3                                    0x05
+#define VERSION_SPC4					0x06
+
+#define TPGS_NO_SUPPORT					0x00
+#define TPGS_IMPLICIT_SUPPORT				0x01
+#define TPGS_EXPLICIT_SUPPORT				0x10
+#define TPGS_IMPLICIT_AND_EXPLICIT_SUPPORT		0x11
+
+static inline unsigned short
+scsi3_get_uint16(unsigned char *p)
+{
+	return (p[0] << 8) + p[1];
+}
+
+static inline void
+scsi3_set_uint16(unsigned char *p, unsigned short v)
+{
+	p[0] = (v >> 8) & 0xff;
+	p[1] = v & 0xff;
+}
+
+static inline unsigned int
+scsi3_get_uint32(unsigned char *p)
+{
+	return (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3];
+}
+
+static inline void
+scsi3_set_uint32(unsigned char *p, unsigned short v)
+{
+	p[0] = (v >> 24) & 0xff;
+	p[1] = (v >> 16) & 0xff;
+	p[2] = (v >> 8) & 0xff;
+	p[3] = v & 0xff;
+}
+
+/* Notes: */
+/* assert 'rdf' field should be equal to 2 */
+
+typedef struct {
+	unsigned char code;		/* Operation Code (0x12) */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	unsigned evpd : 1;
+	unsigned obsolete1 : 1;
+	unsigned reserved1 : 6;
+#else
+	unsigned reserved1 : 6;
+	unsigned obsolete1 : 1;
+	unsigned evpd : 1;
+#endif
+
+	unsigned char pc;		/* Page Code */
+	unsigned char length[2];
+	unsigned char control;
+} __attribute__((packed)) scsi3_inquiry6_t;
+
+typedef struct {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	unsigned pdt : 5;		/* Peripheral Device Type */
+	unsigned pq : 3;		/* Peripheral Qualifier */
+
+	unsigned reserved1 : 7;
+	unsigned rmb : 1;
+
+	unsigned char version;
+
+	unsigned rdf : 4;		/* Response Data Format */
+	unsigned hisup : 1;		/* Hierarchical Addressing Model */
+	unsigned normaca : 1;		/* Supports ACA task attribute */
+	unsigned obsolete1 : 1;
+	unsigned obsolete2 : 1;
+
+	unsigned char	length;		/* Additional Length */
+
+	unsigned protect : 1;
+	unsigned reserved2 : 2;
+	unsigned tpc : 1 ;		/* Third Party Copy */
+	unsigned tpgs : 2;		/* Target Port Group Support */
+	unsigned acc : 1;		/* Access Controls Coordinator */
+	unsigned sccs : 1;		/* Embedded Storage Array Controller */
+					/* ... See SCC-2 for details */
+
+	unsigned addr16 : 1;
+	unsigned obsolete3 : 1;
+	unsigned obsolete4 : 1;
+	unsigned obsolete5 : 1;
+	unsigned multip : 1;
+	unsigned vendor1 : 1;		/* Vendor Specific */
+	unsigned encserv : 1;
+	unsigned bque : 1;
+
+	unsigned vendor2 : 1;		/* Vendor Specific */
+	unsigned cmdque : 1;
+	unsigned obsolete6 : 1;
+	unsigned linked : 1;
+	unsigned sync : 1;
+	unsigned wbus16 : 1;
+	unsigned obsolete7 : 1;
+	unsigned obsolete8 : 1;
+
+#else
+	unsigned pq : 3;		/* Peripheral Qualifier */
+	unsigned pdt : 5;		/* Peripheral Device Type */
+
+	unsigned rmb : 1;
+	unsigned reserved1 : 7;
+
+	unsigned char version;
+
+	unsigned obsolete2 : 1;
+	unsigned obsolete1 : 1;
+	unsigned normaca : 1;		/* Supports ACA task attribute */
+	unsigned hisup : 1;		/* Hierarchical Addressing Model */
+	unsigned rdf : 4;		/* Response Data Format */
+
+	unsigned char	length;		/* Additional Length */
+
+	unsigned sccs : 1;
+	unsigned acc : 1;
+	unsigned tpgs : 2;		/* */
+	unsigned tpc : 1 ;		/* Third Party Copy */
+	unsigned reserved2 : 2;
+	unsigned protect : 1;
+
+	unsigned bque : 1;
+	unsigned encserv : 1;
+	unsigned vendor1 : 1;		/* Vendor Specific */
+	unsigned multip : 1;
+	unsigned obsolete5 : 1;
+	unsigned obsolete4 : 1;
+	unsigned obsolete3 : 1;
+	unsigned addr16 : 1;
+
+	unsigned obsolete8 : 1;
+	unsigned obsolete7 : 1;
+	unsigned wbus16 : 1;
+	unsigned sync : 1;
+	unsigned linked : 1;
+	unsigned obsolete6 : 1;
+	unsigned cmdque : 1;
+	unsigned vendor2 : 1;		/* Vendor Specific */
+#endif
+	unsigned char	vendor_identification[8];
+	unsigned char	product_identification[8];
+	unsigned char	product_revision[4];
+	unsigned char	vendor3[20];
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	unsigned ius : 1;
+	unsigned qas : 1;
+	unsigned clocking : 2;
+	unsigned reserved3 : 4;
+#else
+	unsigned reserved3 : 4;
+	unsigned clocking : 2;
+	unsigned qas : 1;
+	unsigned iua : 1;
+#endif
+
+	unsigned char	reserved4;
+
+	unsigned char	version_descriptor[8][2];
+
+	unsigned char	reserved5[22];
+
+	unsigned char	vendor_parameters[0];
+} __attribute__((packed)) scsi3_inquiry_t;
+
+static inline int
+scsi3_inquiry_multiport(scsi3_inquiry_t *id)
+{
+	return id->multip;
+}
+
+static inline int
+sun_tx_inquiry_active_path(scsi3_inquiry_t *id)
+{
+	return id->multip && id->vendor1;
+}
+
+#define SCSI3_CHECK_CONDITION		0x2
+#define SCSI3_COMMAND_TERMINATED	0x22
+#define SCSI3_SG_ERROR_DRIVER_SENSE	0x08
+#define SCSI3_RECOVERED_ERROR		0x01
+
+static int
+scsi3_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 == SCSI3_CHECK_CONDITION)    ||
+                (hdr->status == SCSI3_COMMAND_TERMINATED) ||
+                ((hdr->driver_status & 0xf) == SCSI3_SG_ERROR_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 == SCSI3_RECOVERED_ERROR)
+                                return 0;
+                }
+        }
+
+        return 1;
+}
+
+
+static inline int
+scsi3_inquiry(int fd, int evpd, unsigned int pc, void *resp, int resplen)
+{
+	scsi3_inquiry6_t	cmd;
+	struct sg_io_hdr	hdr;
+	unsigned char		sense[32];
+
+	memset(&cmd, 0, sizeof(scsi3_inquiry6_t));
+
+	cmd.code = SCSI3_CMD_INQUIRY;
+
+	if (evpd) {
+		cmd.evpd = 1;
+		cmd.pc = pc;
+	}
+
+	scsi3_set_uint16(cmd.length, resplen);
+
+	memset(&hdr, 0, sizeof(struct sg_io_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		= 60000;
+	
+
+	if (ioctl(fd, SG_IO, &hdr) < 0) {
+		return -1;
+	}
+
+	if (scsi3_error(&hdr)) {
+		return -2;
+	}
+
+	return 0;
+}
+
+#define SCSI3_ASSOC_LUN		0x00
+#define SCSI3_ASSOC_PORT	0x01
+#define SCSI3_ASSOC_TARGET	0x10
+
+#define SCSI3_PROTOCOL_FC		0x0		/* FCP-2 */
+#define SCSI3_PROTOCOL_pSCSI		0x1		/* SPI-5 */
+#define SCSI3_PROTOCOL_SSA		0x2		/* SSA-S3P */
+#define SCSI3_PROTOCOL_IEEE1394		0x3		/* SBP-3 */
+#define SCSI3_PROTOCOL_SRP		0x4		/* SRP */
+#define SCSI3_PROTOCOL_iSCSI		0x5		/* iSCSI */
+#define SCSI3_PROTOCOL_SAS		0x6		/* SAS */
+#define SCSI3_PROTOCOL_ADT		0x7		/* ADT */
+#define SCSI3_PROTOCOL_ATAPI		0x8		/* ATA/ATAPI-7 */
+#define SCSI3_UNKNOWN			0xf
+
+#define SCSI3_CODESET_BINARY		0x1
+#define SCSI3_CODESET_ASCII		0x2
+#define SCSI3_CODESET_UTF8		0x3
+
+#define SCSI3_TYPE_VENDOR1		0x0
+#define SCSI3_TYPE_VENDOR_ID		0x1
+#define SCSI3_TYPE_EUI64		0x2
+#define SCSI3_TYPE_NAA			0x3
+#define SCSI3_TYPE_RTPI			0x4		/* Relative Target Port Identifier */
+#define SCSI3_TYPE_TPG			0x5		/* Target Port Group */
+#define SCSI3_TYPE_LPG			0x6		/* Logical Unit Group */
+#define SCSI3_TYPE_MD5			0x7		/* MD% Logical Unit Identifier */
+#define SCSI3_TYPE_NAME			0x8		/* SCSI name string */
+
+#define SCSI3_DEVICE_ID_PAGE 0x83
+typedef struct {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	unsigned pdt : 5;		/* Peripheral Device Type */
+	unsigned pq : 3;		/* Peripheral Qualifier */
+#else
+	unsigned pq : 3;		/* Peripheral Qualifier */
+	unsigned pdt : 5;		/* Peripheral Device Type */
+#endif
+
+	unsigned char pc;		/* Page Code == 0x83 */
+
+	unsigned char length[2];	/* Page Length */
+
+	unsigned char list[0];
+} __attribute__((packed)) scsi3_device_id_page_t;
+
+typedef struct {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	unsigned codeset : 4;		/* Code Set */
+	unsigned pi : 4;		/* Protocol Identifier */
+
+	unsigned type : 4;		/* Designator Type */
+	unsigned assoc : 2;		/* Port or Target */
+	unsigned reserved1 : 1;
+	unsigned piv : 1;
+#else
+	unsigned pi : 4;		/* Protocol Identifier */
+	unsigned codeset : 4;		/* Code Set */
+
+	unsigned piv : 1;
+	unsigned reserved1 : 1;
+	unsigned assoc : 2;
+	unsigned type : 4;
+#endif
+
+	unsigned char reserved2;
+
+	unsigned char length;		/* Designator Length */
+
+	unsigned char data[0];
+} __attribute__((packed)) scsi3_device_descr_page_t;
+
+static inline unsigned int
+scsi3_protocol( scsi3_device_descr_page_t *ddp )
+{
+	if (ddp->piv == 1 && 
+	    (ddp->assoc == SCSI3_ASSOC_PORT || ddp->assoc == SCSI3_ASSOC_TARGET ))
+		return ddp->pi;
+	else
+		return SCSI3_UNKNOWN;
+}
+
+
+static inline unsigned char *
+scsi3_get_device_descriptor(scsi3_device_id_page_t *dip,
+                            unsigned char codeset,
+                            unsigned char assoc,
+                            unsigned char type,
+                            unsigned char length )
+{
+	scsi3_device_descr_page_t *p = NULL;
+	unsigned char *pend = NULL;
+
+	if (dip->pc != SCSI3_DEVICE_ID_PAGE)
+		return NULL;
+
+	pend = (unsigned char *) dip->list;
+	pend += scsi3_get_uint16( dip->length );
+
+
+	p = (scsi3_device_descr_page_t *) dip->list;
+
+	while ( (unsigned char *)p < pend )
+	{
+		if ( codeset == p->codeset && assoc == p->assoc &&
+		     type == p->type && length == p->length )
+			return (unsigned char *)p->data;
+
+		/* next */
+		p = (scsi3_device_descr_page_t *) (((unsigned char *)p) + p->length + sizeof(scsi3_device_descr_page_t));
+	}
+
+	return NULL;
+}
+
+typedef struct {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	unsigned company_id_3 : 4;	/* Company ID MSB */
+	unsigned naa : 4;		/* NAA (0x6) */
+#else
+	unsigned naa : 4;		/* NAA (0x6 */
+	unsigned company_id_3 : 4;	/* Company ID MSB */
+#endif
+
+	unsigned company_id_2;		/* Company ID Middle */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	unsigned vendor_id_2 : 4;	/* Vendor ID MSB */
+	unsigned company_id_1 : 4;	/* Company ID LSB */
+#else
+	unsigned company_id_1 : 4;	/* Company ID LSB */
+	unsigned vendor_id_2 : 4;	/* Vendor ID MSB */
+#endif
+
+	unsigned char vendor_id_1;	/* Vendor ID LSB */
+
+	unsigned char vendir_id_ext[2];	/* Vendor ID Extension */
+} __attribute__((packed)) scsi3_naa_iee_reg_ext_t;
+
+
+#endif 
diff -uNr multipath-tools-0.4.7.orig/path_priority/pp_sun_tx/sun_tx.h multipath-tools-0.4.7/path_priority/pp_sun_tx/sun_tx.h
--- multipath-tools-0.4.7.orig/path_priority/pp_sun_tx/sun_tx.h	1969-12-31 19:00:00.000000000 -0500
+++ multipath-tools-0.4.7/path_priority/pp_sun_tx/sun_tx.h	2006-06-09 09:04:30.995849250 -0400
@@ -0,0 +1,94 @@
+
+
+#ifndef __SUN_TX_H__
+#define __SUN_TX_H__
+
+#include "scsi3.h"
+
+/*
+ * Notes:
+ * 	* inquiry.vendor3[0...8] = Serial Number
+ * 	* inquiry.vendor3[9] = '1' is Master Controller, '0' is Alternative Master
+ *
+ * 	* LUN WWN contained in device_id_page
+ * 	* inquiry.vendor1 = 0x0 is Preferred Path, 0x1 is Standby Path
+ */
+
+
+/* Sun vendor specific SCSI CDB commands for T3 and T4 arrays */
+/* the command should be of length 10 */
+#define SUN_TX_FAILOVER_CDB_OP			0xd0
+
+
+/* place in CDB[1] of the command */
+#define SUN_TX_FAILOVER_CDB_GRAB		0x01	/* change passive path to active */
+#define SUN_TX_FAILOVER_CDB_RESERVATION_CHECK	0x02	/* check for reservations */
+
+/* ASC values for Sun's T3/T4 indicating failover */
+#define SUN_TX_ASC_LUN_NOT_READY		0x04	/* SCSI standard */
+#define SUN_TX_ASC_FAILOVER_IN_PROGRESS		0x90	/* Sun specific */
+
+/* ASCQ values for ASC = ASC_FAILOVER_IN_PROGRESS */
+#define SUN_TX_ASCQ_BECOMING_INACTIVE		0x00
+#define SUN_TX_ASCQ_BECOMING_ACTIVE		0x01
+
+/* Sun's ASCQ values for ASC = SUN_TX_ASC_LUN_NOT_READY */
+#define SUN_TX_ASCQ_PATH_INACTIVE		0x88	/* Sun specific */
+
+
+typedef enum {
+	SUN_TX_PORT_FO_MODE_NONE,
+	SUN_TX_PORT_FO_MODE_RW,
+	SUN_TX_PORT_FO_MODE_MPXIO,
+	SUN_TX_PORT_FO_MODE_IOCTL,
+	SUN_TX_PORT_FO_MAX_MODE
+} sun_tx_fo_mode_t;
+
+typedef enum {
+	SUN_TX_PORT_STATE_ACTIVE,
+	SUN_TX_PORT_STATE_INACTIVE
+} sun_tx_port_state_t;
+
+/* System failover mode (VPD Page 0x83 -- Device Identification) */
+#define SUN_TX_PORT_ID_TYPE 0x0f
+typedef struct {
+	unsigned char	mode;
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	unsigned state : 2;
+	unsigned reserved1 : 6;
+#else
+	unsigned reserved1 : 6;
+	unsigned state : 2;
+#endif
+
+	unsigned char	reserved2[2];
+} sun_tx_port_id_page_t;
+
+/* System ID Page (VPD Page 0xD0) */
+#define SUN_TX_SYSTEM_ID_PAGE	0xd0
+typedef struct {
+	unsigned char	pq;
+	unsigned char	page_code;
+	unsigned char	reserved1;
+	unsigned char	page_length;
+	unsigned char	code_set;
+	unsigned char	id_type;
+	unsigned char	reserved2;
+	unsigned char	id_length;
+	unsigned char	system_wwn[16];
+} sun_tx_system_id_page_t;
+
+
+/* Device IP Address Page (VPD Page 0xD1) */
+#define SUN_TX_IP_ADDRESS_ID_PAGE	0xd1
+typedef struct {
+	unsigned char	pagecode;
+	unsigned char	reserved1;
+	unsigned char	pagelength;
+	unsigned char	ip_address[15];
+} sun_tx_ip_address_page_t;
+
+
+#define SUN_TX
+#endif
Files multipath-tools-0.4.7.orig/path_priority/pp_sun_tx/test and multipath-tools-0.4.7/path_priority/pp_sun_tx/test differ
* Apply bio-sense-data.patch first.
* Apply dm-mpath-hw-handler-sense-data.patch first.

diff -uNr linux/drivers/md/Kconfig linux.sun_tx/drivers/md/Kconfig
--- linux/drivers/md/Kconfig	2006-06-05 10:26:24.000000000 -0400
+++ linux.sun_tx/drivers/md/Kconfig	2006-06-09 12:09:46.174504500 -0400
@@ -236,6 +236,12 @@
 	---help---
 	  Multipath support for EMC CX/AX series hardware.
 
+config DM_MULTIPATH_SUN_TX
+	tristate "SUN T3/T4 multipath support (EXPIERMENTAL)"
+	depends on DM_MULTIPATH && BLK_DEV_DM && EXPERIMENTAL
+	---help---
+	  Multipath support for SUN T3/T4/6120 series hardware.
+
 config BLK_DEV_DM_BBR
 	tristate "Bad Block Relocation Device Target (EXPERIMENTAL)"
 	depends on BLK_DEV_DM && EXPERIMENTAL
diff -uNr linux/drivers/md/Makefile linux.sun_tx/drivers/md/Makefile
--- linux/drivers/md/Makefile	2006-06-05 10:26:24.000000000 -0400
+++ linux.sun_tx/drivers/md/Makefile	2006-06-09 12:10:12.252134250 -0400
@@ -34,6 +34,7 @@
 obj-$(CONFIG_DM_CRYPT)		+= dm-crypt.o
 obj-$(CONFIG_DM_MULTIPATH)	+= dm-multipath.o dm-round-robin.o
 obj-$(CONFIG_DM_MULTIPATH_EMC)	+= dm-emc.o
+obj-$(CONFIG_DM_MULTIPATH_SUN_TX) += dm-sun-tx.o
 obj-$(CONFIG_DM_SNAPSHOT)	+= dm-snapshot.o
 obj-$(CONFIG_DM_MIRROR)		+= dm-mirror.o
 obj-$(CONFIG_DM_ZERO)		+= dm-zero.o
diff -uNr linux/drivers/md/dm-sun-tx.c linux.sun_tx/drivers/md/dm-sun-tx.c
--- linux/drivers/md/dm-sun-tx.c	1969-12-31 19:00:00.000000000 -0500
+++ linux.sun_tx/drivers/md/dm-sun-tx.c	2006-06-13 14:50:27.477507250 -0400
@@ -0,0 +1,319 @@
+/*
+ * Multipath support for SUN T3/T4
+ */
+
+#include "dm.h"
+#include "dm-hw-handler.h"
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+
+#define SUN_TX_FAILOVER_TIMEOUT (60 * HZ)
+#define SUN_TX_FAILOVER_OP 0xd0
+#define SUN_TX_FAILOVER_GRAB 0x01
+
+#define SUN_TX_INQUIRY_TIMEOUT (60 * HZ)
+#define SUN_TX_INQUIRY_CMD 0x12;
+#define SUN_TX_INQUIRY_RESP_SIZE		255
+
+#define SUN_TX_ASC_FAILOVER_IN_PROGRESS		0x90
+
+/* ASCQ values for ASC = ASC_FAILOVER_IN_PROGRESS */
+#define SUN_TX_ASCQ_BECOMING_INACTIVE		0x00
+#define SUN_TX_ASCQ_BECOMING_ACTIVE		0x01
+
+/* ASCQ values for ASC = SAM_STAT_BUSY */
+#define SUN_TX_ASCQ_PATH_INACTIVE		0x88
+
+
+struct sun_tx_handler {
+	spinlock_t lock;
+
+	unsigned char sense[SCSI_SENSE_BUFFERSIZE];
+};
+
+struct sun_tx_bio_data {
+	struct sun_tx_handler *  h;
+	struct path *            path;
+	struct request *	 rq;
+};
+
+static inline void free_bio(struct bio *bio)
+{
+	__free_page(bio->bi_io_vec[0].bv_page);
+	bio_put(bio);
+}
+
+static int sun_tx_failover_endio(struct bio *bio, unsigned int bytes_done, int error)
+{
+	struct sun_tx_bio_data *bd = bio->bi_private;
+
+	/* LUN GRAB Request should not return any data */
+	if (bio->bi_size)
+		return 1;
+
+	if (error)
+		dm_pg_init_complete(bd->path, MP_FAIL_PATH);
+	else
+		dm_pg_init_complete(bd->path, 0);
+
+	free_bio(bio);
+	kfree(bd);
+	return 0;
+}
+
+
+static struct bio *sun_tx_bio(void)
+{
+	struct bio *bio;
+
+	bio = bio_alloc(GFP_ATOMIC, 1);
+	if (!bio) {
+		DMERR("dm-sun-tx: get_failover_bio: bio_alloc() failed.");
+		return NULL;
+	}
+
+	bio->bi_rw |= (1 << BIO_RW);
+	bio->bi_sector = 0;
+
+	return bio;
+}
+
+static struct page *sun_tx_alloc_page(struct bio *bio, unsigned int data_size)
+{
+	struct page *page;
+
+	page = alloc_page(GFP_ATOMIC);
+	if(!page) {
+		DMERR("dm-sun-tx: sun_tx_alloc_page: alloc_page() failed.");
+		bio_put(bio);
+		return NULL;
+	}
+
+	if (bio_add_page(bio, page, data_size, 0) != data_size) {
+		DMERR("dm-sun-tx: sun_tx_alloc_page: bio_add_page() failed.");
+		__free_page(page);
+		bio_put(bio);
+		return NULL;
+	}
+
+	return bio_data(bio);
+}
+
+static struct request *sun_tx_rq(struct sun_tx_handler *h,
+                                        struct bio *bio, struct path *path)
+{
+	struct request *rq;
+	struct block_device *bdev = bio->bi_bdev;
+	struct request_queue *q = bdev_get_queue(bdev);
+
+	rq = blk_get_request(q, WRITE, __GFP_WAIT);
+	if (!rq) {
+		DMERR("dm-sun-tx: sun_tx_rq: blk_get_request failed");
+		return NULL;
+	}
+
+	rq->bio = rq->biotail = bio;
+	blk_rq_bio_prep(q, rq, bio);
+
+	rq->rq_disk = bdev->bd_contains->bd_disk;
+
+	/* bio back ed don't set data */
+	rq->buffer = rq->data = NULL;
+	/* rq data_len used for pc cmd's request_bufflen */
+	rq->data_len = bio->bi_size;
+
+	rq->sense = h->sense;
+	memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
+	rq->sense_len = 0;
+
+	memset(&rq->cmd, 0, BLK_MAX_CDB);
+
+	return rq;
+}
+
+static struct sun_tx_bio_data *sun_tx_failover_grab(struct sun_tx_handler *h,
+                                            struct path *path)
+{
+	struct bio *bio;
+	struct sun_tx_bio_data *bd;
+	unsigned char *page;
+
+	bd = kmalloc(sizeof(*bd), GFP_KERNEL);
+	if (!bd) {
+		DMERR("dm-sun-tx: sun_tx_failover_grab: no bd");
+		return NULL;
+	}
+
+	memset(bd, 0, sizeof(*bd));
+	bd->h = h;
+	bd->path = path;
+
+	bio = sun_tx_bio();
+	if (!bio) {
+		DMERR("dm-sun-tx: sun_tx_failover_grab: no bio");
+		kfree(bd);
+		return NULL;
+	}
+	bio->bi_end_io = sun_tx_failover_endio;
+	bio->bi_bdev = path->dev->bdev;
+	bio->bi_private = bd;
+
+	page = (unsigned char *)sun_tx_alloc_page(bio, 1);
+	if (!page) {
+		DMERR("dm-sun-tx: sun_tx_failover_grab: alloc page failed");
+		free_bio(bio);
+		kfree(bd);
+		return NULL;
+	}
+
+	memset(page, 0, 1);
+
+	bd->rq = sun_tx_rq(h, bio, path);
+	if (!bd->rq) {
+		DMERR("dm-sun-tx: sun_tx_failover_grab: no rq");
+		free_bio(bio);
+		kfree(bd);
+		return NULL;
+	}
+
+	bd->rq->timeout = SUN_TX_FAILOVER_TIMEOUT;
+	bd->rq->flags  |= (REQ_BLOCK_PC | REQ_FAILFAST | REQ_NOMERGE);
+
+	/* Prepare Sun T3/T4 command to grab lun on standby path */
+	bd->rq->cmd[0] = SUN_TX_FAILOVER_OP;
+	bd->rq->cmd[1] = SUN_TX_FAILOVER_GRAB;
+	bd->rq->cmd_len = 10;
+
+	return bd;
+}
+
+static void sun_tx_pg_init(struct hw_handler *hwh, unsigned bypassed,
+                           struct path *path)
+{
+	struct sun_tx_bio_data *bd = NULL;
+	struct request_queue *q = bdev_get_queue(path->dev->bdev);
+
+	DMINFO("sun_tx_pg_init called");
+
+	if (!q) {
+		DMINFO("dm-sun-tx: sun_tx_pg_init: no queue");
+		goto fail_path;
+	}
+
+	/* Determine if this is a preferred path */
+	/* This requires SCSI inquiry command */
+	bd = sun_tx_failover_grab(hwh->context, path);
+	if (!bd) {
+		DMINFO("dm-sun-tx: sun_tx_pg_init: no bd");
+		goto fail_path;
+	}
+
+	DMINFO("dm-sun-tx: sun_tx_pg_init: sending lun grab command");
+	elv_add_request(q, bd->rq, ELEVATOR_INSERT_FRONT, 1);
+	return;
+
+fail_path:
+	if (bd)
+		kfree(bd);
+
+	dm_pg_init_complete(path, MP_FAIL_PATH);
+}
+
+static int sun_tx_create(struct hw_handler *hwh, unsigned argc, char **argv)
+{
+	struct sun_tx_handler *h;
+
+	DMINFO("sun_tx_create called");
+
+	h = kmalloc(sizeof(*h), GFP_KERNEL);
+
+	if (!h)
+		return -ENOMEM;
+
+	memset(h, 0, sizeof(*h));
+	spin_lock_init(&h->lock);
+
+	hwh->context = h;
+
+	return 0;
+}
+
+static void sun_tx_destroy(struct hw_handler *hwh)
+{
+	struct sun_tx_handler *h = (struct sun_tx_handler *) hwh->context;
+
+	kfree(h);
+	hwh->context = NULL;
+}
+
+static unsigned sun_tx_error(struct hw_handler *hwh, struct bio *bio)
+{
+	unsigned char key;
+	unsigned char asc;
+	unsigned char ascq;
+
+	if (bio_sense_valid(bio)) {
+		key = (bio_sense_value(bio) >> 16) & 0xff;
+		asc = (bio_sense_value(bio) >> 8) & 0xff;
+		ascq = bio_sense_value(bio) & 0xff;
+
+		if (asc == SUN_TX_ASC_FAILOVER_IN_PROGRESS &&
+		    ascq == SUN_TX_ASCQ_BECOMING_ACTIVE) {
+			/* This path should be available soon, so just keep
+			 * trying till it is */
+			return 0;
+		} else if (asc == SUN_TX_ASC_FAILOVER_IN_PROGRESS &&
+		           ascq == SUN_TX_ASCQ_BECOMING_INACTIVE) {
+			/* This is a passive path, so we should bypass it */
+			return MP_BYPASS_PG;
+		} else if (asc == SAM_STAT_BUSY &&
+			   ascq == SUN_TX_ASCQ_PATH_INACTIVE) {
+			/* Tried to use the inactive path */
+			return MP_BYPASS_PG;
+		} else if (key == UNIT_ATTENTION) {
+			/* Unit Attention Code.  This is the first IO
+			 * to the new path, so just retry. */
+			return 0;
+		}
+	}
+
+	return dm_scsi_err_handler(hwh, bio);
+}
+
+
+static struct hw_handler_type sun_tx_hwh = {
+	.name = "sun_tx",
+	.module = THIS_MODULE,
+	.create = sun_tx_create,
+	.destroy = sun_tx_destroy,
+	.pg_init = sun_tx_pg_init,
+	.error = sun_tx_error,
+};
+
+static int __init dm_sun_tx_init(void)
+{
+	int r = dm_register_hw_handler(&sun_tx_hwh);
+
+	if (r < 0)
+		DMERR("sun-tx: register failed %d", r);
+
+	DMINFO("dm-sun-tx version 0.0.1 loaded");
+
+	return r;
+}
+
+static void __exit dm_sun_tx_exit(void)
+{
+	int r = dm_unregister_hw_handler(&sun_tx_hwh);
+
+	if (r < 0)
+		DMERR("sun-tx: unregister failed %d", r);
+}
+
+module_init(dm_sun_tx_init);
+module_exit(dm_sun_tx_exit);
+
+
+MODULE_DESCRIPTION(DM_NAME " SUN T3/T4-family multipath");
+MODULE_AUTHOR("James Cassidy <jcassidy-lkernel qfire net>");
+MODULE_LICENSE("GPL");

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]