2002-09-17 22:44:52

by Patrick Mansfield

[permalink] [raw]
Subject: [RFC] [PATCH] 0/7 2.5.35 SCSI multi-path

Hi -

Any comments or testing of this patch is appreciated.

SCSI mid-layer multi-path IO version 0.4.0

The release note, multi-path patch rollup (against 2.5.35), qla
6.01b5 patch, more documentation, and TODO list can be found under:

http://www-124.ibm.com/storageio/multipath/scsi-multipath/index.php

The patch sets (following separate emails) are based on _top_ of Mike
Anderson's scsi_error patch, apply this before the patch sets, it is
against 2.5.34, but applies clean against 2.5.35:

http://www-124.ibm.com/storageio/gen-io/patch-scsi_error-2.5.34-1.gz

If you want to see this as one big patch against 2.5.35, a rollup
patch (including Mike Anderson's patch) is at:

http://www-124.ibm.com/storageio/multipath/scsi-multipath/releases/0.4/patch-scsi_mpath_rollup-2.5.35-0.4.gz

The qla 6.01b5 driver (for use with the Qlogic 2200 or 2300) can be found
under:

http://www.qlogic.com/support/os_detail.asp?seriesid=76&osid=26

Or directly at:

http://download.qlogic.com/drivers/5642/qla2x00-v6.1b5-dist.tgz


Apply the patch(es), and configure SCSI mid layer multi-path IO support
(CONFIG_SCSI_MULTI_PATH_IO). If you have multi-port devices with no
penalty for switching ports, or only multi-path hosts (multi-initiated
fabrics and busses), use round-robin multipath routing
(CONFIG_SCSI_PATH_POLICY_RR); if you are unsure, use the last path used
selection (CONFIG_SCSI_PATH_POLICY_LPU).

Currently, multi-path support requires a SCSI device that supports one of
the SCSI INQUIRY device identification pages (page 0x80 or 0x83). Devices not
supporting one of these pages are treated as if they were separate devices.
Devices that do not give a unique serial number per LUN for these commands
might incorrectly be identified as multi-pathed.

The biggest functional change since the last (0.1.0) posted version of the
patch is the addition of support for NUMA systems - current support treats
all node local adapters as being closest to a node (i.e. distance of 0),
and all other adapters as being equal distance from that node. This means
that if a node has no local adapters, it will treat all other adapters as
equal distance from itself.

2.5.32 + discontigmem patch + numa topology patch + numaq patches + soft
irq patch + a bit earlier version of this multi-path patch was tested, but
I haven't been able to keep up with that train, so no NUMA testing has
been run since 2.5.32. A simple test program was used to read a "raw"
device on a specified cpu. Some file system testing was also run. The raw
device testing and file system testing showed that node local adapters
(paths) were used for IO rather than non-local adapters (FC switch
statistics were used to see the traffic, there are no per-paths statistics).

IBM NUMAQ boxes were used for the NUMA testing, with qlogic 2300 adapters.
The latest qla 6.01b5 driver had problems configuring the non-node 0
adapters (with memory mapped IO on), so an older version of the driver was
used.


Other Multi-path testing:

Tests were run with an IBM 3542 (fibre channel disk array) as well as
dual-ported FCP Seagate drives (ST39173F, in a Clariion DAE), with all
devices attached via multiple adapters connected to an IBM 2109 fibre
channel switch via qlogic 2200 and 2300 adapters.

Switch portdisable/portenable commands were used to simulate failures.
The /proc/scsi/scsi_path/paths interface was used to re-enable failed
paths.

In addition, systems were booted (with only single paths to devices)
using:

Adaptec 7896 with the aic7xxx driver, with IBM-PSG ST318203LC disks
IPS adapter and driver

Simple single path tape IO was tested using a ISP1020 adapter with the
qlogicisp driver, and a single DLT drive.

CD read tests were run using a Plextor CD-RW attached to an aic7896.

No tests were run using multiple paths (multiple initiators or dual ported
devices) on any SCSI parallel interfaces (such as the aic or qlogic isp).

No testing was run using the qlogicfc driver (tests were run previously
with 2.5.14 and the 0.1 version of this patch, but no tests have been run
with this current patch).


The multi-path patch removes several structures commonly used by adapter
drivers, the main patch includes modifications for the AIC, qlogicfc, and
IPS drivers; there is a separate patch for the qla 6.01b5 driver. It's
likely you'll have to make modifications if you use an adapter and driver
combination other than these.


Some of the more significant items in the TODO list include:

separating queue_lock from host_lock - likely create a Scsi_Device::lock

add SMP locking while manipulating paths (the solution is likely to
use a Scsi_Device::lock as a queue_lock and for multi-path) - without
this problems can occur when a path state is changed at the same time
IO is being sent (the BUG_ON in scsi_get_best_path can be hit).

fix the error handling code - currently, if all paths fail, the error
handler will run but not be able to retry any IO. A first pass
solution will be to run the current error handler after the last path
to a devic has failed; this is likely inadequate in the long run.

add device model support for multiple-paths (at this time, only the
first path shows up in the device model)

-- Patrick Mansfield


2002-09-17 22:47:35

by Patrick Mansfield

[permalink] [raw]
Subject: [RFC] [PATCH] 2/7 2.5.35 SCSI multi-path

Patch 2 of 3 to add mid-layer scsi changes to simplify and enable the
addition of scsi multi-path IO support.

This does not add multi-path support, it adds changes that simplify the
addition of the actual scsi multi-path code.

scsi.h | 201 +++++++++++++++++++++++++-
scsi_ioctl.c | 85 ++++++-----
scsi_lib.c | 444 +++++++++++++++++++++++++++++++++++++++++++++--------------
3 files changed, 585 insertions(+), 145 deletions(-)

diff -Nru a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h
--- a/drivers/scsi/scsi.h Mon Sep 16 15:29:45 2002
+++ b/drivers/scsi/scsi.h Mon Sep 16 15:29:45 2002
@@ -133,12 +133,14 @@
#define sense_error(sense) ((sense) & 0xf)
#define sense_valid(sense) ((sense) & 0x80);

+#define UNKNOWN_ERROR 0x0000
#define NEEDS_RETRY 0x2001
#define SUCCESS 0x2002
#define FAILED 0x2003
#define QUEUED 0x2004
#define SOFT_ERROR 0x2005
#define ADD_TO_MLQUEUE 0x2006
+#define REQUEUE 0x2007

/*
* These are the values that scsi_cmd->state can take.
@@ -387,6 +389,13 @@
#define SYNC_RESET 0x40

/*
+ * Prefix values for the SCSI id's. These are stored in the first byte of the
+ * driverfs name field, see scsi_scan.c.
+ */
+#define SCSI_UID_SER_NUM 'S'
+#define SCSI_UID_UNKNOWN 'Z'
+
+/*
* This is the crap from the old error handling code. We have it in a special
* place so that we can more easily delete it later on.
*/
@@ -398,6 +407,7 @@
typedef struct scsi_device Scsi_Device;
typedef struct scsi_cmnd Scsi_Cmnd;
typedef struct scsi_request Scsi_Request;
+struct scsi_path_id;

#define SCSI_CMND_MAGIC 0xE25C23A5
#define SCSI_REQ_MAGIC 0x75F6D354
@@ -428,6 +438,7 @@
void (*complete) (Scsi_Cmnd *));
extern int scsi_delete_timer(Scsi_Cmnd * SCset);
extern void scsi_error_handler(void *host);
+extern int scsi_check_sense(Scsi_Cmnd * SCpnt);
extern int scsi_decide_disposition(Scsi_Cmnd * SCpnt);
extern int scsi_block_when_processing_errors(Scsi_Device *);
extern void scsi_sleep(int);
@@ -458,6 +469,7 @@
*/
extern void scsi_initialize_merge_fn(Scsi_Device *SDpnt);
extern int scsi_init_io(Scsi_Cmnd *SCpnt);
+extern void scsi_cleanup_io(Scsi_Cmnd *SCpnt);

/*
* Prototypes for functions in scsi_queue.c
@@ -467,6 +479,7 @@
/*
* Prototypes for functions in scsi_lib.c
*/
+struct Scsi_Host;
extern int scsi_maybe_unblock_host(Scsi_Device * SDpnt);
extern Scsi_Cmnd *scsi_end_request(Scsi_Cmnd * SCpnt, int uptodate,
int sectors);
@@ -475,9 +488,12 @@
extern int scsi_insert_special_cmd(Scsi_Cmnd * SCpnt, int);
extern void scsi_io_completion(Scsi_Cmnd * SCpnt, int good_sectors,
int block_sectors);
-extern void scsi_queue_next_request(request_queue_t * q, Scsi_Cmnd * SCpnt);
+extern void scsi_queue_next_request(request_queue_t * q, Scsi_Cmnd * SCpnt,
+ struct Scsi_Host * SHpnt);
extern void scsi_request_fn(request_queue_t * q);
extern int scsi_starvation_completion(Scsi_Device * SDpnt);
+extern void scsi_add_scsi_device(Scsi_Device *SDpnt, struct Scsi_Host *SHpnt);
+extern void scsi_remove_scsi_device(Scsi_Device *SDpnt);

/*
* Prototypes for functions in scsi.c
@@ -489,7 +505,8 @@
extern void scsi_done(Scsi_Cmnd * SCpnt);
extern void scsi_finish_command(Scsi_Cmnd *);
extern int scsi_retry_command(Scsi_Cmnd *);
-extern Scsi_Cmnd *scsi_allocate_device(Scsi_Device *, int, int);
+extern Scsi_Cmnd *scsi_allocate_device(Scsi_Device *, int, int,
+ struct scsi_path_id *);
extern void __scsi_release_command(Scsi_Cmnd *);
extern void scsi_release_command(Scsi_Cmnd *);
extern void scsi_do_cmd(Scsi_Cmnd *, const void *cmnd,
@@ -498,6 +515,9 @@
int timeout, int retries);
extern int scsi_dev_init(void);

+extern int scsi_paths_proc_print_paths(Scsi_Device *SDpnt, char *buffer,
+ char *format);
+
/*
* Newer request-based interfaces.
*/
@@ -542,6 +562,17 @@
extern const char *scsi_extd_sense_format(unsigned char, unsigned char);

/*
+ * The scsi_path_id struct contains the basic description of a path. IO to any
+ * logical LUN must use all four of these elements.
+ */
+struct scsi_path_id {
+ struct Scsi_Host *spi_shpnt; /* Host adapter to use */
+ unsigned int spi_channel; /* channel on the host */
+ unsigned int spi_id; /* target id on the channel */
+ unsigned int spi_lun; /* LUN on the target */
+};
+
+/*
* The scsi_device struct contains what we know about each given scsi
* device.
*
@@ -558,19 +589,25 @@
/*
* This information is private to the scsi mid-layer.
*/
- struct scsi_device *next; /* Used for linked list */
- struct scsi_device *prev; /* Used for linked list */
+ /*
+ * Only the SCSI mid-layer routines should access sdev_next and
+ * sdev_prev. If you need to traverse through scsi_device's, use
+ * one of the scsi traverse functions - adapter drivers probably want
+ * to use scsi_for_each_host_sdev.
+ */
+ struct scsi_device *sdev_next; /* Used for linked list */
+ struct scsi_device *sdev_prev; /* Used for linked list */
wait_queue_head_t scpnt_wait; /* Used to wait if
device is busy */
- struct Scsi_Host *host;
request_queue_t request_queue;
atomic_t device_active; /* commands checked out for device */
volatile unsigned short device_busy; /* commands actually active on low-level */
Scsi_Cmnd *device_queue; /* queue of SCSI Command structures */
Scsi_Cmnd *current_cmnd; /* currently active command */
-
- unsigned int id, lun, channel;
-
+ struct Scsi_Host *host;
+ unsigned int channel;
+ unsigned int id;
+ unsigned int lun;
unsigned int manufacturer; /* Manufacturer of device, for using
* vendor-specific cmd's */
unsigned sector_size; /* size in bytes */
@@ -621,6 +658,7 @@
unsigned remap:1; /* support remapping */
unsigned starved:1; /* unable to process commands because
host busy */
+ unsigned scanning:1; /* set while scanning */

// Flag to allow revalidate to succeed in sd_open
int allow_revalidate;
@@ -664,7 +702,6 @@
* received on original command
* (auto-sense) */

- struct Scsi_Host *sr_host;
Scsi_Device *sr_device;
Scsi_Cmnd *sr_command;
struct request *sr_request; /* A copy of the command we are
@@ -738,6 +775,10 @@
struct scsi_cmnd *bh_next; /* To enumerate the commands waiting
to be processed. */

+ /*
+ * XXX a scsi_path_id should eventually replace the following,
+ * including the *host.
+ */
unsigned int target;
unsigned int lun;
unsigned int channel;
@@ -832,6 +873,7 @@
*/
#define SCSI_MLQUEUE_HOST_BUSY 0x1055
#define SCSI_MLQUEUE_DEVICE_BUSY 0x1056
+#define SCSI_MLQUEUE_RETRY 0x1057

#define SCSI_SLEEP(QUEUE, CONDITION) { \
if (CONDITION) { \
@@ -852,6 +894,90 @@
current->state = TASK_RUNNING; \
}; }

+typedef struct scsi_traverse_hndl {
+ Scsi_Device *last_scsi_device_p;
+ struct Scsi_Host *last_scsi_host_p;
+} scsi_traverse_hndl_t;
+
+extern Scsi_Device *scsi_traverse_sdevs(scsi_traverse_hndl_t *handle,
+ uint host_no, uint channel, uint id, uint lun);
+/*
+ * Values used for scsi_find_lun_start()
+ */
+#define SCSI_FIND_ALL_HOST_NO 0xffffffff
+#define SCSI_FIND_ALL_CHANNEL 0xffffffff
+#define SCSI_FIND_ALL_ID 0xffffffff
+#define SCSI_FIND_ALL_LUN 0xffffffff
+
+#define __INIT_TRAVERSE_HNDL(name) \
+ (name)->last_scsi_device_p = NULL, \
+ (name)->last_scsi_host_p = NULL
+
+#define SCSI_TRAVERSE_ALL_SDEVS(hndl) \
+ scsi_traverse_sdevs(hndl, \
+ SCSI_FIND_ALL_HOST_NO, \
+ SCSI_FIND_ALL_CHANNEL, \
+ SCSI_FIND_ALL_ID, \
+ SCSI_FIND_ALL_LUN)
+
+#define scsi_for_all_sdevs(hndl, sdev) \
+ for (__INIT_TRAVERSE_HNDL(hndl), \
+ sdev = SCSI_TRAVERSE_ALL_SDEVS(hndl); \
+ sdev != NULL; \
+ sdev = SCSI_TRAVERSE_ALL_SDEVS(hndl))
+
+#define SCSI_TRAVERSE_HOST_SDEVS(hndl, host) \
+ scsi_traverse_sdevs(hndl, \
+ host, \
+ SCSI_FIND_ALL_CHANNEL, \
+ SCSI_FIND_ALL_ID, \
+ SCSI_FIND_ALL_LUN)
+
+#define scsi_for_each_host_sdev(hndl, sdev, host) \
+ for (__INIT_TRAVERSE_HNDL(hndl), \
+ sdev = SCSI_TRAVERSE_HOST_SDEVS(hndl, host); \
+ sdev != NULL; \
+ sdev = SCSI_TRAVERSE_HOST_SDEVS(hndl, host))
+
+#define SCSI_TRAVERSE_HOST_CHAN_SDEVS(hndl, host, chan) \
+ scsi_traverse_sdevs(hndl, \
+ host, \
+ chan, \
+ SCSI_FIND_ALL_ID, \
+ SCSI_FIND_ALL_LUN)
+
+#define scsi_for_each_host_chan_sdev(hndl, sdev, host, chan) \
+ for (__INIT_TRAVERSE_HNDL(hndl), \
+ sdev = SCSI_TRAVERSE_HOST_CHAN_SDEVS(hndl, host, chan); \
+ sdev != NULL; \
+ sdev = SCSI_TRAVERSE_HOST_CHAN_SDEVS(hndl, host, chan))
+
+#define SCSI_TRAVERSE_SDEV_LUNS(hndl, host, chan, id) \
+ scsi_traverse_sdevs(hndl, \
+ host, \
+ chan, \
+ id, \
+ SCSI_FIND_ALL_LUN)
+
+#define scsi_for_each_sdev_lun(hndl, sdev, host, chan, id) \
+ for (__INIT_TRAVERSE_HNDL(hndl), \
+ sdev = SCSI_TRAVERSE_SDEV_LUNS(hndl, host, chan, id); \
+ sdev != NULL; \
+ sdev = SCSI_TRAVERSE_SDEV_LUNS(hndl, host, chan, id))
+
+
+#define scsi_for_each_sdev(hndl, sdev, host, chan, id, lun) \
+ for (__INIT_TRAVERSE_HNDL(hndl), \
+ sdev = scsi_traverse_sdevs(hndl, host, chan, id, lun); \
+ sdev != NULL; \
+ sdev = scsi_traverse_sdevs(hndl, host, chan, id, lun))
+
+#define scsi_locate_sdev(host, chan, id, lun) \
+ scsi_traverse_sdevs((scsi_traverse_hndl_t *) NULL, \
+ host, \
+ chan, \
+ id, \
+ lun)
/*
* old style reset request from external source
* (private to sg.c and scsi_error.c, supplied by scsi_obsolete.c)
@@ -944,6 +1070,63 @@

return (Scsi_Cmnd *)req->special;
}
+
+static inline Scsi_Device *scsi_lookup_id(char *id, Scsi_Device *sdev)
+{
+ return(NULL);
+}
+
+static inline struct Scsi_Host *scsi_get_host(Scsi_Device *SDpnt)
+{
+ return (SDpnt->host);
+}
+
+static inline int scsi_add_path(Scsi_Device *SDpnt, struct Scsi_Host *shpnt,
+ unsigned int channel, unsigned int dev,
+ unsigned int lun)
+{
+ SDpnt->host = shpnt;
+ SDpnt->channel = channel;
+ SDpnt->lun = lun;
+ SDpnt->id = dev;
+ return 0;
+}
+
+static inline void scsi_remove_path(Scsi_Device *SDpnt, unsigned int host_no,
+ unsigned int channel, unsigned int dev,
+ unsigned int lun)
+{
+}
+
+static inline void scsi_replace_path(Scsi_Device *sdev, struct Scsi_Host *shost,
+ unsigned int channel, unsigned int id, unsigned int lun)
+{
+ sdev->host = shost;
+ sdev->channel = channel;
+ sdev->id = id;
+ sdev->lun = lun;
+}
+
+static inline int scsi_get_path(Scsi_Device *SDpnt, struct scsi_path_id *pathp)
+{
+ pathp->spi_shpnt = SDpnt->host;
+ pathp->spi_id = SDpnt->id;
+ pathp->spi_lun = SDpnt->lun;
+ pathp->spi_channel = SDpnt->channel;
+ return 0;
+}
+
+static inline int scsi_path_decide_disposition(Scsi_Cmnd * SCpnt)
+{
+ return UNKNOWN_ERROR;
+}
+
+extern void scsi_paths_printk(Scsi_Device *SDpnt, char *prefix, char *format);
+#define scsi_path_set_scmnd_ids(scp, pathp) do { \
+ scp->host = pathp->spi_shpnt; \
+ scp->target = pathp->spi_id; \
+ scp->lun = pathp->spi_lun; \
+ scp->channel = pathp->spi_channel; } while(0)

#define scsi_eh_eflags_chk(scp, flags) (scp->eh_eflags & flags)

diff -Nru a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c
--- a/drivers/scsi/scsi_ioctl.c Mon Sep 16 15:29:45 2002
+++ b/drivers/scsi/scsi_ioctl.c Mon Sep 16 15:29:45 2002
@@ -129,10 +129,15 @@
break;
}
default: /* Fall through for non-removable media */
- printk("SCSI error: host %d id %d lun %d return code = %x\n",
- dev->host->host_no,
- dev->id,
- dev->lun,
+ /*
+ * This is a bit klugey, since we rely on SRpnt
+ * to contain the scsi_cmnd for the IO.
+ */
+ printk("SCSI error: host %d channel %d id %d lun %d return code = %x\n",
+ SRpnt->sr_command->host->host_no,
+ SRpnt->sr_command->channel,
+ SRpnt->sr_command->target,
+ SRpnt->sr_command->lun,
SRpnt->sr_result);
printk("\tSense class %x, sense error %x, extended sense %x\n",
sense_class(SRpnt->sr_sense_buffer[0]),
@@ -191,6 +196,7 @@
char *cmd_in;
Scsi_Request *SRpnt;
Scsi_Device *SDpnt;
+ struct Scsi_Host *SHpnt;
unsigned char opcode;
unsigned int inlen, outlen, cmdlen;
unsigned int needed, buf_needed;
@@ -200,7 +206,9 @@
if (!sic)
return -EINVAL;

- if (dev->host->unchecked_isa_dma)
+ if ((SHpnt = scsi_get_host(dev)) == NULL)
+ return -ENODEV;
+ if (SHpnt->unchecked_isa_dma)
gfp_mask |= GFP_DMA;

/*
@@ -275,11 +283,9 @@
goto error;

/*
- * Set the lun field to the correct value.
+ * The lun field in cmd[1] is conditionally set in the mid-layer
+ * before sending cmd.
*/
- if (dev->scsi_level <= SCSI_2)
- cmd[1] = (cmd[1] & 0x1f) | (dev->lun << 5);
-
switch (opcode) {
case FORMAT_UNIT:
timeout = FORMAT_UNIT_TIMEOUT;
@@ -378,10 +384,16 @@
static int
scsi_ioctl_get_pci(Scsi_Device * dev, void *arg)
{
+ struct Scsi_Host *SHpnt;

- if (!dev->host->pci_dev) return -ENXIO;
- return copy_to_user(arg, dev->host->pci_dev->slot_name,
- sizeof(dev->host->pci_dev->slot_name));
+ /*
+ * XXX this ioctl should be replaced by a host specific interface
+ */
+ if ((SHpnt = scsi_get_host(dev)) == NULL)
+ return -ENODEV;
+ if (!SHpnt->pci_dev) return -ENXIO;
+ return copy_to_user(arg, SHpnt->pci_dev->slot_name,
+ sizeof(SHpnt->pci_dev->slot_name));
}


@@ -393,7 +405,8 @@
int scsi_ioctl(Scsi_Device * dev, int cmd, void *arg)
{
char scsi_cmd[MAX_COMMAND_SIZE];
- char cmd_byte1;
+ struct scsi_path_id scsi_path;
+ struct Scsi_Host *SHpnt;

/* No idea how this happens.... */
if (!dev)
@@ -408,22 +421,29 @@
if (!scsi_block_when_processing_errors(dev)) {
return -ENODEV;
}
- cmd_byte1 = (dev->scsi_level <= SCSI_2) ? (dev->lun << 5) : 0;

switch (cmd) {
case SCSI_IOCTL_GET_IDLUN:
if (verify_area(VERIFY_WRITE, arg, sizeof(Scsi_Idlun)))
return -EFAULT;

- __put_user((dev->id & 0xff)
- + ((dev->lun & 0xff) << 8)
- + ((dev->channel & 0xff) << 16)
- + ((dev->host->host_no & 0xff) << 24),
+ /*
+ * Just get first path, and use its
+ * id/lun/channel/host_no. This is fine for single path
+ * devices, or when no multi-path IO.
+ */
+ scsi_get_path(dev, &scsi_path);
+ __put_user((scsi_path.spi_id & 0xff)
+ + ((scsi_path.spi_lun & 0xff) << 8)
+ + ((scsi_path.spi_channel & 0xff) << 16)
+ + ((scsi_path.spi_shpnt->host_no & 0xff) << 24),
&((Scsi_Idlun *) arg)->dev_id);
- __put_user(dev->host->unique_id, &((Scsi_Idlun *) arg)->host_unique_id);
+ __put_user(scsi_path.spi_shpnt->unique_id, &((Scsi_Idlun *) arg)->host_unique_id);
return 0;
case SCSI_IOCTL_GET_BUS_NUMBER:
- return put_user(dev->host->host_no, (int *) arg);
+ if ((SHpnt = scsi_get_host(dev)) == NULL)
+ return -ENODEV;
+ return put_user(SHpnt->host_no, (int *) arg);
case SCSI_IOCTL_TAGGED_ENABLE:
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
@@ -441,7 +461,9 @@
dev->current_tag = 0;
return 0;
case SCSI_IOCTL_PROBE_HOST:
- return ioctl_probe(dev->host, arg);
+ if ((SHpnt = scsi_get_host(dev)) == NULL)
+ return -ENODEV;
+ return ioctl_probe(SHpnt, arg);
case SCSI_IOCTL_SEND_COMMAND:
if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
return -EACCES;
@@ -451,8 +473,7 @@
if (!dev->removable || !dev->lockable)
return 0;
scsi_cmd[0] = ALLOW_MEDIUM_REMOVAL;
- scsi_cmd[1] = cmd_byte1;
- scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+ scsi_cmd[1] = scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
scsi_cmd[4] = SCSI_REMOVAL_PREVENT;
return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd,
IOCTL_NORMAL_TIMEOUT, NORMAL_RETRIES);
@@ -461,31 +482,27 @@
if (!dev->removable || !dev->lockable)
return 0;
scsi_cmd[0] = ALLOW_MEDIUM_REMOVAL;
- scsi_cmd[1] = cmd_byte1;
- scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+ scsi_cmd[1] = scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
scsi_cmd[4] = SCSI_REMOVAL_ALLOW;
return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd,
IOCTL_NORMAL_TIMEOUT, NORMAL_RETRIES);
case SCSI_IOCTL_TEST_UNIT_READY:
scsi_cmd[0] = TEST_UNIT_READY;
- scsi_cmd[1] = cmd_byte1;
- scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+ scsi_cmd[1] = scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
scsi_cmd[4] = 0;
return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd,
IOCTL_NORMAL_TIMEOUT, NORMAL_RETRIES);
break;
case SCSI_IOCTL_START_UNIT:
scsi_cmd[0] = START_STOP;
- scsi_cmd[1] = cmd_byte1;
- scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+ scsi_cmd[1] = scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
scsi_cmd[4] = 1;
return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd,
START_STOP_TIMEOUT, NORMAL_RETRIES);
break;
case SCSI_IOCTL_STOP_UNIT:
scsi_cmd[0] = START_STOP;
- scsi_cmd[1] = cmd_byte1;
- scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+ scsi_cmd[1] = scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
scsi_cmd[4] = 0;
return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd,
START_STOP_TIMEOUT, NORMAL_RETRIES);
@@ -494,8 +511,10 @@
return scsi_ioctl_get_pci(dev, arg);
break;
default:
- if (dev->host->hostt->ioctl)
- return dev->host->hostt->ioctl(dev, cmd, arg);
+ if ((SHpnt = scsi_get_host(dev)) == NULL)
+ return -ENODEV;
+ if (SHpnt->hostt->ioctl)
+ return SHpnt->hostt->ioctl(dev, cmd, arg);
return -EINVAL;
}
return -EINVAL;
diff -Nru a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
--- a/drivers/scsi/scsi_lib.c Mon Sep 16 15:29:45 2002
+++ b/drivers/scsi/scsi_lib.c Mon Sep 16 15:29:45 2002
@@ -37,6 +37,7 @@
#define __KERNEL_SYSCALLS__

#include <linux/unistd.h>
+#include <linux/slab.h>

#include <asm/system.h>
#include <asm/irq.h>
@@ -195,12 +196,14 @@
* permutations grows as 2**N, and if too many more special cases
* get added, we start to get screwed.
*/
-void scsi_queue_next_request(request_queue_t * q, Scsi_Cmnd * SCpnt)
+void scsi_queue_next_request(request_queue_t * q, Scsi_Cmnd * SCpnt,
+ struct Scsi_Host * SHpnt)
{
- int all_clear;
unsigned long flags;
Scsi_Device *SDpnt;
- struct Scsi_Host *SHpnt;
+ /* see comment further below */
+ int all_clear;
+ scsi_traverse_hndl_t STrav_hndl;

ASSERT_LOCK(q->queue_lock, 0);

@@ -225,7 +228,6 @@
q->request_fn(q);

SDpnt = (Scsi_Device *) q->queuedata;
- SHpnt = SDpnt->host;

/*
* If this is a single-lun device, and we are currently finished
@@ -237,7 +239,7 @@
if (SDpnt->single_lun && blk_queue_empty(q) && SDpnt->device_busy ==0) {
request_queue_t *q;

- for (SDpnt = SHpnt->host_queue; SDpnt; SDpnt = SDpnt->next) {
+ scsi_for_each_host_sdev(&STrav_hndl, SDpnt, SHpnt->host_no) {
if (((SHpnt->can_queue > 0)
&& (SHpnt->host_busy >= SHpnt->can_queue))
|| (SHpnt->host_blocked)
@@ -261,7 +263,7 @@
*/
all_clear = 1;
if (SHpnt->some_device_starved) {
- for (SDpnt = SHpnt->host_queue; SDpnt; SDpnt = SDpnt->next) {
+ scsi_for_each_host_sdev(&STrav_hndl, SDpnt, SHpnt->host_no) {
request_queue_t *q;
if ((SHpnt->can_queue > 0 && (SHpnt->host_busy >= SHpnt->can_queue))
|| (SHpnt->host_blocked)
@@ -314,6 +316,7 @@
request_queue_t *q = &SCpnt->device->request_queue;
struct request *req = SCpnt->request;
unsigned long flags;
+ struct Scsi_Host * SHpnt = SCpnt->host;

ASSERT_LOCK(q->queue_lock, 0);

@@ -331,7 +334,7 @@
* Bleah. Leftovers again. Stick the leftovers in
* the front of the queue, and goose the queue again.
*/
- scsi_queue_next_request(q, SCpnt);
+ scsi_queue_next_request(q, SCpnt, SHpnt);
return SCpnt;
}

@@ -351,7 +354,7 @@
__scsi_release_command(SCpnt);

if (frequeue)
- scsi_queue_next_request(q, NULL);
+ scsi_queue_next_request(q, NULL, SHpnt);

return NULL;
}
@@ -467,38 +470,7 @@
*/
ASSERT_LOCK(q->queue_lock, 0);

- /*
- * Free up any indirection buffers we allocated for DMA purposes.
- * For the case of a READ, we need to copy the data out of the
- * bounce buffer and into the real buffer.
- */
- if (SCpnt->use_sg) {
- struct scatterlist *sgpnt;
-
- sgpnt = (struct scatterlist *) SCpnt->buffer;
- scsi_free_sgtable(SCpnt->buffer, SCpnt->sglist_len);
- } else {
- if (SCpnt->buffer != req->buffer) {
- if (rq_data_dir(req) == READ) {
- unsigned long flags;
- char *to = bio_kmap_irq(req->bio, &flags);
-
- memcpy(to, SCpnt->buffer, SCpnt->bufflen);
- bio_kunmap_irq(to, &flags);
- }
- kfree(SCpnt->buffer);
- }
- }
-
- /*
- * Zero these out. They now point to freed memory, and it is
- * dangerous to hang onto the pointers.
- */
- SCpnt->buffer = NULL;
- SCpnt->bufflen = 0;
- SCpnt->request_buffer = NULL;
- SCpnt->request_bufflen = 0;
-
+ scsi_cleanup_io(SCpnt);
/*
* Next deal with any sectors which we were able to correctly
* handle.
@@ -558,7 +530,7 @@
*/
if (SCpnt->sense_buffer[12] == 0x04 &&
SCpnt->sense_buffer[13] == 0x01) {
- scsi_queue_next_request(q, SCpnt);
+ scsi_queue_next_request(q, SCpnt, SCpnt->host);
return;
}
if ((SCpnt->sense_buffer[2] & 0xf) == UNIT_ATTENTION) {
@@ -576,7 +548,8 @@
* media change, so we just retry the
* request and see what happens.
*/
- scsi_queue_next_request(q, SCpnt);
+ scsi_queue_next_request(q, SCpnt,
+ SCpnt->host);
return;
}
}
@@ -596,7 +569,7 @@
* This will cause a retry with a 6-byte
* command.
*/
- scsi_queue_next_request(q, SCpnt);
+ scsi_queue_next_request(q, SCpnt, SCpnt->host);
result = 0;
} else {
SCpnt = scsi_end_request(SCpnt, 0, this_count);
@@ -628,7 +601,7 @@
* recovery reasons. Just retry the request
* and see what happens.
*/
- scsi_queue_next_request(q, SCpnt);
+ scsi_queue_next_request(q, SCpnt, SCpnt->host);
return;
}
if (result) {
@@ -637,10 +610,10 @@
STpnt = scsi_get_request_dev(SCpnt->request);
printk("SCSI %s error : host %d channel %d id %d lun %d return code = %x\n",
(STpnt ? STpnt->name : "device"),
- SCpnt->device->host->host_no,
- SCpnt->device->channel,
- SCpnt->device->id,
- SCpnt->device->lun, result);
+ SCpnt->host->host_no,
+ SCpnt->channel,
+ SCpnt->target,
+ SCpnt->lun, result);

if (driver_byte(result) & DRIVER_SENSE)
print_sense("sd", SCpnt);
@@ -706,6 +679,74 @@
}

/*
+ * Function: scsi_dec_check_host_busy
+ *
+ * Purpose: Decrement host_busy, and see if any action should be taken.
+ *
+ * Arguments: SHpnt and request q.
+ *
+ * Lock status: Assumes that queue_lock is held when called.
+ *
+ * Returns: Nothing
+ *
+ * Notes: The q arg and local flags are only needed for the locking hack.
+ */
+void scsi_dec_check_host_busy (struct Scsi_Host *SHpnt, request_queue_t * q)
+{
+ unsigned long flags;
+
+ /* XXX gross lock hack */
+ if (SHpnt->host_lock != q->queue_lock)
+ spin_lock_irqsave(SHpnt->host_lock, flags);
+ SHpnt->host_busy--;
+ /*
+ * XXX check to see if the change in host_busy should kick off
+ * error handling. This is really only needed if we drop and then
+ * later lock queue_lock, but it is OK to always check it.
+ */
+ if (SHpnt->host_lock != q->queue_lock)
+ spin_unlock_irqrestore(SHpnt->host_lock, flags);
+}
+
+/*
+ * Function: scsi_get_best_path, not-multi-path IO version.
+ *
+ * Purpose: Get and then check pathpnt to see if we can send IO.
+ *
+ * Arguments: Scsi_Device pointer SDpnt, and struct scsi_path_id
+ * *pathpnt.
+ *
+ * Returns: 0 if it's OK to send IO, else 1.
+ *
+ * Notes: This is not with the other not-multi-path-io functions,
+ * because it is not extern, and it is inlined; it is not in
+ * scsi.h because it needs Scsi_Host (in hosts.h) and
+ * Scsi_Device (in scsi.h).
+ */
+static int inline scsi_get_best_path(Scsi_Device *SDpnt, struct scsi_path_id
+ *pathpnt, struct request *req)
+{
+ scsi_get_path(SDpnt, pathpnt);
+ if (pathpnt->spi_shpnt->in_recovery)
+ return 1;
+ if (SDpnt->device_blocked)
+ return 1;
+ if ((pathpnt->spi_shpnt->can_queue > 0 &&
+ (pathpnt->spi_shpnt->host_busy >= pathpnt->spi_shpnt->can_queue))
+ || (pathpnt->spi_shpnt->host_blocked) ||
+ (pathpnt->spi_shpnt->host_self_blocked)) {
+ if (SDpnt->device_busy == 0) {
+ SDpnt->starved = 1;
+ pathpnt->spi_shpnt->some_device_starved = 1;
+ }
+ return 1;
+ } else
+ SDpnt->starved = 0;
+ pathpnt->spi_shpnt->host_busy++;
+ return 0;
+}
+
+/*
* Function: scsi_request_fn()
*
* Purpose: Generic version of request function for SCSI hosts.
@@ -730,8 +771,9 @@
Scsi_Cmnd *SCpnt;
Scsi_Request *SRpnt;
Scsi_Device *SDpnt;
- struct Scsi_Host *SHpnt;
struct Scsi_Device_Template *STpnt;
+ struct scsi_path_id path;
+ int res;

ASSERT_LOCK(q->queue_lock, 1);

@@ -739,7 +781,6 @@
if (!SDpnt) {
panic("Missing device");
}
- SHpnt = SDpnt->host;

/*
* To start with, we keep looping until the queue is empty, or until
@@ -747,12 +788,18 @@
*/
while (1 == 1) {
/*
- * Check this again - each time we loop through we will have
- * released the lock and grabbed it again, so each time
- * we need to check to see if the queue is plugged or not.
+ * Each time we loop through we will have released the
+ * lock and grabbed it again, so each time we need to
+ * check to see if the queue is plugged or not.
*/
- if (SHpnt->in_recovery || blk_queue_plugged(q))
+ if (blk_queue_plugged(q))
return;
+ /*
+ * If we couldn't find a request that could be queued, then we
+ * can also quit.
+ */
+ if (blk_queue_empty(q))
+ break;

/*
* If the device cannot accept another request, then quit.
@@ -760,24 +807,28 @@
if (SDpnt->device_blocked) {
break;
}
- if ((SHpnt->can_queue > 0 && (SHpnt->host_busy >= SHpnt->can_queue))
- || (SHpnt->host_blocked)
- || (SHpnt->host_self_blocked)) {
+
+ /*
+ * get next queueable request.
+ */
+ req = elv_next_request(q);
+
+ res = scsi_get_best_path(SDpnt, &path, req);
+ if (res == 1) {
/*
- * If we are unable to process any commands at all for
- * this device, then we consider it to be starved.
- * What this means is that there are no outstanding
- * commands for this device and hence we need a
- * little help getting it started again
- * once the host isn't quite so busy.
+ * IO is blocked.
*/
- if (SDpnt->device_busy == 0) {
- SDpnt->starved = 1;
- SHpnt->some_device_starved = 1;
- }
- break;
- } else {
- SDpnt->starved = 0;
+ req->flags &= ~REQ_STARTED;
+ return;
+ } else if (res == 2) {
+ /*
+ * No active paths available.
+ */
+ blkdev_dequeue_request(req);
+ if (end_that_request_first(req, 0, req->nr_sectors))
+ BUG();
+ end_that_request_last(req);
+ return;
}

/*
@@ -803,23 +854,13 @@
spin_unlock_irq(q->queue_lock);
scsi_ioctl(SDpnt, SCSI_IOCTL_DOORLOCK, 0);
spin_lock_irq(q->queue_lock);
+ scsi_host_busy_dec_and_test(path.spi_shpnt,
+ SDpnt);
continue;
}
}

/*
- * If we couldn't find a request that could be queued, then we
- * can also quit.
- */
- if (blk_queue_empty(q))
- break;
-
- /*
- * get next queueable request.
- */
- req = elv_next_request(q);
-
- /*
* Find the actual device driver associated with this command.
* The SPECIAL requests are things like character device or
* ioctls, which did not originate from ll_rw_blk. Note that
@@ -834,15 +875,38 @@
SCpnt = (Scsi_Cmnd *) req->special;
SRpnt = (Scsi_Request *) req->special;

+ /*
+ * A scsi_do_cmd() or scsi_do_req().
+ *
+ * XXX modify Scsi_Request to include a path, if a
+ * path is set in Scsi_Request, use it here.
+ */
if( SRpnt->sr_magic == SCSI_REQ_MAGIC ) {
SCpnt = scsi_allocate_device(SRpnt->sr_device,
- FALSE, FALSE);
- if (!SCpnt)
+ FALSE, FALSE, &path);
+ if (!SCpnt) {
+ scsi_host_busy_dec_and_test(path.spi_shpnt, SDpnt);
+ req->flags &= ~REQ_STARTED;
break;
+ }
scsi_init_cmd_from_req(SCpnt, SRpnt);
+ } else {
+ /*
+ * Either a scsi_do_cmd() or a retry of
+ * scsi_do_req() or scsi_do_cmd().
+ *
+ * set SCpnt host/id/lun/channel
+ */
+ SCpnt->host = path.spi_shpnt;
+ SCpnt->target = path.spi_id;
+ SCpnt->lun = path.spi_lun;
+ SCpnt->channel = path.spi_channel;
}

} else if (req->flags & REQ_CMD) {
+ /*
+ * Block driver.
+ */
SRpnt = NULL;
STpnt = scsi_get_request_dev(req);
if (!STpnt) {
@@ -852,30 +916,37 @@
* Now try and find a command block that we can use.
*/
if (req->special) {
+ /*
+ * Retry.
+ */
SCpnt = (Scsi_Cmnd *) req->special;
+ SCpnt->host = path.spi_shpnt;
+ SCpnt->target = path.spi_id;
+ SCpnt->lun = path.spi_lun;
+ SCpnt->channel = path.spi_channel;
} else {
- SCpnt = scsi_allocate_device(SDpnt, FALSE, FALSE);
+ SCpnt = scsi_allocate_device(SDpnt, FALSE, FALSE, &path);
}
/*
* If so, we are ready to do something. Bump the count
* while the queue is locked and then break out of the
* loop. Otherwise loop around and try another request.
*/
- if (!SCpnt)
+ if (!SCpnt) {
+ scsi_host_busy_dec_and_test(path.spi_shpnt, SDpnt);
+ req->flags &= ~REQ_STARTED;
break;
+ }

/* pull a tag out of the request if we have one */
SCpnt->tag = req->tag;
} else {
blk_dump_rq_flags(req, "SCSI bad req");
+ scsi_host_busy_dec_and_test(path.spi_shpnt, SDpnt);
+ req->flags &= ~REQ_STARTED;
break;
}

- /*
- * Now bump the usage count for both the host and the
- * device.
- */
- SHpnt->host_busy++;
SDpnt->device_busy++;

/*
@@ -926,11 +997,11 @@
*/
if (!scsi_init_io(SCpnt)) {
spin_lock_irq(q->queue_lock);
- SHpnt->host_busy--;
- SDpnt->device_busy--;
+ scsi_host_busy_dec_and_test(path.spi_shpnt, SDpnt);
+ SDpnt->device_busy--; /* XXX race */
if (SDpnt->device_busy == 0) {
SDpnt->starved = 1;
- SHpnt->some_device_starved = 1;
+ path.spi_shpnt->some_device_starved = 1;
}
SCpnt->request->special = SCpnt;
SCpnt->request->flags |= REQ_SPECIAL;
@@ -952,12 +1023,19 @@
panic("Should not have leftover blocks\n");
}
spin_lock_irq(q->queue_lock);
- SHpnt->host_busy--;
- SDpnt->device_busy--;
+ scsi_host_busy_dec_and_test(path.spi_shpnt, SDpnt);
+ SDpnt->device_busy--; /* XXX race */
continue;
}
}
/*
+ * If SCSI-2 or lower, store the LUN value in cmnd.
+ */
+ if (SDpnt->scsi_level <= SCSI_2)
+ SCpnt->cmnd[1] = (SCpnt->cmnd[1] & 0x1f) |
+ (SCpnt->lun << 5 & 0xe0);
+
+ /*
* Finally, initialize any error handling parameters, and set up
* the timers for timeouts.
*/
@@ -1020,11 +1098,13 @@
void scsi_unblock_requests(struct Scsi_Host * SHpnt)
{
Scsi_Device *SDloop;
+ scsi_traverse_hndl_t STrav_hndl;

SHpnt->host_self_blocked = FALSE;
/* Now that we are unblocked, try to start the queues. */
- for (SDloop = SHpnt->host_queue; SDloop; SDloop = SDloop->next)
- scsi_queue_next_request(&SDloop->request_queue, NULL);
+ scsi_for_each_host_sdev(&STrav_hndl, SDloop, SHpnt->host_no) {
+ scsi_queue_next_request(&SDloop->request_queue, NULL, SHpnt);
+ }
}

/*
@@ -1051,11 +1131,11 @@
void scsi_report_bus_reset(struct Scsi_Host * SHpnt, int channel)
{
Scsi_Device *SDloop;
- for (SDloop = SHpnt->host_queue; SDloop; SDloop = SDloop->next) {
- if (channel == SDloop->channel) {
- SDloop->was_reset = 1;
- SDloop->expecting_cc_ua = 1;
- }
+ scsi_traverse_hndl_t STrav_hndl;
+
+ scsi_for_each_host_sdev(&STrav_hndl, SDloop, SHpnt->host_no) {
+ SDloop->was_reset = 1;
+ SDloop->expecting_cc_ua = 1;
}
}

@@ -1075,3 +1155,161 @@
void scsi_deregister_blocked_host(struct Scsi_Host * SHpnt)
{
}
+
+/*
+ * Function: scsi_add_scsi_device
+ *
+ * Purpose: Add a SDpnt to the list of Scsi_Devices, add it to the
+ * "tail" of the list. If needed, add it to the list for
+ * shostpnt.
+ *
+ * Arguments: Scsi_Device pointer SDpnt.
+ *
+ * Notes: Doesn't do anything other than link in SDpnt, we should add
+ * other code that is common to the addition of all Scsi_Devices.
+ */
+void scsi_add_scsi_device(Scsi_Device *SDpnt, struct Scsi_Host *shostpnt)
+{
+ Scsi_Device *sdtailpnt;
+
+ SDpnt->sdev_prev = NULL;
+ SDpnt->sdev_next = NULL;
+
+ if (shostpnt->host_queue != NULL) {
+ sdtailpnt = shostpnt->host_queue;
+ while (sdtailpnt->sdev_next != NULL)
+ sdtailpnt = sdtailpnt->sdev_next;
+
+ sdtailpnt->sdev_next = SDpnt;
+ SDpnt->sdev_prev = sdtailpnt;
+ } else {
+ shostpnt->host_queue = SDpnt;
+ }
+}
+
+/*
+ * Function: scsi_remove_scsi_device
+ *
+ * Purpose: Remove SDpnt from the list of Scsi_Devices.
+ *
+ * Arguments: Scsi_Device pointer SDpnt.
+ *
+ * Notes: unlink and kfree SDpnt, we should add other code that is common
+ * to removal of all Scsi_Devices.
+ */
+void scsi_remove_scsi_device(Scsi_Device *SDpnt)
+{
+ struct Scsi_Host *hbapnt;
+
+ if (SDpnt == NULL)
+ return;
+ if (SDpnt->sdev_next != NULL)
+ SDpnt->sdev_next->sdev_prev = SDpnt->sdev_prev;
+ if (SDpnt->sdev_prev != NULL)
+ SDpnt->sdev_prev->sdev_next = SDpnt->sdev_next;
+
+ hbapnt = SDpnt->host;
+ if ((hbapnt != NULL) && (hbapnt->host_queue == SDpnt))
+ hbapnt->host_queue = SDpnt->sdev_next;
+ if (SDpnt->inquiry != NULL)
+ kfree(SDpnt->inquiry);
+ kfree((char *) SDpnt);
+}
+
+/*
+ * scsi_traverse_sdevs - Return the next Scsi_Device matching the search
+ * requested, non-multi-path version
+ *
+ * @handle: Handle used to bookmark location of search thus far.
+ * @host_no: Host to search for.
+ * @channel: Channel to search for.
+ * @id: Id to search for.
+ * @lun: Lun to search for.
+ *
+ * Description:
+ * Returns - A pointer to Scsi_Device, NULL when the list is complete.
+ * Notes - Assumes that we do not remove a device between calls.
+ *
+ */
+Scsi_Device *scsi_traverse_sdevs(scsi_traverse_hndl_t *handle, uint host_no, uint channel,
+ uint id, uint lun)
+{
+ Scsi_Device *SDpnt;
+ struct Scsi_Host *shostpnt;
+
+ if (handle == NULL) {
+ shostpnt = NULL;
+ SDpnt = NULL;
+ } else {
+ shostpnt = handle->last_scsi_host_p;
+ SDpnt = handle->last_scsi_device_p;
+ }
+
+
+ if (shostpnt == NULL) {
+ /*
+ * New search starts with handles NULL.
+ * Set shostpnt here and let SDpnt get set
+ * in the for loop below.
+ */
+ shostpnt = scsi_hostlist;
+ SDpnt = shostpnt->host_queue;
+ } else
+ SDpnt = SDpnt->sdev_next;
+
+ while (shostpnt) {
+ if (shostpnt->host_no == host_no ||
+ host_no == SCSI_FIND_ALL_HOST_NO) {
+ for(; SDpnt; SDpnt = SDpnt->sdev_next) {
+ if ((channel == SCSI_FIND_ALL_CHANNEL ||
+ channel == SDpnt->channel) &&
+ (id == SCSI_FIND_ALL_ID ||
+ id == SDpnt->id) &&
+ (lun == SCSI_FIND_ALL_LUN ||
+ lun == SDpnt->lun))
+ goto done;
+
+ }
+
+ }
+ if (shostpnt->host_no == host_no)
+ goto done;
+
+ shostpnt = shostpnt->next;
+ if (shostpnt)
+ SDpnt = shostpnt->host_queue;
+ }
+
+done:
+ if (handle) {
+ handle->last_scsi_device_p = SDpnt;
+ handle->last_scsi_host_p = shostpnt;
+ }
+ return SDpnt;
+}
+
+/*
+ * Function: scsi_paths_printk, not-multi-path IO version.
+ *
+ * Purpose: For the path in SDpnt, printk the host/channel/id/lun
+ * using the specified format.
+ *
+ * Arguments: Scsi_Device pointer SDpnt, *prefix the string to print first.
+ *
+ * Notes: This is not inlined because of circular dependencies (Scsi_Host
+ * is in hosts.h, Scsi_Device in scsi.h).
+ */
+void scsi_paths_printk(Scsi_Device *SDpnt, char *prefix,
+ char *format)
+{
+ printk(format, SDpnt->host->host_no, SDpnt->channel, SDpnt->id,
+ SDpnt->lun);
+}
+
+#ifdef CONFIG_PROC_FS
+int scsi_paths_proc_print_paths(Scsi_Device *SDpnt, char *buffer, char *format)
+{
+ return(sprintf(buffer, format, SDpnt->host->host_no,
+ SDpnt->channel, SDpnt->id, SDpnt->lun));
+}
+#endif

2002-09-17 22:46:35

by Patrick Mansfield

[permalink] [raw]
Subject: [RFC] [PATCH] 1/7 2.5.35 SCSI multi-path

Patch 1 of 3 to add mid-layer scsi changes to simplify and enable the
addition of scsi multi-path IO support.

The bulk (number of lines changed) of these changes are:

add a Scsi_Device iterator

add and use functions for removal and addition of a Scsi_Device

add hooks for calls to multi-path functions - with this patch, the
hooks are either NULL code, or code that matches the current scsi
behaviour

hosts.c | 25 +--
hosts.h | 30 +++-
scsi.c | 429 +++++++++++++++++++++++++++++------------------------------
scsi_error.c | 189 +++++++++++++++----------
4 files changed, 364 insertions(+), 309 deletions(-)

diff -Nru a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
--- a/drivers/scsi/hosts.c Mon Sep 16 15:29:45 2002
+++ b/drivers/scsi/hosts.c Mon Sep 16 15:29:45 2002
@@ -259,30 +259,27 @@
return retval;
}

-void scsi_host_busy_inc(struct Scsi_Host *shost, Scsi_Device *sdev)
-{
- unsigned long flags;
-
- spin_lock_irqsave(shost->host_lock, flags);
- shost->host_busy++;
- sdev->device_busy++;
- spin_unlock_irqrestore(shost->host_lock, flags);
-}
-
+/*
+ * Decrement shost->host_busy, and check if any conditions require action.
+ * Must be called with the queue_lock held. Hack to get the host_lock if
+ * host_lock and queue_lock are different.
+ */
void scsi_host_busy_dec_and_test(struct Scsi_Host *shost, Scsi_Device *sdev)
{
- unsigned long flags;
+ unsigned long flags = 0;
+ request_queue_t *q = &sdev->request_queue;

- spin_lock_irqsave(shost->host_lock, flags);
+ if (q->queue_lock != shost->host_lock)
+ spin_lock_irqsave(shost->host_lock, flags);
shost->host_busy--;
- sdev->device_busy--;
if (shost->in_recovery && (shost->host_busy == shost->host_failed)) {
up(shost->eh_wait);
SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler"
"thread (%d)\n",
atomic_read(&shost->eh_wait->count)));
}
- spin_unlock_irqrestore(shost->host_lock, flags);
+ if (q->queue_lock != shost->host_lock)
+ spin_unlock_irqrestore(shost->host_lock, flags);
}

void scsi_host_failed_inc_and_test(struct Scsi_Host *shost)
diff -Nru a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h
--- a/drivers/scsi/hosts.h Mon Sep 16 15:29:45 2002
+++ b/drivers/scsi/hosts.h Mon Sep 16 15:29:45 2002
@@ -576,7 +576,6 @@
#define SD_EXTRA_DEVS CONFIG_SD_EXTRA_DEVS
#define SR_EXTRA_DEVS CONFIG_SR_EXTRA_DEVS

-
/**
* scsi_find_device - find a device given the host
* @channel: SCSI channel (zero if only one channel)
@@ -589,13 +588,40 @@

for(SDpnt = host->host_queue;
SDpnt != NULL;
- SDpnt = SDpnt->next)
+ SDpnt = SDpnt->sdev_next)
if(SDpnt->channel == channel && SDpnt->id == pun
&& SDpnt->lun ==lun)
break;
return SDpnt;
}

+/*
+ * XXX For now, check for the existence of the NUMA topology patch via the
+ * define of MAX_NR_MEMBLKS.
+ */
+#if defined(CONFIG_MULTIQUAD) && defined(MAX_NR_MEMBLKS)
+static inline int scsihost_to_node(struct Scsi_Host *host)
+{
+ if (!host) {
+ printk("%s: host == NULL \n", __FUNCTION__);
+ return 0;
+ }
+ if(host->pci_dev)
+ return pcidev_to_node(host->pci_dev);
+
+ /*
+ * XXX umm what about usb and ide over scsi?
+ */
+ printk("%s: Unable to determine bus type\n", __FUNCTION__);
+ return 0;
+}
+#else
+/*
+ * In the absence of NUMA, assign to node 0
+ */
+#define scsihost_to_node(host) 0
+#endif /* CONFIG_MULTIQUAD */
+
#endif
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff -Nru a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
--- a/drivers/scsi/scsi.c Mon Sep 16 15:29:45 2002
+++ b/drivers/scsi/scsi.c Mon Sep 16 15:29:45 2002
@@ -142,7 +142,7 @@
* Note - the initial logging level can be set here to log events at boot time.
* After the system is up, you may enable logging via the /proc interface.
*/
-unsigned int scsi_logging_level;
+unsigned int scsi_logging_level = 0x00000140 /* XXX scan log remove */;

const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] =
{
@@ -318,7 +318,6 @@
memset(SRpnt, 0, size);
SRpnt->sr_request = (struct request *)(((char *)SRpnt) + offset);
SRpnt->sr_device = device;
- SRpnt->sr_host = device->host;
SRpnt->sr_magic = SCSI_REQ_MAGIC;
SRpnt->sr_data_direction = SCSI_DATA_UNKNOWN;

@@ -381,17 +380,18 @@
*/

Scsi_Cmnd *scsi_allocate_device(Scsi_Device * device, int wait,
- int interruptable)
+ int interruptable, struct scsi_path_id *path_p)
{
struct Scsi_Host *host;
Scsi_Cmnd *SCpnt = NULL;
Scsi_Device *SDpnt;
unsigned long flags;
+ scsi_traverse_hndl_t STrav_hndl;

if (!device)
panic("No device passed to scsi_allocate_device().\n");

- host = device->host;
+ host = path_p->spi_shpnt;

spin_lock_irqsave(&device_request_lock, flags);

@@ -413,16 +413,11 @@
* allow us to more easily figure out whether we should
* do anything here or not.
*/
- for (SDpnt = host->host_queue;
- SDpnt;
- SDpnt = SDpnt->next) {
- /*
- * Only look for other devices on the same bus
- * with the same target ID.
- */
- if (SDpnt->channel != device->channel
- || SDpnt->id != device->id
- || SDpnt == device) {
+ scsi_for_each_sdev_lun(&STrav_hndl, SDpnt,
+ host->host_no,
+ path_p->spi_channel,
+ path_p->spi_id) {
+ if (SDpnt == device) {
continue;
}
if( atomic_read(&SDpnt->device_active) != 0)
@@ -513,6 +508,8 @@
}
}

+ scsi_path_set_scmnd_ids(SCpnt, path_p);
+
SCpnt->request = NULL;
atomic_inc(&SCpnt->host->host_active);
atomic_inc(&SCpnt->device->device_active);
@@ -608,8 +605,10 @@
{
request_queue_t *q;
Scsi_Device * SDpnt;
+ struct Scsi_Host * SHpnt;

SDpnt = SCpnt->device;
+ SHpnt = SCpnt->host;

__scsi_release_command(SCpnt);

@@ -620,7 +619,7 @@
* will go on.
*/
q = &SDpnt->request_queue;
- scsi_queue_next_request(q, NULL);
+ scsi_queue_next_request(q, NULL, SHpnt);
}

/*
@@ -689,10 +688,12 @@
* We will use a queued command if possible, otherwise we will emulate the
* queuing and calling of completion function ourselves.
*/
- SCSI_LOG_MLQUEUE(3, printk("scsi_dispatch_cmnd (host = %d, channel = %d, target = %d, "
- "command = %p, buffer = %p, \nbufflen = %d, done = %p)\n",
- SCpnt->host->host_no, SCpnt->channel, SCpnt->target, SCpnt->cmnd,
- SCpnt->buffer, SCpnt->bufflen, SCpnt->done));
+ SCSI_LOG_MLQUEUE(3,
+ printk("scsi_dispatch_cmnd (host = %d, channel = %d, target"
+ " = %d, lun = %d,\n command = %p, buffer = %p,"
+ " bufflen = %d, done = %p)\n", SCpnt->host->host_no,
+ SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->cmnd,
+ SCpnt->buffer, SCpnt->bufflen, SCpnt->done));

SCpnt->state = SCSI_STATE_QUEUED;
SCpnt->owner = SCSI_OWNER_LOWLEVEL;
@@ -808,30 +809,17 @@
void *buffer, unsigned bufflen, void (*done) (Scsi_Cmnd *),
int timeout, int retries)
{
- Scsi_Device * SDpnt = SRpnt->sr_device;
- struct Scsi_Host *host = SDpnt->host;
-
- ASSERT_LOCK(host->host_lock, 0);
-
SCSI_LOG_MLQUEUE(4,
{
int i;
- int target = SDpnt->id;
int size = COMMAND_SIZE(((const unsigned char *)cmnd)[0]);
- printk("scsi_do_req (host = %d, channel = %d target = %d, "
- "buffer =%p, bufflen = %d, done = %p, timeout = %d, "
- "retries = %d)\n"
- "command : ", host->host_no, SDpnt->channel, target, buffer,
- bufflen, done, timeout, retries);
+ printk("scsi_do_req (buffer =%p, bufflen = %d, done = "
+ " %p, timeout = %d, retries = %d)\n command : ",
+ buffer, bufflen, done, timeout, retries);
for (i = 0; i < size; ++i)
printk("%02x ", ((unsigned char *) cmnd)[i]);
printk("\n");
});
-
- if (!host) {
- panic("Invalid or not present host.\n");
- }
-
/*
* If the upper level driver is reusing these things, then
* we should release the low-level block now. Another one will
@@ -1199,6 +1187,15 @@

scsi_retry_command(SCpnt);
break;
+ case REQUEUE:
+ /*
+ * Resend the IO, likely using a different
+ * path.
+ */
+ SCSI_LOG_MLCOMPLETE(3, printk("Requeuing IO %d %d 0x%x\n", SCpnt->host->host_busy,
+ SCpnt->host->host_failed, SCpnt->result));
+ scsi_mlqueue_insert(SCpnt, SCSI_MLQUEUE_RETRY);
+ break;
case ADD_TO_MLQUEUE:
/*
* This typically happens for a QUEUE_FULL
@@ -1291,6 +1288,7 @@
struct Scsi_Host *host;
Scsi_Device *device;
Scsi_Request * SRpnt;
+ unsigned long flags;

host = SCpnt->host;
device = SCpnt->device;
@@ -1304,7 +1302,11 @@
* one execution context, but the device and host structures are
* shared.
*/
+ spin_lock_irqsave(device->request_queue.queue_lock, flags);
+ device->device_busy--;
scsi_host_busy_dec_and_test(host, device);
+ spin_unlock_irqrestore(device->request_queue.queue_lock, flags);
+

/*
* Clear the flags which say that the device/host is no longer
@@ -1323,7 +1325,7 @@
SCpnt->result |= (DRIVER_SENSE << 24);
}
SCSI_LOG_MLCOMPLETE(3, printk("Notifying upper driver of completion for device %d %x\n",
- SCpnt->device->id, SCpnt->result));
+ SCpnt->target, SCpnt->result));

SCpnt->owner = SCSI_OWNER_HIGHLEVEL;
SCpnt->state = SCSI_STATE_FINISHED;
@@ -1392,12 +1394,18 @@
void scsi_build_commandblocks(Scsi_Device * SDpnt)
{
unsigned long flags;
- struct Scsi_Host *host = SDpnt->host;
+ struct Scsi_Host *host;
int j;
Scsi_Cmnd *SCpnt;

spin_lock_irqsave(&device_request_lock, flags);

+ host = scsi_get_host(SDpnt);
+ /*
+ * We are called during scan, and must always have at least one
+ * path.
+ */
+ BUG_ON(host == NULL);
if (SDpnt->queue_depth == 0)
{
SDpnt->queue_depth = host->cmd_per_lun;
@@ -1415,10 +1423,10 @@
break; /* If not, the next line will oops ... */
memset(SCpnt, 0, sizeof(Scsi_Cmnd));
SCpnt->host = host;
- SCpnt->device = SDpnt;
SCpnt->target = SDpnt->id;
SCpnt->lun = SDpnt->lun;
SCpnt->channel = SDpnt->channel;
+ SCpnt->device = SDpnt;
SCpnt->request = NULL;
SCpnt->use_sg = 0;
SCpnt->old_use_sg = 0;
@@ -1477,44 +1485,37 @@
static int scsi_proc_info(char *buffer, char **start, off_t offset, int length)
{
Scsi_Device *scd;
- struct Scsi_Host *HBA_ptr;
+ scsi_traverse_hndl_t STrav_hndl;
int size, len = 0;
off_t begin = 0;
off_t pos = 0;
+ int header_output = 0;

- /*
- * First, see if there are any attached devices or not.
- */
- for (HBA_ptr = scsi_hostlist; HBA_ptr; HBA_ptr = HBA_ptr->next) {
- if (HBA_ptr->host_queue != NULL) {
- break;
+ scsi_for_all_sdevs(&STrav_hndl, scd) {
+ if (!header_output) {
+ size = sprintf(buffer + len, "Attached devices:\n");
+ len += size;
+ pos = begin + len;
+ header_output = 1;
}
- }
- size = sprintf(buffer + len, "Attached devices: %s\n", (HBA_ptr) ? "" : "none");
- len += size;
- pos = begin + len;
- for (HBA_ptr = scsi_hostlist; HBA_ptr; HBA_ptr = HBA_ptr->next) {
-#if 0
- size += sprintf(buffer + len, "scsi%2d: %s\n", (int) HBA_ptr->host_no,
- HBA_ptr->hostt->procname);
+ proc_print_scsidevice(scd, buffer, &size, len);
len += size;
pos = begin + len;
-#endif
- for (scd = HBA_ptr->host_queue; scd; scd = scd->next) {
- proc_print_scsidevice(scd, buffer, &size, len);
- len += size;
- pos = begin + len;

- if (pos < offset) {
- len = 0;
- begin = pos;
- }
- if (pos > offset + length)
- goto stop_output;
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
}
+ if (pos > offset + length)
+ goto stop_output;
}

stop_output:
+ if (!header_output) {
+ size = sprintf(buffer + len, "Attached devices: none\n");
+ len += size;
+ pos = begin + len;
+ }
*start = buffer + (offset - begin); /* Start of wanted data */
len -= (offset - begin); /* Start slop */
if (len > length)
@@ -1669,13 +1670,7 @@
if (!HBA_ptr)
goto out;

- for (scd = HBA_ptr->host_queue; scd; scd = scd->next) {
- if ((scd->channel == channel
- && scd->id == id
- && scd->lun == lun)) {
- break;
- }
- }
+ scd = scsi_locate_sdev(host, channel, id, lun);

err = -ENOSYS;
if (scd)
@@ -1721,13 +1716,7 @@
if (!HBA_ptr)
goto out;

- for (scd = HBA_ptr->host_queue; scd; scd = scd->next) {
- if ((scd->channel == channel
- && scd->id == id
- && scd->lun == lun)) {
- break;
- }
- }
+ scd = scsi_locate_sdev(host, channel, id, lun);

if (scd == NULL)
goto out; /* there is no such device attached */
@@ -1744,6 +1733,10 @@
}

if (scd->attached == 0) {
+ scsi_remove_path(scd, SCSI_FIND_ALL_HOST_NO,
+ SCSI_FIND_ALL_CHANNEL,
+ SCSI_FIND_ALL_ID,
+ SCSI_FIND_ALL_LUN);
/*
* Nobody is using this device any more.
* Free all of the command structures.
@@ -1752,21 +1745,11 @@
HBA_ptr->hostt->revoke(scd);
devfs_unregister (scd->de);
scsi_release_commandblocks(scd);
-
- /* Now we can remove the device structure */
- if (scd->next != NULL)
- scd->next->prev = scd->prev;
-
- if (scd->prev != NULL)
- scd->prev->next = scd->next;
-
- if (HBA_ptr->host_queue == scd) {
- HBA_ptr->host_queue = scd->next;
- }
blk_cleanup_queue(&scd->request_queue);
if (scd->inquiry)
kfree(scd->inquiry);
- kfree((char *) scd);
+ /* Now we can remove the device structure */
+ scsi_remove_scsi_device(scd);
} else {
goto out;
}
@@ -1791,6 +1774,7 @@
struct Scsi_Device_Template *sdtpnt;
const char *name;
int out_of_space = 0;
+ scsi_traverse_hndl_t STrav_hndl;

if (tpnt->next || !tpnt->detect)
return 1; /* Must be already loaded, or
@@ -1901,18 +1885,19 @@
/*
* Next we create the Scsi_Cmnd structures for this host
*/
- for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
- for (SDpnt = shpnt->host_queue; SDpnt; SDpnt = SDpnt->next)
- if (SDpnt->host->hostt == tpnt) {
- for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
- if (sdtpnt->attach)
- (*sdtpnt->attach) (SDpnt);
- if (SDpnt->attached) {
- scsi_build_commandblocks(SDpnt);
- if (0 == SDpnt->has_cmdblocks)
- out_of_space = 1;
- }
+ scsi_for_all_sdevs(&STrav_hndl, SDpnt) {
+ shpnt = scsi_get_host(SDpnt);
+ if (shpnt && shpnt->hostt == tpnt) {
+ for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if (sdtpnt->attach)
+ (*sdtpnt->attach) (SDpnt);
+ if (SDpnt->attached && (0 ==
+ SDpnt->has_cmdblocks)) {
+ scsi_build_commandblocks(SDpnt);
+ if (0 == SDpnt->has_cmdblocks)
+ out_of_space = 1;
}
+ }
}

/* This does any final handling that is required. */
@@ -1943,8 +1928,9 @@
Scsi_Device *SDpnt1;
struct Scsi_Device_Template *sdtpnt;
struct Scsi_Host *sh1;
- struct Scsi_Host *shpnt;
+ struct Scsi_Host *shpnt, *shpnt2;
char name[10]; /* host_no>=10^9? I don't think so. */
+ scsi_traverse_hndl_t STrav_hndl;

/* get the big kernel lock, so we don't race with open() */
lock_kernel();
@@ -1954,12 +1940,16 @@
* commands
*/
for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
- for (SDpnt = shpnt->host_queue; SDpnt;
- SDpnt = SDpnt->next) {
- if (SDpnt->host->hostt == tpnt
- && SDpnt->host->hostt->module
- && GET_USE_COUNT(SDpnt->host->hostt->module))
- goto err_out;
+ scsi_for_each_host_sdev(&STrav_hndl, SDpnt, shpnt->host_no) {
+ /*
+ * If any shpnt2 is still in use, it should be
+ * impossible to call this function.
+ */
+ shpnt2 = scsi_get_host(SDpnt);
+ if (shpnt2 && shpnt2->hostt == tpnt
+ && shpnt2->hostt->module
+ && GET_USE_COUNT(shpnt2->hostt->module))
+ BUG();
/*
* FIXME(eric) - We need to find a way to notify the
* low level driver that we are shutting down - via the
@@ -1976,9 +1966,9 @@
* get in and queue a command.
*/
for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
- for (SDpnt = shpnt->host_queue; SDpnt;
- SDpnt = SDpnt->next) {
- if (SDpnt->host->hostt == tpnt)
+ scsi_for_each_host_sdev(&STrav_hndl, SDpnt, shpnt->host_no) {
+ shpnt2 = scsi_get_host(SDpnt);
+ if (shpnt2 && shpnt2->hostt == tpnt)
SDpnt->online = FALSE;

}
@@ -1988,8 +1978,7 @@
if (shpnt->hostt != tpnt) {
continue;
}
- for (SDpnt = shpnt->host_queue; SDpnt;
- SDpnt = SDpnt->next) {
+ scsi_for_each_host_sdev(&STrav_hndl, SDpnt, shpnt->host_no) {
/*
* Loop over all of the commands associated with the device. If any of
* them are busy, then set the state back to inactive and bail.
@@ -1998,12 +1987,12 @@
SCpnt = SCpnt->next) {
online_status = SDpnt->online;
SDpnt->online = FALSE;
- if (SCpnt->request && SCpnt->request->rq_status != RQ_INACTIVE) {
+ if ((SCpnt->request && SCpnt->request->rq_status != RQ_INACTIVE)
+ && SCpnt->request->rq_status != RQ_SCSI_DISCONNECTING) {
printk(KERN_ERR "SCSI device not inactive - rq_status=%d, target=%d, pid=%ld, state=%d, owner=%d.\n",
SCpnt->request->rq_status, SCpnt->target, SCpnt->pid,
SCpnt->state, SCpnt->owner);
- for (SDpnt1 = shpnt->host_queue; SDpnt1;
- SDpnt1 = SDpnt1->next) {
+ scsi_for_each_host_sdev(&STrav_hndl, SDpnt1, shpnt->host_no) {
for (SCpnt = SDpnt1->device_queue; SCpnt;
SCpnt = SCpnt->next)
if (SCpnt->request->rq_status == RQ_SCSI_DISCONNECTING)
@@ -2029,18 +2018,20 @@
if (shpnt->hostt != tpnt) {
continue;
}
- for (SDpnt = shpnt->host_queue; SDpnt;
- SDpnt = SDpnt->next) {
- for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
- if (sdtpnt->detach)
- (*sdtpnt->detach) (SDpnt);
-
- /* If something still attached, punt */
+ scsi_for_each_host_sdev(&STrav_hndl, SDpnt, shpnt->host_no) {
if (SDpnt->attached) {
- printk(KERN_ERR "Attached usage count = %d\n", SDpnt->attached);
- goto err_out;
+ for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) {
+ if (sdtpnt->detach)
+ (*sdtpnt->detach) (SDpnt);
+ }
+
+ /* If something still attached, punt */
+ if (SDpnt->attached) {
+ printk(KERN_ERR "Attached usage count = %d\n", SDpnt->attached);
+ goto err_out;
+ }
+ devfs_unregister (SDpnt->de);
}
- devfs_unregister (SDpnt->de);
put_device(&SDpnt->sdev_driverfs_dev);
}
}
@@ -2066,17 +2057,15 @@
if (shpnt->hostt != tpnt) {
continue;
}
- for (SDpnt = shpnt->host_queue; SDpnt;
- SDpnt = shpnt->host_queue) {
+ scsi_for_each_host_sdev(&STrav_hndl, SDpnt, shpnt->host_no) {
+ /* Next free up the Scsi_Device structures for this host */
+ scsi_remove_path(SDpnt, shpnt->host_no,
+ SCSI_FIND_ALL_CHANNEL,
+ SCSI_FIND_ALL_ID,
+ SCSI_FIND_ALL_LUN);
scsi_release_commandblocks(SDpnt);
-
blk_cleanup_queue(&SDpnt->request_queue);
- /* Next free up the Scsi_Device structures for this host */
- shpnt->host_queue = SDpnt->next;
- if (SDpnt->inquiry)
- kfree(SDpnt->inquiry);
- kfree((char *) SDpnt);
-
+ scsi_remove_scsi_device(SDpnt);
}
}

@@ -2150,8 +2139,8 @@
int scsi_register_device(struct Scsi_Device_Template *tpnt)
{
Scsi_Device *SDpnt;
- struct Scsi_Host *shpnt;
int out_of_space = 0;
+ scsi_traverse_hndl_t STrav_hndl;

#ifdef CONFIG_KMOD
if (scsi_hosts == NULL)
@@ -2168,12 +2157,9 @@
* First scan the devices that we know about, and see if we notice them.
*/

- for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
- for (SDpnt = shpnt->host_queue; SDpnt;
- SDpnt = SDpnt->next) {
- if (tpnt->detect)
- SDpnt->attached += (*tpnt->detect) (SDpnt);
- }
+ scsi_for_all_sdevs(&STrav_hndl, SDpnt) {
+ if (tpnt->detect)
+ SDpnt->attached += (*tpnt->detect) (SDpnt);
}

/*
@@ -2187,21 +2173,24 @@
/*
* Now actually connect the devices to the new driver.
*/
- for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
- for (SDpnt = shpnt->host_queue; SDpnt;
- SDpnt = SDpnt->next) {
- if (tpnt->attach)
- (*tpnt->attach) (SDpnt);
- /*
- * If this driver attached to the device, and don't have any
- * command blocks for this device, allocate some.
- */
- if (SDpnt->attached && SDpnt->has_cmdblocks == 0) {
- SDpnt->online = TRUE;
- scsi_build_commandblocks(SDpnt);
- if (0 == SDpnt->has_cmdblocks)
- out_of_space = 1;
- }
+ scsi_for_all_sdevs(&STrav_hndl, SDpnt) {
+ /*
+ * We know this device cannot already be attached,
+ * since this is the first chance we have to call
+ * the tpnt->attach, unlike registration of the adapter
+ * (in scsi_register_host).
+ */
+ if (tpnt->attach)
+ (*tpnt->attach) (SDpnt);
+ /*
+ * If this driver attached to the device, and don't have any
+ * command blocks for this device, allocate some.
+ */
+ if (SDpnt->attached && SDpnt->has_cmdblocks == 0) {
+ SDpnt->online = TRUE;
+ scsi_build_commandblocks(SDpnt);
+ if (0 == SDpnt->has_cmdblocks)
+ out_of_space = 1;
}
}

@@ -2222,9 +2211,9 @@
int scsi_unregister_device(struct Scsi_Device_Template *tpnt)
{
Scsi_Device *SDpnt;
- struct Scsi_Host *shpnt;
struct Scsi_Device_Template *spnt;
struct Scsi_Device_Template *prev_spnt;
+ scsi_traverse_hndl_t STrav_hndl;

lock_kernel();
/*
@@ -2237,22 +2226,20 @@
* Next, detach the devices from the driver.
*/

- for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
- for (SDpnt = shpnt->host_queue; SDpnt;
- SDpnt = SDpnt->next) {
- if (tpnt->detach)
- (*tpnt->detach) (SDpnt);
- if (SDpnt->attached == 0) {
- SDpnt->online = FALSE;
+ scsi_for_all_sdevs(&STrav_hndl, SDpnt) {
+ if (tpnt->detach)
+ (*tpnt->detach) (SDpnt);
+ if (SDpnt->attached == 0) {
+ SDpnt->online = FALSE;

- /*
- * Nobody is using this device any more. Free all of the
- * command structures.
- */
- scsi_release_commandblocks(SDpnt);
- }
+ /*
+ * Nobody is using this device any more. Free all of the
+ * command structures.
+ */
+ scsi_release_commandblocks(SDpnt);
}
}
+
/*
* Extract the template from the linked list.
*/
@@ -2305,6 +2292,8 @@
struct Scsi_Host *shpnt;
Scsi_Cmnd *SCpnt;
Scsi_Device *SDpnt;
+ scsi_traverse_hndl_t STrav_hndl;
+
printk(KERN_INFO "Dump of scsi host parameters:\n");
i = 0;
for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
@@ -2318,38 +2307,36 @@

printk(KERN_INFO "\n\n");
printk(KERN_INFO "Dump of scsi command parameters:\n");
- for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
- printk(KERN_INFO "h:c:t:l (dev sect nsect cnumsec sg) (ret all flg) (to/cmd to ito) cmd snse result\n");
- for (SDpnt = shpnt->host_queue; SDpnt; SDpnt = SDpnt->next) {
- for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) {
- /* (0) h:c:t:l (dev sect nsect cnumsec sg) (ret all flg) (to/cmd to ito) cmd snse result %d %x */
- printk(KERN_INFO "(%3d) %2d:%1d:%2d:%2d (%6s %4ld %4ld %4ld %4x %1d) (%1d %1d 0x%2x) (%4d %4d %4d) 0x%2.2x 0x%2.2x 0x%8.8x\n",
- i++,
-
- SCpnt->host->host_no,
- SCpnt->channel,
- SCpnt->target,
- SCpnt->lun,
-
- kdevname(SCpnt->request->rq_dev),
- SCpnt->request->sector,
- SCpnt->request->nr_sectors,
- (long)SCpnt->request->current_nr_sectors,
- SCpnt->request->rq_status,
- SCpnt->use_sg,
-
- SCpnt->retries,
- SCpnt->allowed,
- SCpnt->flags,
-
- SCpnt->timeout_per_command,
- SCpnt->timeout,
- SCpnt->internal_timeout,
-
- SCpnt->cmnd[0],
- SCpnt->sense_buffer[2],
- SCpnt->result);
- }
+ printk(KERN_INFO "h:c:t:l (dev sect nsect cnumsec sg) (ret all flg) (to/cmd to ito) cmd snse result\n");
+ scsi_for_all_sdevs(&STrav_hndl, SDpnt) {
+ for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) {
+ /* (0) h:c:t:l (dev sect nsect cnumsec sg) (ret all flg) (to/cmd to ito) cmd snse result %d %x */
+ printk(KERN_INFO "(%3d) %2d:%1d:%2d:%2d (%6s %4ld %4ld %4ld %4x %1d) (%1d %1d 0x%2x) (%4d %4d %4d) 0x%2.2x 0x%2.2x 0x%8.8x\n",
+ i++,
+
+ SCpnt->host->host_no,
+ SCpnt->channel,
+ SCpnt->target,
+ SCpnt->lun,
+
+ kdevname(SCpnt->request->rq_dev),
+ SCpnt->request->sector,
+ SCpnt->request->nr_sectors,
+ (long)SCpnt->request->current_nr_sectors,
+ SCpnt->request->rq_status,
+ SCpnt->use_sg,
+
+ SCpnt->retries,
+ SCpnt->allowed,
+ SCpnt->flags,
+
+ SCpnt->timeout_per_command,
+ SCpnt->timeout,
+ SCpnt->internal_timeout,
+
+ SCpnt->cmnd[0],
+ SCpnt->sense_buffer[2],
+ SCpnt->result);
}
}
#endif /* CONFIG_SCSI_LOGGING */ /* } */
@@ -2592,15 +2579,21 @@
if(SDpnt == NULL)
return NULL;

+ scsi_add_scsi_device(SDpnt, SHpnt);
memset(SDpnt, 0, sizeof(Scsi_Device));
SDpnt->vendor = scsi_null_device_strs;
SDpnt->model = scsi_null_device_strs;
SDpnt->rev = scsi_null_device_strs;

- SDpnt->host = SHpnt;
- SDpnt->id = SHpnt->this_id;
SDpnt->type = -1;
SDpnt->queue_depth = 1;
+ /*
+ * add a path: host is SHpnt, channel 0, id is SHpnt->this_id, lun 0
+ */
+ if (scsi_add_path(SDpnt, SHpnt, 0, SHpnt->this_id, 0)) {
+ scsi_remove_scsi_device(SDpnt);
+ return NULL;
+ }

scsi_build_commandblocks(SDpnt);

@@ -2630,11 +2623,17 @@
*/
void scsi_free_host_dev(Scsi_Device * SDpnt)
{
- if( (unsigned char) SDpnt->id != (unsigned char) SDpnt->host->this_id )
- {
+ struct scsi_path_id scsi_path;
+
+ scsi_get_path(SDpnt, &scsi_path);
+ if ((unsigned char) scsi_path.spi_id != (unsigned char)
+ scsi_path.spi_shpnt->this_id) {
panic("Attempt to delete wrong device\n");
}
-
+ scsi_remove_path(SDpnt, scsi_path.spi_shpnt->host_no,
+ scsi_path.spi_channel,
+ scsi_path.spi_id,
+ scsi_path.spi_lun);
blk_cleanup_queue(&SDpnt->request_queue);

/*
@@ -2642,9 +2641,7 @@
* it now.
*/
scsi_release_commandblocks(SDpnt);
- if (SDpnt->inquiry)
- kfree(SDpnt->inquiry);
- kfree(SDpnt);
+ scsi_remove_scsi_device(SDpnt);
}

/*
@@ -2682,14 +2679,16 @@
Scsi_Cmnd SC, *SCpnt = &SC;
struct request req;
int rtn;
+ struct scsi_path_id scsi_path;

SCpnt->request = &req;
memset(&SCpnt->eh_timeout, 0, sizeof(SCpnt->eh_timeout));
- SCpnt->host = dev->host;
+ scsi_get_path(dev, &scsi_path);
+ SCpnt->host = scsi_path.spi_shpnt;
SCpnt->device = dev;
- SCpnt->target = dev->id;
- SCpnt->lun = dev->lun;
- SCpnt->channel = dev->channel;
+ SCpnt->target = scsi_path.spi_id;
+ SCpnt->lun = scsi_path.spi_lun;
+ SCpnt->channel = scsi_path.spi_channel;
SCpnt->request->rq_status = RQ_SCSI_BUSY;
SCpnt->request->waiting = NULL;
SCpnt->use_sg = 0;
diff -Nru a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
--- a/drivers/scsi/scsi_error.c Mon Sep 16 15:29:45 2002
+++ b/drivers/scsi/scsi_error.c Mon Sep 16 15:29:45 2002
@@ -126,37 +126,6 @@
}

/**
- * scsi_times_out - Timeout function for normal scsi commands.
- * @scmd: Cmd that is timing out.
- *
- * Notes:
- * We do not need to lock this. There is the potential for a race
- * only in that the normal completion handling might run, but if the
- * normal completion function determines that the timer has already
- * fired, then it mustn't do anything.
- **/
-void scsi_times_out(Scsi_Cmnd *scmd)
-{
- /* Set the serial_number_at_timeout to the current serial_number */
- scmd->serial_number_at_timeout = scmd->serial_number;
-
- scsi_eh_eflags_set(scmd, SCSI_EH_CMD_TIMEOUT | SCSI_EH_CMD_ERR);
-
- if( scmd->host->eh_wait == NULL ) {
- panic("Error handler thread not present at %p %p %s %d",
- scmd, scmd->host, __FILE__, __LINE__);
- }
-
- scsi_host_failed_inc_and_test(scmd->host);
-
- SCSI_LOG_TIMEOUT(3, printk("Command timed out active=%d busy=%d "
- "failed=%d\n",
- atomic_read(&scmd->host->host_active),
- scmd->host->host_busy,
- scmd->host->host_failed));
-}
-
-/**
* scsi_block_when_processing_errors - Prevent cmds from being queued.
* @sdev: Device on which we are performing recovery.
*
@@ -172,7 +141,6 @@
{

SCSI_SLEEP(&sdev->host->host_wait, sdev->host->in_recovery);
-
SCSI_LOG_ERROR_RECOVERY(5, printk("Open returning %d\n",
sdev->online));

@@ -193,9 +161,9 @@
int cmd_failed = 0;
int cmd_timed_out = 0;
int devices_failed = 0;
+ scsi_traverse_hndl_t strav_hndl;

-
- for (sdev = shost->host_queue; sdev; sdev = sdev->next) {
+ scsi_for_each_host_sdev(&strav_hndl, sdev, shost->host_no) {
for (scmd = sc_list; scmd; scmd = scmd->bh_next) {
if (scmd->device == sdev) {
++total_failures;
@@ -208,12 +176,13 @@
}

if (cmd_timed_out || cmd_failed) {
- SCSI_LOG_ERROR_RECOVERY(3,
- printk("scsi_eh: %d:%d:%d:%d cmds failed: %d,"
- "timedout: %d\n",
- shost->host_no, sdev->channel,
- sdev->id, sdev->lun,
- cmd_failed, cmd_timed_out));
+ SCSI_LOG_ERROR_RECOVERY(3, {
+ printk("scsi_eh: device at ");
+ scsi_paths_printk(sdev, " ",
+ "<%d, %d, %d, %d>");
+ printk(" cmds failed: %d, timedout: %d\n",
+ cmd_failed, cmd_timed_out);
+ });
cmd_timed_out = 0;
cmd_failed = 0;
++devices_failed;
@@ -239,8 +208,10 @@
int found;
Scsi_Device *sdev;
Scsi_Cmnd *scmd;
+ scsi_traverse_hndl_t strav_hndl;

- for (found = 0, sdev = shost->host_queue; sdev; sdev = sdev->next) {
+ found = 0;
+ scsi_for_each_host_sdev(&strav_hndl, sdev, shost->host_no) {
for (scmd = sdev->device_queue; scmd; scmd = scmd->next) {
if (scsi_eh_eflags_chk(scmd, SCSI_EH_CMD_ERR)) {
scmd->bh_next = *sc_list;
@@ -285,7 +256,7 @@
* scsi_check_sense - Examine scsi cmd sense
* @scmd: Cmd to have sense checked.
**/
-static int scsi_check_sense(Scsi_Cmnd *scmd)
+int scsi_check_sense(Scsi_Cmnd *scmd)
{
if (!SCSI_SENSE_VALID(scmd)) {
return FAILED;
@@ -836,6 +807,72 @@
}

/**
+ * scsi_times_out - Timeout function for normal scsi commands.
+ * @scmd: Cmd that is timing out.
+ *
+ * Notes:
+ * We do not need to lock this. There is the potential for a race
+ * only in that the normal completion handling might run, but if the
+ * normal completion function determines that the timer has already
+ * fired, then it mustn't do anything.
+ **/
+void scsi_times_out(Scsi_Cmnd *scmd)
+{
+
+ /*
+ * We can get here with DID_ERROR set, meaning someone has touched
+ * the result. For qlogicfc, it looks like it might change the
+ * result, even though it does not let us know the IO has
+ * completed (on loop down)! So, don't just or in DID_TIME_OUT.
+ *
+ * XXX maybe only set DRIVER_TIMEOUT? We then need changes in
+ * scsi_path_decide_disposition.
+ */
+ scmd->result = (scmd->result & 0xff00ffff) | (DID_TIME_OUT << 16) |
+ (DRIVER_TIMEOUT << 24);
+ if (scsi_path_decide_disposition(scmd) == REQUEUE) {
+ /*
+ * Abort the command, and then requeue it.
+ *
+ * XXX If the abort is not possible, what do we do?
+ * Probably should not retry. But, for example, qlogicfc.c
+ * times out commands after a loop down, and the command
+ * is no longer live; so the abort command always fails
+ * after a loop down causes the command to timeout. This
+ * is also a problem for the current error handling, since
+ * it does not know if the command was not able to be
+ * aborted, or if the command failed to abort because the
+ * adapter no longer has it.
+ */
+ (void)scsi_try_to_abort_cmd(scmd);
+ SCSI_LOG_TIMEOUT(3,
+ printk("Requeuing timed out IO host %d channel %d id % d lun %d; result 0x%x\n",
+ scmd->host->host_no, scmd->channel,
+ scmd->target, scmd->lun, scmd->result));
+ scsi_mlqueue_insert(scmd, SCSI_MLQUEUE_RETRY);
+ return;
+ }
+
+ /* Set the serial_number_at_timeout to the current serial_number */
+ scmd->serial_number_at_timeout = scmd->serial_number;
+
+ scsi_eh_eflags_set(scmd, SCSI_EH_CMD_TIMEOUT | SCSI_EH_CMD_ERR);
+
+ if( scmd->host->eh_wait == NULL ) {
+ panic("Error handler thread not present at %p %p %s %d",
+ scmd, scmd->host, __FILE__, __LINE__);
+ }
+
+ scsi_host_failed_inc_and_test(scmd->host);
+
+ SCSI_LOG_TIMEOUT(3, printk("Command timed out active=%d busy=%d "
+ "failed=%d\n",
+ atomic_read(&scmd->host->host_active),
+ scmd->host->host_busy,
+ scmd->host->host_failed));
+}
+
+/**
* scsi_eh_tur - Send TUR to device.
* @scmd: Scsi cmd to send TUR
*
@@ -982,10 +1019,11 @@
int rtn;
Scsi_Cmnd *scmd;
Scsi_Device *sdev;
+ scsi_traverse_hndl_t strav_hndl;

SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Trying BDR\n", __FUNCTION__));

- for (sdev = shost->host_queue; sdev; sdev = sdev->next) {
+ scsi_for_each_host_sdev(&strav_hndl, sdev, shost->host_no) {
for (scmd = sc_todo; scmd; scmd = scmd->bh_next)
if ((scmd->device == sdev) &&
scsi_eh_eflags_chk(scmd, SCSI_EH_CMD_ERR))
@@ -1021,6 +1059,7 @@
unsigned long flags;
int rtn;
Scsi_Device *sdev;
+ scsi_traverse_hndl_t strav_hndl;

SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Bus RST\n",
__FUNCTION__));
@@ -1039,11 +1078,11 @@
/*
* Mark all affected devices to expect a unit attention.
*/
- for (sdev = scmd->host->host_queue; sdev; sdev = sdev->next)
- if (scmd->channel == sdev->channel) {
- sdev->was_reset = 1;
- sdev->expecting_cc_ua = 1;
- }
+ scsi_for_each_host_chan_sdev(&strav_hndl, sdev,
+ scmd->host->host_no, scmd->channel) {
+ sdev->was_reset = 1;
+ sdev->expecting_cc_ua = 1;
+ }
}
return rtn;
}
@@ -1056,7 +1095,8 @@
{
unsigned long flags;
int rtn;
- Scsi_Device *sdev;
+ Scsi_Device *sdev;
+ scsi_traverse_hndl_t strav_hndl;

SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Host RST\n",
__FUNCTION__));
@@ -1075,11 +1115,11 @@
/*
* Mark all affected devices to expect a unit attention.
*/
- for (sdev = scmd->host->host_queue; sdev; sdev = sdev->next)
- if (scmd->channel == sdev->channel) {
- sdev->was_reset = 1;
- sdev->expecting_cc_ua = 1;
- }
+ scsi_for_each_host_chan_sdev(&strav_hndl, sdev,
+ scmd->host->host_no, scmd->channel) {
+ sdev->was_reset = 1;
+ sdev->expecting_cc_ua = 1;
+ }
}
return rtn;
}
@@ -1132,8 +1172,7 @@
* we now know that we are able to perform a reset for the
* channel that scmd points to.
*/
- rtn = scsi_try_bus_reset(scmd);
- if (rtn != SUCCESS)
+ rtn = scsi_try_bus_reset(scmd); if (rtn != SUCCESS)
rtn = scsi_try_host_reset(scmd);

if (rtn == SUCCESS) {
@@ -1168,14 +1207,11 @@
if (!scsi_eh_eflags_chk(scmd, SCSI_EH_CMD_ERR))
continue;

- printk(KERN_INFO "%s: Device set offline - not"
- "ready or command retry failed"
- "after error recovery: host"
- "%d channel %d id %d lun %d\n",
- __FUNCTION__, shost->host_no,
- scmd->device->channel,
- scmd->device->id,
- scmd->device->lun);
+ printk(KERN_INFO "%s: device at ", __FUNCTION__);
+ scsi_paths_printk(scmd->device,
+ " ", "<%d, %d, %d, %d>");
+ printk(" set offline - not ready or command retry"
+ " failed after bus and host reset\n");
scmd->device->online = FALSE;
scsi_eh_finish_cmd(scmd, shost);
}
@@ -1247,6 +1283,12 @@
__FUNCTION__));
return SUCCESS;
}
+
+ rtn = scsi_path_decide_disposition(scmd);
+ if (rtn != UNKNOWN_ERROR) {
+ return(rtn);
+ }
+
/*
* first check the host byte, to see if there is anything in there
* that would indicate what we need to do.
@@ -1364,7 +1406,7 @@
case RESERVATION_CONFLICT:
printk("scsi%d (%d,%d,%d) : reservation conflict\n",
scmd->host->host_no, scmd->channel,
- scmd->device->id, scmd->device->lun);
+ scmd->target, scmd->lun);
return SUCCESS; /* causes immediate i/o error */
default:
return FAILED;
@@ -1394,7 +1436,7 @@
static void scsi_restart_operations(struct Scsi_Host *shost)
{
Scsi_Device *sdev;
- unsigned long flags;
+ scsi_traverse_hndl_t strav_hndl;

ASSERT_LOCK(shost->host_lock, 0);

@@ -1414,21 +1456,12 @@
* now that error recovery is done, we will need to ensure that these
* requests are started.
*/
- spin_lock_irqsave(shost->host_lock, flags);
- for (sdev = shost->host_queue; sdev; sdev = sdev->next) {
+ scsi_for_each_host_sdev(&strav_hndl, sdev, shost->host_no) {
request_queue_t *q = &sdev->request_queue;
-
- if ((shost->can_queue > 0 &&
- (shost->host_busy >= shost->can_queue))
- || (shost->host_blocked)
- || (shost->host_self_blocked)
- || (sdev->device_blocked)) {
- break;
- }
-
+ spin_lock_irq(q->queue_lock);
q->request_fn(q);
+ spin_unlock_irq(q->queue_lock);
}
- spin_unlock_irqrestore(shost->host_lock, flags);
}

/**

2002-09-17 22:56:10

by Patrick Mansfield

[permalink] [raw]
Subject: [RFC] [PATCH] 4/7 2.5.35 SCSI multi-path

Patches to scsi upper level drivers to simplify and enable the addition of
scsi multi-path IO support.

This does not add multi-path support, it adds changes that simplify the
addition of the actual scsi multi-path code.

sd.c | 69 ++++++++++++++++++++------------------
sg.c | 107 ++++++++++++++++++++++++++++++++++++++----------------------
sr.c | 34 +++++++------------
sr_ioctl.c | 31 ++++-------------
sr_vendor.c | 18 +---------
st.c | 52 ++++++++++++++++-------------
6 files changed, 159 insertions(+), 152 deletions(-)

diff -Nru a/drivers/scsi/sd.c b/drivers/scsi/sd.c
--- a/drivers/scsi/sd.c Mon Sep 16 15:29:45 2002
+++ b/drivers/scsi/sd.c Mon Sep 16 15:29:45 2002
@@ -213,8 +213,9 @@
if(!loc)
return -EINVAL;

- host = sdp->host;
-
+ host = scsi_get_host(sdkp->device);
+ if (host == NULL)
+ return -ENODEV;
/* default to most commonly used values */

diskinfo[0] = 0x40; /* 1 << 6 */
@@ -397,8 +398,7 @@
nbuff, (rq_data_dir(SCpnt->request) == WRITE) ?
"writing" : "reading", this_count, SCpnt->request->nr_sectors));

- SCpnt->cmnd[1] = (SCpnt->device->scsi_level <= SCSI_2) ?
- ((SCpnt->lun << 5) & 0xe0) : 0;
+ SCpnt->cmnd[1] = 0;

if (((this_count > 0xff) || (block > 0x1fffff)) || SCpnt->device->ten) {
if (this_count > 0xffff)
@@ -463,6 +463,7 @@
**/
static int sd_open(struct inode *inode, struct file *filp)
{
+ struct Scsi_Host * shp;
int retval = -ENXIO;
Scsi_Device * sdp;
Scsi_Disk * sdkp;
@@ -485,8 +486,11 @@
* The following code can sleep.
* Module unloading must be prevented
*/
- if (sdp->host->hostt->module)
- __MOD_INC_USE_COUNT(sdp->host->hostt->module);
+ shp = scsi_get_host(sdp);
+ if (shp == NULL)
+ return -ENODEV;
+ if (shp->hostt->module)
+ __MOD_INC_USE_COUNT(shp->hostt->module);
if (sd_template.module)
__MOD_INC_USE_COUNT(sd_template.module);
sdp->access_count++;
@@ -530,8 +534,8 @@

error_out:
sdp->access_count--;
- if (sdp->host->hostt->module)
- __MOD_DEC_USE_COUNT(sdp->host->hostt->module);
+ if (shp->hostt->module)
+ __MOD_DEC_USE_COUNT(shp->hostt->module);
if (sd_template.module)
__MOD_DEC_USE_COUNT(sd_template.module);
return retval;
@@ -553,6 +557,7 @@
Scsi_Disk * sdkp;
int dsk_nr = DEVICE_NR(inode->i_rdev);
Scsi_Device * sdp;
+ struct Scsi_Host * shp;

SCSI_LOG_HLQUEUE(3, printk("sd_release: dsk_nr=%d, part_nr=%d\n",
dsk_nr, SD_PARTITION(inode->i_rdev)));
@@ -570,8 +575,9 @@
if (scsi_block_when_processing_errors(sdp))
scsi_ioctl(sdp, SCSI_IOCTL_DOORUNLOCK, NULL);
}
- if (sdp->host->hostt->module)
- __MOD_DEC_USE_COUNT(sdp->host->hostt->module);
+ shp = scsi_get_host(sdp);
+ if (shp && shp->hostt->module)
+ __MOD_DEC_USE_COUNT(shp->hostt->module);
if (sd_template.module)
__MOD_DEC_USE_COUNT(sd_template.module);
return 0;
@@ -809,8 +815,9 @@

while (retries < 3) {
cmd[0] = TEST_UNIT_READY;
- cmd[1] = (sdp->scsi_level <= SCSI_2) ?
- ((sdp->lun << 5) & 0xe0) : 0;
+ /*
+ * cmd[1] LUN is set in the mid-layer.
+ */
memset((void *) &cmd[2], 0, 8);

SRpnt->sr_cmd_len = 0;
@@ -845,9 +852,7 @@
printk(KERN_NOTICE "%s: Spinning up disk...",
diskname);
cmd[0] = START_STOP;
- cmd[1] = (sdp->scsi_level <= SCSI_2) ?
- ((sdp->lun << 5) & 0xe0) : 0;
- cmd[1] |= 1; /* Return immediately */
+ cmd[1] = 1; /* Return immediately */
memset((void *) &cmd[2], 0, 8);
cmd[4] = 1; /* Start spin cycle */
SRpnt->sr_cmd_len = 0;
@@ -894,9 +899,7 @@
retries = 3;
do {
cmd[0] = READ_CAPACITY;
- cmd[1] = (sdp->scsi_level <= SCSI_2) ?
- ((sdp->lun << 5) & 0xe0) : 0;
- memset((void *) &cmd[2], 0, 8);
+ memset((void *) &cmd[1], 0, 9);
memset((void *) buffer, 0, 8);

SRpnt->sr_cmd_len = 0;
@@ -1003,13 +1006,13 @@
}

static int
-sd_do_mode_sense6(Scsi_Device *sdp, Scsi_Request *SRpnt,
- int modepage, unsigned char *buffer, int len) {
+sd_do_mode_sense6(Scsi_Request *SRpnt, int modepage, unsigned char *buffer,
+ int len) {
unsigned char cmd[8];

memset((void *) &cmd[0], 0, 8);
cmd[0] = MODE_SENSE;
- cmd[1] = (sdp->scsi_level <= SCSI_2) ? ((sdp->lun << 5) & 0xe0) : 0;
+ cmd[1] = 0;
cmd[2] = modepage;
cmd[4] = len;

@@ -1030,7 +1033,6 @@
static void
sd_read_write_protect_flag(Scsi_Disk *sdkp, char *diskname,
Scsi_Request *SRpnt, unsigned char *buffer) {
- Scsi_Device *sdp = sdkp->device;
int res;

/*
@@ -1038,7 +1040,7 @@
* We have to start carefully: some devices hang if we ask
* for more than is available.
*/
- res = sd_do_mode_sense6(sdp, SRpnt, 0x3F, buffer, 4);
+ res = sd_do_mode_sense6(SRpnt, 0x3F, buffer, 4);

/*
* Second attempt: ask for page 0
@@ -1046,13 +1048,13 @@
* Sense Key 5: Illegal Request, Sense Code 24: Invalid field in CDB.
*/
if (res)
- res = sd_do_mode_sense6(sdp, SRpnt, 0, buffer, 4);
+ res = sd_do_mode_sense6(SRpnt, 0, buffer, 4);

/*
* Third attempt: ask 255 bytes, as we did earlier.
*/
if (res)
- res = sd_do_mode_sense6(sdp, SRpnt, 0x3F, buffer, 255);
+ res = sd_do_mode_sense6(SRpnt, 0x3F, buffer, 255);

if (res) {
printk(KERN_WARNING
@@ -1316,8 +1318,9 @@
return 1;
gd = &p->disk;

- SCSI_LOG_HLQUEUE(3, printk("sd_attach: scsi device: <%d,%d,%d,%d>\n",
- sdp->host->host_no, sdp->channel, sdp->id, sdp->lun));
+ SCSI_LOG_HLQUEUE(3, { printk("sd_attach: scsi_device: ");
+ scsi_paths_printk(sdp, ", ", "<%d,%d,%d,%d>");
+ printk("\n");});
if (sd_template.nr_dev >= sd_template.dev_max) {
sdp->attached--;
printk(KERN_ERR "sd_init: no more room for device\n");
@@ -1361,9 +1364,9 @@
gd->flags |= GENHD_FL_DRIVERFS | GENHD_FL_DEVFS;
sd_disks[dsk_nr] = gd;
sd_dskname(dsk_nr, diskname);
- printk(KERN_NOTICE "Attached scsi %sdisk %s at scsi%d, channel %d, "
- "id %d, lun %d\n", sdp->removable ? "removable " : "",
- diskname, sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
+ printk("Attached scsi %sdisk %s at: ",
+ sdp->removable ? "removable " : "", diskname);
+ scsi_paths_printk(sdp, "\t", "scsi%d, channel %d, id %d, lun %d\n");
return 0;
}

@@ -1398,9 +1401,9 @@
int dsk_nr;
unsigned long iflags;

- SCSI_LOG_HLQUEUE(3, printk("sd_detach: <%d,%d,%d,%d>\n",
- sdp->host->host_no, sdp->channel, sdp->id,
- sdp->lun));
+ SCSI_LOG_HLQUEUE(3, { printk("sd_detach: scsi_device: ");
+ scsi_paths_printk(sdp, ", ", "<%d,%d,%d,%d>");
+ printk("\n");});
write_lock_irqsave(&sd_dsk_arr_lock, iflags);
for (dsk_nr = 0; dsk_nr < sd_template.dev_max; dsk_nr++) {
sdkp = sd_dsk_arr[dsk_nr];
diff -Nru a/drivers/scsi/sg.c b/drivers/scsi/sg.c
--- a/drivers/scsi/sg.c Mon Sep 16 15:29:45 2002
+++ b/drivers/scsi/sg.c Mon Sep 16 15:29:45 2002
@@ -236,6 +236,7 @@
Sg_fd *sfp;
int res;
int retval = -EBUSY;
+ struct Scsi_Host * shp;

SCSI_LOG_TIMEOUT(3, printk("sg_open: dev=%d, flags=0x%x\n", dev, flags));
sdp = sg_get_dev(dev);
@@ -246,8 +247,11 @@

/* This driver's module count bumped by fops_get in <linux/fs.h> */
/* Prevent the device driver from vanishing while we sleep */
- if (sdp->device->host->hostt->module)
- __MOD_INC_USE_COUNT(sdp->device->host->hostt->module);
+ shp = scsi_get_host(sdp->device);
+ if (shp == NULL)
+ return -ENODEV;
+ if (shp->hostt->module)
+ __MOD_INC_USE_COUNT(shp->hostt->module);
sdp->device->access_count++;

if (!((flags & O_NONBLOCK) ||
@@ -288,7 +292,7 @@
}
if (!sdp->headfp) { /* no existing opens on this device */
sdp->sgdebug = 0;
- sdp->sg_tablesize = sdp->device->host->sg_tablesize;
+ sdp->sg_tablesize = shp->sg_tablesize;
}
if ((sfp = sg_add_sfp(sdp, dev)))
filp->private_data = sfp;
@@ -302,8 +306,8 @@

error_out:
sdp->device->access_count--;
- if ((!sdp->detached) && sdp->device->host->hostt->module)
- __MOD_DEC_USE_COUNT(sdp->device->host->hostt->module);
+ if ((!sdp->detached) && shp->hostt->module)
+ __MOD_DEC_USE_COUNT(shp->hostt->module);
return retval;
}

@@ -313,6 +317,7 @@
{
Sg_device *sdp;
Sg_fd *sfp;
+ struct Scsi_Host * shp;

if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
return -ENXIO;
@@ -320,10 +325,10 @@
sg_fasync(-1, filp, 0); /* remove filp from async notification list */
if (0 == sg_remove_sfp(sdp, sfp)) { /* Returns 1 when sdp gone */
if (!sdp->detached) {
+ shp = scsi_get_host(sdp->device);
sdp->device->access_count--;
- if (sdp->device->host->hostt->module)
- __MOD_DEC_USE_COUNT(sdp->device->host->hostt->
- module);
+ if (shp && shp->hostt->module)
+ __MOD_DEC_USE_COUNT(shp->hostt->module);
}
sdp->exclude = 0;
wake_up_interruptible(&sdp->o_excl_wait);
@@ -692,8 +697,11 @@
SRpnt->sr_sense_buffer[0] = 0;
SRpnt->sr_cmd_len = hp->cmd_len;
if (!(hp->flags & SG_FLAG_LUN_INHIBIT)) {
- if (sdp->device->scsi_level <= SCSI_2)
- cmnd[1] = (cmnd[1] & 0x1f) | (sdp->device->lun << 5);
+ /*
+ * XXX The mid-layer now sets cmnd[1] LUN, and we can't
+ * easily stop it. Maybe this is an artifact of setting
+ * the cmnd[1] LUN when it should not have been set?
+ */
}
SRpnt->sr_use_sg = srp->data.k_use_sg;
SRpnt->sr_sglist_len = srp->data.sglist_len;
@@ -740,6 +748,8 @@
Sg_fd *sfp;
Sg_request *srp;
unsigned long iflags;
+ struct Scsi_Host * shp;
+ struct scsi_path_id scsi_path;

if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
return -ENXIO;
@@ -808,7 +818,10 @@
} else {
if (sdp->detached)
return -ENODEV;
- sfp->low_dma = sdp->device->host->unchecked_isa_dma;
+ shp = scsi_get_host(sdp->device);
+ if (shp == NULL)
+ return -ENODEV;
+ sfp->low_dma = shp->unchecked_isa_dma;
}
return 0;
case SG_GET_LOW_DMA:
@@ -824,14 +837,15 @@

if (sdp->detached)
return -ENODEV;
- __put_user((int) sdp->device->host->host_no,
+ scsi_get_path(sdp->device, &scsi_path);
+ __put_user((int) scsi_path.spi_shpnt->host_no,
&sg_idp->host_no);
- __put_user((int) sdp->device->channel,
+ __put_user((int) scsi_path.spi_channel,
&sg_idp->channel);
- __put_user((int) sdp->device->id, &sg_idp->scsi_id);
- __put_user((int) sdp->device->lun, &sg_idp->lun);
+ __put_user((int) scsi_path.spi_id, &sg_idp->scsi_id);
+ __put_user((int) scsi_path.spi_lun, &sg_idp->lun);
__put_user((int) sdp->device->type, &sg_idp->scsi_type);
- __put_user((short) sdp->device->host->cmd_per_lun,
+ __put_user((short) scsi_path.spi_shpnt->cmd_per_lun,
&sg_idp->h_cmd_per_lun);
__put_user((short) sdp->device->queue_depth,
&sg_idp->d_queue_depth);
@@ -947,12 +961,18 @@
case SG_EMULATED_HOST:
if (sdp->detached)
return -ENODEV;
- return put_user(sdp->device->host->hostt->emulated, (int *) arg);
+ shp = scsi_get_host(sdp->device);
+ if (shp == NULL)
+ return -ENODEV;
+ return put_user(shp->hostt->emulated, (int *) arg);
case SG_SCSI_RESET:
if (sdp->detached)
return -ENODEV;
if (filp->f_flags & O_NONBLOCK) {
- if (sdp->device->host->in_recovery)
+ shp = scsi_get_host(sdp->device);
+ if (shp == NULL)
+ return -ENODEV;
+ if (shp->in_recovery)
return -EBUSY;
} else if (!scsi_block_when_processing_errors(sdp->device))
return -EBUSY;
@@ -1213,6 +1233,7 @@
Sg_device *sdp = NULL;
Sg_fd *sfp;
Sg_request *srp = NULL;
+ struct Scsi_Host * shp;

if (SCpnt && (SRpnt = SCpnt->sc_request))
srp = (Sg_request *) SRpnt->upper_private_data;
@@ -1288,8 +1309,9 @@
SCSI_LOG_TIMEOUT(1, printk("sg...bh: already closed, final cleanup\n"));
if (0 == sg_remove_sfp(sdp, sfp)) { /* device still present */
sdp->device->access_count--;
- if (sdp->device->host->hostt->module)
- __MOD_DEC_USE_COUNT(sdp->device->host->hostt->module);
+ shp = scsi_get_host(sdp->device);
+ if (shp && shp->hostt->module)
+ __MOD_DEC_USE_COUNT(shp->hostt->module);
}
if (sg_template.module)
__MOD_DEC_USE_COUNT(sg_template.module);
@@ -1414,6 +1436,7 @@
Sg_device *sdp;
unsigned long iflags;
int k;
+ struct Scsi_Host * shp;

write_lock_irqsave(&sg_dev_arr_lock, iflags);
if (sg_template.nr_dev >= sg_template.dev_max) { /* try to resize */
@@ -1443,11 +1466,10 @@
if (k > SG_MAX_DEVS_MASK) {
scsidp->attached--;
write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
- printk(KERN_WARNING
- "Unable to attach sg device <%d, %d, %d, %d>"
- " type=%d, minor number exceed %d\n",
- scsidp->host->host_no, scsidp->channel, scsidp->id,
- scsidp->lun, scsidp->type, SG_MAX_DEVS_MASK);
+ printk(KERN_WARNING "Unable to attach sg device ");
+ scsi_paths_printk(scsidp, " ", "<%d, %d, %d, %d>");
+ printk(KERN_WARNING " type=%d, minor number exceed %d\n",
+ scsidp->type, SG_MAX_DEVS_MASK);
return 1;
}
if (k < sg_template.dev_max)
@@ -1468,7 +1490,8 @@
sdp->exclude = 0;
sdp->sgdebug = 0;
sdp->detached = 0;
- sdp->sg_tablesize = scsidp->host ? scsidp->host->sg_tablesize : 0;
+ shp = scsi_get_host(sdp->device);
+ sdp->sg_tablesize = shp ? shp->sg_tablesize : 0;
sdp->i_rdev = mk_kdev(SCSI_GENERIC_MAJOR, k);

memset(&sdp->sg_driverfs_dev, 0, sizeof (struct device));
@@ -1497,11 +1520,10 @@
case TYPE_TAPE:
break;
default:
- printk(KERN_NOTICE
- "Attached scsi generic sg%d at scsi%d, channel"
- " %d, id %d, lun %d, type %d\n", k,
- scsidp->host->host_no, scsidp->channel, scsidp->id,
- scsidp->lun, scsidp->type);
+ printk(KERN_NOTICE "Attached scsi generic sg%d type %d at ",
+ k, scsidp->type);
+ scsi_paths_printk(scsidp, KERN_NOTICE " ",
+ "scsi%d, channel %d, id %d, lun %d\n");
}
return 0;
}
@@ -1516,6 +1538,7 @@
Sg_request *srp;
Sg_request *tsrp;
int k, delay;
+ struct Scsi_Host * shp;

if (NULL == sg_dev_arr)
return;
@@ -1538,10 +1561,10 @@
sdp->device->access_count--;
if (sg_template.module)
__MOD_DEC_USE_COUNT(sg_template.module);
- if (sdp->device->host->hostt->module)
+ shp = scsi_get_host(sdp->device);
+ if (shp->hostt->module)
__MOD_DEC_USE_COUNT(
- sdp->device->host->
- hostt->module);
+ shp->hostt->module);
__sg_remove_sfp(sdp, sfp);
} else {
delay = 1;
@@ -1631,13 +1654,17 @@
int dxfer_dir = hp->dxfer_direction;
Sg_scatter_hold *req_schp = &srp->data;
Sg_scatter_hold *rsv_schp = &sfp->reserve;
+ struct Scsi_Host * shp;

SCSI_LOG_TIMEOUT(4, printk("sg_start_req: dxfer_len=%d\n", dxfer_len));
if ((dxfer_len <= 0) || (dxfer_dir == SG_DXFER_NONE))
return 0;
+ shp = scsi_get_host(sfp->parentdp->device);
+ if (shp == NULL)
+ return 0;
if (sg_allow_dio && (hp->flags & SG_FLAG_DIRECT_IO) &&
(dxfer_dir != SG_DXFER_UNKNOWN) && (0 == hp->iovec_count) &&
- (!sfp->parentdp->device->host->unchecked_isa_dma)) {
+ (!shp->unchecked_isa_dma)) {
res = sg_build_direct(srp, sfp, dxfer_len);
if (res <= 0) /* -ve -> error, 0 -> done, 1 -> try indirect */
return res;
@@ -2422,6 +2449,7 @@
{
Sg_fd *sfp;
unsigned long iflags;
+ struct Scsi_Host * shp;

sfp = (Sg_fd *) sg_page_malloc(sizeof (Sg_fd), 0, 0);
if (!sfp)
@@ -2432,8 +2460,9 @@

sfp->timeout = SG_DEFAULT_TIMEOUT;
sfp->force_packid = SG_DEF_FORCE_PACK_ID;
+ shp = scsi_get_host(sdp->device);
sfp->low_dma = (SG_DEF_FORCE_LOW_DMA == 0) ?
- sdp->device->host->unchecked_isa_dma : 1;
+ ((shp == NULL) ? 1 : shp->unchecked_isa_dma) : 1;
sfp->cmd_q = SG_DEF_COMMAND_Q;
sfp->keep_orphan = SG_DEF_KEEP_ORPHAN;
sfp->parentdp = sdp;
@@ -2493,6 +2522,7 @@
Sg_request *tsrp;
int dirty = 0;
int res = 0;
+ struct Scsi_Host * shp;

for (srp = sfp->headrp; srp; srp = tsrp) {
tsrp = srp->nextrp;
@@ -2526,8 +2556,9 @@
/* MOD_INC's to inhibit unloading sg and associated adapter driver */
if (sg_template.module)
__MOD_INC_USE_COUNT(sg_template.module);
- if (sdp->device->host->hostt->module)
- __MOD_INC_USE_COUNT(sdp->device->host->hostt->module);
+ shp = scsi_get_host(sdp->device);
+ if (shp && shp->hostt->module)
+ __MOD_INC_USE_COUNT(shp->hostt->module);
SCSI_LOG_TIMEOUT(1, printk("sg_remove_sfp: worrisome, %d writes pending\n",
dirty));
}
diff -Nru a/drivers/scsi/sr.c b/drivers/scsi/sr.c
--- a/drivers/scsi/sr.c Mon Sep 16 15:29:45 2002
+++ b/drivers/scsi/sr.c Mon Sep 16 15:29:45 2002
@@ -98,13 +98,15 @@

static void sr_release(struct cdrom_device_info *cdi)
{
+ struct Scsi_Host *shp;
Scsi_CD *cd = cdi->handle;

if (cd->device->sector_size > 2048)
sr_set_blocklength(cd, 2048);
cd->device->access_count--;
- if (cd->device->host->hostt->module)
- __MOD_DEC_USE_COUNT(cd->device->host->hostt->module);
+ shp = scsi_get_host(cd->device);
+ if (shp && shp->hostt->module)
+ __MOD_DEC_USE_COUNT(shp->hostt->module);
if (sr_template.module)
__MOD_DEC_USE_COUNT(sr_template.module);
}
@@ -336,8 +338,7 @@
(rq_data_dir(SCpnt->request) == WRITE) ? "writing" : "reading",
this_count, SCpnt->request->nr_sectors));

- SCpnt->cmnd[1] = (SCpnt->device->scsi_level <= SCSI_2) ?
- ((SCpnt->lun << 5) & 0xe0) : 0;
+ SCpnt->cmnd[1] = 0;

block = SCpnt->request->sector / (s_size >> 9);

@@ -399,6 +400,7 @@

static int sr_open(struct cdrom_device_info *cdi, int purpose)
{
+ struct Scsi_Host *shp;
Scsi_CD *cd = cdi->handle;

if (!cd->device)
@@ -411,8 +413,9 @@
return -ENXIO;
}
cd->device->access_count++;
- if (cd->device->host->hostt->module)
- __MOD_INC_USE_COUNT(cd->device->host->hostt->module);
+ shp = scsi_get_host(cd->device);
+ if (shp && shp->hostt->module)
+ __MOD_INC_USE_COUNT(shp->hostt->module);
if (sr_template.module)
__MOD_INC_USE_COUNT(sr_template.module);

@@ -462,8 +465,8 @@
if (sr_template.nr_dev > sr_template.dev_max)
panic("scsi_devices corrupt (sr)");

- printk("Attached scsi CD-ROM %s at scsi%d, channel %d, id %d, lun %d\n",
- scsi_CDs[i].cdi.name, SDp->host->host_no, SDp->channel, SDp->id, SDp->lun);
+ printk("Attached scsi CD-ROM %s at: ", scsi_CDs[i].cdi.name);
+ scsi_paths_printk(SDp, " ", "scsi%d, channel %d, id %d, lun %d\n");
return 0;
}

@@ -486,9 +489,7 @@

do {
cmd[0] = READ_CAPACITY;
- cmd[1] = (cd->device->scsi_level <= SCSI_2) ?
- ((cd->device->lun << 5) & 0xe0) : 0;
- memset((void *) &cmd[2], 0, 8);
+ memset((void *) &cmd[1], 0, 9);
SRpnt->sr_request->rq_status = RQ_SCSI_BUSY; /* Mark as really busy */
SRpnt->sr_cmd_len = 0;

@@ -598,8 +599,7 @@
return;
}
cmd[0] = MODE_SENSE;
- cmd[1] = (cd->device->scsi_level <= SCSI_2) ?
- ((cd->device->lun << 5) & 0xe0) : 0;
+ cmd[1] = 0;
cmd[2] = 0x2a;
cmd[4] = 128;
cmd[3] = cmd[5] = 0;
@@ -673,15 +673,7 @@
*/
static int sr_packet(struct cdrom_device_info *cdi, struct cdrom_generic_command *cgc)
{
- Scsi_CD *cd = cdi->handle;
- Scsi_Device *device = cd->device;
-
- /* set the LUN */
- if (device->scsi_level <= SCSI_2)
- cgc->cmd[1] |= device->lun << 5;
-
cgc->stat = sr_do_ioctl(cdi->handle, cgc->cmd, cgc->buffer, cgc->buflen, cgc->quiet, cgc->data_direction, cgc->sense);
-
return cgc->stat;
}

diff -Nru a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c
--- a/drivers/scsi/sr_ioctl.c Mon Sep 16 15:29:45 2002
+++ b/drivers/scsi/sr_ioctl.c Mon Sep 16 15:29:45 2002
@@ -79,6 +79,7 @@
Scsi_Request *SRpnt;
Scsi_Device *SDev;
struct request *req;
+ struct Scsi_Host *SHpnt;
int result, err = 0, retries = 0;
char *bounce_buffer;

@@ -92,7 +93,8 @@

/* use ISA DMA buffer if necessary */
SRpnt->sr_request->buffer = buffer;
- if (buffer && SRpnt->sr_host->unchecked_isa_dma &&
+ SHpnt = scsi_get_host(SDev);
+ if (buffer && SHpnt && SHpnt->unchecked_isa_dma &&
(virt_to_phys(buffer) + buflength - 1 > ISA_DMA_THRESHOLD)) {
bounce_buffer = (char *) kmalloc(buflength, GFP_DMA);
if (bounce_buffer == NULL) {
@@ -194,9 +196,7 @@
u_char sr_cmd[10];

sr_cmd[0] = GPCMD_TEST_UNIT_READY;
- sr_cmd[1] = (cd->device->scsi_level <= SCSI_2) ?
- ((cd->device->lun) << 5) : 0;
- sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0;
+ sr_cmd[1] = sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0;
return sr_do_ioctl(cd, sr_cmd, NULL, 0, 1, SCSI_DATA_NONE, NULL);
}

@@ -206,9 +206,7 @@
u_char sr_cmd[10];

sr_cmd[0] = GPCMD_START_STOP_UNIT;
- sr_cmd[1] = (cd->device->scsi_level <= SCSI_2) ?
- ((cd->device->lun) << 5) : 0;
- sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0;
+ sr_cmd[1] = sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0;
sr_cmd[4] = (pos == 0) ? 0x03 /* close */ : 0x02 /* eject */ ;

return sr_do_ioctl(cd, sr_cmd, NULL, 0, 0, SCSI_DATA_NONE, NULL);
@@ -283,8 +281,7 @@
int result;

sr_cmd[0] = GPCMD_READ_SUBCHANNEL;
- sr_cmd[1] = (cd->device->scsi_level <= SCSI_2) ?
- ((cd->device->lun) << 5) : 0;
+ sr_cmd[1] = 0;
sr_cmd[2] = 0x40; /* I do want the subchannel info */
sr_cmd[3] = 0x02; /* Give me medium catalog number info */
sr_cmd[4] = sr_cmd[5] = 0;
@@ -318,8 +315,6 @@

memset(sr_cmd, 0, MAX_COMMAND_SIZE);
sr_cmd[0] = GPCMD_SET_SPEED; /* SET CD SPEED */
- sr_cmd[1] = (cd->device->scsi_level <= SCSI_2) ?
- ((cd->device->lun) << 5) : 0;
sr_cmd[2] = (speed >> 8) & 0xff; /* MSB for speed (in kbytes/sec) */
sr_cmd[3] = speed & 0xff; /* LSB */

@@ -349,8 +344,6 @@
struct cdrom_tochdr *tochdr = (struct cdrom_tochdr *) arg;

sr_cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
- sr_cmd[1] = (cd->device->scsi_level <= SCSI_2) ?
- ((cd->device->lun) << 5) : 0;
sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0;
sr_cmd[8] = 12; /* LSB of length */

@@ -367,9 +360,7 @@
struct cdrom_tocentry *tocentry = (struct cdrom_tocentry *) arg;

sr_cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
- sr_cmd[1] = (cd->device->scsi_level <= SCSI_2) ?
- ((cd->device->lun) << 5) : 0;
- sr_cmd[1] |= (tocentry->cdte_format == CDROM_MSF) ? 0x02 : 0;
+ sr_cmd[1] = (tocentry->cdte_format == CDROM_MSF) ? 0x02 : 0;
sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0;
sr_cmd[6] = tocentry->cdte_track;
sr_cmd[8] = 12; /* LSB of length */
@@ -394,8 +385,6 @@
struct cdrom_ti* ti = (struct cdrom_ti*)arg;

sr_cmd[0] = GPCMD_PLAYAUDIO_TI;
- sr_cmd[1] = (cd->device->scsi_level <= SCSI_2) ?
- (cd->device->lun << 5) : 0;
sr_cmd[4] = ti->cdti_trk0;
sr_cmd[5] = ti->cdti_ind0;
sr_cmd[7] = ti->cdti_trk1;
@@ -445,9 +434,7 @@

memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = GPCMD_READ_CD; /* READ_CD */
- cmd[1] = (cd->device->scsi_level <= SCSI_2) ?
- (cd->device->lun << 5) : 0;
- cmd[1] |= ((format & 7) << 2);
+ cmd[1] = ((format & 7) << 2);
cmd[2] = (unsigned char) (lba >> 24) & 0xff;
cmd[3] = (unsigned char) (lba >> 16) & 0xff;
cmd[4] = (unsigned char) (lba >> 8) & 0xff;
@@ -499,8 +486,6 @@

memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = GPCMD_READ_10;
- cmd[1] = (cd->device->scsi_level <= SCSI_2) ?
- (cd->device->lun << 5) : 0;
cmd[2] = (unsigned char) (lba >> 24) & 0xff;
cmd[3] = (unsigned char) (lba >> 16) & 0xff;
cmd[4] = (unsigned char) (lba >> 8) & 0xff;
diff -Nru a/drivers/scsi/sr_vendor.c b/drivers/scsi/sr_vendor.c
--- a/drivers/scsi/sr_vendor.c Mon Sep 16 15:29:45 2002
+++ b/drivers/scsi/sr_vendor.c Mon Sep 16 15:29:45 2002
@@ -122,9 +122,7 @@
#endif
memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = MODE_SELECT;
- cmd[1] = (cd->device->scsi_level <= SCSI_2) ?
- (cd->device->lun << 5) : 0;
- cmd[1] |= (1 << 4);
+ cmd[1] = (1 << 4);
cmd[4] = 12;
modesel = (struct ccs_modesel_head *) buffer;
memset(modesel, 0, sizeof(*modesel));
@@ -173,8 +171,6 @@
case VENDOR_SCSI3:
memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = READ_TOC;
- cmd[1] = (cd->device->scsi_level <= SCSI_2) ?
- (cd->device->lun << 5) : 0;
cmd[8] = 12;
cmd[9] = 0x40;
rc = sr_do_ioctl(cd, cmd, buffer, 12, 1, SCSI_DATA_READ, NULL);
@@ -199,9 +195,7 @@
unsigned long min, sec, frame;
memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = 0xde;
- cmd[1] = (cd->device->scsi_level <= SCSI_2) ?
- (cd->device->lun << 5) : 0;
- cmd[1] |= 0x03;
+ cmd[1] = 0x03;
cmd[2] = 0xb0;
rc = sr_do_ioctl(cd, cmd, buffer, 0x16, 1, SCSI_DATA_READ, NULL);
if (rc != 0)
@@ -227,9 +221,7 @@
* where starts the last session ?) */
memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = 0xc7;
- cmd[1] = (cd->device->scsi_level <= SCSI_2) ?
- (cd->device->lun << 5) : 0;
- cmd[1] |= 0x03;
+ cmd[1] = 0x03;
rc = sr_do_ioctl(cd, cmd, buffer, 4, 1, SCSI_DATA_READ, NULL);
if (rc == -EINVAL) {
printk(KERN_INFO "%s: Hmm, seems the drive "
@@ -253,8 +245,6 @@
case VENDOR_WRITER:
memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = READ_TOC;
- cmd[1] = (cd->device->scsi_level <= SCSI_2) ?
- (cd->device->lun << 5) : 0;
cmd[8] = 0x04;
cmd[9] = 0x40;
rc = sr_do_ioctl(cd, cmd, buffer, 0x04, 1, SCSI_DATA_READ, NULL);
@@ -267,8 +257,6 @@
break;
}
cmd[0] = READ_TOC; /* Read TOC */
- cmd[1] = (cd->device->scsi_level <= SCSI_2) ?
- (cd->device->lun << 5) : 0;
cmd[6] = rc & 0x7f; /* number of last session */
cmd[8] = 0x0c;
cmd[9] = 0x40;
diff -Nru a/drivers/scsi/st.c b/drivers/scsi/st.c
--- a/drivers/scsi/st.c Mon Sep 16 15:29:45 2002
+++ b/drivers/scsi/st.c Mon Sep 16 15:29:45 2002
@@ -379,8 +379,6 @@
}
}

- if (SRpnt->sr_device->scsi_level <= SCSI_2)
- cmd[1] |= (SRpnt->sr_device->lun << 5) & 0xe0;
init_completion(&STp->wait);
SRpnt->sr_use_sg = STp->buffer->do_dio || (bytes > (STp->buffer)->frp[0].length);
if (SRpnt->sr_use_sg) {
@@ -929,6 +927,7 @@
Scsi_Tape *STp;
ST_partstat *STps;
int dev = TAPE_NR(inode->i_rdev);
+ struct Scsi_Host *SHpnt;

write_lock(&st_dev_arr_lock);
if (dev >= st_template.dev_max || scsi_tapes == NULL ||
@@ -946,8 +945,11 @@
write_unlock(&st_dev_arr_lock);
STp->rew_at_close = STp->autorew_dev = (minor(inode->i_rdev) & 0x80) == 0;

- if (STp->device->host->hostt->module)
- __MOD_INC_USE_COUNT(STp->device->host->hostt->module);
+ SHpnt = scsi_get_host(STp->device);
+ if (!SHpnt)
+ return (-ENXIO);
+ if (SHpnt->hostt->module)
+ __MOD_INC_USE_COUNT(SHpnt->hostt->module);
STp->device->access_count++;

if (!scsi_block_when_processing_errors(STp->device)) {
@@ -990,8 +992,8 @@
normalize_buffer(STp->buffer);
STp->in_use = 0;
STp->device->access_count--;
- if (STp->device->host->hostt->module)
- __MOD_DEC_USE_COUNT(STp->device->host->hostt->module);
+ if (SHpnt->hostt->module)
+ __MOD_DEC_USE_COUNT(SHpnt->hostt->module);
return retval;

}
@@ -1126,6 +1128,7 @@
{
int result = 0;
Scsi_Tape *STp;
+ struct Scsi_Host *SHpnt;

kdev_t devt = inode->i_rdev;
int dev;
@@ -1143,8 +1146,9 @@
STp->in_use = 0;
write_unlock(&st_dev_arr_lock);
STp->device->access_count--;
- if (STp->device->host->hostt->module)
- __MOD_DEC_USE_COUNT(STp->device->host->hostt->module);
+ SHpnt = scsi_get_host(STp->device);
+ if (SHpnt && SHpnt->hostt->module)
+ __MOD_DEC_USE_COUNT(SHpnt->hostt->module);

return result;
}
@@ -3659,22 +3663,26 @@
ST_buffer *buffer;
int i, mode, dev_num;
char *stp;
+ struct Scsi_Host *SHpnt;
u64 bounce_limit;

if (SDp->type != TYPE_TAPE)
return 1;
if ((stp = st_incompatible(SDp))) {
- printk(KERN_INFO
- "st: Found incompatible tape at scsi%d, channel %d, id %d, lun %d\n",
- SDp->host->host_no, SDp->channel, SDp->id, SDp->lun);
+ printk(KERN_INFO "st: Found incompatible tape at ");
+ scsi_paths_printk(SDp, KERN_INFO "\t",
+ "scsi%d, channel %d, id %d, lun %d\n");
printk(KERN_INFO "st: The suggested driver is %s.\n", stp);
return 1;
}

- i = SDp->host->sg_tablesize;
+ SHpnt = scsi_get_host(SDp);
+ if (SHpnt == NULL)
+ return 1;
+ i = SHpnt->sg_tablesize;
if (st_max_sg_segs < i)
i = st_max_sg_segs;
- buffer = new_tape_buffer(TRUE, (SDp->host)->unchecked_isa_dma, i);
+ buffer = new_tape_buffer(TRUE, (SHpnt)->unchecked_isa_dma, i);
if (buffer == NULL) {
printk(KERN_ERR "st: Can't allocate new tape buffer. Device not attached.\n");
return 1;
@@ -3796,7 +3804,7 @@
tpnt->dirty = 0;
tpnt->in_use = 0;
tpnt->drv_buffer = 1; /* Try buffering if no mode sense */
- tpnt->restr_dma = (SDp->host)->unchecked_isa_dma;
+ tpnt->restr_dma = SHpnt->unchecked_isa_dma;
tpnt->use_pf = (SDp->scsi_level >= SCSI_2);
tpnt->density = 0;
tpnt->do_auto_lock = ST_AUTO_LOCK;
@@ -3814,17 +3822,17 @@
tpnt->timeout = ST_TIMEOUT;
tpnt->long_timeout = ST_LONG_TIMEOUT;

- tpnt->try_dio = try_direct_io && !SDp->host->unchecked_isa_dma;
+ tpnt->try_dio = try_direct_io && !SHpnt->unchecked_isa_dma;
bounce_limit = BLK_BOUNCE_HIGH; /* Borrowed from scsi_merge.c */
- if (SDp->host->highmem_io) {
+ if (SHpnt->highmem_io) {
if (!PCI_DMA_BUS_IS_PHYS)
/* Platforms with virtual-DMA translation
* hardware have no practical limit.
*/
bounce_limit = BLK_BOUNCE_ANY;
- else if (SDp->host->pci_dev)
- bounce_limit = SDp->host->pci_dev->dma_mask;
- } else if (SDp->host->unchecked_isa_dma)
+ else if (SHpnt->pci_dev)
+ bounce_limit = SHpnt->pci_dev->dma_mask;
+ } else if (SHpnt->unchecked_isa_dma)
bounce_limit = BLK_BOUNCE_ISA;
bounce_limit >>= PAGE_SHIFT;
if (bounce_limit > ULONG_MAX)
@@ -3863,9 +3871,9 @@

st_template.nr_dev++;
write_unlock(&st_dev_arr_lock);
- printk(KERN_WARNING
- "Attached scsi tape st%d at scsi%d, channel %d, id %d, lun %d\n",
- dev_num, SDp->host->host_no, SDp->channel, SDp->id, SDp->lun);
+ printk(KERN_WARNING "Attached scsi tape st%d at ", dev_num);
+ scsi_paths_printk(SDp, KERN_WARNING "\t",
+ "scsi%d, channel %d, id %d, lun %d\n");
printk(KERN_WARNING "st%d: try direct i/o: %s, max page reachable by HBA %lu\n",
dev_num, tpnt->try_dio ? "yes" : "no", tpnt->max_pfn);

2002-09-17 22:53:22

by Patrick Mansfield

[permalink] [raw]
Subject: [RFC] [PATCH] 7/7 2.5.35 SCSI multi-path

Patch to add scsi midlayer multi-path support.

Ideally, the code not compiled when CONFIG_SCSI_MULTI_PATH_IO is undefined
would be removed.

Config.help | 20
Config.in | 8
Makefile | 7
hosts.c | 2
hosts.h | 8
ips.c | 5
scsi.c | 40 +
scsi.h | 32 +
scsi_error.c | 33 -
scsi_lib.c | 106 ++++
scsi_paths.c | 1486 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
scsi_paths.h | 152 ++++++
scsi_scan.c | 29 +
sg.c | 13
14 files changed, 1925 insertions(+), 16 deletions(-)

diff -Nru a/drivers/scsi/Config.help b/drivers/scsi/Config.help
--- a/drivers/scsi/Config.help Mon Sep 16 16:11:26 2002
+++ b/drivers/scsi/Config.help Mon Sep 16 16:11:26 2002
@@ -152,6 +152,26 @@
REPORT LUNS scanning is done only for SCSI-3 devices. Most users can safely
answer N here.

+CONFIG_SCSI_MULTI_PATH_IO
+ If your system contains SCSI host adapters connecting into a shared
+ transport or you have storage devices that have multiple ports say Y
+ here to enable the multiple paths/ports to be merged into one mid layer
+ SCSI device.
+
+ If you are unsure if your system is configured to support this
+ capability or that your device has this capability say N.
+
+CONFIG_SCSI_PATH_POLICY_LPU
+ When CONFIG_SCSI_MULTI_PATH_IO is selected this option sets the path
+ selection policy.
+
+ The last path used policy only uses another path after a path failure.
+
+ The round robin policy round robins across all active paths.
+
+ The last path used policy is the safest path policy to select if you
+ cannot determine the best path policy for your storage device.
+
CONFIG_SCSI_CONSTANTS
The error messages regarding your SCSI hardware will be easier to
understand if you say Y here; it will enlarge your kernel by about
diff -Nru a/drivers/scsi/Config.in b/drivers/scsi/Config.in
--- a/drivers/scsi/Config.in Mon Sep 16 16:11:26 2002
+++ b/drivers/scsi/Config.in Mon Sep 16 16:11:26 2002
@@ -22,6 +22,14 @@

bool ' Probe all LUNs on each SCSI device' CONFIG_SCSI_MULTI_LUN
bool ' Build with SCSI REPORT LUNS support' CONFIG_SCSI_REPORT_LUNS
+
+bool ' Enable Mid Layer Multi-Path IO support' CONFIG_SCSI_MULTI_PATH_IO
+if [ "$CONFIG_SCSI_MULTI_PATH_IO" != "n" ] ; then
+ choice ' Multipath Routing algorithim' \
+ "LastPathUsed CONFIG_SCSI_PATH_POLICY_LPU \
+ RoundRobin CONFIG_SCSI_PATH_POLICY_RR" \
+ LastPathUsed
+fi

bool ' Verbose SCSI error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS
bool ' SCSI logging facility' CONFIG_SCSI_LOGGING
diff -Nru a/drivers/scsi/Makefile b/drivers/scsi/Makefile
--- a/drivers/scsi/Makefile Mon Sep 16 16:11:26 2002
+++ b/drivers/scsi/Makefile Mon Sep 16 16:11:26 2002
@@ -122,6 +122,11 @@
scsi_mod-objs := scsi.o hosts.o scsi_ioctl.o constants.o scsicam.o \
scsi_proc.o scsi_error.o scsi_queue.o scsi_lib.o \
scsi_merge.o scsi_scan.o scsi_syms.o
+
+ifeq ($(CONFIG_SCSI_MULTI_PATH_IO),y)
+scsi_mod-objs += scsi_paths.o
+endif
+
sd_mod-objs := sd.o
sr_mod-objs := sr.o sr_ioctl.o sr_vendor.o
initio-objs := ini9100u.o i91uscsi.o
@@ -159,4 +164,4 @@
$(obj)/53c700_d.h: $(src)/53c700.scr $(src)/script_asm.pl
$(PERL) -s $(src)/script_asm.pl -ncr7x0_family $@ $(@:_d.h=_u.h) < $<

-endif
\ No newline at end of file
+endif
diff -Nru a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
--- a/drivers/scsi/hosts.c Mon Sep 16 16:11:26 2002
+++ b/drivers/scsi/hosts.c Mon Sep 16 16:11:26 2002
@@ -181,7 +181,9 @@
retval->host_no = shn->host_no;
}
next_scsi_host++;
+#ifndef CONFIG_SCSI_MULTI_PATH_IO
retval->host_queue = NULL;
+#endif
init_waitqueue_head(&retval->host_wait);
retval->resetting = 0;
retval->last_reset = 0;
diff -Nru a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h
--- a/drivers/scsi/hosts.h Mon Sep 16 16:11:26 2002
+++ b/drivers/scsi/hosts.h Mon Sep 16 16:11:26 2002
@@ -316,7 +316,9 @@
* struct private is a way of marking it in a sort of C++ type of way.
*/
struct Scsi_Host * next;
+#ifndef CONFIG_SCSI_MULTI_PATH_IO
Scsi_Device * host_queue;
+#endif

spinlock_t default_lock;
spinlock_t *host_lock;
@@ -576,6 +578,11 @@
#define SD_EXTRA_DEVS CONFIG_SD_EXTRA_DEVS
#define SR_EXTRA_DEVS CONFIG_SR_EXTRA_DEVS

+#ifndef CONFIG_SCSI_MULTI_PATH_IO
+/*
+ * Use scsi_locate_sdev if CONFIG_SCSI_MULTI_PATH_IO
+ */
+
/**
* scsi_find_device - find a device given the host
* @channel: SCSI channel (zero if only one channel)
@@ -594,6 +601,7 @@
break;
return SDpnt;
}
+#endif

/*
* XXX For now, check for the existence of the NUMA topology patch via the
diff -Nru a/drivers/scsi/ips.c b/drivers/scsi/ips.c
--- a/drivers/scsi/ips.c Mon Sep 16 16:11:26 2002
+++ b/drivers/scsi/ips.c Mon Sep 16 16:11:26 2002
@@ -1911,6 +1911,10 @@
/****************************************************************************/
static void
ips_select_queue_depth(struct Scsi_Host *host, Scsi_Device *scsi_devs) {
+#ifndef CONFIG_SCSI_MULTI_PATH_IO
+ /*
+ * This function is not called for multi-path IO.
+ */
Scsi_Device *device;
ips_ha_t *ha;
int count = 0;
@@ -1941,6 +1945,7 @@
device->queue_depth = 2;
}
}
+#endif
}

/****************************************************************************/
diff -Nru a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
--- a/drivers/scsi/scsi.c Mon Sep 16 16:11:26 2002
+++ b/drivers/scsi/scsi.c Mon Sep 16 16:11:26 2002
@@ -413,6 +413,10 @@
* allow us to more easily figure out whether we should
* do anything here or not.
*/
+ /*
+ * single_lun devices are not allowed to
+ * have multiple paths, see scsi_paths.c.
+ */
scsi_for_each_sdev_lun(&STrav_hndl, SDpnt,
host->host_no,
path_p->spi_channel,
@@ -1422,10 +1426,12 @@
if (NULL == SCpnt)
break; /* If not, the next line will oops ... */
memset(SCpnt, 0, sizeof(Scsi_Cmnd));
+#ifndef CONFIG_SCSI_MULTI_PATH_IO
SCpnt->host = host;
SCpnt->target = SDpnt->id;
SCpnt->lun = SDpnt->lun;
SCpnt->channel = SDpnt->channel;
+#endif
SCpnt->device = SDpnt;
SCpnt->request = NULL;
SCpnt->use_sg = 0;
@@ -1871,9 +1877,16 @@
/* first register parent with driverfs */
device_register(&shpnt->host_driverfs_dev);
scan_scsis(shpnt, 0, 0, 0, 0);
+#ifndef CONFIG_SCSI_MULTI_PATH_IO
+ /*
+ * XXX for multi-path IO, rely on
+ * slave_attach (code not yet present
+ * in scsi_scan.c).
+ */
if (shpnt->select_queue_depths != NULL) {
(shpnt->select_queue_depths) (shpnt, shpnt->host_queue);
}
+#endif
}
}

@@ -1886,6 +1899,12 @@
* Next we create the Scsi_Cmnd structures for this host
*/
scsi_for_all_sdevs(&STrav_hndl, SDpnt) {
+ /*
+ * As long as we only support multiple paths that
+ * all use the same adapter and driver, the attach
+ * function will only be called once for each
+ * SDpnt.
+ */
shpnt = scsi_get_host(SDpnt);
if (shpnt && shpnt->hostt == tpnt) {
for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
@@ -2063,9 +2082,14 @@
SCSI_FIND_ALL_CHANNEL,
SCSI_FIND_ALL_ID,
SCSI_FIND_ALL_LUN);
- scsi_release_commandblocks(SDpnt);
- blk_cleanup_queue(&SDpnt->request_queue);
- scsi_remove_scsi_device(SDpnt);
+#ifdef CONFIG_SCSI_MULTI_PATH_IO
+ if (SDpnt->sdev_paths == NULL )
+#endif
+ {
+ scsi_release_commandblocks(SDpnt);
+ blk_cleanup_queue(&SDpnt->request_queue);
+ scsi_remove_scsi_device(SDpnt);
+ }
}
}

@@ -2497,6 +2521,9 @@
return -ENOMEM;
}
generic->write_proc = proc_scsi_gen_write;
+#ifdef CONFIG_SCSI_MULTI_PATH_IO
+ scsi_paths_proc_init();
+#endif
#endif

scsi_devfs_handle = devfs_mk_dir (NULL, "scsi", NULL);
@@ -2529,6 +2556,9 @@
kfree (shn2);

#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SCSI_MULTI_PATH_IO
+ scsi_paths_proc_cleanup();
+#endif
/* No, we're not here anymore. Don't show the /proc/scsi files. */
remove_proc_entry ("scsi/scsi", 0);
remove_proc_entry ("scsi", 0);
@@ -2684,6 +2714,10 @@
SCpnt->request = &req;
memset(&SCpnt->eh_timeout, 0, sizeof(SCpnt->eh_timeout));
scsi_get_path(dev, &scsi_path);
+ /*
+ * XXX this is broken if we have multiple paths and we want to
+ * reset the bus or the adapter on all paths to dev.
+ */
SCpnt->host = scsi_path.spi_shpnt;
SCpnt->device = dev;
SCpnt->target = scsi_path.spi_id;
diff -Nru a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h
--- a/drivers/scsi/scsi.h Mon Sep 16 16:11:26 2002
+++ b/drivers/scsi/scsi.h Mon Sep 16 16:11:26 2002
@@ -515,6 +515,10 @@
int timeout, int retries);
extern int scsi_dev_init(void);

+/*
+ * The following is in scsi_paths.c or scsi_lib.c depending
+ * CONFIG_SCSI_MULTI_PATH_IO.
+ */
extern int scsi_paths_proc_print_paths(Scsi_Device *SDpnt, char *buffer,
char *format);

@@ -604,10 +608,14 @@
volatile unsigned short device_busy; /* commands actually active on low-level */
Scsi_Cmnd *device_queue; /* queue of SCSI Command structures */
Scsi_Cmnd *current_cmnd; /* currently active command */
+#ifdef CONFIG_SCSI_MULTI_PATH_IO
+ void *sdev_paths;
+#else
struct Scsi_Host *host;
unsigned int channel;
unsigned int id;
unsigned int lun;
+#endif
unsigned int manufacturer; /* Manufacturer of device, for using
* vendor-specific cmd's */
unsigned sector_size; /* size in bytes */
@@ -1071,6 +1079,29 @@
return (Scsi_Cmnd *)req->special;
}

+#ifdef CONFIG_SCSI_MULTI_PATH_IO
+
+extern struct scsi_device *scsi_sdev_list;
+
+extern int scsi_paths_proc_init( void );
+extern void scsi_paths_proc_cleanup( void );
+extern struct Scsi_Host *scsi_get_host(Scsi_Device *SDpnt);
+extern int scsi_get_path(Scsi_Device *SDpnt, struct scsi_path_id *pathp);
+extern int scsi_get_best_path(Scsi_Device *SDpnt, struct scsi_path_id *pathp,
+ struct request *);
+extern int scsi_path_decide_disposition(struct scsi_cmnd *);
+extern int scsi_add_path(Scsi_Device *SDpnt, struct Scsi_Host *shpnt,
+ unsigned int channel, unsigned int dev,
+ unsigned int lun);
+extern void scsi_remove_path(Scsi_Device *SDpnt, unsigned int host_no,
+ unsigned int channel, unsigned int dev,
+ unsigned int lun);
+extern Scsi_Device *scsi_lookup_id(char *id, Scsi_Device *sdev);
+extern void scsi_replace_path(Scsi_Device *sdev, struct Scsi_Host *shost,
+ unsigned int channel, unsigned int id, unsigned int lun);
+
+#else
+
static inline Scsi_Device *scsi_lookup_id(char *id, Scsi_Device *sdev)
{
return(NULL);
@@ -1120,6 +1151,7 @@
{
return UNKNOWN_ERROR;
}
+#endif

extern void scsi_paths_printk(Scsi_Device *SDpnt, char *prefix, char *format);
#define scsi_path_set_scmnd_ids(scp, pathp) do { \
diff -Nru a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
--- a/drivers/scsi/scsi_error.c Mon Sep 16 16:11:26 2002
+++ b/drivers/scsi/scsi_error.c Mon Sep 16 16:11:26 2002
@@ -140,7 +140,17 @@
int scsi_block_when_processing_errors(Scsi_Device *sdev)
{

+#ifndef CONFIG_SCSI_MULTI_PATH_IO
+ /*
+ * If multi-path IO, to match this code, check in_recover of all
+ * hosts on all paths on sdev; more easily delete this code, and
+ * handle it like any other IO sent after in_recovery is set: skip
+ * the request in scsi_request_fn(), and after error recovery
+ * completes, the IO will be sent.
+ */
SCSI_SLEEP(&sdev->host->host_wait, sdev->host->in_recovery);
+#endif
+
SCSI_LOG_ERROR_RECOVERY(5, printk("Open returning %d\n",
sdev->online));

@@ -832,18 +842,18 @@
(DRIVER_TIMEOUT << 24);
if (scsi_path_decide_disposition(scmd) == REQUEUE) {
/*
- * Abort the command, and then requeue it.
- *
- * XXX If the abort is not possible, what do we do?
- * Probably should not retry. But, for example, qlogicfc.c
- * times out commands after a loop down, and the command
- * is no longer live; so the abort command always fails
- * after a loop down causes the command to timeout. This
- * is also a problem for the current error handling, since
- * it does not know if the command was not able to be
- * aborted, or if the command failed to abort because the
- * adapter no longer has it.
+ * XXX Ideally, abort the command, and then requeue it.
+ * The current adapter abort command (eh_abort_handler) is
+ * optional, and is allowed to block, so it will not work
+ * here. Since we need to effectively reuse scmd, and we
+ * can't tell if it is still used by the adapter, the only
+ * solution for now is to allow the standard error handler
+ * to kick in and retry the command - with the current
+ * error handling code, this means the command will likely
+ * not be retried or even failed for quite a bit of time,
+ * maybe minutes.
*/
+#ifdef WILL_NOT_WORK
(void)scsi_try_to_abort_cmd(scmd);
SCSI_LOG_TIMEOUT(3,
printk("Requeuing timed out IO host %d channel %d id % d lun %d; result 0x%x\n",
@@ -851,6 +861,7 @@
scmd->target, scmd->lun, scmd->result));
scsi_mlqueue_insert(scmd, SCSI_MLQUEUE_RETRY);
return;
+#endif
}

/* Set the serial_number_at_timeout to the current serial_number */
diff -Nru a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
--- a/drivers/scsi/scsi_lib.c Mon Sep 16 16:11:26 2002
+++ b/drivers/scsi/scsi_lib.c Mon Sep 16 16:11:26 2002
@@ -47,6 +47,10 @@
#include "hosts.h"
#include <scsi/scsi_ioctl.h>

+#ifdef CONFIG_SCSI_MULTI_PATH_IO
+struct scsi_device *scsi_sdev_list;
+#endif
+
/*
* This entire source file deals with the new queueing code.
*/
@@ -708,6 +712,7 @@
spin_unlock_irqrestore(SHpnt->host_lock, flags);
}

+#ifndef CONFIG_SCSI_MULTI_PATH_IO
/*
* Function: scsi_get_best_path, not-multi-path IO version.
*
@@ -745,6 +750,7 @@
pathpnt->spi_shpnt->host_busy++;
return 0;
}
+#endif

/*
* Function: scsi_request_fn()
@@ -1156,6 +1162,104 @@
{
}

+
+#ifdef CONFIG_SCSI_MULTI_PATH_IO
+/**
+ * scsi_lookup_id - Lookup a scsi device matching an id
+ * @id: id terminated with '\0'
+ * @sdev_skip: possibly NULL Scsi_Device to skip if matched
+ *
+ * Description:
+ * Look for a Scsi_Device matching *@id. Uses a linear search. If a
+ * matching Scsi_Device not equal to sdev_skip is found return a pointer
+ * to it, else return NULL.
+ *
+ * Notes:
+ * Change to use driverfs tree.
+ **/
+Scsi_Device *scsi_lookup_id(char *id, Scsi_Device *sdev_skip)
+{
+ Scsi_Device *sdev;
+ scsi_traverse_hndl_t strav_hndl;
+
+ SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi: id '%s'", id));
+ if (id[0] != SCSI_UID_UNKNOWN)
+ scsi_for_all_sdevs(&strav_hndl, sdev) {
+ /*
+ * Both id and name should be '\0' terminated.
+ */
+ if ((sdev != sdev_skip) &&
+ strcmp(sdev->sdev_driverfs_dev.name,
+ (char *) id) == 0) {
+ SCSI_LOG_SCAN_BUS(3, printk("; found\n"));
+ return sdev;
+ }
+ }
+ SCSI_LOG_SCAN_BUS(3, printk("; no match\n"));
+ return NULL;
+}
+
+/*
+ * Function: scsi_add_scsi_device()
+ *
+ * Purpose: Add a SDpnt to the list of Scsi_Devices, add it to the
+ * "tail" of the list. If needed, add it to the list for
+ * shostpnt.
+ *
+ * Arguments: Scsi_Device pointer SDpnt.
+ *
+ * Notes: Doesn't do anything other than link in SDpnt, we should add
+ * other code that is common to the addition of all Scsi_Devices.
+ */
+void scsi_add_scsi_device(Scsi_Device *SDpnt, struct Scsi_Host *shostpnt)
+{
+ Scsi_Device *sdtailpnt;
+
+ SDpnt->sdev_prev = NULL;
+ SDpnt->sdev_next = NULL;
+ if (scsi_sdev_list != NULL) {
+ sdtailpnt = scsi_sdev_list;
+ while (sdtailpnt->sdev_next != NULL)
+ sdtailpnt = sdtailpnt->sdev_next;
+ sdtailpnt->sdev_next = SDpnt;
+ SDpnt->sdev_prev = sdtailpnt;
+ } else
+ scsi_sdev_list = SDpnt;
+}
+
+/*
+ * Function: scsi_remove_scsi_device
+ *
+ * Purpose: Remove SDpnt from the list of Scsi_Devices.
+ *
+ * Arguments: Scsi_Device pointer SDpnt.
+ *
+ * Notes: unlink and kfree SDpnt, we should add other code that is common
+ * to removal of all Scsi_Devices.
+ */
+void scsi_remove_scsi_device(Scsi_Device *SDpnt)
+{
+ if (SDpnt == NULL)
+ return;
+ if (SDpnt->sdev_next != NULL)
+ SDpnt->sdev_next->sdev_prev = SDpnt->sdev_prev;
+ if (SDpnt->sdev_prev != NULL)
+ SDpnt->sdev_prev->sdev_next = SDpnt->sdev_next;
+ if (scsi_sdev_list == SDpnt)
+ scsi_sdev_list = SDpnt->sdev_next;
+ if (SDpnt->inquiry != NULL)
+ kfree(SDpnt->inquiry);
+ kfree((char *) SDpnt);
+}
+
+#else
+
+/*
+ * Non-multi-path-io versions, replacements for the above multi-path-io
+ * versions, plus replacements for non-inlined extern functions found in
+ * scsi_paths.c.
+ */
+
/*
* Function: scsi_add_scsi_device
*
@@ -1313,3 +1417,5 @@
SDpnt->channel, SDpnt->id, SDpnt->lun));
}
#endif
+
+#endif /* CONFIG_SCSI_MULTI_PATH_IO */
diff -Nru a/drivers/scsi/scsi_paths.c b/drivers/scsi/scsi_paths.c
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/drivers/scsi/scsi_paths.c Mon Sep 16 16:11:26 2002
@@ -0,0 +1,1486 @@
+/*
+ * Scsi Multi-path Support Library
+ *
+ * Copyright (c) 2002 IBM
+ *
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Send feedback to [email protected] and/or [email protected]
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/blk.h>
+#include <linux/bio.h>
+#include <asm/uaccess.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include "scsi_paths.h"
+
+#define SCSI_PATHS_VERSION "Version: 0.04.00"
+
+#if defined (CONFIG_SCSI_PATH_POLICY_LPU)
+static enum scsi_path_policy scsi_path_dflt_path_policy = SCSI_PATH_POLICY_LPU;
+#elif defined (CONFIG_SCSI_PATH_POLICY_RR)
+static enum scsi_path_policy scsi_path_dflt_path_policy =
+ SCSI_PATH_POLICY_ROUND_ROBIN;
+#else
+#error "Unknown path selection policy"
+#endif
+
+#ifdef MODULE
+MODULE_PARM(scsi_path_dflt_path_policy, "i");
+MODULE_PARM_DESC(scsi_path_dflt_path_policy,
+ "path selection policy, Last Path Used = 1, Round Robin = 2");
+#else
+
+static int __init scsi_path_policy_setup(char *str)
+{
+ unsigned int tmp;
+
+ if (get_option(&str, &tmp) == 1) {
+ scsi_path_dflt_path_policy = tmp;
+ return 1;
+ } else {
+ printk(KERN_INFO "scsi_path_policy_setup: usage "
+ "scsi_path_dflt_path_policy=n "
+ "Last Path Used = 1, Round Robin = 2");
+ return 0;
+ }
+}
+
+__setup("scsi_path_dflt_path_policy=", scsi_path_policy_setup);
+
+#endif
+
+/*
+ * XXX audit and ensure that we have a lock whenver accessing any paths.
+ */
+
+/**
+ * scsi_paths_printk - printk for each path using the specified format.
+ * @sdev: printk all paths of this Scsi_Device
+ * @prefix: prefix string
+ * @format: a printf format string
+ *
+ * Description: For each path on @sdev, printk the host/channel/id/lun
+ * using the specified @format. For all but the first printk, output
+ * @prefix before @format. @prefix must not be NULL, but can be an empty
+ * string; @prefix can and should include a level (such as KERN_INFO).
+ **/
+void scsi_paths_printk(Scsi_Device *sdev, char *prefix, char *format)
+{
+ struct list_head *lh;
+ struct scsi_mpath *mpath;
+ struct scsi_path *pathcur;
+ int not_first_path = 0;
+
+ if (sdev->sdev_paths != NULL) {
+ mpath = (struct scsi_mpath *) sdev->sdev_paths;
+ list_for_each(lh, &mpath->scsi_mp_paths_head) {
+ pathcur =
+ list_entry(lh, struct scsi_path, sp_path_list);
+ if (not_first_path)
+ printk(prefix);
+ else
+ not_first_path = 1;
+ printk(format, pathcur->sp_path_id.spi_shpnt->host_no,
+ pathcur->sp_path_id.spi_channel,
+ pathcur->sp_path_id.spi_id,
+ pathcur->sp_path_id.spi_lun);
+ }
+ } else
+ printk("no paths");
+}
+
+/**
+ * scsi_remove_path_from_list - remove a path from an active list
+ * @mpath: get an active list from here
+ * @oldpath: path to remove
+ * @ind_active: remove from this last active list
+ *
+ * Description:
+ * Remove @oldpath from the active list @ind_active of @mpath. It is
+ * safe to call this function if path is not on the active list.
+ **/
+void scsi_remove_path_from_list(struct scsi_mpath *mpath,
+ struct scsi_path *oldpath, int ind_active)
+{
+ struct scsi_path *last_active;
+ struct scsi_path *pathcur;
+ int ind_next;
+
+ SCSI_LOG_SCAN_BUS(4, printk(KERN_INFO
+ "%s: path <%d, %d, %d, %d>, ind_active %d\n",
+ __FUNCTION__, oldpath->sp_path_id.spi_shpnt->host_no,
+ oldpath->sp_path_id.spi_id, oldpath->sp_path_id.spi_lun,
+ oldpath->sp_path_id.spi_channel, ind_active));
+
+ BUG_ON(ind_active >= SCSI_MAX_ACTIVE_LIST);
+ last_active = mpath->scsi_mp_last_active[ind_active];
+ if (last_active == NULL)
+ return;
+
+ if (ind_active == SCSI_PATHS_ALL_LIST) {
+ ind_next = SCSI_ALL_ACTIVE_NEXT_LIST;
+ } else {
+ ind_next = SCSI_NID_ACTIVE_NEXT_LIST;
+ }
+ pathcur = last_active;
+ do {
+ if (pathcur->sp_active_next[ind_next] == oldpath) {
+ if (pathcur == oldpath)
+ mpath->scsi_mp_last_active[ind_active] = NULL;
+ else {
+ pathcur->sp_active_next[ind_next] =
+ oldpath->sp_active_next[ind_next];
+ if (last_active == oldpath)
+ mpath->scsi_mp_last_active[ind_active] =
+ oldpath->sp_active_next[ind_next];
+ }
+ --mpath->scsi_mp_path_active_cnt[ind_active];
+ oldpath->sp_active_next[ind_next] = NULL;
+ return;
+ }
+ pathcur = pathcur->sp_active_next[ind_next];
+ } while (pathcur != last_active);
+}
+
+/**
+ * scsi_remove_active_path - Remove a path from all active lists
+ * @sdev: Scsi_Device
+ * @oldpath: A scsi path
+ *
+ * Description:
+ * Remove @oldpath from the active lists of Scsi_Device @sdev. It is safe to
+ * call this function if path is not on the active list.
+ **/
+static void scsi_remove_active_path(Scsi_Device *sdev,
+ struct scsi_path *oldpath)
+{
+ struct scsi_mpath *mpath;
+ int nid;
+
+ mpath = (struct scsi_mpath *) sdev->sdev_paths;
+ if (mpath == NULL)
+ return;
+
+ nid = scsihost_to_node(oldpath->sp_path_id.spi_shpnt);
+ scsi_remove_path_from_list(mpath, oldpath, nid);
+ if (nid != SCSI_PATHS_ALL_LIST)
+ /*
+ * No CONFIG_NUMA
+ */
+ scsi_remove_path_from_list(mpath, oldpath,
+ SCSI_PATHS_ALL_LIST);
+}
+
+/**
+ * scsi_host_type_check - verify that the host-adapters are the same
+ * @host1 - the Scsi_Host pointer for the first adapter
+ * @host2 - the Scsi_Host pointer for the second adapter
+ *
+ * Description:
+ * Look at host pointers and check the values of certain fields,
+ * looking for incompatibilities that could cause problems.
+ *
+ * Return 1 if they don't match, 0 if they do.
+ **/
+static int scsi_host_type_check(struct Scsi_Host *host1,
+ struct Scsi_Host *host2)
+{
+ int ret = 0;
+
+ static const char message[] = {
+ KERN_WARNING "scsi: the parameter '%s' differs for two"
+ " adapters attached to the same device.\n"
+ };
+
+#define CHECK_HOST_PARAMS(param) \
+ if (host1->param != host2->param) { \
+ printk(message, __stringify(param)); \
+ ret = 1; \
+ }
+
+ CHECK_HOST_PARAMS(hostt);
+ CHECK_HOST_PARAMS(sg_tablesize);
+ CHECK_HOST_PARAMS(max_sectors);
+ CHECK_HOST_PARAMS(highmem_io);
+ CHECK_HOST_PARAMS(unchecked_isa_dma);
+ CHECK_HOST_PARAMS(use_clustering);
+ CHECK_HOST_PARAMS(max_cmd_len);
+
+#undef CHECK_HOST_PARAMS
+
+ return ret;
+}
+
+/**
+ * scsi_add_active_path - Add a path to an active list
+ * @sdev: Scsi_Device
+ * @pathcur: A scsi path
+ * @ind_active: index into the scsi_mp_last_active
+ * @ind_next: index into the sp_active_next
+ *
+ * Description: Add @pathcur to the active list of Scsi_Device @sdev. It is
+ * safe to add a path that is already on the active list (the list will
+ * not be changed).
+ **/
+static void scsi_add_active_path(Scsi_Device *sdev, struct scsi_path *pathcur,
+ int ind_active)
+{
+ struct scsi_mpath *mpath;
+ struct scsi_path *last_active;
+ int ind_next;
+
+ mpath = (struct scsi_mpath *) sdev->sdev_paths;
+ if (mpath == NULL)
+ return;
+ BUG_ON(ind_active >= SCSI_MAX_ACTIVE_LIST);
+ if (ind_active == SCSI_PATHS_ALL_LIST)
+ ind_next = SCSI_ALL_ACTIVE_NEXT_LIST;
+ else
+ ind_next = SCSI_NID_ACTIVE_NEXT_LIST;
+ if (pathcur->sp_active_next[ind_next] != NULL)
+ /*
+ * The path is already on the active list.
+ */
+ return;
+ else {
+ SCSI_LOG_SCAN_BUS(4, printk(KERN_INFO
+ "%s: sdev 0x%p, path <%d, %d, %d, %d> ind_active %d\n",
+ __FUNCTION__, sdev,
+ pathcur->sp_path_id.spi_shpnt->host_no,
+ pathcur->sp_path_id.spi_id,
+ pathcur->sp_path_id.spi_lun,
+ pathcur->sp_path_id.spi_channel, ind_active));
+
+ last_active = mpath->scsi_mp_last_active[ind_active];
+ if (last_active != NULL) {
+ pathcur->sp_active_next[ind_next] =
+ last_active->sp_active_next[ind_next];
+ last_active->sp_active_next[ind_next] = pathcur;
+ } else {
+ mpath->scsi_mp_last_active[ind_active] = pathcur;
+ pathcur->sp_active_next[ind_next] = pathcur;
+ }
+ ++mpath->scsi_mp_path_active_cnt[ind_active];
+ }
+}
+
+/**
+ * scsi_add_path - Add a path to a scsi device
+ * @sdev: Scsi_Device to add a path to
+ * @shost: Scsi_Host of the path
+ * @channel: Channel value of the path
+ * @id: Id value of the path
+ * @lun: Lun value of the path
+ *
+ * Description:
+ * Add a path corresponding to @shost, @channel, @id, and @lun to the
+ * Scsi_Device @sdev. The new path is also added to the active path. Must
+ * be called with user context (kmalloc GFP_KERNEL).
+ *
+ * Return 1 on error, 0 on success.
+ **/
+int scsi_add_path(Scsi_Device *sdev, struct Scsi_Host *shost,
+ unsigned int channel, unsigned int id, unsigned int lun)
+{
+ struct scsi_mpath *mpath;
+ struct scsi_path *pathcur;
+ struct Scsi_Host *shostcur;
+ int i, nid;
+
+ SCSI_LOG_SCAN_BUS(4, printk(KERN_INFO
+ "%s: sdev 0x%p path: <%d, %d, %d, %d>\n", __FUNCTION__,
+ sdev, shost->host_no, channel, id, lun));
+
+ mpath = (struct scsi_mpath *) sdev->sdev_paths;
+
+ pathcur = kmalloc(sizeof(*pathcur), GFP_KERNEL);
+ if (pathcur == NULL) {
+ printk(KERN_WARNING
+ "%s: memory allocation failure.\n", __FUNCTION__);
+ return 1;
+ }
+ for (i = 0; i < SCSI_PATHS_MAX_PATH_LISTS; i++)
+ pathcur->sp_active_next[i] = NULL;
+ pathcur->sp_state = SCSI_PATH_STATE_GOOD;
+ pathcur->sp_failures = 0;
+ pathcur->sp_weight = 0; /* lowest is best */
+ pathcur->sp_path_id.spi_shpnt = shost;
+ pathcur->sp_path_id.spi_id = id;
+ pathcur->sp_path_id.spi_lun = lun;
+ pathcur->sp_path_id.spi_channel = channel;
+
+ if (mpath == NULL) {
+ /*
+ * First path.
+ */
+ SCSI_LOG_SCAN_BUS(5, printk(KERN_INFO
+ "%s: adding first path\n", __FUNCTION__));
+ mpath = kmalloc(sizeof(*mpath), GFP_KERNEL);
+ if (mpath == NULL) {
+ printk(KERN_WARNING "%s: memory allocation failure.\n",
+ __FUNCTION__);
+ kfree(pathcur);
+ return 1;
+ }
+ INIT_LIST_HEAD(&mpath->scsi_mp_paths_head);
+ mpath->scsi_mp_path_policy = scsi_path_dflt_path_policy;
+ for (i = 0; i < SCSI_MAX_ACTIVE_LIST; i++) {
+ mpath->scsi_mp_last_active[i] = NULL;
+ mpath->scsi_mp_path_active_cnt[i] = 0;
+ }
+ (struct scsi_mpath *) sdev->sdev_paths = mpath;
+ } else {
+ if (sdev->single_lun) {
+ printk(KERN_WARNING
+ "scsi: Multi-path single LUN devices are not "
+ "supported. path host %d chan %d id %d lun %d "
+ "not added\n", shost->host_no, channel, id, lun);
+ kfree(pathcur);
+ return 1;
+ }
+ shostcur = scsi_get_host(sdev);
+ BUG_ON(shostcur == NULL);
+ if (scsi_host_type_check(shost, shostcur)) {
+ printk(KERN_ERR "scsi: path host %d chan %d id %d"
+ " lun %d not added\n", shost->host_no, channel,
+ id, lun);
+ kfree(pathcur);
+ return 1;
+ }
+ SCSI_LOG_SCAN_BUS(5, printk(KERN_INFO
+ "scsi: adding more paths\n"));
+ }
+
+ list_add_tail(&pathcur->sp_path_list, &mpath->scsi_mp_paths_head);
+ /*
+ * Add the path to the all list, and possibly to a nid list.
+ */
+ nid = scsihost_to_node(shost);
+ scsi_add_active_path(sdev, pathcur, nid);
+ if (nid != SCSI_PATHS_ALL_LIST)
+ /*
+ * CONFIG_NUMA
+ */
+ scsi_add_active_path(sdev, pathcur, SCSI_PATHS_ALL_LIST);
+
+ SCSI_LOG_SCAN_BUS(5, {
+ printk(KERN_INFO "%s: All paths for sdev 0x%p:\n ",
+ __FUNCTION__, sdev);
+ scsi_paths_printk(sdev, KERN_INFO " ",
+ "<%d, %d, %d, %d>\n");
+ }
+ );
+ return 0;
+}
+
+/**
+ * scsi_remove_path - Remove a path from a scsi device
+ * @host_no: Host number of the path
+ * @channel: Channel value of the path
+ * @id: Id value of the path
+ * @lun: Lun value of the path
+ * @sdev: Scsi_Device to add a path to
+ *
+ * Description:
+ * Remove the path corresponding to @host_no, @channel, @id, and @lun from
+ * the Scsi_Device @sdev. The path does not have to exist, so interfaces
+ * can wild card removal of all paths without first checking if any of the
+ * paths actually exist.
+ **/
+void scsi_remove_path(Scsi_Device *sdev, unsigned int host_no,
+ unsigned int channel, unsigned int id, unsigned int lun)
+{
+ struct scsi_mpath *mpath;
+ struct list_head *lh, *tmplh;
+ struct scsi_path *pathcur;
+
+ SCSI_LOG_SCAN_BUS(4, printk(KERN_INFO
+ "%s: sdev 0x%p path <%d, %d, %d, %d>\n", __FUNCTION__,
+ sdev, host_no, channel, id, lun));
+
+ mpath = (struct scsi_mpath *) sdev->sdev_paths;
+ if (mpath == NULL)
+ return;
+
+ /*
+ * Look for a matching path, and remove it.
+ *
+ * XXX should be common code used by the scsi iterators.
+ */
+ mpath = (struct scsi_mpath *) sdev->sdev_paths;
+ list_for_each_safe(lh, tmplh, &mpath->scsi_mp_paths_head) {
+ pathcur = list_entry(lh, struct scsi_path, sp_path_list);
+ if ((host_no == SCSI_FIND_ALL_HOST_NO ||
+ host_no == pathcur->sp_path_id.spi_shpnt->host_no) &&
+ (channel == SCSI_FIND_ALL_CHANNEL ||
+ channel == pathcur->sp_path_id.spi_channel) &&
+ (id == SCSI_FIND_ALL_ID ||
+ id == pathcur->sp_path_id.spi_id) &&
+ (lun == SCSI_FIND_ALL_LUN ||
+ lun == pathcur->sp_path_id.spi_lun)) {
+ scsi_remove_active_path(sdev, pathcur);
+ list_del(&pathcur->sp_path_list);
+ kfree(pathcur);
+ }
+ }
+
+ if (list_empty(&mpath->scsi_mp_paths_head)) {
+ SCSI_LOG_SCAN_BUS(4, printk(KERN_INFO
+ "%s: sdev 0x%p removed last path\n", __FUNCTION__, sdev));
+ kfree(mpath);
+ sdev->sdev_paths = NULL;
+ }
+ return;
+}
+
+/**
+ * scsi_replace_path - replace the current and only path to a Scsi_Device
+ * @sdev: replace a path on this Scsi_Device
+ * @host_no: new host number
+ * @channel: new channel value
+ * @id: new id
+ * @lun: new lun
+ *
+ * Description:
+ * Modify the one and only path on @sdev to use @host_no, @channel, @id,
+ * and @lun. This function is _only_ for use during scsi scan, where one
+ * sdev is used to scan an entire host. It is an optimization to avoid
+ * doing a scsi_remove_path and scsi_add_path for each LUN we want to
+ * scan.
+ **/
+void scsi_replace_path(Scsi_Device *sdev, struct Scsi_Host *shost,
+ unsigned int channel, unsigned int id, unsigned int lun)
+{
+ struct scsi_mpath *mpath;
+ struct scsi_path_id *pathid;
+ struct scsi_path *pathcur;
+ int nid;
+ struct list_head *lh;
+
+ mpath = (struct scsi_mpath *) sdev->sdev_paths;
+ BUG_ON(mpath == NULL);
+ /*
+ * There should be one and only one path for this sdev.
+ */
+ list_for_each(lh, &mpath->scsi_mp_paths_head) {
+ pathcur = list_entry(lh, struct scsi_path, sp_path_list);
+ pathid = &pathcur->sp_path_id;
+ SCSI_LOG_SCAN_BUS(5, printk(KERN_INFO
+ "%s: sdev* 0x%p replace <%d, %d, %d, %d> with"
+ " <%d, %d, %d, %d>\n", __FUNCTION__, sdev,
+ pathid->spi_shpnt->host_no, pathid->spi_channel,
+ pathid->spi_id, pathid->spi_lun,
+ shost->host_no, channel, id, lun));
+ pathid->spi_shpnt = shost;
+ pathid->spi_channel = channel;
+ pathid->spi_id = id;
+ pathid->spi_lun = lun;
+
+ /*
+ * Make sure it is still an active path, in case previous
+ * IO failed the path.
+ */
+ nid = scsihost_to_node(pathcur->sp_path_id.spi_shpnt);
+ scsi_add_active_path(sdev, pathcur, nid);
+ if (nid != SCSI_PATHS_ALL_LIST)
+ /*
+ * CONFIG_NUMA
+ */
+ scsi_add_active_path(sdev, pathcur,
+ SCSI_PATHS_ALL_LIST);
+ return;
+ }
+ BUG();
+}
+
+/**
+ * scsi_get_host - get a Scsi_Host pointer to a Scsi_Device
+ *
+ * @sdev: Get the Scsi_Host of this Scsi_Device pointer
+ *
+ * Description:
+ * Return a pointer to a Scsi_Host so that the capabilities of the of the
+ * host driver can be determined; if no paths exist to sdev, return NULL.
+ *
+ * This should return some generic information rather than a Scsi_Host*.
+ * Alternatively (and probably even better) have one function for each
+ * value needed.
+ *
+ * This function assumes that all paths to a Scsi_Device use the same host
+ * driver - this is enforced during device scanning/discovery. This
+ * function must only be used to get capabilities or the template of the
+ * host - using it to set or get per-Scsi_Host (verus per-host driver)
+ * specific values is a bug (for example, using this to get a host pointer
+ * and then referencing host_busy).
+ **/
+struct Scsi_Host *scsi_get_host(Scsi_Device *sdev)
+{
+ struct scsi_path *pathcur;
+ struct list_head *lh;
+ struct scsi_mpath *mpath;
+
+ mpath = (struct scsi_mpath *) sdev->sdev_paths;
+ if (mpath == NULL)
+ return NULL;
+ list_for_each(lh, &mpath->scsi_mp_paths_head) {
+ pathcur = list_entry(lh, struct scsi_path, sp_path_list);
+ return pathcur->sp_path_id.spi_shpnt;
+ }
+ return NULL;
+}
+
+/**
+ * scsi_get_path - Get a path to a scsi device
+ * @sdev: Scsi_Device
+ * @pathid: path id (tuple)
+ *
+ * Description:
+ * Get any @pathid to a Scsi_Device, used by semi-broken code that wants
+ * one host/channel/id/lun of a device. The path returned might not be
+ * active. Return 0 if a path is found, else return 1.
+ **/
+int scsi_get_path(Scsi_Device *sdev, struct scsi_path_id *pathid)
+{
+ struct scsi_path *pathcur;
+ struct scsi_mpath *mpath;
+
+ mpath = (struct scsi_mpath *) sdev->sdev_paths;
+ if (mpath == NULL)
+ return 1;
+ pathcur = list_entry(mpath->scsi_mp_paths_head.next, struct scsi_path,
+ sp_path_list);
+ *pathid = pathcur->sp_path_id;
+ return 0;
+}
+
+/**
+ * scsi_path_traverse_sdevs - Return the next Scsi_Device matching the
+ * search requested
+ *
+ * @handle: Handle used to bookmark location of search thus far
+ * @host_no: Host to search for
+ * @channel: Channel to search for
+ * @id: Id to search for
+ * @lun: Lun to search for
+ *
+ * Description:
+ * Returns - A pointer to Scsi_Device, NULL when the list is complete.
+ * Notes - Assumes that we do not remove a device between calls.
+ **/
+Scsi_Device *scsi_traverse_sdevs(scsi_traverse_hndl_t *handle, uint
+ host_no, uint channel, uint id, uint lun)
+{
+ Scsi_Device *sdev;
+
+ if (handle == NULL)
+ /*
+ * Really a locate and not a traversal.
+ */
+ sdev = NULL;
+ else
+ sdev = handle->last_scsi_device_p;
+
+ if (sdev == NULL)
+ sdev = scsi_sdev_list;
+ else
+ sdev = sdev->sdev_next;
+
+ for (; sdev; sdev = sdev->sdev_next) {
+ if (sdev->sdev_paths != NULL) {
+ struct list_head *lh;
+ struct scsi_mpath *mpath_iopnt;
+ struct scsi_path *pathpnt;
+
+ mpath_iopnt = (struct scsi_mpath *) sdev->sdev_paths;
+ list_for_each(lh, &mpath_iopnt->scsi_mp_paths_head) {
+ pathpnt = list_entry(lh, struct scsi_path,
+ sp_path_list);
+ if ((host_no == SCSI_FIND_ALL_HOST_NO ||
+ host_no == pathpnt->sp_path_id.spi_shpnt->host_no ) &&
+ (channel == SCSI_FIND_ALL_CHANNEL ||
+ channel == pathpnt->sp_path_id.spi_channel ) &&
+ (id == SCSI_FIND_ALL_ID ||
+ id == pathpnt->sp_path_id.spi_id ) &&
+ (lun == SCSI_FIND_ALL_LUN ||
+ lun == pathpnt->sp_path_id.spi_lun )) {
+ goto done;
+ }
+ }
+ }
+ }
+
+done:
+ if (handle)
+ handle->last_scsi_device_p = sdev;
+ return sdev;
+}
+
+/**
+ * scsi_get_best_path - Get a path for a scsi device.
+ * @sdev: Scsi_Device
+ * @pathid: A scsi path id (tuple)
+ * @req: request for NUMA so the node closest to req can be found
+ *
+ * Description:
+ * This code is used for normal IO. Get a @pathid for Scsi_Device @sdev.
+ * Skip paths on a busy or blocked host, mark hosts and devices as starved
+ * if needed. Return 2 if no paths at all, 1 if resources are busy, 0 if a
+ * path is found.
+ **/
+int scsi_get_best_path(Scsi_Device *sdev, struct scsi_path_id *pathid,
+ struct request *req)
+{
+ struct scsi_path *pathcur, *first_path;
+ struct scsi_mpath *mpath;
+ struct Scsi_Host *shost;
+ request_queue_t *q;
+ int found_path, starving, ind_active, ind_next;
+ unsigned long flags = 0;
+
+ mpath = (struct scsi_mpath *) sdev->sdev_paths;
+ if ((mpath == NULL) ||
+ (mpath->scsi_mp_last_active[SCSI_PATHS_ALL_LIST] == NULL)) {
+ /*
+ * No paths or no active paths available.
+ *
+ * XXX Fix the following message to include a device name or ?
+ */
+ printk(KERN_WARNING
+ "scsi: No active paths available Scsi_Device 0x%p.\n",
+ sdev);
+ return 2;
+ }
+
+ ind_active = req_to_nid(req);
+#if (MAX_NUMNODES != 1)
+ /*
+ * If MAX_NUMNODES is 1, not NUMA, and the following check is not
+ * needed.
+ */
+ if (ind_active <= MAX_NUMNODES)
+ if (mpath->scsi_mp_last_active[ind_active] == NULL)
+ ind_active = SCSI_PATHS_ALL_LIST;
+#endif
+ first_path = pathcur = mpath->scsi_mp_last_active[ind_active];
+ BUG_ON(ind_active >= SCSI_MAX_ACTIVE_LIST);
+ if (ind_active == SCSI_PATHS_ALL_LIST)
+ ind_next = SCSI_ALL_ACTIVE_NEXT_LIST;
+ else
+ ind_next = SCSI_NID_ACTIVE_NEXT_LIST;
+ found_path = 0;
+ starving = 0;
+ do {
+ shost = pathcur->sp_path_id.spi_shpnt;
+ /*
+ * XXX hack: host_lock might not equal queue_lock for
+ * multi-path, or if code is changed. We already hold
+ * queue_lock.
+ */
+ q = &sdev->request_queue;
+ if (shost->host_lock != q->queue_lock)
+ spin_lock_irqsave(shost->host_lock, flags);
+ if (!shost->in_recovery) {
+ if ((shost->can_queue > 0 &&
+ (shost->host_busy >= shost->can_queue))
+ || (shost->host_blocked)
+ || (shost->host_self_blocked)) {
+ if (sdev->device_busy == 0) {
+ starving = 1;
+ }
+ } else {
+ found_path = 1;
+ starving = 0;
+ /* do not unlock host shost */
+ break;
+ }
+ }
+ if (shost->host_lock != q->queue_lock)
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ if (mpath->scsi_mp_path_policy != SCSI_PATH_POLICY_LPU)
+ pathcur = pathcur->sp_active_next[ind_next];
+ } while (first_path != pathcur);
+
+ if (starving) {
+ /*
+ * For now, wait for the local node (or all paths) to get a
+ * free slot. This means NUMA systems might have a path
+ * available on a non-local node, but we do not try and
+ * use it. A hung device should eventually cause
+ * the local paths to fail, and then the non-local paths
+ * would be used for futher IO.
+ *
+ * All available hosts (on all paths) are busy, and sdev
+ * is starved. Mark all hosts as having a starved device.
+ *
+ * It would be better to put starved devices on a list,
+ * rather than set a flag.
+ */
+ sdev->starved = 1;
+ pathcur = first_path = mpath->scsi_mp_last_active[ind_active];
+ do {
+ if (shost->host_lock != q->queue_lock)
+ spin_lock_irqsave(shost->host_lock, flags);
+ shost->some_device_starved = 1;
+ if (shost->host_lock != q->queue_lock)
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ pathcur = pathcur->sp_active_next[ind_next];
+ } while (first_path != pathcur);
+ return 1;
+ } else if (found_path) {
+ sdev->starved = 0;
+ shost->host_busy++;
+ if (shost->host_lock != q->queue_lock)
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ } else
+ /*
+ * All available hosts (on all paths) are busy, and sdev
+ * is *not* starved.
+ */
+ return 1;
+
+ if (mpath->scsi_mp_path_policy == SCSI_PATH_POLICY_ROUND_ROBIN)
+ /*
+ * This actually sets scsi_mp_last_active[ind_active] to the next
+ * path to be used, not the last path used.
+ */
+ mpath->scsi_mp_last_active[ind_active] =
+ mpath->scsi_mp_last_active[ind_active]->sp_active_next[ind_next];
+ else if (mpath->scsi_mp_path_policy != SCSI_PATH_POLICY_LPU) {
+ printk(KERN_ERR
+ "%s: invalid scsi_mp_path_policy %d\n", __FUNCTION__,
+ mpath->scsi_mp_path_policy);
+ return 1;
+ }
+
+ BUG_ON(pathcur->sp_state == SCSI_PATH_STATE_DEAD);
+ *pathid = pathcur->sp_path_id;
+ return 0;
+}
+
+/**
+ * scsi_check_paths - Check a path in a scsi device.
+ * @path_state: Path state to upate paths with
+ * @check_path: Paths to match
+ * @sdev: Scsi_Device
+ *
+ * Description:
+ * For the paths in @sdev that match path_list_p, update its path state,
+ * and return a value based on the remaining available paths and the
+ * path_state argument. Called for every IO completion.
+ *
+ * XXX optimize so that the normal case (IO completed with no errors)
+ * requires no searching of all paths. Do this by caching if any paths are
+ * failing, and by caching paths_left. So, for an IO with no errors, the
+ * only time we have to search the paths is when there are any paths failing.
+ *
+ * This function currently allows wild carding of paths on @sdev, so all paths
+ * can be marked failing/failed or good
+ *
+ * Returns REQUEUE (redrive the IO) if any paths are left, else returns
+ * SUCCESS (meaning the IO completed, not that it was successful).
+ **/
+static int scsi_check_paths(enum scsi_path_state path_state,
+ struct scsi_path_id *check_path,
+ Scsi_Cmnd *scmd)
+{
+ struct scsi_path *pathcur;
+ struct list_head *lh;
+ struct scsi_mpath *mpath;
+ unsigned int paths_left;
+ Scsi_Device *sdev = scmd->device;
+
+ mpath = (struct scsi_mpath *) sdev->sdev_paths;
+ if ((mpath == NULL) || (sdev->scanning &&
+ (path_state != SCSI_PATH_STATE_GOOD)))
+ /*
+ * IO should not be retried.
+ */
+ return SUCCESS;
+ paths_left = 0;
+ list_for_each(lh, &mpath->scsi_mp_paths_head) {
+ pathcur = list_entry(lh, struct scsi_path, sp_path_list);
+ if ((check_path->spi_shpnt->host_no ==
+ SCSI_FIND_ALL_HOST_NO
+ || check_path->spi_shpnt->host_no ==
+ pathcur->sp_path_id.spi_shpnt->host_no)
+ && (check_path->spi_channel ==
+ SCSI_FIND_ALL_CHANNEL
+ || check_path->spi_channel ==
+ pathcur->sp_path_id.spi_channel)
+ && (check_path->spi_id == SCSI_FIND_ALL_ID
+ || check_path->spi_id == pathcur->sp_path_id.spi_id)
+ && (check_path->spi_lun == SCSI_FIND_ALL_LUN
+ || check_path->spi_lun ==
+ pathcur->sp_path_id.spi_lun)) {
+ if (path_state == SCSI_PATH_STATE_GOOD) {
+ /*
+ * Nothing for now - but this could be
+ * used to decrement or reset sp_failures.
+ */
+ } else if (path_state == SCSI_PATH_STATE_FAILING) {
+ pathcur->sp_failures++;
+ if (pathcur->sp_failures > 0) {
+ /*
+ * For now, treat a possibly failing
+ * path as a dead path.
+ */
+ pathcur->sp_state = SCSI_PATH_STATE_DEAD;
+ scsi_remove_active_path(sdev, pathcur);
+ printk(KERN_ERR "scsi: result 0x%x caused path host %d"
+ " channel %d id %d lun %d failure\n",
+ scmd->result,
+ pathcur->sp_path_id.spi_shpnt->host_no,
+ pathcur->sp_path_id.spi_channel,
+ pathcur->sp_path_id.spi_id,
+ pathcur->sp_path_id.spi_lun);
+ }
+ } else if (path_state == SCSI_PATH_STATE_DEAD) {
+ pathcur->sp_state = SCSI_PATH_STATE_DEAD;
+ scsi_remove_active_path(sdev, pathcur);
+ printk(KERN_ERR "scsi: result 0x%x caused path host %d"
+ " channel %d id %d lun %d failure\n",
+ scmd->result,
+ pathcur->sp_path_id.spi_shpnt->host_no,
+ pathcur->sp_path_id.spi_channel,
+ pathcur->sp_path_id.spi_id,
+ pathcur->sp_path_id.spi_lun);
+ } else
+ BUG();
+ }
+ if (pathcur->sp_state == SCSI_PATH_STATE_GOOD)
+ paths_left++;
+ }
+ if (paths_left > 0)
+ return REQUEUE;
+ else
+ return SUCCESS;
+}
+
+/**
+ * scsi_path_decide_disposition - Check the result of a scsi command
+ * @scmd: Scsi command
+ *
+ * Description:
+ * Check the result of @scmd, and determine how to procede. This code is
+ * based on scsi_decide_disposition, and should really just replace
+ * scsi_decide_disposition.
+ **/
+int scsi_path_decide_disposition(Scsi_Cmnd *scmd)
+{
+ int rtn;
+ struct scsi_path_id pathid;
+
+ pathid.spi_shpnt = scmd->host;
+ pathid.spi_channel = scmd->channel;
+ pathid.spi_id = scmd->target;
+ pathid.spi_lun = scmd->lun;
+ /*
+ * First check the host byte, to see if there is anything in there
+ * that would indicate what we need to do.
+ */
+ switch (host_byte(scmd->result)) {
+ case DID_PASSTHROUGH:
+ /*
+ * No matter what, pass this through to the upper layer.
+ * Nuke this special code so that it looks like we are saying
+ * DID_OK.
+ */
+ scmd->result &= 0xff00ffff;
+ (void) scsi_check_paths(SCSI_PATH_STATE_GOOD, &pathid, scmd);
+ return SUCCESS;
+ case DID_OK:
+ /*
+ * Looks good. Drop through, and check the next byte.
+ */
+ break;
+ case DID_NO_CONNECT:
+ case DID_BAD_TARGET:
+ case DID_ABORT:
+ /*
+ * Assumes the IO could not have made it to the device.
+ */
+ rtn = scsi_check_paths(SCSI_PATH_STATE_DEAD, &pathid, scmd);
+ return rtn;
+ /*
+ * When the low level driver returns DID_SOFT_ERROR,
+ * it is responsible for keeping an internal retry counter
+ * in order to avoid endless loops (DB)
+ *
+ * Actually this is a bug in this function here. We should
+ * be mindful of the maximum number of retries specified
+ * and not get stuck in a loop.
+ */
+ case DID_SOFT_ERROR:
+ /*
+ * Assumes the IO might have made it out to the device.
+ */
+ goto maybe_retry;
+
+ case DID_ERROR:
+ if (msg_byte(scmd->result) == COMMAND_COMPLETE &&
+ status_byte(scmd->result) == RESERVATION_CONFLICT)
+ /*
+ * execute reservation conflict processing code
+ * lower down
+ */
+ break;
+ /* FALLTHROUGH */
+
+ case DID_BUS_BUSY:
+ case DID_PARITY:
+ /*
+ * XXX set path to SCSI_PATH_STATE_FAILING?
+ */
+ goto maybe_retry;
+ case DID_TIME_OUT:
+ /*
+ * When we scan the bus, we get timeout messages for
+ * these commands if there is no device available.
+ * Other hosts report DID_NO_CONNECT for the same thing.
+ */
+ if ((scmd->cmnd[0] == TEST_UNIT_READY ||
+ scmd->cmnd[0] == INQUIRY)) {
+ /*
+ * XXX check the sdev->scanning bit, return
+ * success if it is set. TUR's should no longer be
+ * a problem.
+ */
+ (void) scsi_check_paths(SCSI_PATH_STATE_GOOD, &pathid,
+ scmd);
+ return SUCCESS;
+ } else {
+ rtn = scsi_check_paths(SCSI_PATH_STATE_FAILING, &pathid,
+ scmd);
+ if (rtn == REQUEUE)
+ /*
+ * XXX could cause scsi_check_paths to
+ * incorrectly be called again.
+ */
+ goto maybe_retry;
+ else
+ return FAILED;
+ }
+ case DID_RESET:
+ /*
+ * In the normal case where we haven't initiated a reset, this is
+ * a failure.
+ */
+ if (scmd->flags & IS_RESETTING) {
+ scmd->flags &= ~IS_RESETTING;
+ goto maybe_retry;
+ }
+ (void) scsi_check_paths(SCSI_PATH_STATE_GOOD, &pathid, scmd);
+ return SUCCESS;
+ default:
+ /*
+ * Bug?
+ */
+ return FAILED;
+ }
+
+ /*
+ * Next, check the message byte.
+ */
+ if (msg_byte(scmd->result) != COMMAND_COMPLETE) {
+ /*
+ * XXX Use SCSI_PATH_DEAD or SCSI_PATH_FAILING?
+ *
+ * FIXME this is a bug - don't requeue if scmd->allowed is 0.
+ */
+ rtn = scsi_check_paths(SCSI_PATH_STATE_DEAD, &pathid, scmd);
+ return rtn;
+ }
+ /*
+ * Now, check the status byte to see if this indicates anything special.
+ */
+ switch (status_byte(scmd->result)) {
+ case QUEUE_FULL:
+ /*
+ * The case of trying to send too many commands to a tagged
+ * queueing device.
+ *
+ * XXX this is broken for shared (clustered) devices even
+ * without multi-path IO, since the requeue marks a device
+ * blocked - if another host keeps filling the device, we
+ * might not ever unblock. Plus, code is broken if we
+ * have only one command outstanding - conditional check
+ * for ADD_TO_MLQUEUE is bad.
+ */
+ (void) scsi_check_paths(SCSI_PATH_STATE_GOOD, &pathid, scmd);
+ return ADD_TO_MLQUEUE;
+ case GOOD:
+ case COMMAND_TERMINATED:
+ (void) scsi_check_paths(SCSI_PATH_STATE_GOOD, &pathid, scmd);
+ return SUCCESS;
+ case CHECK_CONDITION:
+ rtn = scsi_check_sense(scmd);
+ if (rtn == NEEDS_RETRY)
+ goto maybe_retry;
+ (void) scsi_check_paths(SCSI_PATH_STATE_GOOD, &pathid, scmd);
+ return rtn;
+ case CONDITION_GOOD:
+ case INTERMEDIATE_GOOD:
+ case INTERMEDIATE_C_GOOD:
+ /*
+ * Who knows? FIXME(eric)
+ */
+ (void) scsi_check_paths(SCSI_PATH_STATE_GOOD, &pathid, scmd);
+ return SUCCESS;
+ case BUSY:
+ goto maybe_retry;
+
+ case RESERVATION_CONFLICT:
+ printk(KERN_ERR "scsi%d (%d,%d,%d) : RESERVATION CONFLICT\n",
+ scmd->host->host_no, scmd->channel,
+ scmd->target, scmd->lun);
+ (void) scsi_check_paths(SCSI_PATH_STATE_GOOD, &pathid, scmd);
+ return SUCCESS; /* causes immediate I/O error */
+ default:
+ (void) scsi_check_paths(SCSI_PATH_STATE_DEAD, &pathid, scmd);
+ return FAILED;
+ }
+ /*
+ * Dead code.
+ */
+ return FAILED;
+
+ maybe_retry:
+
+ if (scmd->allowed == 0)
+ /*
+ * No retries for this command, normal for tape (st) commands.
+ */
+ return SUCCESS;
+
+ /*
+ * XXX have scsi_check_paths increment the scmd->retries, and
+ * set return value as needed? Right now, we can retry forever for
+ * cases such as a CHECK_CONDITION.
+ *
+ * If all paths are going to get the same CHECK_CONDITION, it
+ * would be good to retry some number of times on each path, or
+ * have a life-time of the IO limit the number of retries, rather
+ * than a fixed number of retries independent of the number of
+ * paths and the time taken to send the incomplete IO.
+ */
+ return scsi_check_paths(SCSI_PATH_STATE_GOOD, &pathid, scmd);
+}
+
+#ifdef CONFIG_PROC_FS
+
+/*
+ * FIXME Add locking to protect paths access via /proc.
+ */
+
+/*
+ * FIXME: generally, use driverfs for attributes rather than procfs.
+ */
+
+/**
+ * scsi_paths_modify - modify a path of a Scsi_Device.
+ *
+ * @sdev: Scsi_Device pointer to the device whose path is being
+ * modified.
+ * @host_no: Host number of path
+ * @channel: Channel value for lookup
+ * @dev: Id value for lookup
+ * @lun: Lun value for lookup
+ * @new_state: New value for the path's sp_state
+ * @new_failures: New value for the path's sp_failures count
+ * @new_weight: New value for the path's sp_weight
+ *
+ * Description:
+ * For @sdev, modify any path matching the path id with the new values
+ * passed. This is currently only used by the proc interface, but is
+ * likely useful for any user level interface (sysctl or ioctl).
+ **/
+static void scsi_paths_modify(Scsi_Device *sdev, unsigned int host_no,
+ unsigned int channel, unsigned int id,
+ unsigned int lun, enum scsi_path_state new_state,
+ unsigned int new_failures,
+ unsigned int new_weight)
+{
+ struct scsi_mpath *mpath;
+ struct list_head *lh;
+ struct scsi_path *pathcur;
+ unsigned int prev_path_state;
+
+ if ((sdev == NULL) || (sdev->sdev_paths == NULL))
+ return;
+
+ mpath = (struct scsi_mpath *) sdev->sdev_paths;
+ list_for_each(lh, &mpath->scsi_mp_paths_head) {
+ pathcur = list_entry(lh, struct scsi_path, sp_path_list);
+ if ((host_no == SCSI_FIND_ALL_HOST_NO ||
+ host_no == pathcur->sp_path_id.spi_shpnt->host_no) &&
+ (channel == SCSI_FIND_ALL_CHANNEL ||
+ channel == pathcur->sp_path_id.spi_channel) &&
+ (id == SCSI_FIND_ALL_ID ||
+ id == pathcur->sp_path_id.spi_id) &&
+ (lun == SCSI_FIND_ALL_LUN ||
+ lun == pathcur->sp_path_id.spi_lun)) {
+ prev_path_state = pathcur->sp_state;
+ pathcur->sp_state = new_state;
+ if (prev_path_state != new_state) {
+ if (new_state == SCSI_PATH_STATE_GOOD) {
+ int nid;
+
+ printk(KERN_WARNING "scsi: setting path host %d"
+ " channel %d id %d lun %d"
+ " to good\n",
+ pathcur->sp_path_id.spi_shpnt->host_no,
+ pathcur->sp_path_id.spi_channel,
+ pathcur->sp_path_id.spi_id,
+ pathcur->sp_path_id.spi_lun);
+ nid = scsihost_to_node(pathcur->sp_path_id.spi_shpnt);
+ scsi_add_active_path(sdev, pathcur,
+ nid);
+ if (nid != SCSI_PATHS_ALL_LIST)
+ /*
+ * CONFIG_NUMA
+ */
+ scsi_add_active_path(sdev,
+ pathcur,
+ SCSI_PATHS_ALL_LIST);
+ } else if (new_state == SCSI_PATH_STATE_FAILING) {
+ /*
+ * XXX handle failing case.
+ */
+ printk(KERN_WARNING "scsi: setting path host %d"
+ " channel %d id %d lun %d"
+ " to failing\n",
+ pathcur->sp_path_id.spi_shpnt->host_no,
+ pathcur->sp_path_id.spi_channel,
+ pathcur->sp_path_id.spi_id,
+ pathcur->sp_path_id.spi_lun);
+ scsi_remove_active_path(sdev, pathcur);
+ } else {
+ /*
+ * SCSI_PATH_STATE_DEAD
+ */
+ printk(KERN_WARNING "scsi: setting path host %d"
+ " channel %d id %d lun %d"
+ " to failed\n",
+ pathcur->sp_path_id.spi_shpnt->host_no,
+ pathcur->sp_path_id.spi_channel,
+ pathcur->sp_path_id.spi_id,
+ pathcur->sp_path_id.spi_lun);
+ scsi_remove_active_path(sdev, pathcur);
+ }
+ }
+ if (pathcur->sp_failures != new_failures) {
+ pathcur->sp_failures = new_failures;
+ /*
+ * XXX handle failing case
+ */
+ }
+ if (pathcur->sp_weight != new_weight) {
+ pathcur->sp_weight = new_weight;
+ /*
+ * XXX call a function to re-check active
+ * paths.
+ */
+ }
+ }
+ }
+}
+
+#include <linux/proc_fs.h>
+static struct proc_dir_entry *scsi_paths_proc_mpp = NULL;
+static const char *scsi_paths_proc_dirname = "scsi_path";
+static const char *scsi_paths_proc_leaf_names[] = { "paths", "version" };
+
+static int scsi_paths_proc_paths_read(char *buffer, char **start, off_t offset,
+ int size, int *eof, void *data);
+static int scsi_paths_proc_paths_write(struct file *filp, const char *buffer,
+ unsigned long count, void *data);
+static int scsi_paths_proc_version_read(char *buffer, char **start,
+ off_t offset, int size, int *eof,
+ void *data);
+static read_proc_t *scsi_paths_proc_leaf_reads[] = {
+ scsi_paths_proc_paths_read,
+ scsi_paths_proc_version_read
+};
+
+static write_proc_t *scsi_paths_proc_leaf_writes[] = {
+ scsi_paths_proc_paths_write,
+ NULL /* version write */
+};
+
+/*
+ * These two macros where copied from
+ * Douglas Gilbert's sg.c
+ */
+#define PRINT_PROC(fmt,args...) \
+ do { \
+ *len += sprintf(buffer + *len, fmt, ##args); \
+ if (*begin + *len > offset + size) \
+ return 0; \
+ if (*begin + *len < offset) { \
+ *begin += *len; \
+ *len = 0; \
+ } \
+ } while(0)
+
+#define SP_PROC_READ_FN(infofp) \
+ do { \
+ int len = 0; \
+ off_t begin = 0; \
+ *eof = infofp(buffer, &len, &begin, offset, size); \
+ if (offset >= (begin + len)) \
+ return 0; \
+ *start = buffer + offset - begin; \
+ return (size < (begin + len - offset)) ? \
+ size : begin + len - offset; \
+ } while(0)
+
+int scsi_paths_proc_init(void)
+{
+ int k, mask;
+ int leaves = sizeof(scsi_paths_proc_leaf_names) /
+ sizeof(scsi_paths_proc_leaf_names[0]);
+ struct proc_dir_entry *pdep;
+
+ if (!proc_scsi)
+ return 1;
+ scsi_paths_proc_mpp = create_proc_entry(scsi_paths_proc_dirname,
+ S_IFDIR | S_IRUGO | S_IXUGO,
+ proc_scsi);
+ if (!scsi_paths_proc_mpp)
+ return 1;
+ for (k = 0; k < leaves; k++) {
+ mask = scsi_paths_proc_leaf_writes[k] ? S_IRUGO | S_IWUSR :
+ S_IRUGO;
+ pdep = create_proc_entry(scsi_paths_proc_leaf_names[k], mask,
+ scsi_paths_proc_mpp);
+ if (pdep) {
+ pdep->read_proc = scsi_paths_proc_leaf_reads[k];
+ /*
+ * scsi_paths_proc_leaf_writes[k] can be NULL.
+ */
+ pdep->write_proc = scsi_paths_proc_leaf_writes[k];
+ }
+ }
+
+ return 0;
+}
+
+void scsi_paths_proc_cleanup(void)
+{
+ int k;
+ int leaves =
+ sizeof(scsi_paths_proc_leaf_names) /
+ sizeof(scsi_paths_proc_leaf_names[0]);
+
+ if ((!proc_scsi) || (!scsi_paths_proc_mpp))
+ return;
+ for (k = 0; k < leaves; ++k)
+ remove_proc_entry(scsi_paths_proc_leaf_names[k],
+ scsi_paths_proc_mpp);
+ remove_proc_entry(scsi_paths_proc_dirname, proc_scsi);
+}
+
+int scsi_paths_proc_print_paths(Scsi_Device *sdev, char *buffer,
+ char *format)
+{
+ int len = 0;
+ struct list_head *lh;
+ struct scsi_mpath *mpath;
+ struct scsi_path *pathcur;
+
+ if (sdev->sdev_paths != NULL) {
+ mpath = (struct scsi_mpath *) sdev->sdev_paths;
+ list_for_each(lh, &mpath->scsi_mp_paths_head) {
+ pathcur =
+ list_entry(lh, struct scsi_path, sp_path_list);
+ len += sprintf(buffer + len, format,
+ pathcur->sp_path_id.spi_shpnt->host_no,
+ pathcur->sp_path_id.spi_channel,
+ pathcur->sp_path_id.spi_id,
+ pathcur->sp_path_id.spi_lun);
+ }
+ }
+ return len;
+}
+
+static int scsi_paths_proc_paths_info(char * buffer, int * len, off_t * begin,
+ off_t offset, int size)
+{
+ Scsi_Device *sdev;
+ scsi_traverse_hndl_t STrav_hndl;
+
+ scsi_for_all_sdevs(&STrav_hndl, sdev) {
+ if (sdev->sdev_paths != NULL) {
+ struct list_head *lh;
+ struct scsi_mpath *mpath;
+ struct scsi_path *pathcur;
+
+ /*
+ * For each path of each device, display:
+ *
+ * name:host_no:chan:id:lun:state:failures:weight
+ *
+ * The name can now includes spaces, so add a ':'
+ * separator.
+ */
+ mpath = (struct scsi_mpath *) sdev->sdev_paths;
+ list_for_each(lh, &mpath->scsi_mp_paths_head) {
+ pathcur = list_entry(lh, struct scsi_path,
+ sp_path_list);
+ PRINT_PROC("%s", sdev->sdev_driverfs_dev.name);
+ PRINT_PROC(":%x:%x:%x:%x:%x:%x:%x\n",
+ pathcur->sp_path_id.spi_shpnt->
+ host_no,
+ pathcur->sp_path_id.spi_channel,
+ pathcur->sp_path_id.spi_id,
+ pathcur->sp_path_id.spi_lun,
+ pathcur->sp_state,
+ pathcur->sp_failures,
+ pathcur->sp_weight);
+ }
+ }
+ }
+
+ return 1;
+}
+
+static int scsi_paths_proc_paths_read(char * buffer, char ** start,
+ off_t offset, int size, int * eof,
+ void * data)
+{
+ SP_PROC_READ_FN(scsi_paths_proc_paths_info);
+}
+
+static int scsi_paths_proc_paths_write(struct file *filp, const char *buffer,
+ unsigned long count, void *data)
+{
+ int res;
+ unsigned int host_no, channel, id, lun;
+ unsigned int path_failures, path_weight;
+ enum scsi_path_state path_state;
+ unsigned char *buff_in = NULL;
+ unsigned char *name_id = NULL;
+ unsigned char *ch_in, *ch_out;
+ Scsi_Device *sdev;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ /*
+ * We need one more byte for the '\0'
+ */
+ buff_in = kmalloc(count + 1, GFP_KERNEL);
+ if (buff_in == NULL) {
+ printk(KERN_WARNING "%s: could not malloc\n", __FUNCTION__);
+ count = -ENOMEM;
+ goto done;
+ }
+ name_id = kmalloc(count + 1, GFP_KERNEL);
+ if (name_id == NULL) {
+ printk(KERN_WARNING "%s: could not malloc\n", __FUNCTION__);
+ count = -ENOMEM;
+ goto done;
+ }
+ name_id[0] = '\0';
+
+ if (copy_from_user(buff_in, buffer, count)) {
+ count = -EFAULT;
+ goto done;
+ }
+ buff_in[count] = '\0';
+
+ /*
+ * For now, only support path state modification by passing in a
+ * long string. The input string must be 8 values of the form:
+ *
+ * name_id:host_no:chan:id:lun:path_state:path_failures:path_weight
+ *
+ * name_id is an ASCII string, each of the other values must be hex.
+ *
+ * For driverfs, maybe have each path have a field for each value of
+ * interest; this might be a bit execessive, as we will have (at
+ * least) 3 values for each path for each device, more if we also
+ * want to display non-writable values. For example, with 8 paths,
+ * this is 24 values per Scsi_Device; with 100 Scsi_Devices, this
+ * is 800 values (just path attributes).
+ */
+
+ /*
+ * Since sscanf stops parsing at a space character, pull out the
+ * name_id first.
+ */
+ ch_in = buff_in;
+ ch_out = name_id;
+ while (*ch_in != '\0' && *ch_in != ':')
+ *ch_out++ = *ch_in++;
+ *ch_out = '\0';
+
+ if (*ch_in == '\0') {
+ /*
+ * No ':' at all.
+ */
+ count = -EINVAL;
+ goto done;
+ }
+
+ res = sscanf(ch_in, ":%x:%x:%x:%x:%x:%x:%x", &host_no, &channel, &id,
+ &lun, (unsigned int *) &path_state, &path_failures,
+ &path_weight);
+ if ((res != 7) || ((path_state != SCSI_PATH_STATE_GOOD) &&
+ (path_state != SCSI_PATH_STATE_FAILING) &&
+ (path_state != SCSI_PATH_STATE_DEAD))) {
+ count = -EINVAL;
+ goto done;
+ }
+
+ sdev = scsi_lookup_id(name_id, NULL);
+ if (sdev != NULL)
+ scsi_paths_modify(sdev, host_no, channel, id, lun, path_state,
+ path_failures, path_weight);
+
+ done:
+ if (buff_in != NULL)
+ kfree(buff_in);
+ if (name_id != NULL)
+ kfree(name_id);
+ return count;
+}
+
+static int scsi_paths_proc_version_info(char *buffer, int *len, off_t *begin,
+ off_t offset, int size)
+{
+ PRINT_PROC("%s\n", SCSI_PATHS_VERSION);
+ return 1;
+}
+
+static int scsi_paths_proc_version_read(char *buffer, char **start,
+ off_t offset, int size, int *eof,
+ void *data)
+{
+ SP_PROC_READ_FN(scsi_paths_proc_version_info);
+}
+
+#endif /* CONFIG_PROC_FS */
diff -Nru a/drivers/scsi/scsi_paths.h b/drivers/scsi/scsi_paths.h
--- /dev/null Wed Dec 31 16:00:00 1969
+++ b/drivers/scsi/scsi_paths.h Mon Sep 16 16:11:26 2002
@@ -0,0 +1,152 @@
+/*
+ * Scsi Multi-path Support Library
+ *
+ * Copyright (c) 2002 IBM
+ *
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Send feedback to <[email protected]> and/or [email protected]
+ */
+
+#ifndef _SCSI_PATHS_H
+#define _SCSI_PATHS_H
+
+#include <linux/list.h>
+/*
+ * XXX asm/mmzone.h holds the xxx_to_nid and other information needed, but
+ * right now it is x86 specific, as well as patch specific, so a way is
+ * needed to correctly include it (rather than conditionally). The
+ * asm-i386 mmzone.h is created as part of the topo patch, and xxx_to_nid
+ * api's are added by pdorwin patch. The following is ugly and not even
+ * correct, since CONFIG_X86_NUMAQ means build for IBM NUMAQ box, it does
+ * not mean the API or even asm/mmzone.h exist.
+ */
+#ifdef CONFIG_X86_NUMAQ
+#include <asm/mmzone.h>
+#endif
+
+#ifndef MAX_NUMNODES
+/*
+ * NUMA API patches are not applied.
+ */
+#define MAX_NUMNODES 1
+#define req_to_nid(req) 0
+#define scsihost_to_node(host) 0
+#define page_to_nid(page) 0
+#endif
+
+#if MAX_NUMNODES == 0
+#error Unexpected value of MAX_NUMNODES
+#endif
+
+#if MAX_NUMNODES == 1
+/*
+ * No NUMA support is configured or no NUMA patches. Use one
+ * last active pointer (SCSI_MAX_ACTIVE_LIST), and have one
+ * (SCSI_PATHS_MAX_PATH_LISTS) list of active paths.
+ */
+#define SCSI_MAX_ACTIVE_LIST 1
+#define SCSI_PATHS_MAX_PATH_LISTS 1
+#else
+/*
+ * NUMA support is configured, use one last active pointer per maximum
+ * number of nodes plus one last active pointer for all paths
+ * (SCSI_MAX_ACTIVE_LIST), and have two lists of paths
+ * (SCSI_PATHS_MAX_PATH_LISTS) - one for all paths and one for node local
+ * paths.
+ */
+#define SCSI_MAX_ACTIVE_LIST (MAX_NUMNODES + 1)
+#define SCSI_PATHS_MAX_PATH_LISTS 2
+
+/*
+ * XXX use a real page_to_nid() API.
+ */
+#define page_to_nid(page) PFN_TO_NID(page_to_pfn(page))
+/*
+ * FIXME This does not properly handle requests with req->bio NULL, these are
+ * generated via scsi_do_req calls. Fix this by putting a node number in the
+ * request, and have scsi_do_req() and __make_request() set req->node.
+ */
+#define req_to_nid(req) \
+ (((req)->bio == NULL) ? 0 : page_to_nid(bio_page((req)->bio)))
+#endif
+
+#define SCSI_PATHS_ALL_LIST (SCSI_MAX_ACTIVE_LIST - 1)
+
+#define SCSI_ALL_ACTIVE_NEXT_LIST 0
+#define SCSI_NID_ACTIVE_NEXT_LIST 1
+
+/**
+ * enum scsi_path_policy - Path selection policy.
+ * @SCSI_PATH_POLICY_LPU: Use the same path that was used for the last IO
+ * @SCSI_PATH_POLICY_ROUND_ROBIN: Round robin across all active paths
+ **/
+enum scsi_path_policy {
+ SCSI_PATH_POLICY_LPU = 1,
+ SCSI_PATH_POLICY_ROUND_ROBIN
+};
+
+/**
+ * enum scsi_path_state - Path state flags
+ * @SCSI_PATH_STATE_GOOD: The path is active for IO.
+ * @SCSI_PATH_STATE_FAILING: The path might be failing (XXX not fully
+ * implemented yet)
+ * @SCSI_PATH_STATE_DEAD: The path has failed.
+ **/
+enum scsi_path_state {
+ SCSI_PATH_STATE_GOOD = 1,
+ SCSI_PATH_STATE_FAILING,
+ SCSI_PATH_STATE_DEAD
+};
+
+/**
+ * struct scsi_path - A single scsi path
+ * @sp_path_list: list head for all paths
+ * @sp_active_next: circular list of all active paths, list at 0 is for all
+ * active paths, the list at 1 for all active paths on a given node id.
+ * @sp_path_id: id of path (host/channel/target/lun)
+ * @sp_state: state of path
+ * @sp_failures: number of failures on this path
+ * @sp_weight: path weight 0 best, higher values worse (XXX path weighting
+ * is not implemented, for now setting this has no affect)
+ **/
+struct scsi_path {
+ struct list_head sp_path_list;
+ struct scsi_path *sp_active_next[SCSI_PATHS_MAX_PATH_LISTS];
+ struct scsi_path_id sp_path_id;
+ enum scsi_path_state sp_state;
+ unsigned int sp_failures;
+ unsigned int sp_weight;
+};
+
+/**
+ * struct scsi_mpath - All paths for a scsi device
+ * @scsi_mp_paths: list of scsi paths
+ * @scsi_mp_last_active: last active path used, one for each node, plus
+ * one for the all active list.
+ * @scsi_mp_path_policy: path selection policy - round robin or last used
+ * @scsi_mp_path_active_cnt: number of paths XXX change to an array
+ **/
+struct scsi_mpath {
+ struct list_head scsi_mp_paths_head;
+ struct scsi_path *scsi_mp_last_active[SCSI_MAX_ACTIVE_LIST];
+ unsigned int scsi_mp_path_active_cnt[SCSI_MAX_ACTIVE_LIST];
+ enum scsi_path_policy scsi_mp_path_policy;
+};
+
+#endif
diff -Nru a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
--- a/drivers/scsi/scsi_scan.c Mon Sep 16 16:11:26 2002
+++ b/drivers/scsi/scsi_scan.c Mon Sep 16 16:11:26 2002
@@ -1292,7 +1292,7 @@
static int scsi_add_lun(Scsi_Device *sdevscan, Scsi_Device **sdevnew,
Scsi_Request *sreq, char *inq_result, int *bflags)
{
- Scsi_Device *sdev;
+ Scsi_Device *sdev, *sdev_found;
struct Scsi_Device_Template *sdt;
char devname[64];
extern devfs_handle_t scsi_devfs_handle;
@@ -1344,6 +1344,27 @@
*/
scsi_load_identifier(sdev, sreq);

+ /*
+ * sdev is linked onto the list, so we need to let the lookup id
+ * know to skip sdev.
+ */
+ sdev_found = scsi_lookup_id(sdev->sdev_driverfs_dev.name, sdev);
+ if (sdev_found != NULL) {
+ /*
+ * Found a new path to an existing device, remove the just
+ * allocated sdev.
+ *
+ * FIXME for now don't add any driverfs info for the new path.
+ */
+ scsi_free_sdev(sdev);
+ (void)scsi_add_path(sdev_found, pathid.spi_shpnt,
+ pathid.spi_channel, pathid.spi_id,
+ pathid.spi_lun);
+ if (sdevnew != NULL)
+ *sdevnew = sdev_found;
+ return (SCSI_SCAN_TARGET_PRESENT | SCSI_SCAN_LUN_PRESENT);
+ }
+
if (*bflags & BLIST_ISROM) {
/*
* It would be better to modify sdev->type, and set
@@ -2020,8 +2041,14 @@
* plus have dynamic queue depth adjustment like the
* aic7xxx driver.
*/
+#ifndef CONFIG_SCSI_MULTI_PATH_IO
+ /*
+ * XXX this is broken anyway, Doug Ledford has a patch to
+ * call slave_attach, eventually merge or use slave_attach.
+ */
if (shost->select_queue_depths != NULL)
(shost->select_queue_depths) (shost, shost->host_queue);
+#endif
for (sdt = scsi_devicelist; sdt; sdt = sdt->next)
if (sdt->init && sdt->dev_noticed)
(*sdt->init) ();
diff -Nru a/drivers/scsi/sg.c b/drivers/scsi/sg.c
--- a/drivers/scsi/sg.c Mon Sep 16 16:11:26 2002
+++ b/drivers/scsi/sg.c Mon Sep 16 16:11:26 2002
@@ -2988,6 +2988,8 @@
PRINT_PROC(" >>> device=sg%d ", dev);
if (sdp->detached)
PRINT_PROC("detached pending close ");
+#ifndef CONFIG_SCSI_MULTI_PATH_IO
+ /* XXX multi-path TBD */
else
PRINT_PROC
("scsi%d chan=%d id=%d lun=%d em=%d",
@@ -2995,6 +2997,7 @@
scsidp->channel, scsidp->id,
scsidp->lun,
scsidp->host->hostt->emulated);
+#endif
PRINT_PROC(" sg_tablesize=%d excl=%d\n",
sdp->sg_tablesize, sdp->exclude);
}
@@ -3024,6 +3027,15 @@
for (j = 0; j < max_dev; ++j) {
sdp = sg_get_dev(j);
if (sdp && (scsidp = sdp->device) && (!sdp->detached))
+#ifdef CONFIG_SCSI_MULTI_PATH_IO
+ /* XXX multi-path TBD */
+ PRINT_PROC("%d\t%d\t%d\t%d\t%d\n",
+ (int)scsidp->type,
+ (int)scsidp->access_count,
+ (int)scsidp->queue_depth,
+ (int)scsidp->device_busy,
+ (int)scsidp->online);
+#else
PRINT_PROC("%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
scsidp->host->host_no, scsidp->channel,
scsidp->id, scsidp->lun, (int) scsidp->type,
@@ -3031,6 +3043,7 @@
(int) scsidp->queue_depth,
(int) scsidp->device_busy,
(int) scsidp->online);
+#endif
else
PRINT_PROC("-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\n");
}

2002-09-17 22:56:10

by Patrick Mansfield

[permalink] [raw]
Subject: [RFC] [PATCH] 6/7 2.5.35 SCSI multi-path

Patch to add 2.5 and/or multi-path support to the qla v6b5 adapter driver.

Requires the qla source - this is _not_ a patch for the mainline kernel.

Requires the scsi base changes.

Most of this is renaming the makefile, and creating a simple makefile
(thanks mikeand) that can easily be used with kbuild 2.5.

Download the qla v6b5 tar file, untar, create a src dir, untar the qla
source (yes it is a tar file within a tar file) into a directory named
something/src, and apply this patch.

To build as a module, cd to-your-kernel-source-with-mpath-patch and run:

make SUBDIRS=/something/src/


makefile | 151 ++--------------------------------------------------
makefile-orig | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
qla2x00.c | 45 ++++++++++++---
qla2x00_ioctl.c | 7 ++
4 files changed, 210 insertions(+), 155 deletions(-)

diff -urN -X /home/patman/dontdiff src-orig/makefile src/makefile
--- src-orig/makefile Tue Aug 20 02:43:18 2002
+++ src/makefile Thu Sep 12 15:39:54 2002
@@ -1,162 +1,10 @@
-#
-# Makefile 1.5.1 April 30, 2002
-#
-#
-# 1. To make UP version of ISP2200 driver
-# make qla2200.o
-#
-# 2. To make UP version of ISP2300 driver
-# make qla2300.o
-#
-# 3. To make UP version of ISP2200 and ISP2300 drivers
-# make all
-#or
-# make
-#
-# To make SMP version of any of the above drivers
-# append SMP=1 to one of the (3) make command lines above.
-# make ... SMP=1
-#
-# To make a new firmware file (FILE must be a *.c file of the fw object)
-# make fw FILE2=2200tp.c
-#
+# andmike's condensed makefile
+# To use cd to the kernel source and type the following.
+# make SUBDIRS=/path/to/here

-DRIVER=qla2200.o qla2300.o
+CFLAGS_qla2200.o = -I$(TOPDIR)/drivers/scsi
+CFLAGS_qla2300.o = -I$(TOPDIR)/drivers/scsi

-FILE2=2200tp.h
-FILE3=2300tp.h
+obj-m := qla2200.o qla2300.o

-HOSTTYPE := $(shell uname -m)
-
-#
-# f/W include files
-FWFILE2=ql2200_fw.h
-FWFILE3=ql2300_fw.h
-FWFILE2IP=ql2200ip_fw.h
-FWFILE3IP=ql2300ip_fw.h
-
-# Comment/uncomment the following line to enable/disable debugging
-DEBUGFLAG=y
-HSG80=n
-
-QL_DEBUG=0x6
-
-OSVER=linux-2.4
-
-# Change it here or specify it on the "make" commandline
-#(new)INCLUDEDIR = /lib/modules/`uname -r`/build/include
-INCLUDEDIR = /usr/src/$(OSVER)/include
-
-ifeq ($(DEBUGFLAG),y)
- DEBFLAGS = -O -g -DUDEBUG -DLINUX -Dlinux
-else
- DEBFLAGS = -O2 -DLINUX -Dlinux
-endif
-
-CFLAGS = -D__KERNEL__ -DMODULE -Wall $(DEBFLAGS) -DINTAPI -DEXPORT_SYMTAB
-#CFLAGS = -D__KERNEL__ -DMODULE -Wall $(DEBFLAGS) -DEXPORT_SYMTAB
-
-ifeq ($(HSG80),y)
-CFLAGS += -DCOMPAQ
-endif
-
-
-# set MODVERSIONS if the kernel uses it
-VERSUSED = $(shell grep 'define CONFIG_MODVERSIONS' \
- $(INCLUDEDIR)/linux/autoconf.h | wc -l | sed 's/ //g')
-VERSUSED = 1
-
-ifeq ($(VERSUSED),1)
-CFLAGS += -DMODVERSIONS -include $(INCLUDEDIR)/linux/modversions.h
-endif
-
-CFLAGS += -I$(INCLUDEDIR) -I$(INCLUDEDIR)/../drivers/scsi
-
-ifeq ($(HOSTTYPE),i386)
-CFLAGS += -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce \
--pipe -malign-loops=2 -malign-jumps=2 -malign-functions=2 \
--DCONFIG_X86_LOCAL_APIC -fno-strict-aliasing -fno-common \
--mpreferred-stack-boundary=2 -march=i386
-endif
-
-ifeq ($(HOSTTYPE),i486)
-CFLAGS += -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce \
--pipe -malign-loops=2 -malign-jumps=2 -malign-functions=2 \
--DCONFIG_X86_LOCAL_APIC -fno-strict-aliasing -fno-common \
--mpreferred-stack-boundary=2 -march=i486
-endif
-
-ifeq ($(HOSTTYPE),i586)
-CFLAGS += -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce \
--pipe -malign-loops=2 -malign-jumps=2 -malign-functions=2 \
--DCONFIG_X86_LOCAL_APIC -fno-strict-aliasing -fno-common \
--mpreferred-stack-boundary=2 -march=i586
-endif
-
-ifeq ($(HOSTTYPE),i686)
-CFLAGS += -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce \
--pipe -malign-loops=2 -malign-jumps=2 -malign-functions=2 \
--DCONFIG_X86_LOCAL_APIC -fno-strict-aliasing -fno-common \
--mpreferred-stack-boundary=2 -march=i686
-endif
-
-ifeq ($(HOSTTYPE),ia64)
-CFLAGS += -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce \
--pipe -DWORD_FW_LOAD
-endif
-
-ifeq ($(HOSTTYPE),alpha)
-CFLAGS += -D__alpha__ \
--Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce \
--pipe -fno-strict-aliasing -mno-fp-regs -ffixed-8 -mcpu=ev56 -Wa,-mev6
-endif
-
-ifeq ("1","$(IP)")
-CFLAGS += -DFC_IP_SUPPORT
-endif
-
-ifeq ("1","$(SMP)")
-CFLAGS += -D__SMP__ -DCONFIG_SMP
-endif
-
-COFLAGS = -kv
-
-MPATH = /lib/modules
-
-SRC_FILES=qla_settings.h qla2x00.h qla2x00.c qla_cfg.c qla_cfg.h qla_cfgln.c \
-qla_fo.h qla_fo.c qlfo.h qla2x00_ioctl.c qla_inioct.c \
-qla_mbx.c qla_mbx.h qla_debug.h qla_version.h makefile qla_ip.c qla_ip.h
-
-#
-# Where is all starts..
-#
-# -- default is always first.
-default: $(DRIVER)
-
-all: $(DRIVER)
-
-clean:
- rm -f $(DRIVER)
-
-install: $(DRIVER)
- REL=`uname -r | \
- sed -e 's/.*\"\(.*\)\".*/\1/'` ; \
- cp -p $(DRIVER) /lib/modules/$$REL/kernel/drivers/scsi
-
-fw:
- mv $(FILE2) $(FWFILE2)
- mv $(FILE3) $(FWFILE3)
-
-qla2100.o : $(SRC_FILES)
- $(CC) $(CFLAGS) -c qla2100.c -o $@
-
-qla2200.o : $(SRC_FILES) $(FWFILE2)
- $(CC) $(CFLAGS) -c qla2200.c -o $@
-
-qla2300.o : $(SRC_FILES) $(FWFILE3)
- $(CC) $(CFLAGS) -c qla2300.c -o $@
-
-# @echo "Editing file to produce -> [isp_fw.h]"
-# sh do_fw.sh $(FILE) cvtfw
-# @echo "Editing file to produce -> [isp1_fw.h]"
-# sh do_fw.sh $(FILE2) cvtfw22
+include $(TOPDIR)/Rules.make
diff -urN -X /home/patman/dontdiff src-orig/makefile-orig src/makefile-orig
--- src-orig/makefile-orig Wed Dec 31 16:00:00 1969
+++ src/makefile-orig Tue Aug 20 02:43:18 2002
@@ -0,0 +1,162 @@
+#
+# Makefile 1.5.1 April 30, 2002
+#
+#
+# 1. To make UP version of ISP2200 driver
+# make qla2200.o
+#
+# 2. To make UP version of ISP2300 driver
+# make qla2300.o
+#
+# 3. To make UP version of ISP2200 and ISP2300 drivers
+# make all
+#or
+# make
+#
+# To make SMP version of any of the above drivers
+# append SMP=1 to one of the (3) make command lines above.
+# make ... SMP=1
+#
+# To make a new firmware file (FILE must be a *.c file of the fw object)
+# make fw FILE2=2200tp.c
+#
+
+DRIVER=qla2200.o qla2300.o
+
+FILE2=2200tp.h
+FILE3=2300tp.h
+
+HOSTTYPE := $(shell uname -m)
+
+#
+# f/W include files
+FWFILE2=ql2200_fw.h
+FWFILE3=ql2300_fw.h
+FWFILE2IP=ql2200ip_fw.h
+FWFILE3IP=ql2300ip_fw.h
+
+# Comment/uncomment the following line to enable/disable debugging
+DEBUGFLAG=y
+HSG80=n
+
+QL_DEBUG=0x6
+
+OSVER=linux-2.4
+
+# Change it here or specify it on the "make" commandline
+#(new)INCLUDEDIR = /lib/modules/`uname -r`/build/include
+INCLUDEDIR = /usr/src/$(OSVER)/include
+
+ifeq ($(DEBUGFLAG),y)
+ DEBFLAGS = -O -g -DUDEBUG -DLINUX -Dlinux
+else
+ DEBFLAGS = -O2 -DLINUX -Dlinux
+endif
+
+CFLAGS = -D__KERNEL__ -DMODULE -Wall $(DEBFLAGS) -DINTAPI -DEXPORT_SYMTAB
+#CFLAGS = -D__KERNEL__ -DMODULE -Wall $(DEBFLAGS) -DEXPORT_SYMTAB
+
+ifeq ($(HSG80),y)
+CFLAGS += -DCOMPAQ
+endif
+
+
+# set MODVERSIONS if the kernel uses it
+VERSUSED = $(shell grep 'define CONFIG_MODVERSIONS' \
+ $(INCLUDEDIR)/linux/autoconf.h | wc -l | sed 's/ //g')
+VERSUSED = 1
+
+ifeq ($(VERSUSED),1)
+CFLAGS += -DMODVERSIONS -include $(INCLUDEDIR)/linux/modversions.h
+endif
+
+CFLAGS += -I$(INCLUDEDIR) -I$(INCLUDEDIR)/../drivers/scsi
+
+ifeq ($(HOSTTYPE),i386)
+CFLAGS += -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce \
+-pipe -malign-loops=2 -malign-jumps=2 -malign-functions=2 \
+-DCONFIG_X86_LOCAL_APIC -fno-strict-aliasing -fno-common \
+-mpreferred-stack-boundary=2 -march=i386
+endif
+
+ifeq ($(HOSTTYPE),i486)
+CFLAGS += -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce \
+-pipe -malign-loops=2 -malign-jumps=2 -malign-functions=2 \
+-DCONFIG_X86_LOCAL_APIC -fno-strict-aliasing -fno-common \
+-mpreferred-stack-boundary=2 -march=i486
+endif
+
+ifeq ($(HOSTTYPE),i586)
+CFLAGS += -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce \
+-pipe -malign-loops=2 -malign-jumps=2 -malign-functions=2 \
+-DCONFIG_X86_LOCAL_APIC -fno-strict-aliasing -fno-common \
+-mpreferred-stack-boundary=2 -march=i586
+endif
+
+ifeq ($(HOSTTYPE),i686)
+CFLAGS += -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce \
+-pipe -malign-loops=2 -malign-jumps=2 -malign-functions=2 \
+-DCONFIG_X86_LOCAL_APIC -fno-strict-aliasing -fno-common \
+-mpreferred-stack-boundary=2 -march=i686
+endif
+
+ifeq ($(HOSTTYPE),ia64)
+CFLAGS += -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce \
+-pipe -DWORD_FW_LOAD
+endif
+
+ifeq ($(HOSTTYPE),alpha)
+CFLAGS += -D__alpha__ \
+-Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce \
+-pipe -fno-strict-aliasing -mno-fp-regs -ffixed-8 -mcpu=ev56 -Wa,-mev6
+endif
+
+ifeq ("1","$(IP)")
+CFLAGS += -DFC_IP_SUPPORT
+endif
+
+ifeq ("1","$(SMP)")
+CFLAGS += -D__SMP__ -DCONFIG_SMP
+endif
+
+COFLAGS = -kv
+
+MPATH = /lib/modules
+
+SRC_FILES=qla_settings.h qla2x00.h qla2x00.c qla_cfg.c qla_cfg.h qla_cfgln.c \
+qla_fo.h qla_fo.c qlfo.h qla2x00_ioctl.c qla_inioct.c \
+qla_mbx.c qla_mbx.h qla_debug.h qla_version.h makefile qla_ip.c qla_ip.h
+
+#
+# Where is all starts..
+#
+# -- default is always first.
+default: $(DRIVER)
+
+all: $(DRIVER)
+
+clean:
+ rm -f $(DRIVER)
+
+install: $(DRIVER)
+ REL=`uname -r | \
+ sed -e 's/.*\"\(.*\)\".*/\1/'` ; \
+ cp -p $(DRIVER) /lib/modules/$$REL/kernel/drivers/scsi
+
+fw:
+ mv $(FILE2) $(FWFILE2)
+ mv $(FILE3) $(FWFILE3)
+
+qla2100.o : $(SRC_FILES)
+ $(CC) $(CFLAGS) -c qla2100.c -o $@
+
+qla2200.o : $(SRC_FILES) $(FWFILE2)
+ $(CC) $(CFLAGS) -c qla2200.c -o $@
+
+qla2300.o : $(SRC_FILES) $(FWFILE3)
+ $(CC) $(CFLAGS) -c qla2300.c -o $@
+
+# @echo "Editing file to produce -> [isp_fw.h]"
+# sh do_fw.sh $(FILE) cvtfw
+# @echo "Editing file to produce -> [isp1_fw.h]"
+# sh do_fw.sh $(FILE2) cvtfw22
diff -urN -X /home/patman/dontdiff src-orig/qla2x00.c src/qla2x00.c
--- src-orig/qla2x00.c Tue Aug 20 02:43:18 2002
+++ src/qla2x00.c Thu Sep 12 15:41:53 2002
@@ -2153,7 +2153,7 @@
struct Scsi_Host *host = ha->host;

host->can_queue = max_srbs; /* default value:-MAX_SRBS(4096) */
- host->cmd_per_lun = 1;
+ host->cmd_per_lun = 16;
host->select_queue_depths = qla2x00_select_queue_depth;
host->n_io_port = 0xFF;

@@ -3785,6 +3785,7 @@
*/

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,9)
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,33)
/* As mentioned in kernel/sched.c(RA).....
* Reparent the calling kernel thread to the init task.
*
@@ -3801,6 +3802,7 @@
*/
reparent_to_init();
#endif
+#endif

/*
* Set the name of this process.
@@ -4235,7 +4237,12 @@
if (!(ql2xmaxqdepth == 0 || ql2xmaxqdepth > 256))
device->queue_depth = ql2xmaxqdepth;
#endif
-
+#ifdef CONFIG_SCSI_MULTI_PATH_IO
+ printk(KERN_INFO "Enabled tagged queuing, queue depth %d for ",
+ device->queue_depth);
+ scsi_paths_printk(device, " ", "<%d:%d:%d:%d>");
+ printk("\n");
+#else
printk(KERN_INFO
"scsi(%d:%d:%d:%d): Enabled tagged queuing, "
"queue depth %d.\n",
@@ -4244,6 +4251,7 @@
device->id,
device->lun,
device->queue_depth);
+#endif
}

}
@@ -4260,15 +4268,18 @@
qla2x00_select_queue_depth(struct Scsi_Host *host, Scsi_Device *scsi_devs)
{
Scsi_Device *device;
+#ifdef CONFIG_SCSI_MULTI_PATH_IO
+ scsi_traverse_hndl_t strav_hndl;
+#endif
scsi_qla_host_t *p = (scsi_qla_host_t *) host->hostdata;

ENTER("qla2x00_select_queue_depth");
-
- for (device = scsi_devs; device != NULL; device = device->next) {
- if (device->host == host)
- qla2x00_device_queue_depth(p, device);
- }
-
+#ifdef CONFIG_SCSI_MULTI_PATH_IO
+ scsi_for_each_host_sdev(&strav_hndl, device, host->host_no)
+#else
+ for (device = scsi_devs; device != NULL; device = device->sdev_next)
+#endif
+ qla2x00_device_queue_depth(p, device);
LEAVE("qla2x00_select_queue_depth");
}

@@ -15502,9 +15513,23 @@
unsigned int cmd, unsigned long arg)
{
Scsi_Device fake_scsi_device;
+ int ret;
+
+#ifdef CONFIG_SCSI_MULTI_PATH_IO
+ if (scsi_add_path(&fake_scsi_device, apidev_host, 0,
+ apidev_host->this_id, 0))
+ return -ENODEV;
+#else
fake_scsi_device.host = apidev_host;
+#endif

- return (qla2x00_ioctl(&fake_scsi_device, (int)cmd, (void*)arg));
+ ret = qla2x00_ioctl(&fake_scsi_device, (int)cmd, (void*)arg);
+#ifdef CONFIG_SCSI_MULTI_PATH_IO
+ scsi_remove_path(&fake_scsi_device, SCSI_FIND_ALL_HOST_NO,
+ SCSI_FIND_ALL_CHANNEL, SCSI_FIND_ALL_ID,
+ SCSI_FIND_ALL_LUN);
+#endif
+ return ret;
}

static struct file_operations apidev_fops = {
@@ -15540,7 +15565,7 @@
APIDEV_NODE, apidev_major);)

proc_mknod(APIDEV_NODE, 0777+S_IFCHR, host->hostt->proc_dir,
- (kdev_t)MKDEV(apidev_major, 0));
+ (kdev_t)mk_kdev(apidev_major, 0));

return 0;
}
diff -urN -X /home/patman/dontdiff src-orig/qla2x00_ioctl.c src/qla2x00_ioctl.c
--- src-orig/qla2x00_ioctl.c Tue Aug 20 02:43:18 2002
+++ src/qla2x00_ioctl.c Wed Sep 4 17:19:14 2002
@@ -237,8 +237,13 @@
if (_IOC_TYPE(cmd) != QLMULTIPATH_MAGIC) {
return (-EINVAL);
}
-
+#ifdef CONFIG_SCSI_MULTI_PATH_IO
+ host = scsi_get_host(dev);
+ if (host == NULL)
+ return EXT_STATUS_ERR;
+#else
host = dev->host;
+#endif
ha = (scsi_qla_host_t *) host->hostdata; /* midlayer chosen instance */

ret = verify_area(VERIFY_READ, (void *)arg, sizeof(EXT_IOCTL));

2002-09-17 22:49:57

by Patrick Mansfield

[permalink] [raw]
Subject: [RFC] [PATCH] 5/7 2.5.35 SCSI multi-path

Patches to scsi adapter drivers (scsi low level drivers) to simplify and
enable the addition of scsi multi-path IO support.

This does not add multi-path support, it adds changes that simplify the
addition of the actual scsi multi-path code.

Only the adapters that have been run with the multi-path patch are
included here. Other adpaters will likely fail compilation.

This includes a change to the qlogicfc.c driver to flush all SCSI commands
on a loop down, so that driver is more functional with the multi-path
patch (when patched on top of this patch).

aic7xxx/aic7xxx_linux.c | 28 +++++++++++++++-----------
ips.c | 7 +++++-
qlogicfc.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 73 insertions(+), 13 deletions(-)

diff -Nru a/drivers/scsi/aic7xxx/aic7xxx_linux.c b/drivers/scsi/aic7xxx/aic7xxx_linux.c
--- a/drivers/scsi/aic7xxx/aic7xxx_linux.c Mon Sep 16 15:29:45 2002
+++ b/drivers/scsi/aic7xxx/aic7xxx_linux.c Mon Sep 16 15:29:45 2002
@@ -1457,15 +1457,14 @@
struct ahc_softc *ahc;
u_long flags;
int scbnum;
+ scsi_traverse_hndl_t strav_hndl;

ahc = *((struct ahc_softc **)host->hostdata);
ahc_lock(ahc, &flags);
scbnum = 0;
- for (device = scsi_devs; device != NULL; device = device->next) {
- if (device->host == host) {
- ahc_linux_device_queue_depth(ahc, device);
- scbnum += device->queue_depth;
- }
+ scsi_for_each_host_sdev(&strav_hndl, device, host->host_no) {
+ ahc_linux_device_queue_depth(ahc, device);
+ scbnum += device->queue_depth;
}
ahc_unlock(ahc, &flags);
}
@@ -1480,11 +1479,14 @@
struct ahc_initiator_tinfo *targ_info;
struct ahc_tmode_tstate *tstate;
uint8_t tags;
+ struct scsi_path_id scsi_path;
+
+ scsi_get_path(device, &scsi_path);

ahc_compile_devinfo(&devinfo,
- device->channel == 0 ? ahc->our_id : ahc->our_id_b,
- device->id, device->lun,
- device->channel == 0 ? 'A' : 'B',
+ scsi_path.spi_channel == 0 ? ahc->our_id : ahc->our_id_b,
+ scsi_path.spi_id, scsi_path.spi_lun,
+ scsi_path.spi_channel == 0 ? 'A' : 'B',
ROLE_INITIATOR);
targ_info = ahc_fetch_transinfo(ahc, devinfo.channel,
devinfo.our_scsiid,
@@ -1515,8 +1517,8 @@
device->queue_depth = tags;
ahc_set_tags(ahc, &devinfo, AHC_QUEUE_TAGGED);
printf("scsi%d:%c:%d:%d: Tagged Queuing enabled. Depth %d\n",
- ahc->platform_data->host->host_no, device->channel + 'A',
- device->id, device->lun, tags);
+ ahc->platform_data->host->host_no, scsi_path.spi_channel + 'A',
+ scsi_path.spi_id, scsi_path.spi_lun, tags);
} else {
/*
* We allow the OS to queue 2 untagged transactions to
@@ -2735,8 +2737,10 @@
int extended;
struct ahc_softc *ahc;
unsigned char *buf;
+ struct scsi_path_id scsi_path;

- ahc = *((struct ahc_softc **)disk->device->host->hostdata);
+ scsi_get_path(disk->device, &scsi_path);
+ ahc = *((struct ahc_softc **)scsi_path.spi_shpnt->hostdata);
buf = scsi_bios_ptable(bdev);

if (buf) {
@@ -2752,7 +2756,7 @@

if (aic7xxx_extended != 0)
extended = 1;
- else if (disk->device->channel == 0)
+ else if (scsi_path.spi_channel == 0)
extended = (ahc->flags & AHC_EXTENDED_TRANS_A) != 0;
else
extended = (ahc->flags & AHC_EXTENDED_TRANS_B) != 0;
diff -Nru a/drivers/scsi/ips.c b/drivers/scsi/ips.c
--- a/drivers/scsi/ips.c Mon Sep 16 15:29:45 2002
+++ b/drivers/scsi/ips.c Mon Sep 16 15:29:45 2002
@@ -1858,10 +1858,15 @@
int heads;
int sectors;
int cylinders;
+ struct Scsi_Host *host;

METHOD_TRACE("ips_biosparam", 1);

- ha = (ips_ha_t *) disk->device->host->hostdata;
+ host = scsi_get_host(disk->device);
+ if (host == NULL)
+ return (0);
+
+ ha = (ips_ha_t *) host->hostdata;

if (!ha)
/* ?!?! host adater info invalid */
diff -Nru a/drivers/scsi/qlogicfc.c b/drivers/scsi/qlogicfc.c
--- a/drivers/scsi/qlogicfc.c Mon Sep 16 15:29:45 2002
+++ b/drivers/scsi/qlogicfc.c Mon Sep 16 15:29:45 2002
@@ -679,6 +679,8 @@
static void isp2x00_print_status_entry(struct Status_Entry *);
#endif

+static void do_isp2x00_flush_all_SCSI_cmds(unsigned long arg);
+
static inline void isp2x00_enable_irqs(struct Scsi_Host *host)
{
outw(ISP_EN_INT | ISP_EN_RISC, host->io_port + PCI_INTER_CTL);
@@ -1154,6 +1156,13 @@

DEBUG(isp2x00_print_scsi_cmd(Cmnd));

+ if (hostdata->adapter_state == AS_LOOP_DOWN) {
+ Cmnd->result = DID_NO_CONNECT << 16;
+ if (Cmnd->scsi_done)
+ (*Cmnd->scsi_done) (Cmnd);
+ return 0;
+ }
+
if (hostdata->adapter_state & AS_REDO_FABRIC_PORTDB || hostdata->adapter_state & AS_REDO_LOOP_PORTDB) {
isp2x00_make_portdb(host);
hostdata->adapter_state = AS_LOOP_GOOD;
@@ -1469,6 +1478,7 @@
case LOOP_DOWN:
printk("qlogicfc%d : Link is Down\n", hostdata->host_id);
hostdata->adapter_state = AS_LOOP_DOWN;
+ do_isp2x00_flush_all_SCSI_cmds((unsigned long) host);
break;
case CONNECTION_MODE:
printk("received CONNECTION_MODE irq %x\n", inw(host->io_port + MBOX1));
@@ -2222,6 +2232,47 @@
}

#endif /* DEBUG_ISP2x00 */
+
+/*
+ * Flush all SCSI commands we have.
+ */
+void do_isp2x00_flush_all_SCSI_cmds(unsigned long arg)
+{
+ struct Scsi_Host * host = (struct Scsi_Host *) arg;
+ struct isp2x00_hostdata * hostdata;
+ int i;
+
+ hostdata = (struct isp2x00_hostdata *) host->hostdata;
+
+ printk("qlogicfc%d : Flushing all SCSI commands?\n", hostdata->host_id);
+ for (i = 0; i < QLOGICFC_REQ_QUEUE_LEN; i++){
+ if (hostdata->handle_ptrs[i] ){
+ Scsi_Cmnd *Cmnd = hostdata->handle_ptrs[i];
+
+ if (Cmnd->use_sg)
+ pci_unmap_sg(hostdata->pci_dev,
+ (struct scatterlist *)Cmnd->buffer,
+ Cmnd->use_sg,
+ scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
+ else if (Cmnd->request_bufflen &&
+ Cmnd->sc_data_direction != PCI_DMA_NONE) {
+ pci_unmap_page(hostdata->pci_dev,
+ Cmnd->SCp.dma_handle,
+ Cmnd->request_bufflen,
+ scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
+ }
+
+ Cmnd->result = DID_NO_CONNECT << 16;
+
+ if (Cmnd->scsi_done){
+ (*Cmnd->scsi_done) (Cmnd);
+ }
+ else printk("qlogicfc%d : done is null?\n", hostdata->host_id);
+ hostdata->handle_ptrs[i] = NULL;
+ hostdata->handle_serials[i] = 0;
+ }
+ }
+}

MODULE_LICENSE("GPL");

2002-09-17 22:49:58

by Patrick Mansfield

[permalink] [raw]
Subject: [RFC] [PATCH] 3/7 2.5.35 SCSI multi-path

Patch 3 of 3 to add mid-layer scsi changes to simplify and enable the
addition of scsi multi-path IO support.

This does not add multi-path support, it adds changes that simplify the
addition of the actual scsi multi-path code.

drivers/scsi/scsi_merge.c | 59 +++++++
drivers/scsi/scsi_proc.c | 8 -
drivers/scsi/scsi_queue.c | 29 +++
drivers/scsi/scsi_scan.c | 361 +++++++++++++++++++++++-----------------------
drivers/scsi/scsi_syms.c | 6
include/scsi/scsi.h | 23 ++
6 files changed, 303 insertions(+), 183 deletions(-)

diff -Nru a/drivers/scsi/scsi_merge.c b/drivers/scsi/scsi_merge.c
--- a/drivers/scsi/scsi_merge.c Mon Sep 16 15:29:45 2002
+++ b/drivers/scsi/scsi_merge.c Mon Sep 16 15:29:45 2002
@@ -22,6 +22,7 @@
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/slab.h>
+#include <linux/bio.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/stat.h>
@@ -117,6 +118,58 @@
}

/*
+ * Function: scsi_cleanup_io()
+ *
+ * Purpose: SCSI I/O cleanup io function.
+ *
+ * Arguments: SCpnt - Command descriptor we wish to cleanup
+ *
+ * Returns: N/A
+ *
+ * Lock status:
+ *
+ * Notes: Code was previously part of scsi_io_completion, moved here
+ * as we now also call it when redriving IO that does not make
+ * it to the upper layer.
+ */
+void scsi_cleanup_io(Scsi_Cmnd *SCpnt)
+{
+ struct request *req = SCpnt->request;
+
+ /*
+ * Free up any indirection buffers we allocated for DMA purposes.
+ * For the case of a READ, we need to copy the data out of the
+ * bounce buffer and into the real buffer.
+ */
+ if (SCpnt->use_sg) {
+ struct scatterlist *sgpnt;
+
+ sgpnt = (struct scatterlist *) SCpnt->buffer;
+ scsi_free_sgtable(SCpnt->buffer, SCpnt->sglist_len);
+ } else {
+ if (SCpnt->buffer != req->buffer) {
+ if (rq_data_dir(req) == READ) {
+ unsigned long flags;
+ char *to = bio_kmap_irq(req->bio, &flags);
+
+ memcpy(to, SCpnt->buffer, SCpnt->bufflen);
+ bio_kunmap_irq(to, &flags);
+ }
+ kfree(SCpnt->buffer);
+ }
+ }
+
+ /*
+ * Zero these out. They now point to freed memory, and it is
+ * dangerous to hang onto the pointers.
+ */
+ SCpnt->buffer = NULL;
+ SCpnt->bufflen = 0;
+ SCpnt->request_buffer = NULL;
+ SCpnt->request_bufflen = 0;
+}
+
+/*
* Function: scsi_initialize_merge_fn()
*
* Purpose: Initialize merge function for a host
@@ -131,16 +184,18 @@
*/
void scsi_initialize_merge_fn(Scsi_Device * SDpnt)
{
- struct Scsi_Host *SHpnt = SDpnt->host;
+ struct Scsi_Host *SHpnt;
request_queue_t *q = &SDpnt->request_queue;
u64 bounce_limit;

+ SHpnt = scsi_get_host(SDpnt);
+ BUG_ON(SHpnt == NULL);
/*
* The generic merging functions work just fine for us.
* Enable highmem I/O, if appropriate.
*/
bounce_limit = BLK_BOUNCE_HIGH;
- if (SHpnt->highmem_io && (SDpnt->type == TYPE_DISK)) {
+ if (SHpnt->highmem_io) {
if (!PCI_DMA_BUS_IS_PHYS)
/* Platforms with virtual-DMA translation
* hardware have no practical limit.
diff -Nru a/drivers/scsi/scsi_proc.c b/drivers/scsi/scsi_proc.c
--- a/drivers/scsi/scsi_proc.c Mon Sep 16 15:29:45 2002
+++ b/drivers/scsi/scsi_proc.c Mon Sep 16 15:29:45 2002
@@ -255,15 +255,15 @@
return (cmdIndex);
}

-void proc_print_scsidevice(Scsi_Device * scd, char *buffer, int *size, int len)
+void proc_print_scsidevice(Scsi_Device *scd, char *buffer, int *size, int len)
{

int x, y = *size;
extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE];

- y = sprintf(buffer + len,
- "Host: scsi%d Channel: %02d Id: %02d Lun: %02d\n Vendor: ",
- scd->host->host_no, scd->channel, scd->id, scd->lun);
+ y = scsi_paths_proc_print_paths(scd, buffer + len,
+ "Host: scsi%d Channel: %02d Id: %02d Lun: %02d\n");
+ y += sprintf(buffer + len + y, "Vendor: ");
for (x = 0; x < 8; x++) {
if (scd->vendor[x] >= 0x20)
y += sprintf(buffer + len + y, "%c", scd->vendor[x]);
diff -Nru a/drivers/scsi/scsi_queue.c b/drivers/scsi/scsi_queue.c
--- a/drivers/scsi/scsi_queue.c Mon Sep 16 15:29:45 2002
+++ b/drivers/scsi/scsi_queue.c Mon Sep 16 15:29:45 2002
@@ -76,7 +76,8 @@
*/
int scsi_mlqueue_insert(Scsi_Cmnd * cmd, int reason)
{
- struct Scsi_Host *host;
+ struct Scsi_Host *host;
+ unsigned long flags;

SCSI_LOG_MLQUEUE(1, printk("Inserting command %p into mlqueue\n", cmd));

@@ -106,7 +107,7 @@
}
}
host->host_blocked = TRUE;
- } else {
+ } else if (reason == SCSI_MLQUEUE_DEVICE_BUSY) {
/*
* Protect against race conditions. If the device isn't busy,
* assume that something actually completed, and that we should
@@ -121,6 +122,11 @@
}
}
cmd->device->device_blocked = TRUE;
+ } else {
+ /*
+ * Must be SCSI_MLQUEUE_RETRY: requeue without marking
+ * host or device as busy.
+ */
}

/*
@@ -134,12 +140,27 @@
* Decrement the counters, since these commands are no longer
* active on the host/device.
*/
+ spin_lock_irqsave(cmd->device->request_queue.queue_lock, flags);
+ cmd->device->device_busy--; /* XXX race */
scsi_host_busy_dec_and_test(cmd->host, cmd->device);
-
+ spin_unlock_irqrestore(cmd->device->request_queue.queue_lock, flags);
/*
* Insert this command at the head of the queue for it's device.
* It will go before all other commands that are already in the queue.
*/
- scsi_insert_special_cmd(cmd, 1);
+ if (cmd->request->flags & REQ_SPECIAL) {
+ /*
+ * Calling this for REQ_CMD requests is fatal.
+ */
+ scsi_insert_special_cmd(cmd, 1);
+ } else {
+ /*
+ * Clean up the IO, and requeue it without setting cmd to
+ * REQ_SPECIAL.
+ */
+ scsi_cleanup_io(cmd);
+ scsi_queue_next_request(&cmd->device->request_queue, cmd,
+ cmd->host);
+ }
return 0;
}
diff -Nru a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
--- a/drivers/scsi/scsi_scan.c Mon Sep 16 15:29:45 2002
+++ b/drivers/scsi/scsi_scan.c Mon Sep 16 15:29:45 2002
@@ -189,26 +189,22 @@
" SCSI scanning, some SCSI devices might not be configured\n"

/*
- * Prefix values for the SCSI id's (stored in driverfs name field)
- */
-#define SCSI_UID_SER_NUM 'S'
-#define SCSI_UID_UNKNOWN 'Z'
-
-/*
- * Return values of some of the scanning functions.
+ * Return values for (some of) the scanning functions, these are masks.
*
* SCSI_SCAN_NO_RESPONSE: no valid response received from the target, this
* includes allocation or general failures preventing IO from being sent.
*
- * SCSI_SCAN_TARGET_PRESENT: target responded, but no device is available
- * on the given LUN.
+ * SCSI_SCAN_TARGET_PRESENT: target responded, a device may or may not be
+ * present on a given LUN in such cases.
+ *
+ * SCSI_SCAN_LUN_PRESENT: A device is available on a given LUN.
*
- * SCSI_SCAN_LUN_PRESENT: target responded, and a device is available on a
- * given LUN.
+ * SCSI_SCAN_NEW_LUN: This is a pre-existing device with a new path.
*/
-#define SCSI_SCAN_NO_RESPONSE 0
-#define SCSI_SCAN_TARGET_PRESENT 1
-#define SCSI_SCAN_LUN_PRESENT 2
+#define SCSI_SCAN_NO_RESPONSE 0x00
+#define SCSI_SCAN_TARGET_PRESENT 0x01
+#define SCSI_SCAN_LUN_PRESENT 0x02
+#define SCSI_SCAN_NEW_LUN 0x04

static char *scsi_null_device_strs = "nullnullnullnull";

@@ -291,15 +287,11 @@
**/
static void scsi_unlock_floptical(Scsi_Request *sreq, unsigned char *result)
{
- Scsi_Device *sdscan = sreq->sr_device;
unsigned char scsi_cmd[MAX_COMMAND_SIZE];

printk(KERN_NOTICE "scsi: unlocking floptical drive\n");
scsi_cmd[0] = MODE_SENSE;
- if (sdscan->scsi_level <= SCSI_2)
- scsi_cmd[1] = (sdscan->lun << 5) & 0xe0;
- else
- scsi_cmd[1] = 0;
+ scsi_cmd[1] = 0;
scsi_cmd[2] = 0x2e;
scsi_cmd[3] = 0;
scsi_cmd[4] = 0x2a; /* size */
@@ -490,9 +482,8 @@
* scsi_alloc_sdev - allocate and setup a Scsi_Device
*
* Description:
- * Allocate, initialize for io, and return a pointer to a Scsi_Device.
- * Stores the @shost, @channel, @id, and @lun in the Scsi_Device, and
- * adds Scsi_Device to the appropriate list.
+ * Allocate, initialize for io, add to the device list, and return a
+ * pointer to a Scsi_Device.
*
* Return value:
* Scsi_Device pointer, or NULL on failure.
@@ -510,10 +501,16 @@
sdev->vendor = scsi_null_device_strs;
sdev->model = scsi_null_device_strs;
sdev->rev = scsi_null_device_strs;
- sdev->host = shost;
- sdev->id = id;
- sdev->lun = lun;
- sdev->channel = channel;
+
+ /*
+ * XXX move this out, if it is overly redundant; if so, be
+ * sure to call scsi_add_path somewhere, or modify
+ * scsi_replace_path.
+ */
+ if (scsi_add_path(sdev, shost, channel, id, lun)) {
+ kfree(sdev);
+ return (NULL);
+ }
sdev->online = TRUE;
/*
* Some low level driver could use device->type
@@ -531,17 +528,7 @@
scsi_initialize_merge_fn(sdev);
init_waitqueue_head(&sdev->scpnt_wait);

- /*
- * Add it to the end of the shost->host_queue list.
- */
- if (shost->host_queue != NULL) {
- sdev->prev = shost->host_queue;
- while (sdev->prev->next != NULL)
- sdev->prev = sdev->prev->next;
- sdev->prev->next = sdev;
- } else
- shost->host_queue = sdev;
-
+ scsi_add_scsi_device(sdev, shost);
}
return (sdev);
}
@@ -556,17 +543,13 @@
**/
static void scsi_free_sdev(Scsi_Device *sdev)
{
- if (sdev->prev != NULL)
- sdev->prev->next = sdev->next;
- else
- sdev->host->host_queue = sdev->next;
- if (sdev->next != NULL)
- sdev->next->prev = sdev->prev;
-
+ /*
+ * XXX move the following to scsi_remove_scsi_device?
+ */
+ scsi_remove_path(sdev, SCSI_FIND_ALL_HOST_NO, SCSI_FIND_ALL_CHANNEL,
+ SCSI_FIND_ALL_ID, SCSI_FIND_ALL_LUN);
blk_cleanup_queue(&sdev->request_queue);
- if (sdev->inquiry != NULL)
- kfree(sdev->inquiry);
- kfree(sdev);
+ scsi_remove_scsi_device(sdev);
}

/**
@@ -585,11 +568,10 @@
static int scsi_check_id_size(Scsi_Device *sdev, int size)
{
if (size > DEVICE_NAME_SIZE) {
- printk(KERN_WARNING "scsi scan: host %d channel %d id %d lun %d"
- " identifier too long, length %d, max %d. Device might"
- " be improperly identified.\n", sdev->host->host_no,
- sdev->channel, sdev->id, sdev->lun, size,
- DEVICE_NAME_SIZE);
+ printk(KERN_WARNING "scsi scan: ");
+ scsi_paths_printk(sdev, " ", "host %d channel %d id %d lun %d");
+ printk(" identifier too long, length %d, max %d. Device might"
+ " be improperly identified.\n", size, DEVICE_NAME_SIZE);
return 1;
} else
return 0;
@@ -607,32 +589,28 @@
* Return:
* A pointer to data containing the results on success, else NULL.
**/
-unsigned char *scsi_get_evpd_page(Scsi_Device *sdev, Scsi_Request *sreq)
+unsigned char *scsi_get_evpd_page(Scsi_Device *sdev, Scsi_Request *sreq,
+ struct scsi_path_id *pathid)
{
unsigned char *evpd_page;
unsigned char scsi_cmd[MAX_COMMAND_SIZE];
- int lun = sdev->lun;
- int scsi_level = sdev->scsi_level;
int max_lgth = 255;

retry:
evpd_page = kmalloc(max_lgth, GFP_ATOMIC |
- (sdev->host->unchecked_isa_dma) ?
+ (pathid->spi_shpnt->unchecked_isa_dma) ?
GFP_DMA : 0);
if (!evpd_page) {
- printk(KERN_WARNING "scsi scan: Allocation failure identifying"
- " host %d channel %d id %d lun %d, device might be"
- " improperly identified.\n", sdev->host->host_no,
- sdev->channel, sdev->id, sdev->lun);
+ printk(KERN_WARNING "scsi scan: Allocation failure identifying ");
+ scsi_paths_printk(sdev, " ", "host %d channel %d id %d lun %d");
+ printk("device might be improperly identified.\n");
return NULL;
}
+ memset(evpd_page, 0, max_lgth);

memset(scsi_cmd, 0, MAX_COMMAND_SIZE);
scsi_cmd[0] = INQUIRY;
- if ((lun > 0) && (scsi_level <= SCSI_2))
- scsi_cmd[1] = ((lun << 5) & 0xe0) | 0x01;
- else
- scsi_cmd[1] = 0x01; /* SCSI_3 and higher, don't touch */
+ scsi_cmd[1] = 0x01;
scsi_cmd[4] = max_lgth;
sreq->sr_cmd_len = 0;
sreq->sr_sense_buffer[0] = 0;
@@ -865,33 +843,30 @@
* 0: Failure
* 1: Success
**/
-int scsi_get_deviceid(Scsi_Device *sdev, Scsi_Request *sreq)
+int scsi_get_deviceid(Scsi_Device *sdev, Scsi_Request *sreq,
+ struct scsi_path_id *pathid)
{
unsigned char *id_page;
unsigned char scsi_cmd[MAX_COMMAND_SIZE];
int id_idx, scnt, ret;
- int lun = sdev->lun;
- int scsi_level = sdev->scsi_level;
int max_lgth = 255;

+
retry:
id_page = kmalloc(max_lgth, GFP_ATOMIC |
- (sdev->host->unchecked_isa_dma) ?
+ (pathid->spi_shpnt->unchecked_isa_dma) ?
GFP_DMA : 0);
if (!id_page) {
- printk(KERN_WARNING "scsi scan: Allocation failure identifying"
- " host %d channel %d id %d lun %d, device might be"
- " improperly identified.\n", sdev->host->host_no,
- sdev->channel, sdev->id, sdev->lun);
+ printk(KERN_WARNING "scsi scan: Allocation failure identifying ");
+ scsi_paths_printk(sdev, " ", "host %d channel %d id %d lun %d");
+ printk("device might be improperly identified.\n");
return 0;
}
+ memset(id_page, 0, max_lgth);

memset(scsi_cmd, 0, MAX_COMMAND_SIZE);
scsi_cmd[0] = INQUIRY;
- if ((lun > 0) && (scsi_level <= SCSI_2))
- scsi_cmd[1] = ((lun << 5) & 0xe0) | 0x01;
- else
- scsi_cmd[1] = 0x01; /* SCSI_3 and higher, don't touch */
+ scsi_cmd[1] = 0x01;
scsi_cmd[2] = 0x83;
scsi_cmd[4] = max_lgth;
sreq->sr_cmd_len = 0;
@@ -930,8 +905,9 @@
SCSI_LOG_SCAN_BUS(4, printk(KERN_INFO
"scsi scan: host %d channel %d id %d lun %d"
" used id desc %d/%d/%d\n",
- sdev->host->host_no, sdev->channel,
- sdev->id, sdev->lun,
+ pathid->spi_shpnt->host_no,
+ pathid->spi_channel, pathid->spi_id,
+ pathid->spi_lun,
id_search_list[id_idx].id_type,
id_search_list[id_idx].naa_type,
id_search_list[id_idx].code_set));
@@ -941,8 +917,9 @@
SCSI_LOG_SCAN_BUS(4, printk(KERN_INFO
"scsi scan: host %d channel %d id %d lun %d"
" no match/error id desc %d/%d/%d\n",
- sdev->host->host_no, sdev->channel,
- sdev->id, sdev->lun,
+ pathid->spi_shpnt->host_no,
+ pathid->spi_channel, pathid->spi_id,
+ pathid->spi_lun,
id_search_list[id_idx].id_type,
id_search_list[id_idx].naa_type,
id_search_list[id_idx].code_set));
@@ -973,32 +950,28 @@
* 0: Failure
* 1: Success
**/
-int scsi_get_serialnumber(Scsi_Device *sdev, Scsi_Request *sreq)
+int scsi_get_serialnumber(Scsi_Device *sdev, Scsi_Request *sreq,
+ struct scsi_path_id *pathid)
{
unsigned char *serialnumber_page;
unsigned char scsi_cmd[MAX_COMMAND_SIZE];
- int lun = sdev->lun;
- int scsi_level = sdev->scsi_level;
int max_lgth = 255;

retry:
serialnumber_page = kmalloc(max_lgth, GFP_ATOMIC |
- (sdev->host->unchecked_isa_dma) ?
+ (pathid->spi_shpnt->unchecked_isa_dma) ?
GFP_DMA : 0);
if (!serialnumber_page) {
- printk(KERN_WARNING "scsi scan: Allocation failure identifying"
- " host %d channel %d id %d lun %d, device might be"
- " improperly identified.\n", sdev->host->host_no,
- sdev->channel, sdev->id, sdev->lun);
+ printk(KERN_WARNING "scsi scan: Allocation failure identifying ");
+ scsi_paths_printk(sdev, " ", "host %d channel %d id %d lun %d");
+ printk("device might be improperly identified.\n");
return 0;
}
+ memset(serialnumber_page, 0, max_lgth);

memset(scsi_cmd, 0, MAX_COMMAND_SIZE);
scsi_cmd[0] = INQUIRY;
- if ((lun > 0) && (scsi_level <= SCSI_2))
- scsi_cmd[1] = ((lun << 5) & 0xe0) | 0x01;
- else
- scsi_cmd[1] = 0x01; /* SCSI_3 and higher, don't touch */
+ scsi_cmd[1] = 0x01;
scsi_cmd[2] = 0x80;
scsi_cmd[4] = max_lgth;
sreq->sr_cmd_len = 0;
@@ -1093,14 +1066,20 @@
{
unsigned char *evpd_page = NULL;
int cnt;
+ struct scsi_path_id pathid;

memset(sdev->sdev_driverfs_dev.name, 0, DEVICE_NAME_SIZE);
- evpd_page = scsi_get_evpd_page(sdev, sreq);
+ if (scsi_get_path(sdev, &pathid)) {
+ printk(KERN_ERR "scsi scan: no path to scan device.\n");
+ scsi_get_default_name(sdev);
+ return;
+ }
+ evpd_page = scsi_get_evpd_page(sdev, sreq, &pathid);
if (evpd_page == NULL) {
/*
* try to obtain serial number anyway
*/
- (void)scsi_get_serialnumber(sdev, sreq);
+ (void)scsi_get_serialnumber(sdev, sreq, &pathid);
} else {
/*
* XXX search high to low, since the pages are lowest to
@@ -1108,23 +1087,25 @@
*/
for(cnt = 4; cnt <= evpd_page[3] + 3; cnt++)
if (evpd_page[cnt] == 0x83)
- if (scsi_get_deviceid(sdev, sreq))
+ if (scsi_get_deviceid(sdev, sreq, &pathid))
goto leave;

for(cnt = 4; cnt <= evpd_page[3] + 3; cnt++)
if (evpd_page[cnt] == 0x80)
- if (scsi_get_serialnumber(sdev, sreq))
+ if (scsi_get_serialnumber(sdev, sreq, &pathid))
goto leave;

- if (sdev->sdev_driverfs_dev.name[0] == 0)
- scsi_get_default_name(sdev);

}
+ if (sdev->sdev_driverfs_dev.name[0] == 0)
+ scsi_get_default_name(sdev);
leave:
if (evpd_page) kfree(evpd_page);
- SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: host %d channel %d"
- " id %d lun %d name/id: '%s'\n", sdev->host->host_no,
- sdev->channel, sdev->id, sdev->lun, sdev->sdev_driverfs_dev.name));
+ SCSI_LOG_SCAN_BUS(3, {
+ printk(KERN_INFO "scsi scan: ");
+ scsi_paths_printk(sdev, " ", "host %d channel %d id %d lun %d");
+ printk(" name/id: '%s'\n", sdev->sdev_driverfs_dev.name);
+ });
return;
}

@@ -1143,9 +1124,10 @@
{
int res = SCSI_2;
Scsi_Device *sdev;
+ scsi_traverse_hndl_t strav_hndl;

- for (sdev = shost->host_queue; sdev; sdev = sdev->next)
- if ((id == sdev->id) && (channel == sdev->channel))
+ scsi_for_each_sdev_lun (&strav_hndl, sdev, shost->host_no,
+ channel, id)
return (int) sdev->scsi_level;
/*
* FIXME No matching target id is configured, this needs to get
@@ -1175,14 +1157,13 @@
unsigned char scsi_cmd[MAX_COMMAND_SIZE];
int possible_inq_resp_len;

- SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY to host %d"
- " channel %d id %d lun %d\n", sdev->host->host_no,
- sdev->channel, sdev->id, sdev->lun));
+ SCSI_LOG_SCAN_BUS(3, {
+ printk(KERN_INFO "scsi scan: INQUIRY to ");
+ scsi_paths_printk(sdev, " ", "host %d channel %d id %d lun %d\n");
+ });

memset(scsi_cmd, 0, 6);
scsi_cmd[0] = INQUIRY;
- if ((sdev->lun > 0) && (sdev->scsi_level <= SCSI_2))
- scsi_cmd[1] = (sdev->lun << 5) & 0xe0;
scsi_cmd[4] = 36; /* issue conservative alloc_length */
sreq->sr_cmd_len = 0;
sreq->sr_data_direction = SCSI_DATA_READ;
@@ -1230,8 +1211,6 @@
if (possible_inq_resp_len > 36) { /* do additional INQUIRY */
memset(scsi_cmd, 0, 6);
scsi_cmd[0] = INQUIRY;
- if ((sdev->lun > 0) && (sdev->scsi_level <= SCSI_2))
- scsi_cmd[1] = (sdev->lun << 5) & 0xe0;
scsi_cmd[4] = (unsigned char) possible_inq_resp_len;
sreq->sr_cmd_len = 0;
sreq->sr_data_direction = SCSI_DATA_READ;
@@ -1308,9 +1287,7 @@
* NULL, store the address of the new Scsi_Device in *@sdevnew (needed
* when scanning a particular LUN).
*
- * Return:
- * SCSI_SCAN_NO_RESPONSE: could not allocate or setup a Scsi_Device
- * SCSI_SCAN_LUN_PRESENT: a new Scsi_Device was allocated and initialized
+ * Returns a SCSI_SCAN_ mask value.
**/
static int scsi_add_lun(Scsi_Device *sdevscan, Scsi_Device **sdevnew,
Scsi_Request *sreq, char *inq_result, int *bflags)
@@ -1319,9 +1296,19 @@
struct Scsi_Device_Template *sdt;
char devname[64];
extern devfs_handle_t scsi_devfs_handle;
+ struct scsi_path_id pathid;

- sdev = scsi_alloc_sdev(sdevscan->host, sdevscan->channel,
- sdevscan->id, sdevscan->lun);
+ /*
+ * sdevscan must have only one path, add it to this device. XXX
+ * this is a bit ugly, there must be a better solution, maybe pass
+ * a scsi_path_id everywhere.
+ */
+ if (scsi_get_path(sdevscan, &pathid)) {
+ printk(KERN_ERR "scsi scan: no path to scan device.\n");
+ return SCSI_SCAN_NO_RESPONSE;
+ }
+ sdev = scsi_alloc_sdev(pathid.spi_shpnt, pathid.spi_channel,
+ pathid.spi_id, pathid.spi_lun);
if (sdev == NULL)
return SCSI_SCAN_NO_RESPONSE;

@@ -1348,6 +1335,15 @@
sdev->model = (char *) (sdev->inquiry + 16);
sdev->rev = (char *) (sdev->inquiry + 32);

+ /*
+ * XXX move the identifier and driverfs/devfs setup to a new
+ * function
+ *
+ * scsi_load_identifier is the only reason sreq is needed in this
+ * function; it also requires that the inquiry strings be set up.
+ */
+ scsi_load_identifier(sdev, sreq);
+
if (*bflags & BLIST_ISROM) {
/*
* It would be better to modify sdev->type, and set
@@ -1410,20 +1406,12 @@
sdev->soft_reset = (inq_result[7] & 1) && ((inq_result[3] & 7) == 2);

/*
- * XXX maybe move the identifier and driverfs/devfs setup to a new
- * function, and call them after this function is called.
- *
- * scsi_load_identifier is the only reason sreq is needed in this
- * function.
- */
- scsi_load_identifier(sdev, sreq);
-
- /*
* create driverfs files
*/
sprintf(sdev->sdev_driverfs_dev.bus_id,"%d:%d:%d:%d",
- sdev->host->host_no, sdev->channel, sdev->id, sdev->lun);
- sdev->sdev_driverfs_dev.parent = &sdev->host->host_driverfs_dev;
+ pathid.spi_shpnt->host_no, pathid.spi_channel, pathid.spi_id,
+ pathid.spi_lun);
+ sdev->sdev_driverfs_dev.parent = &pathid.spi_shpnt->host_driverfs_dev;
sdev->sdev_driverfs_dev.bus = &scsi_driverfs_bus_type;
device_register(&sdev->sdev_driverfs_dev);

@@ -1433,7 +1421,8 @@
device_create_file(&sdev->sdev_driverfs_dev, &dev_attr_type);

sprintf(devname, "host%d/bus%d/target%d/lun%d",
- sdev->host->host_no, sdev->channel, sdev->id, sdev->lun);
+ pathid.spi_shpnt->host_no, pathid.spi_channel, pathid.spi_id,
+ pathid.spi_lun);
if (sdev->de)
printk(KERN_WARNING "scsi devfs dir: \"%s\" already exists\n",
devname);
@@ -1468,7 +1457,8 @@
if (sdevnew != NULL)
*sdevnew = sdev;

- return SCSI_SCAN_LUN_PRESENT;
+ return (SCSI_SCAN_TARGET_PRESENT | SCSI_SCAN_LUN_PRESENT |
+ SCSI_SCAN_NEW_LUN);
}

/**
@@ -1481,11 +1471,7 @@
* Call scsi_probe_lun, if a LUN with an attached device is found,
* allocate and set it up by calling scsi_add_lun.
*
- * Return:
- * SCSI_SCAN_NO_RESPONSE: could not allocate or setup a Scsi_Device
- * SCSI_SCAN_TARGET_PRESENT: target responded, but no device is
- * attached at the LUN
- * SCSI_SCAN_LUN_PRESENT: a new Scsi_Device was allocated and initialized
+ * Returns a SCSI_SCAN_ mask value.
**/
static int scsi_probe_and_add_lun(Scsi_Device *sdevscan, Scsi_Device **sdevnew,
int *bflagsp)
@@ -1495,7 +1481,15 @@
unsigned char *scsi_result = NULL;
int bflags;
int res;
+ struct scsi_path_id pathid;

+ if (sdevnew != NULL)
+ *sdevnew = NULL;
+
+ if (scsi_get_path(sdevscan, &pathid)) {
+ printk(KERN_ERR "scsi scan: no path to scan device.\n");
+ return SCSI_SCAN_NO_RESPONSE;
+ }
/*
* Any command blocks allocated are fixed to use sdevscan->lun,
* so they must be allocated and released if sdevscan->lun
@@ -1521,9 +1515,8 @@
* The sreq is for use only with sdevscan.
*/

- scsi_result = kmalloc(256, GFP_ATOMIC |
- (sdevscan->host->unchecked_isa_dma) ?
- GFP_DMA : 0);
+ scsi_result = kmalloc(256, GFP_ATOMIC |
+ (pathid.spi_shpnt->unchecked_isa_dma) ? GFP_DMA : 0);
if (scsi_result == NULL)
goto alloc_failed;

@@ -1552,12 +1545,12 @@
} else {
res = scsi_add_lun(sdevscan, &sdev, sreq, scsi_result,
&bflags);
- if (res == SCSI_SCAN_LUN_PRESENT) {
+ if (res & SCSI_SCAN_LUN_PRESENT) {
BUG_ON(sdev == NULL);
- if ((bflags & BLIST_KEY) != 0) {
+ if ((res & SCSI_SCAN_NEW_LUN) &&
+ (bflags & BLIST_KEY) != 0) {
sdev->lockable = 0;
- scsi_unlock_floptical(sreq,
- scsi_result);
+ scsi_unlock_floptical(sreq, scsi_result);
/*
* scsi_result no longer contains
* the INQUIRY data.
@@ -1606,22 +1599,26 @@
static void scsi_sequential_lun_scan(Scsi_Device *sdevscan, int bflags,
int lun0_res)
{
- struct Scsi_Host *shost = sdevscan->host;
unsigned int sparse_lun;
unsigned int max_dev_lun;
+ struct scsi_path_id pathid;

+ if (scsi_get_path(sdevscan, &pathid)) {
+ printk(KERN_ERR "scsi scan: no path to scan device.\n");
+ return;
+ }
SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: Sequential scan of"
- " host %d channel %d id %d\n", sdevscan->host->host_no,
- sdevscan->channel, sdevscan->id));
+ " host %d channel %d id %d\n", pathid.spi_shpnt->host_no,
+ pathid.spi_channel, pathid.spi_id));

- max_dev_lun = min(max_scsi_luns, shost->max_lun);
+ max_dev_lun = min(max_scsi_luns, pathid.spi_shpnt->max_lun);
/*
* If this device is known to support sparse multiple units,
* override the other settings, and scan all of them. Normally,
* SCSI-3 devices should be scanned via the REPORT LUNS.
*/
if (bflags & BLIST_SPARSELUN) {
- max_dev_lun = shost->max_lun;
+ max_dev_lun = pathid.spi_shpnt->max_lun;
sparse_lun = 1;
} else
sparse_lun = 0;
@@ -1630,7 +1627,7 @@
* If not sparse lun and no device attached at LUN 0 do not scan
* any further.
*/
- if (!sparse_lun && (lun0_res != SCSI_SCAN_LUN_PRESENT))
+ if (!sparse_lun && !(lun0_res & SCSI_SCAN_LUN_PRESENT))
return;

/*
@@ -1647,7 +1644,7 @@
* the other settings, and scan all of them.
*/
if (bflags & BLIST_FORCELUN)
- max_dev_lun = shost->max_lun;
+ max_dev_lun = pathid.spi_shpnt->max_lun;
/*
* REGAL CDC-4X: avoid hang after LUN 4
*/
@@ -1665,10 +1662,16 @@
* until we reach the max, or no LUN is found and we are not
* sparse_lun.
*/
- for (sdevscan->lun = 1; sdevscan->lun < max_dev_lun; ++sdevscan->lun)
- if ((scsi_probe_and_add_lun(sdevscan, NULL, NULL)
- != SCSI_SCAN_LUN_PRESENT) && !sparse_lun)
+
+ for (pathid.spi_lun = 1; pathid.spi_lun < max_dev_lun;
+ ++pathid.spi_lun) {
+ scsi_replace_path(sdevscan, pathid.spi_shpnt,
+ pathid.spi_channel, pathid.spi_id,
+ pathid.spi_lun);
+ if (!(scsi_probe_and_add_lun(sdevscan, NULL, NULL)
+ & SCSI_SCAN_LUN_PRESENT) && !sparse_lun)
return;
+ }
}

#ifdef CONFIG_SCSI_REPORT_LUNS
@@ -1732,6 +1735,7 @@
ScsiLun *fcp_cur_lun, *lun_data;
Scsi_Request *sreq;
char *data;
+ struct scsi_path_id pathid;

/*
* Only support SCSI-3 and up devices.
@@ -1739,6 +1743,13 @@
if (sdevscan->scsi_level < SCSI_3)
return 1;

+ if (scsi_get_path(sdevscan, &pathid)) {
+ printk(KERN_ERR "scsi scan: no path to scan device.\n");
+ /*
+ * An inconsistency, so don't try scanning any further.
+ */
+ return 0;
+ }
sdevscan->queue_depth = 1;
scsi_build_commandblocks(sdevscan);
if (sdevscan->has_cmdblocks == 0) {
@@ -1750,8 +1761,8 @@
}
sreq = scsi_allocate_request(sdevscan);

- sprintf(devname, "host %d channel %d id %d", sdevscan->host->host_no,
- sdevscan->channel, sdevscan->id);
+ sprintf(devname, "host %d channel %d id %d", pathid.spi_shpnt->host_no,
+ pathid.spi_channel, pathid.spi_id);
/*
* Allocate enough to hold the header (the same size as one ScsiLun)
* plus the max number of luns we are requesting.
@@ -1763,9 +1774,8 @@
* prevent us from finding any LUNs on this target.
*/
length = (max_scsi_report_luns + 1) * sizeof(ScsiLun);
- lun_data = (ScsiLun *) kmalloc(length, GFP_ATOMIC |
- (sdevscan->host->unchecked_isa_dma ?
- GFP_DMA : 0));
+ lun_data = (ScsiLun *) kmalloc(length, GFP_ATOMIC |
+ (pathid.spi_shpnt->unchecked_isa_dma ? GFP_DMA : 0));
if (lun_data == NULL) {
printk(ALLOC_FAILURE_MSG, __FUNCTION__);
scsi_release_commandblocks(sdevscan);
@@ -1845,9 +1855,8 @@
} else
num_luns = (length / sizeof(ScsiLun));

- SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: REPORT LUN scan of"
- " host %d channel %d id %d\n", sdevscan->host->host_no,
- sdevscan->channel, sdevscan->id));
+ SCSI_LOG_SCAN_BUS(3,
+ printk(KERN_INFO "scsi scan: REPORT LUN scan of %s\n", devname));
/*
* Scan the luns in lun_data. The entry at offset 0 is really
* the header, so start at 1 and go up to and including num_luns.
@@ -1877,22 +1886,23 @@
/*
* LUN 0 has already been scanned.
*/
- } else if (lun > sdevscan->host->max_lun) {
+ } else if (lun > pathid.spi_shpnt->max_lun) {
printk(KERN_WARNING "scsi: %s lun%d has a LUN larger"
" than allowed by the host adapter\n",
devname, lun);
} else {
- int res;
-
- sdevscan->lun = lun;
- res = scsi_probe_and_add_lun(sdevscan, NULL, NULL);
- if (res == SCSI_SCAN_NO_RESPONSE) {
+ pathid.spi_lun = lun;
+ scsi_replace_path(sdevscan, pathid.spi_shpnt,
+ pathid.spi_channel, pathid.spi_id,
+ pathid.spi_lun);
+ if (scsi_probe_and_add_lun(sdevscan, NULL, NULL) ==
+ SCSI_SCAN_NO_RESPONSE) {
/*
* Got some results, but now none, abort.
*/
printk(KERN_ERR "scsi: Unexpected response"
" from %s lun %d while scanning, scan"
- " aborted\n", devname, sdevscan->lun);
+ " aborted\n", devname, pathid.spi_lun);
break;
}
}
@@ -1938,18 +1948,15 @@
*/
return;

- sdevscan->host = shost;
- sdevscan->id = id;
- sdevscan->channel = channel;
+ scsi_replace_path(sdevscan, shost, channel, id, 0 /* LUN */);
/*
* Scan LUN 0, if there is some response, scan further. Ideally, we
* would not configure LUN 0 until all LUNs are scanned.
*
* The scsi_level is set (in scsi_probe_lun) if a target responds.
*/
- sdevscan->lun = 0;
res = scsi_probe_and_add_lun(sdevscan, NULL, &bflags);
- if (res != SCSI_SCAN_NO_RESPONSE) {
+ if (res & SCSI_SCAN_TARGET_PRESENT) {
/*
* Some scsi devices cannot properly handle a lun != 0.
* BLIST_NOLUN also prevents a REPORT LUN from being sent.
@@ -1995,9 +2002,10 @@
return;

sdevscan->scsi_level = scsi_find_scsi_level(channel, id, shost);
+ sdevscan->scanning = 1;
res = scsi_probe_and_add_lun(sdevscan, &sdev, NULL);
scsi_free_sdev(sdevscan);
- if (res == SCSI_SCAN_LUN_PRESENT) {
+ if (res & SCSI_SCAN_NEW_LUN) {
BUG_ON(sdev == NULL);
/*
* FIXME calling select_queue_depths is wrong for adapters
@@ -2014,7 +2022,6 @@
*/
if (shost->select_queue_depths != NULL)
(shost->select_queue_depths) (shost, shost->host_queue);
-
for (sdt = scsi_devicelist; sdt; sdt = sdt->next)
if (sdt->init && sdt->dev_noticed)
(*sdt->init) ();
@@ -2080,11 +2087,19 @@
* queue_nr_requests requests are allocated. Don't do so
* here for scsi_scan_selected_lun, since we end up
* calling select_queue_depths with an extra Scsi_Device
- * on the host_queue list.
+ * on the device list.
*/
sdevscan = scsi_alloc_sdev(shost, 0, 0, 0);
if (sdevscan == NULL)
return;
+ /*
+ * Set the scanning bit, so error messages about errors
+ * and path failure are not printed; this should be used
+ * in scsi_error.c to prevent overly aggressive action on
+ * failure for a scan. This is never reset, as the default
+ * state for a new Scsi_Device is 0.
+ */
+ sdevscan->scanning = 1;
/*
* The sdevscan host, channel, id and lun are filled in as
* needed to scan.
diff -Nru a/drivers/scsi/scsi_syms.c b/drivers/scsi/scsi_syms.c
--- a/drivers/scsi/scsi_syms.c Mon Sep 16 15:29:45 2002
+++ b/drivers/scsi/scsi_syms.c Mon Sep 16 15:29:45 2002
@@ -102,6 +102,12 @@
EXPORT_SYMBOL(scsi_add_timer);
EXPORT_SYMBOL(scsi_delete_timer);

+EXPORT_SYMBOL(scsi_add_path);
+EXPORT_SYMBOL(scsi_remove_path);
+EXPORT_SYMBOL(scsi_get_path);
+EXPORT_SYMBOL(scsi_get_host);
+EXPORT_SYMBOL(scsi_paths_printk);
+EXPORT_SYMBOL(scsi_traverse_sdevs);
/*
* driverfs support for determining driver types
*/
diff -Nru a/include/scsi/scsi.h b/include/scsi/scsi.h
--- a/include/scsi/scsi.h Mon Sep 16 15:29:45 2002
+++ b/include/scsi/scsi.h Mon Sep 16 15:29:45 2002
@@ -91,6 +91,29 @@
#define WRITE_LONG_2 0xea

/*
+ * SCSI INQUIRY Flags (byte 7)
+ */
+#define SCSI_EVPD 0x01 /* enable vital product data */
+#define SCSI_RMB7 0x80 /* removable media */
+#define SCSI_AENC 0x80 /* asynchronous event notification capability */
+#define SCSI_TRMIOP 0x40 /* terminate I/O process capability */
+#define SCSI_RELADR7 0x80 /* relative addressing capability */
+#define SCSI_WBUS16 0x20 /* 16 byte wide transfer capabilitiy */
+#define SCSI_WBUS32 0x40 /* 32 byte wide transfer capabilitiy */
+#define SCSI_SYNC 0x10 /* synchronous data transfer capability */
+#define SCSI_LINKED 0x08 /* linked command capability */
+#define SCSI_CMDQUE 0x02 /* command queueing capability */
+#define SCSI_SFTRE 0x01 /* soft reset capability */
+
+/*
+ * SCSI INQUIRY Vital Product Data Page Codes
+ */
+#define SCSI_VPROD_SUPPORTED 0x00
+#define SCSI_VPROD_UNIT_SERIAL 0x80
+#define SCSI_VPROD_AODEFN 0x82
+#define SCSI_VPROD_DEVICE_ID 0x83
+
+/*
* Status codes
*/

2002-09-18 15:15:29

by Brian Waite

[permalink] [raw]
Subject: Re: [RFC] [PATCH] 0/7 2.5.35 SCSI multi-path

On Tuesday 17 September 2002 6:49 pm, Patrick Mansfield wrote:

>
> Currently, multi-path support requires a SCSI device that supports one of
> the SCSI INQUIRY device identification pages (page 0x80 or 0x83). Devices
> not supporting one of these pages are treated as if they were separate
> devices. Devices that do not give a unique serial number per LUN for these
> commands might incorrectly be identified as multi-pathed.
>
I might be wrong about this, I have put most of this out of my mind, but I
belive that many tape drives and many cdrom drives do not return a serial
number. Does this mean two seperate tape drives will "appear" as a single
multi-port device, and worse could a cdrom and a tape device appear as the
same device or do you seperate between device types and then serial numbers.\

I was working on exactly this problem in Linux a while ago and we were
running into serial number as uniqueness problems. What we chose to do was
create a "uniqueness" driver that would first use a customer derived
uniquness mecanism, IE "host:bus:channel:device is a single ported device of
type XXX". The fall though mechanism was to query the serial number and if it
was zero, or provided no serial number, then it could not be a multiported
device. Of course for most scsi disks, the serial number was adequate to
provide multiported-ness.

PS. There is nothing funnier than putting 2 tape drives on a system that
decides it is a single multiported device, starting a tar, and pulling the
drive it was writing to, only to watch the tar continue merrily ontl the
second tape drive. Sure you get your backup, the restore is a real bugger tho
:)

Sorry to waste bandwidth if you've already discussed, I am probably a bit late
to the discussion.
Thanks
Brian





2002-09-18 16:03:23

by Patrick Mansfield

[permalink] [raw]
Subject: Re: [RFC] [PATCH] 0/7 2.5.35 SCSI multi-path

On Wed, Sep 18, 2002 at 11:17:59AM -0400, Brian Waite wrote:
> On Tuesday 17 September 2002 6:49 pm, Patrick Mansfield wrote:
>
> >
> > Currently, multi-path support requires a SCSI device that supports one of
> > the SCSI INQUIRY device identification pages (page 0x80 or 0x83). Devices
> > not supporting one of these pages are treated as if they were separate
> > devices. Devices that do not give a unique serial number per LUN for these
> > commands might incorrectly be identified as multi-pathed.
> >
> I might be wrong about this, I have put most of this out of my mind, but I
> belive that many tape drives and many cdrom drives do not return a serial
> number. Does this mean two seperate tape drives will "appear" as a single
> multi-port device, and worse could a cdrom and a tape device appear as the
> same device or do you seperate between device types and then serial numbers.\
>
> I was working on exactly this problem in Linux a while ago and we were
> running into serial number as uniqueness problems. What we chose to do was
> create a "uniqueness" driver that would first use a customer derived
> uniquness mecanism, IE "host:bus:channel:device is a single ported device of
> type XXX". The fall though mechanism was to query the serial number and if it
> was zero, or provided no serial number, then it could not be a multiported
> device. Of course for most scsi disks, the serial number was adequate to
> provide multiported-ness.
>
> PS. There is nothing funnier than putting 2 tape drives on a system that
> decides it is a single multiported device, starting a tar, and pulling the
> drive it was writing to, only to watch the tar continue merrily ontl the
> second tape drive. Sure you get your backup, the restore is a real bugger tho
> :)
>
> Sorry to waste bandwidth if you've already discussed, I am probably a bit late
> to the discussion.
> Thanks
> Brian

Devices without serial numbers are treated as if they had different serial
numbers, they show up as if there was no multi-path support.

Special handling is need for devices with unique identifiers outside of VPD
INQUIRY 0x80 and 0x83. This has to be handled on a per device basis, with
special scanning/probing code to get the unique identifier. Some devices
give the same value for page 0x80 no matter what LUN you connect to - these
are the biggest problem, and could show up as you described.

-- Patrick Mansfield

2002-09-19 21:19:10

by Bill Davidsen

[permalink] [raw]
Subject: Re: [RFC] [PATCH] 0/7 2.5.35 SCSI multi-path

On Wed, 18 Sep 2002, Patrick Mansfield wrote:

> Devices without serial numbers are treated as if they had different serial
> numbers, they show up as if there was no multi-path support.

That doesn't solve the problem, does it? If you have two devices w/o
serial they could look like one with multipath, with the change you note
that is prevented by making a single multipath device w/o serial look like
two. I have visions of programs using /dev/st0 and /dev/st1, having used
backup programs which grabbed every drive with a tape ready.

It is indeed a messy problem.

--
bill davidsen <[email protected]>
CTO, TMR Associates, Inc
Doing interesting things with little computers since 1979.

2002-09-19 21:31:10

by Brian Waite

[permalink] [raw]
Subject: Re: [RFC] [PATCH] 0/7 2.5.35 SCSI multi-path

This is why we had to make another abstraction layer that was the "uniqueness
driver" This allowed the admin to configure a device without seial numbers to
be multipathed if they knew it was multipathed by explicitly specifing the
host bus, target, lun of all the paths to a speciic device. We never got the
chance to develop an intellegent way a determining multipathed-ness so we
left it to the user. Not ideal but it worked for our customer. I would like
to think there is a better way to determind uniqueness, but the nice thing
was we made the uniquness driver a module that could be replaced, thus
letting us develop new uniqueness algorithms in the future. I think the
concept is good, maybe not the implimentation. It also allows you to say you
don't like how we do... well you know the rest


Thanks
Brian

On Thursday 19 September 2002 5:16 pm, Bill Davidsen wrote:
> On Wed, 18 Sep 2002, Patrick Mansfield wrote:
> > Devices without serial numbers are treated as if they had different
> > serial numbers, they show up as if there was no multi-path support.
>
> That doesn't solve the problem, does it? If you have two devices w/o
> serial they could look like one with multipath, with the change you note
> that is prevented by making a single multipath device w/o serial look like
> two. I have visions of programs using /dev/st0 and /dev/st1, having used
> backup programs which grabbed every drive with a tape ready.
>
> It is indeed a messy problem.