2002-10-25 22:15:16

by Patrick Mansfield

[permalink] [raw]
Subject: [PATCH] 0/6 2.5.44 scsi multi-path IO

Hi -

Following is the SCSI mid-layer multi-path patch against 2.5.44,
including NUMA support.

Any flames, comments, patches, etc. appreciated.

This note, the multi-path patch rollup, qla 6.03.00 Beta 7 patch, out
of date documentation, and TODO list can be found under:

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

One rollup patch:

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

The qla 6.03.00 Beta 7 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/6443/qla2x00-v6.03.00b7-dist.tgz

Major changes since last posting:

Made multi-path an experimental option - with it on, all SCSI devices
must have a unique id, and all adapter drivers used must behave
correctly without the current one-to-one relationship between
Scsi_Device and a Scsi_Host.

Removed duplicate code paths. The same code paths are used whether
CONFIG_SCSI_MULTI_PATH_IO is set or not; with it off, it does not check
or add multiple paths on scan/probe, but the same code is otherwise used.

Optimized the IO completion path (don't call scsi_check_paths for a
succesfull IO completion).

Most significant TODO items affecting non-multi-path config:

Never remove a failed path, so the standard scsi error handling
code functions as-is.

Modify all adapter drivers to use new traversal functions (they must no
longer use Scsi_Host::host_queue and/or Scsi_Device::next).

Modify osst upper level driver.

As noted - the config experimental CONFIG_SCSI_MULTI_PATH_IO option must
be enabled to get SCSI multi-path.

If you have multi-port devices with no penalty for switching ports, or
only multi-path hosts (multi-initiated fabrics or 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.


NUMA testing and performance:

NUMA testing was run on a two node IBM NUMAQ box, with qlogic 2300
adapters and FCP connected Seagate drives. The qla driver has problems
(use after freed or stack overflow problems?) when configuring many
devices and adapters on a switch, so I had to run with direct attached
loop.

On a two node (8 processor) IBM NUMAQ box, with 39 drives and 8 qla 2300
adapters, get aggregate throughput (read only using dd and raw IO) of up
to 550 mb/sec was reached. I was actually able to compile a kernel at the
same time in under 3 minutes (make -j9) while still getting about 500
mb/sec of throughput.

Both stock 2.5.44 and 2.5.44-mm3 kernels were run on the NUMAQ +
multi-path, with no noticeable difference in performance (but the system
was very idle, in iowait most of the time).

Unfortunately, high file systems loads with the qla driver were oopsing
the system in odd ways with 2.5.44-mm3 - this is also happening for
2.5.44-mm3 stock kernels with the qla driver, there is some odd memory
corruption happening. I did run fairly file system IO tests with 2.5.44 +
multi-path with 17 of the qla attached drives.


Other 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 both
direct and and switch attached storage (using an IBM 2109 fibre channel
switch) with both qlogic 2200 and 2300 adapters.

Switch portdisable/portenable commands were used to simulate failures.
The qla driver is taking longer to fail commands than with previous
releases. 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
isp1020 adatper with qlogicisp driver and IBM OEM DCHS09X drive

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).

Significant items in the TODO list related to multi-path configuration
(versus single path items listed above) 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.

Apply Dan Mcneil's patch to add device model support for
multiple-paths (at this time, only the first path shows up in the
device model),

-- Patrick Mansfield


2002-10-25 22:17:36

by Patrick Mansfield

[permalink] [raw]
Subject: [PATCH] 1/6 2.5.44 scsi multi-path IO - mid layer changes

SCSI mid-layer multi-path IO changes

Config.help | 22 +++
Config.in | 11 +
Makefile | 2
hosts.c | 107 ++++++++------
hosts.h | 35 +---
scsi.c | 421 +++++++++++++++++++++++++++++------------------------------
scsi.h | 152 +++++++++++++++++++--
scsi_error.c | 361 +++++++++++++++-----------------------------------
8 files changed, 570 insertions(+), 541 deletions(-)

diff -Nru a/drivers/scsi/Config.help b/drivers/scsi/Config.help
--- a/drivers/scsi/Config.help Fri Oct 25 11:26:47 2002
+++ b/drivers/scsi/Config.help Fri Oct 25 11:26:47 2002
@@ -152,6 +152,28 @@
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, and
+ you are using an adapter that properly supports multiple paths, and your
+ storage devices support unique id's (unique serial numbers) 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 can properly support this
+ capability or that your devices have 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 Fri Oct 25 11:26:47 2002
+++ b/drivers/scsi/Config.in Fri Oct 25 11:26:47 2002
@@ -22,7 +22,16 @@

bool ' Probe all LUNs on each SCSI device' CONFIG_SCSI_MULTI_LUN
bool ' Build with SCSI REPORT LUNS support' CONFIG_SCSI_REPORT_LUNS
-
+
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ bool ' Enable Mid Layer Multi-Path IO support (EXPERIMENTAL)' 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
+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 Fri Oct 25 11:26:48 2002
+++ b/drivers/scsi/Makefile Fri Oct 25 11:26:48 2002
@@ -123,7 +123,7 @@

scsi_mod-objs := scsi.o hosts.o scsi_ioctl.o constants.o scsicam.o \
scsi_proc.o scsi_error.o scsi_lib.o scsi_merge.o \
- scsi_scan.o scsi_syms.o
+ scsi_scan.o scsi_syms.o scsi_paths.o

sd_mod-objs := sd.o
sr_mod-objs := sr.o sr_ioctl.o sr_vendor.o
diff -Nru a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
--- a/drivers/scsi/hosts.c Fri Oct 25 11:26:47 2002
+++ b/drivers/scsi/hosts.c Fri Oct 25 11:26:47 2002
@@ -117,6 +117,7 @@
Scsi_Device *sdev;
struct Scsi_Device_Template *sdev_tp;
Scsi_Cmnd *scmd;
+ struct scsi_traverse_hndl strav_hndl;

/*
* Current policy is all shosts go away on unregister.
@@ -129,10 +130,10 @@
* help prevent race conditions where other hosts/processors could
* try and get in and queue a command.
*/
- for (sdev = shost->host_queue; sdev; sdev = sdev->next)
+ scsi_for_each_host_sdev(&strav_hndl, sdev, shost->host_no)
sdev->online = FALSE;

- for (sdev = shost->host_queue; sdev; sdev = sdev->next) {
+ scsi_for_each_host_sdev(&strav_hndl, sdev, shost->host_no) {
/*
* Loop over all of the commands associated with the
* device. If any of them are busy, then set the state
@@ -147,8 +148,8 @@
scmd->request->rq_status,
scmd->target, scmd->pid,
scmd->state, scmd->owner);
- for (sdev = shost->host_queue; sdev;
- sdev = sdev->next) {
+ scsi_for_each_host_sdev(&strav_hndl, sdev,
+ shost->host_no) {
for (scmd = sdev->device_queue; scmd;
scmd = scmd->next)
if (scmd->request->rq_status ==
@@ -174,7 +175,7 @@
* Next we detach the high level drivers from the Scsi_Device
* structures
*/
- for (sdev = shost->host_queue; sdev; sdev = sdev->next) {
+ scsi_for_each_host_sdev(&strav_hndl, sdev, shost->host_no) {
for (sdev_tp = scsi_devicelist; sdev_tp;
sdev_tp = sdev_tp->next)
if (sdev_tp->detach)
@@ -196,16 +197,8 @@

/* Next we free up the Scsi_Cmnd structures for this host */

- for (sdev = shost->host_queue; sdev;
- sdev = shost->host_queue) {
- scsi_release_commandblocks(sdev);
- blk_cleanup_queue(&sdev->request_queue);
- /* Next free up the Scsi_Device structures for this host */
- shost->host_queue = sdev->next;
- if (sdev->inquiry)
- kfree(sdev->inquiry);
- kfree(sdev);
- }
+ scsi_for_each_host_sdev(&strav_hndl, sdev, shost->host_no)
+ scsi_remove_scsi_device(sdev);

/* Remove the instance of the individual hosts */
pcount = scsi_hosts_registered;
@@ -473,6 +466,7 @@
struct Scsi_Device_Template *sdev_tp;
struct list_head *lh;
struct Scsi_Host *shost;
+ struct scsi_traverse_hndl strav_hndl;

INIT_LIST_HEAD(&shost_tp->shtp_list);

@@ -557,21 +551,27 @@
/*
* Next we create the Scsi_Cmnd structures for this host
*/
- list_for_each(lh, &scsi_host_list) {
- shost = list_entry(lh, struct Scsi_Host, sh_list);
- for (sdev = shost->host_queue; sdev; sdev = sdev->next)
- if (sdev->host->hostt == shost_tp) {
- for (sdev_tp = scsi_devicelist;
- sdev_tp;
- sdev_tp = sdev_tp->next)
- if (sdev_tp->attach)
- (*sdev_tp->attach) (sdev);
- if (sdev->attached) {
- scsi_build_commandblocks(sdev);
- if (sdev->current_queue_depth == 0)
- goto out_of_space;
- }
+ scsi_for_all_sdevs(&strav_hndl, sdev) {
+ shost = scsi_get_host(sdev);
+ /*
+ * 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
+ * Scsi_Device. Alternatively, set a flag if the
+ * device has been attached (to upper levels), or
+ * check current_queue_depth.
+ */
+ if (shost && shost->hostt == shost_tp) {
+ for (sdev_tp = scsi_devicelist; sdev_tp;
+ sdev_tp = sdev_tp->next)
+ if (sdev_tp->attach)
+ (*sdev_tp->attach) (sdev);
+ if (sdev->attached) {
+ scsi_build_commandblocks(sdev);
+ if (sdev->current_queue_depth == 0)
+ goto out_of_space;
}
+ }
}

/* This does any final handling that is required. */
@@ -743,29 +743,26 @@
}
}

-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\n"));
}
- 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)
@@ -782,6 +779,30 @@
}
spin_unlock_irqrestore(shost->host_lock, flags);
}
+
+#if defined(CONFIG_NUMA)
+/*
+ * scsihost_to_node: return the node id that *host is on. This only
+ * knows about pci devices.
+ */
+int scsihost_to_node(struct Scsi_Host *host)
+{
+ if (!host) {
+ printk(KERN_ERR "%s: host NULL\n", __FUNCTION__);
+ return 0;
+ }
+ if(host->pci_dev)
+ /*
+ * What interface is available today?
+ * return pcidev_to_node(host->pci_dev);
+ * return BUS2QUAD(host->pci_dev->bus->number);
+ */
+ return mp_bus_id_to_node[host->pci_dev->bus->number];
+
+ printk(KERN_INFO "%s: Unable to determine bus type\n", __FUNCTION__);
+ return 0;
+}
+#endif

/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff -Nru a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h
--- a/drivers/scsi/hosts.h Fri Oct 25 11:26:47 2002
+++ b/drivers/scsi/hosts.h Fri Oct 25 11:26:47 2002
@@ -27,6 +27,7 @@
#include <linux/config.h>
#include <linux/proc_fs.h>
#include <linux/pci.h>
+#include <linux/mm.h>

/* It is senseless to set SG_ALL any higher than this - the performance
* does not get any better, and it wastes memory
@@ -373,7 +374,6 @@
* struct private is a way of marking it in a sort of C++ type of way.
*/
struct list_head sh_list;
- Scsi_Device * host_queue;
struct list_head all_scsi_hosts;
struct list_head my_devices;

@@ -551,9 +551,6 @@
{
shost->pci_dev = pdev;
shost->host_driverfs_dev.parent=&pdev->dev;
-
- /* register parent with driverfs */
- device_register(&shost->host_driverfs_dev);
}


@@ -592,7 +589,14 @@
};

void scsi_initialize_queue(Scsi_Device *, struct Scsi_Host *);
-
+#if defined(CONFIG_NUMA)
+int scsihost_to_node(struct Scsi_Host *host);
+#else
+static inline int scsihost_to_node(struct Scsi_Host *host)
+{
+ return 0;
+}
+#endif

/*
* Driver registration/unregistration.
@@ -640,27 +644,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
- * @shost: SCSI host pointer
- * @channel: SCSI channel (zero if only one channel)
- * @pun: SCSI target number (physical unit number)
- * @lun: SCSI Logical Unit Number
- **/
-static inline Scsi_Device *scsi_find_device(struct Scsi_Host *shost,
- int channel, int pun, int lun) {
- Scsi_Device *sdev;
-
- for (sdev = shost->host_queue;
- sdev != NULL;
- sdev = sdev->next)
- if (sdev->channel == channel && sdev->id == pun
- && sdev->lun ==lun)
- break;
- return sdev;
-}
-
#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 Fri Oct 25 11:26:47 2002
+++ b/drivers/scsi/scsi.c Fri Oct 25 11:26:47 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;
+ struct scsi_traverse_hndl 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);
@@ -551,6 +548,7 @@
{
unsigned long flags;
Scsi_Device * SDpnt;
+ struct Scsi_Host *shpnt;
int alloc_cmd = 0;

spin_lock_irqsave(&device_request_lock, flags);
@@ -601,19 +599,16 @@
* device_request_lock since that's nice and quick, but allocation
* can take more time so do it outside that scope instead.
*/
- if(alloc_cmd) {
+ shpnt = scsi_get_host(SDpnt);
+ if(shpnt && alloc_cmd) {
Scsi_Cmnd *newSCpnt;

newSCpnt = kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC |
- (SDpnt->host->unchecked_isa_dma ?
+ (shpnt->unchecked_isa_dma ?
GFP_DMA : 0));
if(newSCpnt) {
memset(newSCpnt, 0, sizeof(Scsi_Cmnd));
- newSCpnt->host = SDpnt->host;
newSCpnt->device = SDpnt;
- newSCpnt->target = SDpnt->id;
- newSCpnt->lun = SDpnt->lun;
- newSCpnt->channel = SDpnt->channel;
newSCpnt->request = NULL;
newSCpnt->use_sg = 0;
newSCpnt->old_use_sg = 0;
@@ -687,8 +682,14 @@
*/
if (reason == SCSI_MLQUEUE_HOST_BUSY) {
host->host_blocked = host->max_host_blocked;
- } else {
+ cmd->retries = 0;
+ } else if (reason == SCSI_MLQUEUE_DEVICE_BUSY) {
cmd->device->device_blocked = cmd->device->max_device_blocked;
+ cmd->retries = 0;
+ } else if (reason != SCSI_MLQUEUE_RETRY) {
+ printk("%s: unexpected value of reason %d\n", __FUNCTION__,
+ reason);
+ return 0;
}

/*
@@ -702,16 +703,29 @@
* Decrement the counters, since these commands are no longer
* active on the host/device.
*/
- spin_lock_irqsave(cmd->host->host_lock, flags);
- cmd->host->host_busy--;
- cmd->device->device_busy--;
- spin_unlock_irqrestore(cmd->host->host_lock, flags);
+ 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;
}

@@ -746,8 +760,10 @@
{
request_queue_t *q;
Scsi_Device * SDpnt;
+ struct Scsi_Host * SHpnt;

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

__scsi_release_command(SCpnt);

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

/*
@@ -833,10 +849,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;
@@ -952,30 +970,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
@@ -1343,6 +1348,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
@@ -1431,6 +1445,7 @@
struct Scsi_Host *host;
Scsi_Device *device;
Scsi_Request * SRpnt;
+ unsigned long flags;

host = SCpnt->host;
device = SCpnt->device;
@@ -1444,7 +1459,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
@@ -1463,7 +1482,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;
@@ -1533,30 +1552,34 @@
void scsi_build_commandblocks(Scsi_Device * SDpnt)
{
unsigned long flags;
+ struct Scsi_Host *host;
Scsi_Cmnd *SCpnt;

if (SDpnt->current_queue_depth != 0)
return;

+ host = scsi_get_host(SDpnt);
+ /*
+ * We are called during scan, and must always have at least one
+ * path.
+ */
+ BUG_ON(host == NULL);
+
SCpnt = (Scsi_Cmnd *) kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC |
- (SDpnt->host->unchecked_isa_dma ? GFP_DMA : 0));
+ (host->unchecked_isa_dma ? GFP_DMA : 0));
if (NULL == SCpnt) {
/*
* Since we don't currently have *any* command blocks on this
* device, go ahead and try an atomic allocation...
*/
SCpnt = (Scsi_Cmnd *) kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC |
- (SDpnt->host->unchecked_isa_dma ? GFP_DMA : 0));
+ (host->unchecked_isa_dma ? GFP_DMA : 0));
if (NULL == SCpnt)
return; /* Oops, we aren't going anywhere for now */
}

memset(SCpnt, 0, sizeof(Scsi_Cmnd));
- SCpnt->host = SDpnt->host;
SCpnt->device = SDpnt;
- SCpnt->target = SDpnt->id;
- SCpnt->lun = SDpnt->lun;
- SCpnt->channel = SDpnt->channel;
SCpnt->request = NULL;
SCpnt->use_sg = 0;
SCpnt->old_use_sg = 0;
@@ -1649,10 +1672,10 @@
SDpnt->simple_tags = 1;
break;
default:
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) "
- "scsi_adjust_queue_depth, bad queue type, "
- "disabled\n", SDpnt->host->host_no,
- SDpnt->channel, SDpnt->id, SDpnt->lun);
+ printk(KERN_WARNING);
+ scsi_paths_printk(SDpnt, NULL, "<%d, %d, %d, %d>");
+ printk("scsi_adjust_queue_depth, bad queue type, "
+ "disabled\n");
case 0:
SDpnt->ordered_tags = SDpnt->simple_tags = 0;
SDpnt->new_queue_depth = tags;
@@ -1669,46 +1692,37 @@
static int scsi_proc_info(char *buffer, char **start, off_t offset, int length)
{
Scsi_Device *scd;
- struct Scsi_Host *HBA_ptr;
+ struct scsi_traverse_hndl 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_host_get_next(NULL); HBA_ptr;
- HBA_ptr = scsi_host_get_next(HBA_ptr)) {
- 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_host_get_next(NULL); HBA_ptr;
- HBA_ptr = scsi_host_get_next(HBA_ptr)) {
-#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)
@@ -1864,13 +1878,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)
@@ -1910,13 +1918,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 */
@@ -1933,6 +1935,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.
@@ -1942,22 +1948,8 @@
if (HBA_ptr->hostt->slave_detach)
(*HBA_ptr->hostt->slave_detach) (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);
+ scsi_remove_scsi_device(scd);
} else {
goto out;
}
@@ -1977,8 +1969,8 @@
int scsi_register_device(struct Scsi_Device_Template *tpnt)
{
Scsi_Device *SDpnt;
- struct Scsi_Host *shpnt;
int out_of_space = 0;
+ struct scsi_traverse_hndl strav_hndl;

#ifdef CONFIG_KMOD
if (scsi_host_get_next(NULL) == NULL)
@@ -1994,15 +1986,9 @@
/*
* First scan the devices that we know about, and see if we notice them.
*/
-
- for (shpnt = scsi_host_get_next(NULL); shpnt;
- shpnt = scsi_host_get_next(shpnt)) {
- 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);

/*
* If any of the devices would match this driver, then perform the
@@ -2015,22 +2001,24 @@
/*
* Now actually connect the devices to the new driver.
*/
- for (shpnt = scsi_host_get_next(NULL); shpnt;
- shpnt = scsi_host_get_next(shpnt)) {
- 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->current_queue_depth == 0) {
- SDpnt->online = TRUE;
- scsi_build_commandblocks(SDpnt);
- if (SDpnt->current_queue_depth == 0)
- 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->current_queue_depth == 0) {
+ SDpnt->online = TRUE;
+ scsi_build_commandblocks(SDpnt);
+ if (SDpnt->current_queue_depth == 0)
+ out_of_space = 1;
}
}

@@ -2051,9 +2039,10 @@
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;
+ struct scsi_traverse_hndl strav_hndl;
+ struct Scsi_Host *shpnt;

lock_kernel();
/*
@@ -2066,25 +2055,29 @@
* Next, detach the devices from the driver.
*/

- for (shpnt = scsi_host_get_next(NULL); shpnt;
- shpnt = scsi_host_get_next(shpnt)) {
- 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.
- */
- if (shpnt->hostt->slave_detach)
- (*shpnt->hostt->slave_detach) (SDpnt);
- scsi_release_commandblocks(SDpnt);
- }
+ /*
+ * Nobody is using this device any more. Free all of the
+ * command structures.
+ */
+ shpnt = scsi_get_host(SDpnt);
+ /*
+ * XXX fix if no paths left, we still want one
+ * detach call. Maybe call slave_detach() when
+ * the last path goes away, and slave_attach()
+ * when the first path is added.
+ */
+ if (shpnt && shpnt->hostt->slave_detach)
+ (*shpnt->hostt->slave_detach) (SDpnt);
+ scsi_release_commandblocks(SDpnt);
}
}
+
/*
* Extract the template from the linked list.
*/
@@ -2137,6 +2130,8 @@
struct Scsi_Host *shpnt;
Scsi_Cmnd *SCpnt;
Scsi_Device *SDpnt;
+ struct scsi_traverse_hndl strav_hndl;
+
printk(KERN_INFO "Dump of scsi host parameters:\n");
i = 0;
for (shpnt = scsi_host_get_next(NULL); shpnt;
@@ -2151,39 +2146,36 @@

printk(KERN_INFO "\n\n");
printk(KERN_INFO "Dump of scsi command parameters:\n");
- for (shpnt = scsi_host_get_next(NULL); shpnt;
- shpnt = scsi_host_get_next(shpnt)) {
- 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 %4llu %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),
- (unsigned long long)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 %4llu %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),
+ (unsigned long long)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 */ /* } */
@@ -2326,6 +2318,7 @@
return -ENOMEM;
}
generic->write_proc = proc_scsi_gen_write;
+ scsi_paths_proc_init();
#endif

scsi_devfs_handle = devfs_mk_dir (NULL, "scsi", NULL);
@@ -2349,6 +2342,7 @@
scsi_host_hn_release();

#ifdef CONFIG_PROC_FS
+ scsi_paths_proc_cleanup();
/* No, we're not here anymore. Don't show the /proc/scsi files. */
remove_proc_entry ("scsi/scsi", 0);
remove_proc_entry ("scsi", 0);
@@ -2399,19 +2393,25 @@
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;
+ /*
+ * 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;
+ }
SDpnt->new_queue_depth = 1;

scsi_build_commandblocks(SDpnt);
if(SDpnt->current_queue_depth == 0) {
- kfree(SDpnt);
+ scsi_remove_scsi_device(SDpnt);
return NULL;
}

@@ -2441,21 +2441,14 @@
*/
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");
}
-
- blk_cleanup_queue(&SDpnt->request_queue);
-
- /*
- * We only have a single SCpnt attached to this device. Free
- * it now.
- */
- scsi_release_commandblocks(SDpnt);
- if (SDpnt->inquiry)
- kfree(SDpnt->inquiry);
- kfree(SDpnt);
+ scsi_remove_scsi_device(SDpnt);
}

/*
@@ -2493,14 +2486,20 @@
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);
+ /*
+ * 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 = 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.h b/drivers/scsi/scsi.h
--- a/drivers/scsi/scsi.h Fri Oct 25 11:26:47 2002
+++ b/drivers/scsi/scsi.h Fri Oct 25 11:26:47 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,7 +438,7 @@
void (*complete) (Scsi_Cmnd *));
extern int scsi_delete_timer(Scsi_Cmnd * SCset);
extern void scsi_error_handler(void *host);
-extern int scsi_decide_disposition(Scsi_Cmnd * SCpnt);
+extern int scsi_check_sense(Scsi_Cmnd * SCpnt);
extern int scsi_block_when_processing_errors(Scsi_Device *);
extern void scsi_sleep(int);

@@ -458,10 +468,12 @@
*/
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_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);
@@ -471,9 +483,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
@@ -486,7 +501,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,
@@ -495,6 +511,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.
*/
@@ -539,6 +558,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 unit (Scsi_Device) 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.
*
@@ -555,13 +585,18 @@
/*
* 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 */
struct list_head siblings; /* list of all devices on this host */
struct list_head same_target_siblings; /* just the devices sharing same target id */
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 */
@@ -569,11 +604,9 @@
struct list_head busy_cmnds; /* list of Scsi_Cmnd structs in use */
Scsi_Cmnd *device_queue; /* queue of SCSI Command structures */
Scsi_Cmnd *current_cmnd; /* currently active command */
+ void *sdev_paths;
unsigned short current_queue_depth;/* How deep of a queue we have */
unsigned short new_queue_depth; /* How deep of a queue we want */
-
- unsigned int id, lun, channel;
-
unsigned int manufacturer; /* Manufacturer of device, for using
* vendor-specific cmd's */
unsigned sector_size; /* size in bytes */
@@ -626,6 +659,7 @@
unsigned remap:1; /* support remapping */
unsigned starved:1; /* unable to process commands because
host busy */
+ unsigned scanning:1; /* set while scanning */
// unsigned sync:1; /* Sync transfer state, managed by host */
// unsigned wide:1; /* WIDE transfer state, managed by host */

@@ -635,7 +669,6 @@
/* default value if the device doesn't override */
#define SCSI_DEFAULT_DEVICE_BLOCKED 3

-
// Flag to allow revalidate to succeed in sd_open
int allow_revalidate;
struct device sdev_driverfs_dev;
@@ -678,7 +711,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
@@ -753,6 +785,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;
@@ -847,6 +883,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) { \
@@ -867,6 +904,72 @@
current->state = TASK_RUNNING; \
}; }

+struct scsi_traverse_hndl {
+ Scsi_Device *next_sdev;
+ struct Scsi_Host *cur_shost;
+};
+
+extern Scsi_Device *scsi_traverse_sdevs(struct scsi_traverse_hndl *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)->next_sdev = scsi_sdev_list
+
+#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((struct scsi_traverse_hndl *) 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)
@@ -960,6 +1063,33 @@

return (Scsi_Cmnd *)req->special;
}
+
+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_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);
+
+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_error.c b/drivers/scsi/scsi_error.c
--- a/drivers/scsi/scsi_error.c Fri Oct 25 11:26:47 2002
+++ b/drivers/scsi/scsi_error.c Fri Oct 25 11:26:47 2002
@@ -133,37 +133,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.
*
@@ -177,12 +146,15 @@
**/
int scsi_block_when_processing_errors(Scsi_Device *sdev)
{
-
- SCSI_SLEEP(&sdev->host->host_wait, sdev->host->in_recovery);
-
SCSI_LOG_ERROR_RECOVERY(5, printk("%s: rtn: %d\n", __FUNCTION__,
sdev->online));
-
+ /*
+ * If a device is in_recovery, IO will be not be processed when
+ * scsi_request_fn is called. We used to sleep here, doing so
+ * raced with the setting of in_recovery, but prevented lots of IO
+ * from queueing up after the open returns, but did not prevent IO
+ * from queueing up for already open devices.
+ */
return sdev->online;
}

@@ -200,9 +172,9 @@
int cmd_failed = 0;
int cmd_timed_out = 0;
int devices_failed = 0;
+ struct scsi_traverse_hndl 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;
@@ -215,12 +187,13 @@
}

if (cmd_timed_out || cmd_failed) {
- SCSI_LOG_ERROR_RECOVERY(3,
- printk("%s: %d:%d:%d:%d cmds failed: %d,"
- " timedout: %d\n",
- __FUNCTION__, 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, timed out: %d\n",
+ cmd_failed, cmd_timed_out);
+ });
cmd_timed_out = 0;
cmd_failed = 0;
++devices_failed;
@@ -246,8 +219,10 @@
int found;
Scsi_Device *sdev;
Scsi_Cmnd *scmd;
+ struct scsi_traverse_hndl 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;
@@ -297,7 +272,7 @@
* Return value:
* SUCCESS or FAILED or NEEDS_RETRY
**/
-static int scsi_check_sense(Scsi_Cmnd *scmd)
+int scsi_check_sense(Scsi_Cmnd *scmd)
{
if (!SCSI_SENSE_VALID(scmd)) {
return FAILED;
@@ -823,6 +798,73 @@
}

/**
+ * 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_decide_disposition.
+ */
+ scmd->result = (scmd->result & 0xff00ffff) | (DID_TIME_OUT << 16) |
+ (DRIVER_TIMEOUT << 24);
+ if (scsi_decide_disposition(scmd) == REQUEUE) {
+#ifdef WILL_NOT_WORK
+ /*
+ * 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.
+ */
+ (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;
+#endif
+ }
+
+ /* 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
*
@@ -959,10 +1001,11 @@
int rtn;
Scsi_Cmnd *scmd;
Scsi_Device *sdev;
+ struct scsi_traverse_hndl 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))
@@ -998,6 +1041,7 @@
unsigned long flags;
int rtn;
Scsi_Device *sdev;
+ struct scsi_traverse_hndl strav_hndl;

SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Bus RST\n",
__FUNCTION__));
@@ -1016,11 +1060,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;
}
@@ -1033,7 +1077,8 @@
{
unsigned long flags;
int rtn;
- Scsi_Device *sdev;
+ Scsi_Device *sdev;
+ struct scsi_traverse_hndl strav_hndl;

SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Host RST\n",
__FUNCTION__));
@@ -1052,11 +1097,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;
}
@@ -1109,7 +1154,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);
+ rtn = scsi_try_bus_reset(scmd);
if (rtn != SUCCESS)
rtn = scsi_try_host_reset(scmd);

@@ -1145,14 +1190,11 @@
if (!scsi_eh_eflags_chk(scmd, SCSI_EH_CMD_ERR))
continue;

- printk(KERN_INFO "%s: Device offlined - 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);
}
@@ -1197,175 +1239,6 @@
}

/**
- * scsi_decide_disposition - Disposition a cmd on return from LLD.
- * @scmd: SCSI cmd to examine.
- *
- * Notes:
- * This is *only* called when we are examining the status after sending
- * out the actual data command. any commands that are queued for error
- * recovery (i.e. test_unit_ready) do *not* come through here.
- *
- * When this routine returns failed, it means the error handler thread
- * is woken. in cases where the error code indicates an error that
- * doesn't require the error handler read (i.e. we don't need to
- * abort/reset), then this function should return SUCCESS.
- **/
-int scsi_decide_disposition(Scsi_Cmnd *scmd)
-{
- int rtn;
-
- /*
- * if the device is offline, then we clearly just pass the result back
- * up to the top level.
- */
- if (scmd->device->online == FALSE) {
- SCSI_LOG_ERROR_RECOVERY(5, printk("%s: device offline - report"
- " as SUCCESS\n",
- __FUNCTION__));
- return SUCCESS;
- }
- /*
- * 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;
- 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:
- /*
- * note - this means that we just report the status back
- * to the top level driver, not that we actually think
- * that it indicates SUCCESS.
- */
- return SUCCESS;
- /*
- * 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:
- 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:
- 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)) {
- return SUCCESS;
- } 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;
- }
- return SUCCESS;
- default:
- return FAILED;
- }
-
- /*
- * next, check the message byte.
- */
- if (msg_byte(scmd->result) != COMMAND_COMPLETE) {
- return FAILED;
- }
- /*
- * 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.
- */
- case BUSY:
- /*
- * device can't talk to us at the moment. Should only
- * occur (SAM-3) when the task queue is empty, so will cause
- * the empty queue handling to trigger a stall in the
- * device.
- */
- return ADD_TO_MLQUEUE;
- case GOOD:
- case COMMAND_TERMINATED:
- return SUCCESS;
- case CHECK_CONDITION:
- rtn = scsi_check_sense(scmd);
- if (rtn == NEEDS_RETRY) {
- goto maybe_retry;
- }
- return rtn;
- case CONDITION_GOOD:
- case INTERMEDIATE_GOOD:
- case INTERMEDIATE_C_GOOD:
- /*
- * who knows? FIXME(eric)
- */
- return SUCCESS;
-
- case RESERVATION_CONFLICT:
- printk("scsi%d (%d,%d,%d) : reservation conflict\n",
- scmd->host->host_no, scmd->channel,
- scmd->device->id, scmd->device->lun);
- return SUCCESS; /* causes immediate i/o error */
- default:
- return FAILED;
- }
- return FAILED;
-
- maybe_retry:
-
- if ((++scmd->retries) < scmd->allowed) {
- return NEEDS_RETRY;
- } else {
- /*
- * no more retries - report this one back to upper level.
- */
- return SUCCESS;
- }
-}
-
-/**
* scsi_eh_lock_done - done function for eh door lock request
* @scmd: SCSI command block for the door lock request
*
@@ -1445,7 +1318,7 @@
static void scsi_restart_operations(struct Scsi_Host *shost)
{
Scsi_Device *sdev;
- unsigned long flags;
+ struct scsi_traverse_hndl strav_hndl;

ASSERT_LOCK(shost->host_lock, 0);

@@ -1454,7 +1327,7 @@
* onto the head of the SCSI request queue for the device. There
* is no point trying to lock the door of an off-line device.
*/
- for (sdev = shost->host_queue; sdev; sdev = sdev->next)
+ scsi_for_each_host_sdev(&strav_hndl, sdev, shost->host_no)
if (sdev->online && sdev->locked)
scsi_eh_lock_door(sdev);

@@ -1474,20 +1347,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)) {
- 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-10-25 22:19:49

by Patrick Mansfield

[permalink] [raw]
Subject: [PATCH] 2/6 2.5.44 scsi multi-path IO - mid layer changes

SCSI mid-layer multi-path IO changes

scsi_ioctl.c | 62 +-
scsi_lib.c | 348 ++++++++-----
scsi_merge.c | 57 ++
scsi_paths.c | 1499 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 1827 insertions(+), 139 deletions(-)

diff -Nru a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c
--- a/drivers/scsi/scsi_ioctl.c Fri Oct 25 11:26:48 2002
+++ b/drivers/scsi/scsi_ioctl.c Fri Oct 25 11:26:48 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]),
@@ -214,6 +219,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;
@@ -223,7 +229,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;

/*
@@ -395,10 +403,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));
}


@@ -410,6 +424,8 @@
int scsi_ioctl(Scsi_Device * dev, int cmd, void *arg)
{
char scsi_cmd[MAX_COMMAND_SIZE];
+ struct scsi_path_id scsi_path;
+ struct Scsi_Host *SHpnt;

/* No idea how this happens.... */
if (!dev)
@@ -430,15 +446,23 @@
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);
/*
* The next two ioctls either need to go or need to be changed to
* pass tagged queueing changes through the low level drivers.
@@ -464,7 +488,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;
@@ -502,8 +528,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 Fri Oct 25 11:26:47 2002
+++ b/drivers/scsi/scsi_lib.c Fri Oct 25 11:26:47 2002
@@ -37,6 +37,7 @@
#define __KERNEL_SYSCALLS__

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

#include <asm/system.h>
#include <asm/irq.h>
@@ -46,6 +47,8 @@
#include "hosts.h"
#include <scsi/scsi_ioctl.h>

+struct scsi_device *scsi_sdev_list;
+
/*
* This entire source file deals with the new queueing code.
*/
@@ -126,7 +129,14 @@
SCpnt->serial_number = 0;
SCpnt->serial_number_at_timeout = 0;
SCpnt->flags = 0;
+#if 0
+ /*
+ * FIXME - what do we do for multi-path? Not resetting this means that
+ * QUEUE_FULL and host busy cases will not be retried. There are
+ * probably other cases where we should also retry longer.
+ */
SCpnt->retries = 0;
+#endif

SCpnt->abort_reason = 0;

@@ -219,12 +229,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;
+ struct scsi_traverse_hndl strav_hndl;

ASSERT_LOCK(q->queue_lock, 0);

@@ -249,7 +261,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
@@ -261,7 +272,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)
@@ -285,7 +296,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)
@@ -338,6 +349,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);

@@ -353,7 +365,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;
}

@@ -375,7 +387,7 @@
__scsi_release_command(SCpnt);

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

return NULL;
}
@@ -491,38 +503,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.
@@ -582,7 +563,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) {
@@ -600,7 +581,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;
}
}
@@ -620,7 +602,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);
@@ -652,7 +634,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) {
@@ -661,10 +643,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);
@@ -730,6 +712,36 @@
}

/*
+ * 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_request_fn()
*
* Purpose: Generic version of request function for SCSI hosts.
@@ -754,8 +766,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);

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

/*
* To start with, we keep looping until the queue is empty, or until
@@ -771,69 +783,41 @@
*/
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(SHpnt->host_busy == 0 && SHpnt->host_blocked) {
- /* unblock after host_blocked iterates to zero */
- if(--SHpnt->host_blocked == 0) {
- SCSI_LOG_MLQUEUE(3, printk("scsi%d unblocking host at zero depth\n", SHpnt->host_no));
- } else {
- blk_plug_device(q);
- break;
- }
- }
- if(SDpnt->device_busy == 0 && SDpnt->device_blocked) {
- /* unblock after device_blocked iterates to zero */
- if(--SDpnt->device_blocked == 0) {
- SCSI_LOG_MLQUEUE(3, printk("scsi%d (%d:%d) unblocking device at zero depth\n", SHpnt->host_no, SDpnt->id, SDpnt->lun));
- } else {
- blk_plug_device(q);
- break;
- }
- }
- /*
- * If the device cannot accept another request, then quit.
- */
- if (SDpnt->device_blocked) {
- break;
- }
- if ((SHpnt->can_queue > 0 && (SHpnt->host_busy >= SHpnt->can_queue))
- || (SHpnt->host_blocked)
- || (SHpnt->host_self_blocked)) {
- /*
- * 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.
- */
- if (SDpnt->device_busy == 0) {
- SDpnt->starved = 1;
- SHpnt->some_device_starved = 1;
- }
- break;
- } else {
- SDpnt->starved = 0;
- }
-
/*
* 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);

+ res = scsi_get_best_path(SDpnt, &path, req);
+ if (res == 1) {
+ /*
+ * IO is blocked.
+ */
+ 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;
+ }
+
/*
* Find the actual device driver associated with this command.
* The SPECIAL requests are things like character device or
@@ -849,12 +833,32 @@
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 | REQ_BLOCK_PC)) {
@@ -867,30 +871,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++;

/*
@@ -941,11 +952,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;
@@ -967,8 +978,8 @@
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;
}
}
@@ -1035,11 +1046,13 @@
void scsi_unblock_requests(struct Scsi_Host * SHpnt)
{
Scsi_Device *SDloop;
+ struct scsi_traverse_hndl 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);
+ }
}

/*
@@ -1066,11 +1079,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;
- }
+ struct scsi_traverse_hndl strav_hndl;
+
+ scsi_for_each_host_sdev(&strav_hndl, SDloop, SHpnt->host_no) {
+ SDloop->was_reset = 1;
+ SDloop->expecting_cc_ua = 1;
}
}

@@ -1089,4 +1102,97 @@

void scsi_deregister_blocked_host(struct Scsi_Host * SHpnt)
{
+}
+
+
+/**
+ * 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;
+ struct scsi_traverse_hndl 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;
+ blk_cleanup_queue(&SDpnt->request_queue);
+ scsi_release_commandblocks(SDpnt);
+ scsi_remove_path(SDpnt, SCSI_FIND_ALL_HOST_NO, SCSI_FIND_ALL_CHANNEL,
+ SCSI_FIND_ALL_ID, SCSI_FIND_ALL_LUN);
+ if (SDpnt->inquiry != NULL)
+ kfree(SDpnt->inquiry);
+ kfree((char *) SDpnt);
}
diff -Nru a/drivers/scsi/scsi_merge.c b/drivers/scsi/scsi_merge.c
--- a/drivers/scsi/scsi_merge.c Fri Oct 25 11:26:48 2002
+++ b/drivers/scsi/scsi_merge.c Fri Oct 25 11:26:48 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>
@@ -130,6 +131,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
@@ -144,10 +197,12 @@
*/
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.
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 Fri Oct 25 11:26:48 2002
@@ -0,0 +1,1499 @@
+/*
+ * 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.05.00"
+
+#ifdef CONFIG_SCSI_MULTI_PATH_IO
+
+#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
+#else
+/*
+ * The path policy has no affect, since there is only one path.
+ */
+static enum scsi_path_policy scsi_path_dflt_path_policy =
+ SCSI_PATH_POLICY_ROUND_ROBIN;
+#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 can 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 && prefix)
+ 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(struct scsi_traverse_hndl *handle, uint
+ host_no, uint channel, uint id, uint lun)
+{
+ Scsi_Device *sdev;
+
+ if (handle == NULL)
+ /*
+ * Really a locate and not a traversal.
+ */
+ sdev = scsi_sdev_list;
+ else
+ sdev = handle->next_sdev;
+
+ 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 && sdev)
+ handle->next_sdev = sdev->sdev_next;
+ 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;
+ }
+
+ /*
+ * XXX races with path becoming failed via /proc or user level
+ * change, this is not too horrible, as the IO will be sent and
+ * then fail the path again. Fix with proper SMP locking.
+ */
+ *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.
+ * Right now, just caching paths_left is enough since
+ * SCSI_PATH_STATE_FAILING and SCSI_PATH_STATE_DEAD are treated the same.
+ *
+ * Returns REQUEUE (resend 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 if there is no sdev_paths, or
+ * if we have a failure during scanning.
+ */
+ 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 ==
+ pathcur->sp_path_id.spi_shpnt->host_no)
+ && (check_path->spi_channel ==
+ pathcur->sp_path_id.spi_channel)
+ && (check_path->spi_id == pathcur->sp_path_id.spi_id)
+ && (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++;
+ }
+ /*
+ * Allow up to scmd->allowed retries per path. This is not ideal, but
+ * allows us to retry at least a few more times - this probably will
+ * work OK but for example if a new path is added and we get a
+ * CHECK_CONDITION, if we were in the middle of retrying, we might
+ * not retry enough times to clear the check on all initiators. There
+ * might be a similiar issue when failing or removing a path.
+ *
+ * A time limit on the IO rather than a retry count would help fix this
+ * issue - retry until so much time goes by, rather than retry a fixed
+ * number of times. There are still problems for commands that will
+ * keep getting (for example) a DID_ERROR.
+ *
+ * Also, for tape IO, this prevents any command from being requeued,
+ * a bad thing for errors that we know could not have gone out to
+ * the drive.
+ */
+ SCSI_LOG_ERROR_RECOVERY(5, printk("%s: retries %d, allowed %d,"
+ " paths_left %d\n", __FUNCTION__,
+ scmd->retries, scmd->allowed, paths_left));
+ if (++scmd->retries < (scmd->allowed * paths_left))
+ return REQUEUE;
+ else
+ return SUCCESS;
+}
+
+/**
+ * scsi_decide_disposition - Check the result of a scsi command
+ * @scmd: Scsi command
+ *
+ * Description:
+ * Check the result of @scmd, and determine how to procede.
+ **/
+int scsi_decide_disposition(Scsi_Cmnd *scmd)
+{
+ int rtn;
+ struct scsi_path_id pathid;
+
+ if (scmd->device->online == FALSE) {
+ SCSI_LOG_ERROR_RECOVERY(5, printk("%s: device offline - report"
+ " as SUCCESS\n",
+ __FUNCTION__));
+ return SUCCESS;
+ }
+ 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:
+ /*
+ * Assumes the IO could not have made it to the device.
+ */
+ rtn = scsi_check_paths(SCSI_PATH_STATE_DEAD, &pathid, scmd);
+ return rtn;
+ case DID_ABORT:
+ /*
+ * Could this IO have made it to the device?
+ */
+ rtn = scsi_check_paths(SCSI_PATH_STATE_FAILING, &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)
+ /*
+ * FIXME 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:
+ /*
+ * Likely a bug.
+ */
+ return FAILED;
+ }
+
+ /*
+ * Next, check the message byte.
+ */
+ if (msg_byte(scmd->result) != COMMAND_COMPLETE) {
+ /*
+ * XXX Use SCSI_PATH_DEAD or SCSI_PATH_FAILING?
+ */
+ 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.
+ */
+ /* fallthrough */
+ case BUSY:
+ /*
+ * device can't talk to us at the moment. Should only
+ * occur (SAM-3) when the task queue is empty, so will cause
+ * the empty queue handling to trigger a stall in the
+ * device.
+ */
+ (void) scsi_check_paths(SCSI_PATH_STATE_GOOD, &pathid, scmd);
+ return ADD_TO_MLQUEUE;
+ case GOOD:
+ case COMMAND_TERMINATED:
+ /*
+ * Optimize IO completion with no errors case by not calling
+ * scsi_check_paths here. This is OK as long as there is no
+ * SCSI_PATH_STATE_FAILING support.
+ */
+ 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 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:
+
+ 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;
+ struct scsi_traverse_hndl 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 */

2002-10-25 22:25:32

by Patrick Mansfield

[permalink] [raw]
Subject: [PATCH] 5/6 2.5.44 scsi multi-path IO - lower layer (adapter) changes

SCSI low-layer (adapter drivers) multi-path IO changes

53c700.c | 27 +++++++++++++++++--------
aic7xxx/aic7xxx_linux.c | 27 ++++++++++++++++---------
ips.c | 13 ++++++++++--
qlogicfc.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 99 insertions(+), 19 deletions(-)

diff -Nru a/drivers/scsi/53c700.c b/drivers/scsi/53c700.c
--- a/drivers/scsi/53c700.c Fri Oct 25 11:26:47 2002
+++ b/drivers/scsi/53c700.c Fri Oct 25 11:26:47 2002
@@ -1084,7 +1084,7 @@
DEBUG(("scsi%d: (%d:%d) RESELECTED!\n",
host->host_no, reselection_id, lun));
/* clear the reselection indicator */
- SDp = scsi_find_device(host, 0, reselection_id, lun);
+ SDp = scsi_locate_sdev(host->host_no, 0, reselection_id, lun);
if(unlikely(SDp == NULL)) {
printk(KERN_ERR "scsi%d: (%d:%d) HAS NO device\n",
host->host_no, reselection_id, lun);
@@ -1506,6 +1506,7 @@
if(sstat0 & SCSI_RESET_DETECTED) {
Scsi_Device *SDp;
int i;
+ struct scsi_traverse_hndl strav_hndl;

hostdata->state = NCR_700_HOST_BUSY;

@@ -1513,7 +1514,7 @@
host->host_no, SCp, SCp == NULL ? NULL : SCp->host_scribble, dsp, dsp - hostdata->pScript);

/* clear all the negotiated parameters */
- for(SDp = host->host_queue; SDp != NULL; SDp = SDp->next)
+ scsi_for_each_host_sdev(&strav_hndl, SDp, host->host_no)
SDp->hostdata = 0;

/* clear all the slots and their pending commands */
@@ -1726,6 +1727,7 @@
struct Scsi_Host *host;
struct NCR_700_Host_Parameters *hostdata;
Scsi_Device *SDp;
+ struct scsi_traverse_hndl strav_hndl;

host = scsi_host_hn_get(host_no);
if(host == NULL)
@@ -1740,8 +1742,10 @@
len += sprintf(&buf[len],"\
Target Depth Active Next Tag\n\
====== ===== ====== ========\n");
- for(SDp = host->host_queue; SDp != NULL; SDp = SDp->next) {
- len += sprintf(&buf[len]," %2d:%2d %4d %4d %4d\n", SDp->id, SDp->lun, SDp->current_queue_depth, NCR_700_get_depth(SDp), SDp->current_tag);
+ scsi_for_each_host_sdev(&strav_hndl, SDp, host->host_no) {
+ len += scsi_paths_proc_print_paths(SDp, &buf[len],
+ "%2d:%2d:%2d:%2d");
+ len += sprintf(&buf[len]," %4d %4d %4d\n", SDp->current_queue_depth, NCR_700_get_depth(SDp), SDp->current_tag);
}
if((len -= offset) <= 0)
return 0;
@@ -1814,13 +1818,13 @@
* tag queuing per LUN. We only support it per PUN because
* of potential reselection issues */
printk(KERN_NOTICE "scsi%d: (%d:%d) beginning blk layer TCQ\n",
- SCp->device->host->host_no, SCp->target, SCp->lun);
+ SCp->host->host_no, SCp->target, SCp->lun);
scsi_activate_tcq(SCp->device, NCR_700_MAX_TAGS);
}

if(blk_rq_tagged(SCp->request)
&& (hostdata->tag_negotiated &(1<<SCp->target)) == 0) {
- printk(KERN_INFO "scsi%d: (%d:%d) Enabling Tag Command Queuing\n", SCp->device->host->host_no, SCp->target, SCp->lun);
+ printk(KERN_INFO "scsi%d: (%d:%d) Enabling Tag Command Queuing\n", SCp->host->host_no, SCp->target, SCp->lun);
hostdata->tag_negotiated |= (1<<SCp->target);
NCR_700_set_flag(SCp->device, NCR_700_DEV_BEGIN_TAG_QUEUEING);
}
@@ -1833,7 +1837,7 @@
* */
if(!blk_rq_tagged(SCp->request)
&& (hostdata->tag_negotiated &(1<<SCp->target))) {
- printk(KERN_INFO "scsi%d: (%d:%d) Disabling Tag Command Queuing\n", SCp->device->host->host_no, SCp->target, SCp->lun);
+ printk(KERN_INFO "scsi%d: (%d:%d) Disabling Tag Command Queuing\n", SCp->host->host_no, SCp->target, SCp->lun);
hostdata->tag_negotiated &= ~(1<<SCp->target);
}

@@ -2003,12 +2007,19 @@
STATIC int
NCR_700_slave_attach(Scsi_Device *SDp)
{
+ struct Scsi_Host *host;
/* to do here: allocate memory; build a queue_full list */
if(SDp->tagged_supported) {
/* do TCQ stuff here */
} else {
/* initialise to default depth */
- scsi_adjust_queue_depth(SDp, 0, SDp->host->cmd_per_lun);
+ host = scsi_get_host(SDp);
+ if (!host)
+ /*
+ * Probably a BUG.
+ */
+ return 1;
+ scsi_adjust_queue_depth(SDp, 0, host->cmd_per_lun);
}
return 0;
}
diff -Nru a/drivers/scsi/aic7xxx/aic7xxx_linux.c b/drivers/scsi/aic7xxx/aic7xxx_linux.c
--- a/drivers/scsi/aic7xxx/aic7xxx_linux.c Fri Oct 25 11:26:47 2002
+++ b/drivers/scsi/aic7xxx/aic7xxx_linux.c Fri Oct 25 11:26:47 2002
@@ -1452,8 +1452,12 @@
{
struct ahc_softc *ahc;
u_long flags;
+ struct Scsi_Host *shost;

- ahc = *((struct ahc_softc **)device->host->hostdata);
+ shost = scsi_get_host(device);
+ if (!shost)
+ return 1;
+ ahc = *((struct ahc_softc **)shost->hostdata);
ahc_lock(ahc, &flags);
ahc_linux_device_queue_depth(ahc, device);
ahc_unlock(ahc, &flags);
@@ -1470,11 +1474,14 @@
struct ahc_initiator_tinfo *targ_info;
struct ahc_tmode_tstate *tstate;
uint8_t tags;
+ struct scsi_path_id path;
+
+ scsi_get_path(device, &path);

ahc_compile_devinfo(&devinfo,
- device->channel == 0 ? ahc->our_id : ahc->our_id_b,
- device->id, device->lun,
- device->channel == 0 ? 'A' : 'B',
+ path.spi_channel == 0 ? ahc->our_id : ahc->our_id_b,
+ path.spi_id, path.spi_lun,
+ path.spi_channel == 0 ? 'A' : 'B',
ROLE_INITIATOR);
targ_info = ahc_fetch_transinfo(ahc, devinfo.channel,
devinfo.our_scsiid,
@@ -1506,8 +1513,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, path.spi_channel + 'A',
+ path.spi_id, path.spi_lun, tags);
} else {
/*
* We allow the OS to queue 2 untagged transactions to
@@ -1516,7 +1523,7 @@
* some latency.
device->queue_depth = 2;
*/
- scsi_adjust_queue_depth(device, 0, device->host->cmd_per_lun);
+ scsi_adjust_queue_depth(device, 0, path.spi_shpnt->cmd_per_lun);
}
}

@@ -2727,8 +2734,10 @@
int extended;
struct ahc_softc *ahc;
unsigned char *buf;
+ struct scsi_path_id path;

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

if (buf) {
@@ -2744,7 +2753,7 @@

if (aic7xxx_extended != 0)
extended = 1;
- else if (disk->device->channel == 0)
+ else if (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 Fri Oct 25 11:26:47 2002
+++ b/drivers/scsi/ips.c Fri Oct 25 11:26:47 2002
@@ -1782,10 +1782,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 */
@@ -1877,8 +1882,12 @@
{
ips_ha_t *ha;
int min;
+ struct Scsi_Host *sh;

- ha = IPS_HA(SDptr->host);
+ sh = scsi_get_host(SDptr);
+ if (!sh)
+ return 1;
+ ha = IPS_HA(sh);
if (SDptr->tagged_supported) {
min = ha->max_cmds / 2;
if (min <= 16)
diff -Nru a/drivers/scsi/qlogicfc.c b/drivers/scsi/qlogicfc.c
--- a/drivers/scsi/qlogicfc.c Fri Oct 25 11:26:48 2002
+++ b/drivers/scsi/qlogicfc.c Fri Oct 25 11:26:48 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-10-25 22:28:09

by Patrick Mansfield

[permalink] [raw]
Subject: [PATCH] 4/6 2.5.44 scsi multi-path IO - upper layer

SCSI upper-layer multi-path IO changes

sd.c | 52 ++++++++++++++----------
sg.c | 130 ++++++++++++++++++++++++++++++++++++++++---------------------
sr.c | 18 +++++---
sr_ioctl.c | 4 +
st.c | 50 ++++++++++++++---------
5 files changed, 160 insertions(+), 94 deletions(-)

diff -Nru a/drivers/scsi/sd.c b/drivers/scsi/sd.c
--- a/drivers/scsi/sd.c Fri Oct 25 11:26:48 2002
+++ b/drivers/scsi/sd.c Fri Oct 25 11:26:48 2002
@@ -219,8 +219,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 */
@@ -466,6 +467,7 @@
**/
static int sd_open(struct inode *inode, struct file *filp)
{
+ struct Scsi_Host * shp;
struct gendisk *disk = inode->i_bdev->bd_disk;
Scsi_Disk *sdkp = disk->private_data;
Scsi_Device * sdp = sdkp->device;
@@ -486,8 +488,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++;
@@ -531,8 +536,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;
@@ -554,6 +559,7 @@
struct gendisk *disk = inode->i_bdev->bd_disk;
Scsi_Disk *sdkp = disk->private_data;
Scsi_Device *sdp = sdkp->device;
+ struct Scsi_Host * shp;

SCSI_LOG_HLQUEUE(3, printk("sd_release: disk=%s\n", disk->disk_name));
if (!sdp)
@@ -569,8 +575,9 @@
if (scsi_block_when_processing_errors(sdp))
scsi_set_medium_removal(sdp, SCSI_REMOVAL_ALLOW);
}
- 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);

@@ -1071,8 +1078,8 @@
}

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);
@@ -1097,7 +1104,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;

/*
@@ -1105,7 +1111,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
@@ -1113,13 +1119,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
@@ -1371,8 +1377,9 @@
if (!gd)
return 1;

- 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");
@@ -1413,9 +1420,10 @@
gd->driverfs_dev = &sdp->sdev_driverfs_dev;
gd->flags |= GENHD_FL_DRIVERFS | GENHD_FL_DEVFS;
sd_disks[dsk_nr] = gd;
- printk(KERN_NOTICE "Attached scsi %sdisk %s at scsi%d, channel %d, "
- "id %d, lun %d\n", sdp->removable ? "removable " : "",
- gd->disk_name, sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
+ printk(KERN_NOTICE "Attached scsi %sdisk %s at: ",
+ sdp->removable ? "removable " : "", gd->disk_name);
+ scsi_paths_printk(sdp, "; ", "scsi%d, channel %d, id %d, lun %d");
+ printk("\n");
return 0;
}

@@ -1448,9 +1456,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 Fri Oct 25 11:26:47 2002
+++ b/drivers/scsi/sg.c Fri Oct 25 11:26:47 2002
@@ -250,6 +250,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);
@@ -260,8 +261,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) ||
@@ -302,7 +306,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;
@@ -316,8 +320,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;
}

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

if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
return -ENXIO;
@@ -334,10 +339,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);
@@ -750,6 +755,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;
@@ -823,7 +830,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:
@@ -839,14 +849,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->new_queue_depth,
&sg_idp->d_queue_depth);
@@ -962,12 +973,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;
@@ -1228,6 +1245,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;
@@ -1303,8 +1321,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);
@@ -1439,6 +1458,7 @@
Sg_device *sdp = NULL;
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 */
@@ -1470,11 +1490,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);
if (NULL != sdp)
vfree((char *) sdp);
return 1;
@@ -1504,7 +1523,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));
@@ -1534,11 +1554,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;
}
@@ -1553,6 +1572,7 @@
Sg_request *srp;
Sg_request *tsrp;
int k, delay;
+ struct Scsi_Host * shp;

if (NULL == sg_dev_arr)
return;
@@ -1575,10 +1595,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;
@@ -1607,7 +1627,7 @@
sdp->de = NULL;
device_remove_file(&sdp->sg_driverfs_dev, &dev_attr_type);
device_remove_file(&sdp->sg_driverfs_dev, &dev_attr_kdev);
- put_device(&sdp->sg_driverfs_dev);
+ device_unregister(&sdp->sg_driverfs_dev);
if (NULL == sdp->headfp)
vfree((char *) sdp);
}
@@ -1668,13 +1688,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;
@@ -2459,6 +2483,7 @@
{
Sg_fd *sfp;
unsigned long iflags;
+ struct Scsi_Host * shp;

sfp = (Sg_fd *) sg_page_malloc(sizeof (Sg_fd), 0, 0);
if (!sfp)
@@ -2470,8 +2495,9 @@
sfp->timeout = SG_DEFAULT_TIMEOUT;
sfp->timeout_user = SG_DEFAULT_TIMEOUT_USER;
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;
@@ -2531,6 +2557,7 @@
Sg_request *tsrp;
int dirty = 0;
int res = 0;
+ struct Scsi_Host * shp;

for (srp = sfp->headrp; srp; srp = tsrp) {
tsrp = srp->nextrp;
@@ -2564,8 +2591,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));
}
@@ -2995,13 +3023,25 @@
PRINT_PROC(" >>> device=sg%d ", dev);
if (sdp->detached)
PRINT_PROC("detached pending close ");
+#if 0
else
+ /*
+ * FIXME multi-path print all
+ * paths. Ideally, something like:
+ */
+ foo = scsi_paths_proc_print_paths(scd,
+ buffer + bar,
+ "scsi%d chan=%d id=%d lun=%d\n");
+ /*
+ * Previous code:
+ */
PRINT_PROC
("scsi%d chan=%d id=%d lun=%d em=%d",
scsidp->host->host_no,
scsidp->channel, scsidp->id,
scsidp->lun,
scsidp->host->hostt->emulated);
+#endif
PRINT_PROC(" sg_tablesize=%d excl=%d\n",
sdp->sg_tablesize, sdp->exclude);
}
@@ -3031,13 +3071,15 @@
for (j = 0; j < max_dev; ++j) {
sdp = sg_get_dev(j);
if (sdp && (scsidp = sdp->device) && (!sdp->detached))
- 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,
- (int) scsidp->access_count,
- (int) scsidp->new_queue_depth,
- (int) scsidp->device_busy,
- (int) scsidp->online);
+ /*
+ * FIXME multi-path to print all paths.
+ */
+ PRINT_PROC("%d\t%d\t%d\t%d\t%d\n",
+ (int)scsidp->type,
+ (int)scsidp->access_count,
+ (int)scsidp->new_queue_depth,
+ (int)scsidp->device_busy,
+ (int)scsidp->online);
else
PRINT_PROC("-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\n");
}
diff -Nru a/drivers/scsi/sr.c b/drivers/scsi/sr.c
--- a/drivers/scsi/sr.c Fri Oct 25 11:26:47 2002
+++ b/drivers/scsi/sr.c Fri Oct 25 11:26:47 2002
@@ -97,13 +97,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);
}
@@ -428,6 +430,7 @@

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

if (!cd->device)
@@ -440,8 +443,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);

@@ -491,8 +495,9 @@
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");
+ printk("\n");
return 0;
}

@@ -707,7 +712,6 @@
cgc->timeout = IOCTL_TIMEOUT;

sr_do_ioctl(cdi->handle, cgc);
-
return cgc->stat;
}

diff -Nru a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c
--- a/drivers/scsi/sr_ioctl.c Fri Oct 25 11:26:48 2002
+++ b/drivers/scsi/sr_ioctl.c Fri Oct 25 11:26:48 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;

@@ -93,7 +94,8 @@

/* use ISA DMA buffer if necessary */
SRpnt->sr_request->buffer = cgc->buffer;
- if (cgc->buffer && SRpnt->sr_host->unchecked_isa_dma &&
+ SHpnt = scsi_get_host(SDev);
+ if (cgc->buffer && SHpnt && SHpnt->unchecked_isa_dma &&
(virt_to_phys(cgc->buffer) + cgc->buflen - 1 > ISA_DMA_THRESHOLD)) {
bounce_buffer = (char *) kmalloc(cgc->buflen, GFP_DMA);
if (bounce_buffer == NULL) {
diff -Nru a/drivers/scsi/st.c b/drivers/scsi/st.c
--- a/drivers/scsi/st.c Fri Oct 25 11:26:47 2002
+++ b/drivers/scsi/st.c Fri Oct 25 11:26:48 2002
@@ -975,6 +975,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 ||
@@ -992,8 +993,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)) {
@@ -1036,8 +1040,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;

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

kdev_t devt = inode->i_rdev;
int dev;
@@ -1189,8 +1194,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;
}
@@ -3683,22 +3689,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;
@@ -3775,7 +3785,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;
@@ -3793,17 +3803,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)
@@ -3889,9 +3899,9 @@
}
devfs_register_tape (tpnt->de_r[0]);

- 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-10-25 22:24:34

by Patrick Mansfield

[permalink] [raw]
Subject: [PATCH] 5/6 2.5.44 scsi multi-path IO - qla driver changes

multi-path IO and 2.5.x changes for the qla 6.03.00 Beta 7 driver.

Apply this on top of the driver found at:

http://download.qlogic.com/drivers/6443/qla2x00-v6.03.00b7-dist.tgz

Makefile | 9 ++++++
qla2x00.c | 74 ++++++++++++++++++++------------------------------------
qla2x00.h | 7 ++---
qla2x00_ioctl.c | 4 ++-
qla_settings.h | 2 -
5 files changed, 43 insertions(+), 53 deletions(-)

diff -urN -X /home/patman/dontdiff qla2x00src-v6.03.00b7/Makefile qla2x00src-v6.03.00b7-mpath/Makefile
--- qla2x00src-v6.03.00b7/Makefile Wed Dec 31 16:00:00 1969
+++ qla2x00src-v6.03.00b7-mpath/Makefile Wed Oct 23 13:01:45 2002
@@ -0,0 +1,9 @@
+# andmike's condensed makefile
+# To use cd to kernel source and type the following.
+# make SUBDIRS=/path/to/your/module modules
+
+CFLAGS_qla2200.o = -I$(TOPDIR)/drivers/scsi
+CFLAGS_qla2300.o = -I$(TOPDIR)/drivers/scsi
+obj-m := qla2200.o qla2300.o
+
+include $(TOPDIR)/Rules.make
diff -urN -X /home/patman/dontdiff qla2x00src-v6.03.00b7/qla2x00.c qla2x00src-v6.03.00b7-mpath/qla2x00.c
--- qla2x00src-v6.03.00b7/qla2x00.c Mon Oct 14 06:15:26 2002
+++ qla2x00src-v6.03.00b7-mpath/qla2x00.c Wed Oct 23 13:15:06 2002
@@ -77,7 +77,6 @@
#include <linux/pci.h>
#include <linux/proc_fs.h>
#include <linux/blk.h>
-#include <linux/tqueue.h>
#include <linux/interrupt.h>
#include <linux/stat.h>
#include <linux/slab.h>
@@ -228,7 +227,7 @@
STATIC uint8_t qla2x00_register_with_Linux(scsi_qla_host_t *ha,
uint8_t maxchannels);
STATIC int qla2x00_done(scsi_qla_host_t *);
-STATIC void qla2x00_select_queue_depth(struct Scsi_Host *, Scsi_Device *);
+STATIC int qla2x00_slave_attach(Scsi_Device *);

STATIC void qla2x00_timer(scsi_qla_host_t *);

@@ -2227,7 +2226,6 @@

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

#if MEMORY_MAPPED_IO
@@ -3690,11 +3688,11 @@

heads = 64;
sectors = 32;
- cylinders = disk->capacity / (heads * sectors);
+ cylinders = sector_div(disk->capacity, heads * sectors);
if (cylinders > 1024) {
heads = 255;
sectors = 63;
- cylinders = disk->capacity / (heads * sectors);
+ cylinders = sector_div(disk->capacity, heads * sectors);
}

geom[0] = heads;
@@ -4318,7 +4316,7 @@
}

/**************************************************************************
-* qla2x00_device_queue_depth
+* qla2x00_slave_attach
* Determines the queue depth for a given device. There are two ways
* a queue depth can be obtained for a tagged queueing device. One
* way is the default queue depth which is determined by whether
@@ -4326,54 +4324,25 @@
* as the default queue depth. Otherwise, we use either 4 or 8 as the
* default queue depth (dependent on the number of hardware SCBs).
**************************************************************************/
-void
-qla2x00_device_queue_depth(scsi_qla_host_t *p, Scsi_Device *device)
+STATIC int
+qla2x00_slave_attach(Scsi_Device *device)
{
- int default_depth = 16;
+ int queue_depth = 16;

- device->queue_depth = default_depth;
if (device->tagged_supported) {
device->tagged_queue = 1;
device->current_tag = 0;
#if defined(MODULE)
if (!(ql2xmaxqdepth == 0 || ql2xmaxqdepth > 256))
- device->queue_depth = ql2xmaxqdepth;
+ queue_depth = ql2xmaxqdepth;
#endif
-
- printk(KERN_INFO
- "scsi(%d:%d:%d:%d): Enabled tagged queuing, "
- "queue depth %d.\n",
- (int)p->host_no,
- device->channel,
- device->id,
- device->lun,
- device->queue_depth);
- }
-
-}
-
-/**************************************************************************
-* qla2x00_select_queue_depth
-*
-* Description:
-* Sets the queue depth for each SCSI device hanging off the input
-* host adapter. We use a queue depth of 2 for devices that do not
-* support tagged queueing.
-**************************************************************************/
-STATIC void
-qla2x00_select_queue_depth(struct Scsi_Host *host, Scsi_Device *scsi_devs)
-{
- Scsi_Device *device;
- 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);
+ printk(KERN_INFO "Enabled tagged queuing, queue depth %d for ",
+ queue_depth);
+ scsi_paths_printk(device, " ", "<%d:%d:%d:%d>");
+ printk("\n");
}
-
- LEAVE("qla2x00_select_queue_depth");
+ scsi_adjust_queue_depth(device, MSG_ORDERED_TAG, queue_depth);
+ return 0;
}

/**************************************************************************
@@ -5793,7 +5762,9 @@
pci_set_master(ha->pdev);
pci_read_config_word(ha->pdev,OFFSET(creg->revision_id), &buf_wd);
ha->revision = buf_wd;
+#if (MEMORY_MAPPED_IO == 0)
if (!ha->iobase) {
+#endif
/* Get command register. */
if (pci_read_config_word(
ha->pdev,OFFSET(creg->command),
@@ -5872,7 +5843,9 @@
}
}
}
+#if (MEMORY_MAPPED_IO == 0)
} else
+#endif
status = 0;


@@ -15903,9 +15876,16 @@
unsigned int cmd, unsigned long arg)
{
Scsi_Device fake_scsi_device;
- fake_scsi_device.host = apidev_host;
+ int ret;

- return (qla2x00_ioctl(&fake_scsi_device, (int)cmd, (void*)arg));
+ if (scsi_add_path(&fake_scsi_device, apidev_host, 0,
+ apidev_host->this_id, 0))
+ return -ENODEV;
+ ret = qla2x00_ioctl(&fake_scsi_device, (int)cmd, (void*)arg);
+ scsi_remove_path(&fake_scsi_device, SCSI_FIND_ALL_HOST_NO,
+ SCSI_FIND_ALL_CHANNEL, SCSI_FIND_ALL_ID,
+ SCSI_FIND_ALL_LUN);
+ return ret;
}

static struct file_operations apidev_fops = {
diff -urN -X /home/patman/dontdiff qla2x00src-v6.03.00b7/qla2x00.h qla2x00src-v6.03.00b7-mpath/qla2x00.h
--- qla2x00src-v6.03.00b7/qla2x00.h Mon Oct 14 06:15:26 2002
+++ qla2x00src-v6.03.00b7-mpath/qla2x00.h Wed Oct 23 13:12:42 2002
@@ -129,8 +129,8 @@
/*
* I/O register
*/
-/* #define MEMORY_MAPPED_IO */ /* Enable memory mapped I/O */
-#undef MEMORY_MAPPED_IO /* Disable memory mapped I/O */
+#define MEMORY_MAPPED_IO 1 /* Enable memory mapped I/O */
+/* #undef MEMORY_MAPPED_IO Disable memory mapped I/O */

#if defined(MEMORY_MAPPED_IO)
#define RD_REG_BYTE(addr) readb(addr)
@@ -2710,7 +2710,6 @@


#define QLA2100_LINUX_TEMPLATE { \
- next: NULL, \
module: NULL, \
proc_dir: NULL, \
proc_info: qla2x00_proc_info, \
@@ -2728,7 +2727,7 @@
eh_host_reset_handler: qla2xxx_eh_host_reset, \
abort: NULL, \
reset: NULL, \
- slave_attach: NULL, \
+ slave_attach: qla2x00_slave_attach, \
bios_param: QLA2100_BIOSPARAM, \
can_queue: 255, /* max simultaneous cmds */\
this_id: -1, /* scsi id of host adapter */\
diff -urN -X /home/patman/dontdiff qla2x00src-v6.03.00b7/qla2x00_ioctl.c qla2x00src-v6.03.00b7-mpath/qla2x00_ioctl.c
--- qla2x00src-v6.03.00b7/qla2x00_ioctl.c Mon Oct 14 06:15:26 2002
+++ qla2x00src-v6.03.00b7-mpath/qla2x00_ioctl.c Wed Oct 23 13:01:02 2002
@@ -240,7 +240,9 @@
return (-EINVAL);
}

- host = dev->host;
+ host = scsi_get_host(dev);
+ if (!host)
+ return(-EINVAL);
ha = (scsi_qla_host_t *) host->hostdata; /* midlayer chosen instance */

ret = verify_area(VERIFY_READ, (void *)arg, sizeof(EXT_IOCTL));
diff -urN -X /home/patman/dontdiff qla2x00src-v6.03.00b7/qla_settings.h qla2x00src-v6.03.00b7-mpath/qla_settings.h
--- qla2x00src-v6.03.00b7/qla_settings.h Mon Oct 14 06:15:26 2002
+++ qla2x00src-v6.03.00b7-mpath/qla_settings.h Wed Oct 23 13:11:58 2002
@@ -65,7 +65,7 @@
* - a tasklet to process the done queue and send requests back to
* the OS.
*/
-#define QLA2X_PERFORMANCE 1
+#define QLA2X_PERFORMANCE 0

/* The following WORD_FW_LOAD is defined in Makefile for ia-64 builds
and can also be decommented here for Word by Word confirmation of

2002-10-25 22:21:01

by Patrick Mansfield

[permalink] [raw]
Subject: [PATCH] 3/6 2.5.44 scsi multi-path IO - mid layer changes

SCSI mid-layer multi-path IO changes

drivers/scsi/scsi_paths.h | 146 ++++++++++++++++
drivers/scsi/scsi_proc.c | 8
drivers/scsi/scsi_scan.c | 408 +++++++++++++++++++++++++++-------------------
drivers/scsi/scsi_syms.c | 7
include/scsi/scsi.h | 23 ++
5 files changed, 422 insertions(+), 170 deletions(-)

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 Fri Oct 25 11:26:48 2002
@@ -0,0 +1,146 @@
+/*
+ * 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
+
+
+#if !defined(MAX_NUMNODES) || (MAX_NUMNODES == 0)
+#error Undefined or 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
+#define req_to_nid(req) 0
+#define scsihost_to_node(host) 0
+#define page_to_nid(page) 0
+#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_proc.c b/drivers/scsi/scsi_proc.c
--- a/drivers/scsi/scsi_proc.c Fri Oct 25 11:26:47 2002
+++ b/drivers/scsi/scsi_proc.c Fri Oct 25 11:26:47 2002
@@ -264,15 +264,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_scan.c b/drivers/scsi/scsi_scan.c
--- a/drivers/scsi/scsi_scan.c Fri Oct 25 11:26:48 2002
+++ b/drivers/scsi/scsi_scan.c Fri Oct 25 11:26:48 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: target responded, and a device is available on a
- * given LUN.
+ * SCSI_SCAN_LUN_PRESENT: 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";

@@ -486,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.
@@ -506,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
@@ -527,45 +528,12 @@
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);
}

/**
- * scsi_free_sdev - cleanup and free a Scsi_Device
- * @sdev: cleanup and free this Scsi_Device
- *
- * Description:
- * Undo the actions in scsi_alloc_sdev, including removing @sdev from
- * the list, and freeing @sdev.
- **/
-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;
-
- blk_cleanup_queue(&sdev->request_queue);
- if (sdev->inquiry != NULL)
- kfree(sdev->inquiry);
- kfree(sdev);
-}
-
-/**
* scsi_check_id_size - check if size fits in the driverfs name
* @sdev: Scsi_Device to use for error message
* @size: the length of the id we want to store
@@ -581,11 +549,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;
@@ -603,7 +570,8 @@
* 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];
@@ -611,15 +579,15 @@

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;
@@ -856,24 +824,26 @@
* 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 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;
@@ -916,8 +886,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));
@@ -927,8 +898,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));
@@ -959,7 +931,8 @@
* 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];
@@ -967,15 +940,15 @@

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;
@@ -1074,14 +1047,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
@@ -1089,23 +1068,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;
}

@@ -1124,9 +1105,10 @@
{
int res = SCSI_2;
Scsi_Device *sdev;
+ struct scsi_traverse_hndl 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
@@ -1156,9 +1138,10 @@
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;
@@ -1271,6 +1254,55 @@
return;
}

+#ifdef CONFIG_SCSI_MULTI_PATH_IO
+/*
+ * scsi_check_sdev_id - check if a path already exist for sdev, if so add
+ * a new path, and return an appropriate value.
+ * @sdev: check for exiting paths on this
+ * @path: path to sdev
+ *
+ * Returns 0 if this is a new device, else a SCSI_SCAN_ mask value.
+ */
+static int scsi_check_sdev_id(Scsi_Device *sdev, Scsi_Device **sdevnew,
+ struct scsi_path_id *pathid)
+{
+ Scsi_Device *sdev_found;
+
+ /*
+ * XXX check here if host adapter is OK with multipath, or if a
+ * particular device is white or black listed for a unique id.
+ */
+
+ /*
+ * 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_remove_scsi_device(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);
+ }
+ return 0;
+}
+#else
+static int scsi_check_sdev_id(Scsi_Device *sdev, Scsi_Device **sdevnew,
+ struct scsi_path_id *pathid)
+{
+ return 0;
+}
+#endif
+
/**
* scsi_add_lun - allocate and fully initialze a Scsi_Device
* @sdevscan: holds information to be stored in the new Scsi_Device
@@ -1285,9 +1317,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)
@@ -1296,9 +1326,20 @@
struct Scsi_Device_Template *sdt;
char devname[64];
extern devfs_handle_t scsi_devfs_handle;
+ struct scsi_path_id pathid;
+ int ret;

- 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;

@@ -1316,7 +1357,7 @@
sdev->inquiry_len = sdevscan->inquiry_len;
sdev->inquiry = kmalloc(sdev->inquiry_len, GFP_ATOMIC);
if (sdev->inquiry == NULL) {
- scsi_free_sdev(sdev);
+ scsi_remove_scsi_device(sdev);
return SCSI_SCAN_NO_RESPONSE;
}

@@ -1325,6 +1366,22 @@
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);
+
+ /*
+ * Check for multi-path device.
+ */
+ ret = scsi_check_sdev_id(sdev, sdevnew, &pathid);
+ if (ret)
+ return(ret);
+
if (*bflags & BLIST_ISROM) {
/*
* It would be better to modify sdev->type, and set
@@ -1395,20 +1452,12 @@
sdev->sdtr = 1;

/*
- * 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);

@@ -1418,7 +1467,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);
@@ -1454,19 +1504,20 @@
if (sdt->detect)
sdev->attached += (*sdt->detect) (sdev);

- if (sdev->host->hostt->slave_attach != NULL) {
- if (sdev->host->hostt->slave_attach(sdev) != 0) {
+ if (pathid.spi_shpnt->hostt->slave_attach != NULL) {
+ if (pathid.spi_shpnt->hostt->slave_attach(sdev) != 0) {
printk(KERN_INFO "%s: scsi_add_lun: failed low level driver attach, setting device offline", devname);
sdev->online = FALSE;
}
- } else if(sdev->host->cmd_per_lun) {
- scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);
+ } else if(pathid.spi_shpnt->cmd_per_lun) {
+ scsi_adjust_queue_depth(sdev, 0, pathid.spi_shpnt->cmd_per_lun);
}

if (sdevnew != NULL)
*sdevnew = sdev;

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

/**
@@ -1479,11 +1530,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)
@@ -1493,7 +1540,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
@@ -1519,9 +1574,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;

@@ -1550,12 +1604,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.
@@ -1604,22 +1658,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;
@@ -1628,7 +1686,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;

/*
@@ -1645,7 +1703,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
*/
@@ -1663,10 +1721,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
@@ -1730,6 +1794,7 @@
ScsiLun *fcp_cur_lun, *lun_data;
Scsi_Request *sreq;
char *data;
+ struct scsi_path_id pathid;

/*
* Only support SCSI-3 and up devices.
@@ -1737,6 +1802,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->new_queue_depth = 1;
scsi_build_commandblocks(sdevscan);
if (sdevscan->current_queue_depth == 0) {
@@ -1748,8 +1820,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.
@@ -1761,9 +1833,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);
@@ -1843,9 +1914,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.
@@ -1875,22 +1945,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;
}
}
@@ -1936,18 +2007,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.
@@ -1993,11 +2061,11 @@
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) {
+ scsi_remove_scsi_device(sdevscan);
+ if (res & SCSI_SCAN_NEW_LUN) {
BUG_ON(sdev == NULL);
-
for (sdt = scsi_devicelist; sdt; sdt = sdt->next)
if (sdt->init && sdt->dev_noticed)
(*sdt->init) ();
@@ -2063,12 +2131,20 @@
* 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.
*/
@@ -2094,6 +2170,6 @@
order_id);
}
}
- scsi_free_sdev(sdevscan);
+ scsi_remove_scsi_device(sdevscan);
}
}
diff -Nru a/drivers/scsi/scsi_syms.c b/drivers/scsi/scsi_syms.c
--- a/drivers/scsi/scsi_syms.c Fri Oct 25 11:26:47 2002
+++ b/drivers/scsi/scsi_syms.c Fri Oct 25 11:26:47 2002
@@ -71,6 +71,7 @@

EXPORT_SYMBOL(scsi_get_host_dev);
EXPORT_SYMBOL(scsi_free_host_dev);
+EXPORT_SYMBOL(scsi_sdev_list);

EXPORT_SYMBOL(scsi_sleep);

@@ -105,6 +106,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 Fri Oct 25 11:26:48 2002
+++ b/include/scsi/scsi.h Fri Oct 25 11:26:48 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
*/