From 3e082a910d217b2e7b186077ebf5a1126a68c62f Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 28 Sep 2006 15:19:20 -0600 Subject: [PATCH 01/73] [SCSI] Add ability to scan scsi busses asynchronously Since it often takes around 20-30 seconds to scan a scsi bus, it's highly advantageous to do this in parallel with other things. The bulk of this patch is ensuring that devices don't change numbering, and that all devices are discovered prior to trying to start init. For those who build SCSI as modules, there's a new scsi_wait_scan module that will ensure all bus scans are finished. This patch only handles drivers which call scsi_scan_host. Fibre Channel, SAS, SATA, USB and Firewire all need additional work. Signed-off-by: Matthew Wilcox Signed-off-by: James Bottomley --- Documentation/kernel-parameters.txt | 5 + drivers/scsi/Makefile | 2 + drivers/scsi/scsi_priv.h | 3 + drivers/scsi/scsi_scan.c | 203 +++++++++++++++++++++++++++- drivers/scsi/scsi_wait_scan.c | 31 +++++ include/scsi/scsi_device.h | 30 ++-- include/scsi/scsi_host.h | 3 + 7 files changed, 256 insertions(+), 21 deletions(-) create mode 100644 drivers/scsi/scsi_wait_scan.c diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index ff571f9298e0..5a92ac085969 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1410,6 +1410,11 @@ and is between 256 and 4096 characters. It is defined in the file scsi_logging= [SCSI] + scsi_mod.scan= [SCSI] sync (default) scans SCSI busses as they are + discovered. async scans them in kernel threads, + allowing boot to proceed. none ignores them, expecting + user space to do the scan. + selinux [SELINUX] Disable or enable SELinux at boot time. Format: { "0" | "1" } See security/selinux/Kconfig help text. diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index bcca39c3bcbf..a0a77fde3708 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -141,6 +141,8 @@ obj-$(CONFIG_CHR_DEV_SCH) += ch.o # This goes last, so that "real" scsi devices probe earlier obj-$(CONFIG_SCSI_DEBUG) += scsi_debug.o +obj-$(CONFIG_SCSI) += scsi_wait_scan.o + scsi_mod-y += scsi.o hosts.o scsi_ioctl.o constants.o \ scsicam.o scsi_error.o scsi_lib.o \ scsi_scan.o scsi_sysfs.o \ diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index 5d023d44e5e7..f458c2f686d2 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -39,6 +39,9 @@ static inline void scsi_log_completion(struct scsi_cmnd *cmd, int disposition) { }; #endif +/* scsi_scan.c */ +int scsi_complete_async_scans(void); + /* scsi_devinfo.c */ extern int scsi_get_device_flags(struct scsi_device *sdev, const unsigned char *vendor, diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index fd9e281c3bfe..148e24cc3222 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -29,7 +29,9 @@ #include #include #include -#include +#include +#include +#include #include #include @@ -87,6 +89,11 @@ module_param_named(max_luns, max_scsi_luns, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(max_luns, "last scsi LUN (should be between 1 and 2^32-1)"); +static char scsi_scan_type[6] = "sync"; + +module_param_string(scan, scsi_scan_type, sizeof(scsi_scan_type), S_IRUGO); +MODULE_PARM_DESC(scan, "sync, async or none"); + /* * max_scsi_report_luns: the maximum number of LUNS that will be * returned from the REPORT LUNS command. 8 times this value must @@ -108,6 +115,68 @@ MODULE_PARM_DESC(inq_timeout, "Timeout (in seconds) waiting for devices to answer INQUIRY." " Default is 5. Some non-compliant devices need more."); +static DEFINE_SPINLOCK(async_scan_lock); +static LIST_HEAD(scanning_hosts); + +struct async_scan_data { + struct list_head list; + struct Scsi_Host *shost; + struct completion prev_finished; +}; + +/** + * scsi_complete_async_scans - Wait for asynchronous scans to complete + * + * Asynchronous scans add themselves to the scanning_hosts list. Once + * that list is empty, we know that the scans are complete. Rather than + * waking up periodically to check the state of the list, we pretend to be + * a scanning task by adding ourselves at the end of the list and going to + * sleep. When the task before us wakes us up, we take ourselves off the + * list and return. + */ +int scsi_complete_async_scans(void) +{ + struct async_scan_data *data; + + do { + if (list_empty(&scanning_hosts)) + return 0; + /* If we can't get memory immediately, that's OK. Just + * sleep a little. Even if we never get memory, the async + * scans will finish eventually. + */ + data = kmalloc(sizeof(*data), GFP_KERNEL); + if (!data) + msleep(1); + } while (!data); + + data->shost = NULL; + init_completion(&data->prev_finished); + + spin_lock(&async_scan_lock); + /* Check that there's still somebody else on the list */ + if (list_empty(&scanning_hosts)) + goto done; + list_add_tail(&data->list, &scanning_hosts); + spin_unlock(&async_scan_lock); + + printk(KERN_INFO "scsi: waiting for bus probes to complete ...\n"); + wait_for_completion(&data->prev_finished); + + spin_lock(&async_scan_lock); + list_del(&data->list); + done: + spin_unlock(&async_scan_lock); + + kfree(data); + return 0; +} + +#ifdef MODULE +/* Only exported for the benefit of scsi_wait_scan */ +EXPORT_SYMBOL_GPL(scsi_complete_async_scans); +#endif + /** * scsi_unlock_floptical - unlock device via a special MODE SENSE command * @sdev: scsi device to send command to @@ -619,7 +688,7 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result, * SCSI_SCAN_LUN_PRESENT: a new scsi_device was allocated and initialized **/ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, - int *bflags) + int *bflags, int async) { /* * XXX do not save the inquiry, since it can change underneath us, @@ -795,7 +864,7 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, * register it and tell the rest of the kernel * about it. */ - if (scsi_sysfs_add_sdev(sdev) != 0) + if (!async && scsi_sysfs_add_sdev(sdev) != 0) return SCSI_SCAN_NO_RESPONSE; return SCSI_SCAN_LUN_PRESENT; @@ -964,7 +1033,7 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, goto out_free_result; } - res = scsi_add_lun(sdev, result, &bflags); + res = scsi_add_lun(sdev, result, &bflags, shost->async_scan); if (res == SCSI_SCAN_LUN_PRESENT) { if (bflags & BLIST_KEY) { sdev->lockable = 0; @@ -1464,6 +1533,9 @@ void scsi_scan_target(struct device *parent, unsigned int channel, { struct Scsi_Host *shost = dev_to_shost(parent); + if (!shost->async_scan) + scsi_complete_async_scans(); + mutex_lock(&shost->scan_mutex); if (scsi_host_scan_allowed(shost)) __scsi_scan_target(parent, channel, id, lun, rescan); @@ -1509,6 +1581,9 @@ int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel, "%s: <%u:%u:%u>\n", __FUNCTION__, channel, id, lun)); + if (!shost->async_scan) + scsi_complete_async_scans(); + if (((channel != SCAN_WILD_CARD) && (channel > shost->max_channel)) || ((id != SCAN_WILD_CARD) && (id >= shost->max_id)) || ((lun != SCAN_WILD_CARD) && (lun > shost->max_lun))) @@ -1529,14 +1604,130 @@ int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel, return 0; } +static void scsi_sysfs_add_devices(struct Scsi_Host *shost) +{ + struct scsi_device *sdev; + shost_for_each_device(sdev, shost) { + if (scsi_sysfs_add_sdev(sdev) != 0) + scsi_destroy_sdev(sdev); + } +} + +/** + * scsi_prep_async_scan - prepare for an async scan + * @shost: the host which will be scanned + * Returns: a cookie to be passed to scsi_finish_async_scan() + * + * Tells the midlayer this host is going to do an asynchronous scan. + * It reserves the host's position in the scanning list and ensures + * that other asynchronous scans started after this one won't affect the + * ordering of the discovered devices. + */ +struct async_scan_data *scsi_prep_async_scan(struct Scsi_Host *shost) +{ + struct async_scan_data *data; + + if (strncmp(scsi_scan_type, "sync", 4) == 0) + return NULL; + + if (shost->async_scan) { + printk("%s called twice for host %d", __FUNCTION__, + shost->host_no); + dump_stack(); + return NULL; + } + + data = kmalloc(sizeof(*data), GFP_KERNEL); + if (!data) + goto err; + data->shost = scsi_host_get(shost); + if (!data->shost) + goto err; + init_completion(&data->prev_finished); + + spin_lock(&async_scan_lock); + shost->async_scan = 1; + if (list_empty(&scanning_hosts)) + complete(&data->prev_finished); + list_add_tail(&data->list, &scanning_hosts); + spin_unlock(&async_scan_lock); + + return data; + + err: + kfree(data); + return NULL; +} + +/** + * scsi_finish_async_scan - asynchronous scan has finished + * @data: cookie returned from earlier call to scsi_prep_async_scan() + * + * All the devices currently attached to this host have been found. + * This function announces all the devices it has found to the rest + * of the system. + */ +void scsi_finish_async_scan(struct async_scan_data *data) +{ + struct Scsi_Host *shost; + + if (!data) + return; + + shost = data->shost; + if (!shost->async_scan) { + printk("%s called twice for host %d", __FUNCTION__, + shost->host_no); + dump_stack(); + return; + } + + wait_for_completion(&data->prev_finished); + + scsi_sysfs_add_devices(shost); + + spin_lock(&async_scan_lock); + shost->async_scan = 0; + list_del(&data->list); + if (!list_empty(&scanning_hosts)) { + struct async_scan_data *next = list_entry(scanning_hosts.next, + struct async_scan_data, list); + complete(&next->prev_finished); + } + spin_unlock(&async_scan_lock); + + scsi_host_put(shost); + kfree(data); +} + +static int do_scan_async(void *_data) +{ + struct async_scan_data *data = _data; + scsi_scan_host_selected(data->shost, SCAN_WILD_CARD, SCAN_WILD_CARD, + SCAN_WILD_CARD, 0); + + scsi_finish_async_scan(data); + return 0; +} + /** * scsi_scan_host - scan the given adapter * @shost: adapter to scan **/ void scsi_scan_host(struct Scsi_Host *shost) { - scsi_scan_host_selected(shost, SCAN_WILD_CARD, SCAN_WILD_CARD, - SCAN_WILD_CARD, 0); + struct async_scan_data *data; + + if (strncmp(scsi_scan_type, "none", 4) == 0) + return; + + data = scsi_prep_async_scan(shost); + if (!data) { + scsi_scan_host_selected(shost, SCAN_WILD_CARD, SCAN_WILD_CARD, + SCAN_WILD_CARD, 0); + return; + } + kthread_run(do_scan_async, data, "scsi_scan_%d", shost->host_no); } EXPORT_SYMBOL(scsi_scan_host); diff --git a/drivers/scsi/scsi_wait_scan.c b/drivers/scsi/scsi_wait_scan.c new file mode 100644 index 000000000000..8a636103083d --- /dev/null +++ b/drivers/scsi/scsi_wait_scan.c @@ -0,0 +1,31 @@ +/* + * scsi_wait_scan.c + * + * Copyright (C) 2006 James Bottomley + * + * This is a simple module to wait until all the async scans are + * complete. The idea is to use it in initrd/initramfs scripts. You + * modprobe it after all the modprobes of the root SCSI drivers and it + * will wait until they have all finished scanning their busses before + * allowing the boot to proceed + */ + +#include +#include "scsi_priv.h" + +static int __init wait_scan_init(void) +{ + scsi_complete_async_scans(); + return 0; +} + +static void __exit wait_scan_exit(void) +{ +} + +MODULE_DESCRIPTION("SCSI wait for scans"); +MODULE_AUTHOR("James Bottomley"); +MODULE_LICENSE("GPL"); + +late_initcall(wait_scan_init); +module_exit(wait_scan_exit); diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index b401c82036be..ebf31b16dc49 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -223,13 +223,13 @@ extern struct scsi_device *__scsi_iterate_devices(struct Scsi_Host *, struct scsi_device *); /** - * shost_for_each_device - iterate over all devices of a host - * @sdev: iterator - * @host: host whiches devices we want to iterate over + * shost_for_each_device - iterate over all devices of a host + * @sdev: the &struct scsi_device to use as a cursor + * @shost: the &struct scsi_host to iterate over * - * This traverses over each devices of @shost. The devices have - * a reference that must be released by scsi_host_put when breaking - * out of the loop. + * Iterator that returns each device attached to @shost. This loop + * takes a reference on each device and releases it at the end. If + * you break out of the loop, you must call scsi_device_put(sdev). */ #define shost_for_each_device(sdev, shost) \ for ((sdev) = __scsi_iterate_devices((shost), NULL); \ @@ -237,17 +237,17 @@ extern struct scsi_device *__scsi_iterate_devices(struct Scsi_Host *, (sdev) = __scsi_iterate_devices((shost), (sdev))) /** - * __shost_for_each_device - iterate over all devices of a host (UNLOCKED) - * @sdev: iterator - * @host: host whiches devices we want to iterate over + * __shost_for_each_device - iterate over all devices of a host (UNLOCKED) + * @sdev: the &struct scsi_device to use as a cursor + * @shost: the &struct scsi_host to iterate over * - * This traverses over each devices of @shost. It does _not_ take a - * reference on the scsi_device, thus it the whole loop must be protected - * by shost->host_lock. + * Iterator that returns each device attached to @shost. It does _not_ + * take a reference on the scsi_device, so the whole loop must be + * protected by shost->host_lock. * - * Note: The only reason why drivers would want to use this is because - * they're need to access the device list in irq context. Otherwise you - * really want to use shost_for_each_device instead. + * Note: The only reason to use this is because you need to access the + * device list in interrupt context. Otherwise you really want to use + * shost_for_each_device instead. */ #define __shost_for_each_device(sdev, shost) \ list_for_each_entry((sdev), &((shost)->__devices), siblings) diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 39c6f8cc20c3..ba5b3eb6b43f 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -552,6 +552,9 @@ struct Scsi_Host { /* task mgmt function in progress */ unsigned tmf_in_progress:1; + /* Asynchronous scan in progress */ + unsigned async_scan:1; + /* * Optional work queue to be utilized by the transport */ From b4d38e38e66f8e1b32a1b1c00e533175314c8d56 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Oct 2006 16:48:28 -0400 Subject: [PATCH 02/73] [SCSI] Reduce polling in sd.c If a drive reports that no media is present, there's no point in continuing to ask it about media status. This patch (as696) cuts the TUR polling short as soon as the drive reports no media instead of going a full 3 iterations. Signed-off-by: Alan Stern Signed-off-by: James Bottomley --- drivers/scsi/sd.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 84ff203ffedd..152f4ff4ed7e 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1051,6 +1051,14 @@ sd_spinup_disk(struct scsi_disk *sdkp, char *diskname) &sshdr, SD_TIMEOUT, SD_MAX_RETRIES); + /* + * If the drive has indicated to us that it + * doesn't have any media in it, don't bother + * with any more polling. + */ + if (media_not_present(sdkp, &sshdr)) + return; + if (the_result) sense_valid = scsi_sense_valid(&sshdr); retries++; @@ -1059,14 +1067,6 @@ sd_spinup_disk(struct scsi_disk *sdkp, char *diskname) ((driver_byte(the_result) & DRIVER_SENSE) && sense_valid && sshdr.sense_key == UNIT_ATTENTION))); - /* - * If the drive has indicated to us that it doesn't have - * any media in it, don't bother with any of the rest of - * this crap. - */ - if (media_not_present(sdkp, &sshdr)) - return; - if ((driver_byte(the_result) & DRIVER_SENSE) == 0) { /* no sense, TUR either succeeded or failed * with a status error */ From f456393e195e0aa16029985f63cd93b601a0d315 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 30 Oct 2006 15:18:39 -0800 Subject: [PATCH 03/73] [SCSI] libsas: modify error handler to use scsi_eh_* functions This patch adds an EH done queue to sas_ha, converts the error handling strategy function and the sas_scsi_task_done functions in libsas to use the scsi_eh_* commands for error'd commands, and adds checks for the INITIATOR_ABORTED flag so that we do the right thing if a sas_task has been aborted by the initiator. Signed-off-by: Darrick J. Wong Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_init.c | 2 ++ drivers/scsi/libsas/sas_scsi_host.c | 31 ++++++++++++++++++++++++----- include/scsi/libsas.h | 9 ++++++--- 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index c836a237fb79..a2b479a65908 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -112,6 +112,8 @@ int sas_register_ha(struct sas_ha_struct *sas_ha) } } + INIT_LIST_HEAD(&sas_ha->eh_done_q); + return 0; Undo_ports: diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index e46e79355b77..6a97b07849b4 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include "../scsi_sas_internal.h" @@ -46,6 +47,7 @@ static void sas_scsi_task_done(struct sas_task *task) { struct task_status_struct *ts = &task->task_status; struct scsi_cmnd *sc = task->uldd_task; + struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(sc->device->host); unsigned ts_flags = task->task_state_flags; int hs = 0, stat = 0; @@ -116,7 +118,7 @@ static void sas_scsi_task_done(struct sas_task *task) sas_free_task(task); /* This is very ugly but this is how SCSI Core works. */ if (ts_flags & SAS_TASK_STATE_ABORTED) - scsi_finish_command(sc); + scsi_eh_finish_cmd(sc, &sas_ha->eh_done_q); else sc->scsi_done(sc); } @@ -307,6 +309,15 @@ static enum task_disposition sas_scsi_find_task(struct sas_task *task) spin_unlock_irqrestore(&core->task_queue_lock, flags); } + spin_lock_irqsave(&task->task_state_lock, flags); + if (task->task_state_flags & SAS_TASK_INITIATOR_ABORTED) { + spin_unlock_irqrestore(&task->task_state_lock, flags); + SAS_DPRINTK("%s: task 0x%p already aborted\n", + __FUNCTION__, task); + return TASK_IS_ABORTED; + } + spin_unlock_irqrestore(&task->task_state_lock, flags); + for (i = 0; i < 5; i++) { SAS_DPRINTK("%s: aborting task 0x%p\n", __FUNCTION__, task); res = si->dft->lldd_abort_task(task); @@ -409,13 +420,16 @@ Again: SAS_DPRINTK("going over list...\n"); list_for_each_entry_safe(cmd, n, &error_q, eh_entry) { struct sas_task *task = TO_SAS_TASK(cmd); - - SAS_DPRINTK("trying to find task 0x%p\n", task); list_del_init(&cmd->eh_entry); + + if (!task) { + SAS_DPRINTK("%s: taskless cmd?!\n", __FUNCTION__); + continue; + } + SAS_DPRINTK("trying to find task 0x%p\n", task); res = sas_scsi_find_task(task); cmd->eh_eflags = 0; - shost->host_failed--; switch (res) { case TASK_IS_DONE: @@ -491,6 +505,7 @@ Again: } } out: + scsi_eh_flush_done_q(&ha->eh_done_q); SAS_DPRINTK("--- Exit %s\n", __FUNCTION__); return; clear_q: @@ -508,12 +523,18 @@ enum scsi_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) unsigned long flags; if (!task) { - SAS_DPRINTK("command 0x%p, task 0x%p, timed out: EH_HANDLED\n", + SAS_DPRINTK("command 0x%p, task 0x%p, gone: EH_HANDLED\n", cmd, task); return EH_HANDLED; } spin_lock_irqsave(&task->task_state_lock, flags); + if (task->task_state_flags & SAS_TASK_INITIATOR_ABORTED) { + spin_unlock_irqrestore(&task->task_state_lock, flags); + SAS_DPRINTK("command 0x%p, task 0x%p, aborted by initiator: " + "EH_NOT_HANDLED\n", cmd, task); + return EH_NOT_HANDLED; + } if (task->task_state_flags & SAS_TASK_STATE_DONE) { spin_unlock_irqrestore(&task->task_state_lock, flags); SAS_DPRINTK("command 0x%p, task 0x%p, timed out: EH_HANDLED\n", diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 9582e8401669..7bf2e8b9903c 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -338,6 +338,8 @@ struct sas_ha_struct { void (*notify_phy_event)(struct asd_sas_phy *, enum phy_event); void *lldd_ha; /* not touched by sas class code */ + + struct list_head eh_done_q; }; #define SHOST_TO_SAS_HA(_shost) (*(struct sas_ha_struct **)(_shost)->hostdata) @@ -530,9 +532,10 @@ struct sas_task { -#define SAS_TASK_STATE_PENDING 1 -#define SAS_TASK_STATE_DONE 2 -#define SAS_TASK_STATE_ABORTED 4 +#define SAS_TASK_STATE_PENDING 1 +#define SAS_TASK_STATE_DONE 2 +#define SAS_TASK_STATE_ABORTED 4 +#define SAS_TASK_INITIATOR_ABORTED 8 static inline struct sas_task *sas_alloc_task(gfp_t flags) { From 79a5eb609b74e7b3638861c41b98eafa74920a1f Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 30 Oct 2006 15:18:50 -0800 Subject: [PATCH 04/73] [SCSI] libsas: add sas_abort_task This patch adds an external function, sas_abort_task, to enable LLDDs to abort sas_tasks. It also adds a work_struct so that the actual work of aborting a task can be shifted from tasklet context (in the LLDD) onto the scsi_host's workqueue. Signed-off-by: Darrick J. Wong Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_scsi_host.c | 60 +++++++++++++++++++++++++++++ include/scsi/libsas.h | 4 ++ 2 files changed, 64 insertions(+) diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 6a97b07849b4..c5fd37522728 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -33,6 +33,7 @@ #include #include #include "../scsi_sas_internal.h" +#include "../scsi_transport_api.h" #include #include @@ -798,6 +799,64 @@ void sas_shutdown_queue(struct sas_ha_struct *sas_ha) spin_unlock_irqrestore(&core->task_queue_lock, flags); } +static int do_sas_task_abort(struct sas_task *task) +{ + struct scsi_cmnd *sc = task->uldd_task; + struct sas_internal *si = + to_sas_internal(task->dev->port->ha->core.shost->transportt); + unsigned long flags; + int res; + + spin_lock_irqsave(&task->task_state_lock, flags); + if (task->task_state_flags & SAS_TASK_STATE_ABORTED) { + spin_unlock_irqrestore(&task->task_state_lock, flags); + SAS_DPRINTK("%s: Task %p already aborted.\n", __FUNCTION__, + task); + return 0; + } + + task->task_state_flags |= SAS_TASK_INITIATOR_ABORTED; + if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) + task->task_state_flags |= SAS_TASK_STATE_ABORTED; + spin_unlock_irqrestore(&task->task_state_lock, flags); + + if (!si->dft->lldd_abort_task) + return -ENODEV; + + res = si->dft->lldd_abort_task(task); + if ((task->task_state_flags & SAS_TASK_STATE_DONE) || + (res == TMF_RESP_FUNC_COMPLETE)) + { + /* SMP commands don't have scsi_cmds(?) */ + if (!sc) { + task->task_done(task); + return 0; + } + scsi_req_abort_cmd(sc); + scsi_schedule_eh(sc->device->host); + return 0; + } + + spin_lock_irqsave(&task->task_state_lock, flags); + task->task_state_flags &= ~SAS_TASK_INITIATOR_ABORTED; + if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) + task->task_state_flags &= ~SAS_TASK_STATE_ABORTED; + spin_unlock_irqrestore(&task->task_state_lock, flags); + + return -EAGAIN; +} + +void sas_task_abort(struct sas_task *task) +{ + int i; + + for (i = 0; i < 5; i++) + if (!do_sas_task_abort(task)) + return; + + SAS_DPRINTK("%s: Could not kill task!\n", __FUNCTION__); +} + EXPORT_SYMBOL_GPL(sas_queuecommand); EXPORT_SYMBOL_GPL(sas_target_alloc); EXPORT_SYMBOL_GPL(sas_slave_configure); @@ -805,3 +864,4 @@ EXPORT_SYMBOL_GPL(sas_slave_destroy); EXPORT_SYMBOL_GPL(sas_change_queue_depth); EXPORT_SYMBOL_GPL(sas_change_queue_type); EXPORT_SYMBOL_GPL(sas_bios_param); +EXPORT_SYMBOL_GPL(sas_task_abort); diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 7bf2e8b9903c..a1fc20a47c50 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -528,6 +528,8 @@ struct sas_task { void *lldd_task; /* for use by LLDDs */ void *uldd_task; + + struct work_struct abort_work; }; @@ -627,4 +629,6 @@ void sas_unregister_dev(struct domain_device *); void sas_init_dev(struct domain_device *); +void sas_task_abort(struct sas_task *task); + #endif /* _SASLIB_H_ */ From fe4a36cf652031d2744a536ba5121032840380cb Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 30 Oct 2006 15:18:56 -0800 Subject: [PATCH 05/73] [SCSI] aic94xx: handle REQ_TASK_ABORT This patch straightens out the code that distinguishes the various escb opcodes in escb_tasklet_complete so that they can be handled correctly. It also provides all the necessary code to create a workqueue item that tells libsas to abort a sas_task. Signed-off-by: Darrick J. Wong Signed-off-by: James Bottomley --- drivers/scsi/aic94xx/aic94xx_scb.c | 83 ++++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 17 deletions(-) diff --git a/drivers/scsi/aic94xx/aic94xx_scb.c b/drivers/scsi/aic94xx/aic94xx_scb.c index 7ee49b51b724..1911c5d17875 100644 --- a/drivers/scsi/aic94xx/aic94xx_scb.c +++ b/drivers/scsi/aic94xx/aic94xx_scb.c @@ -25,6 +25,7 @@ */ #include +#include #include "aic94xx.h" #include "aic94xx_reg.h" @@ -342,6 +343,18 @@ void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id) } } +/* start up the ABORT TASK tmf... */ +static void task_kill_later(struct asd_ascb *ascb) +{ + struct asd_ha_struct *asd_ha = ascb->ha; + struct sas_ha_struct *sas_ha = &asd_ha->sas_ha; + struct Scsi_Host *shost = sas_ha->core.shost; + struct sas_task *task = ascb->uldd_task; + + INIT_WORK(&task->abort_work, (void (*)(void *))sas_task_abort, task); + queue_work(shost->work_q, &task->abort_work); +} + static void escb_tasklet_complete(struct asd_ascb *ascb, struct done_list_struct *dl) { @@ -368,6 +381,58 @@ static void escb_tasklet_complete(struct asd_ascb *ascb, ascb->scb->header.opcode); } + /* Catch these before we mask off the sb_opcode bits */ + switch (sb_opcode) { + case REQ_TASK_ABORT: { + struct asd_ascb *a, *b; + u16 tc_abort; + + tc_abort = *((u16*)(&dl->status_block[1])); + tc_abort = le16_to_cpu(tc_abort); + + ASD_DPRINTK("%s: REQ_TASK_ABORT, reason=0x%X\n", + __FUNCTION__, dl->status_block[3]); + + /* Find the pending task and abort it. */ + list_for_each_entry_safe(a, b, &asd_ha->seq.pend_q, list) + if (a->tc_index == tc_abort) { + task_kill_later(a); + break; + } + goto out; + } + case REQ_DEVICE_RESET: { + struct asd_ascb *a, *b; + u16 conn_handle; + + conn_handle = *((u16*)(&dl->status_block[1])); + conn_handle = le16_to_cpu(conn_handle); + + ASD_DPRINTK("%s: REQ_DEVICE_RESET, reason=0x%X\n", __FUNCTION__, + dl->status_block[3]); + + /* Kill all pending tasks and reset the device */ + list_for_each_entry_safe(a, b, &asd_ha->seq.pend_q, list) { + struct sas_task *task = a->uldd_task; + struct domain_device *dev = task->dev; + u16 x; + + x = *((u16*)(&dev->lldd_dev)); + if (x == conn_handle) + task_kill_later(a); + } + + /* FIXME: Reset device port (huh?) */ + goto out; + } + case SIGNAL_NCQ_ERROR: + ASD_DPRINTK("%s: SIGNAL_NCQ_ERROR\n", __FUNCTION__); + goto out; + case CLEAR_NCQ_ERROR: + ASD_DPRINTK("%s: CLEAR_NCQ_ERROR\n", __FUNCTION__); + goto out; + } + sb_opcode &= ~DL_PHY_MASK; switch (sb_opcode) { @@ -397,22 +462,6 @@ static void escb_tasklet_complete(struct asd_ascb *ascb, sas_phy_disconnected(sas_phy); sas_ha->notify_port_event(sas_phy, PORTE_TIMER_EVENT); break; - case REQ_TASK_ABORT: - ASD_DPRINTK("%s: phy%d: REQ_TASK_ABORT\n", __FUNCTION__, - phy_id); - break; - case REQ_DEVICE_RESET: - ASD_DPRINTK("%s: phy%d: REQ_DEVICE_RESET\n", __FUNCTION__, - phy_id); - break; - case SIGNAL_NCQ_ERROR: - ASD_DPRINTK("%s: phy%d: SIGNAL_NCQ_ERROR\n", __FUNCTION__, - phy_id); - break; - case CLEAR_NCQ_ERROR: - ASD_DPRINTK("%s: phy%d: CLEAR_NCQ_ERROR\n", __FUNCTION__, - phy_id); - break; default: ASD_DPRINTK("%s: phy%d: unknown event:0x%x\n", __FUNCTION__, phy_id, sb_opcode); @@ -432,7 +481,7 @@ static void escb_tasklet_complete(struct asd_ascb *ascb, break; } - +out: asd_invalidate_edb(ascb, edb); } From 2dc611de5a3fd955cd0298c50691d4c05046db97 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 4 Nov 2006 20:04:21 +0100 Subject: [PATCH 06/73] [SCSI] use one-element sg list in scsi_send_eh_cmnd scsi_send_eh_cmnd is the last user of non-sg commands currently. This patch switches it to a one-element SG list. Also updates the kerneldoc comment for scsi_send_eh_cmnd to reflect reality while we're at it. Test on my mptsas card, but this should get testing with as many drivers as possible. Signed-off-by: Christoph Hellwig Signed-off-by: James Bottomley --- drivers/scsi/scsi_error.c | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index aff1b0cfd4b2..2ecb6ff42444 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -453,9 +453,18 @@ static void scsi_eh_done(struct scsi_cmnd *scmd) } /** - * scsi_send_eh_cmnd - send a cmd to a device as part of error recovery. - * @scmd: SCSI Cmd to send. - * @timeout: Timeout for cmd. + * scsi_send_eh_cmnd - submit a scsi command as part of error recory + * @scmd: SCSI command structure to hijack + * @cmnd: CDB to send + * @cmnd_size: size in bytes of @cmnd + * @timeout: timeout for this request + * @copy_sense: request sense data if set to 1 + * + * This function is used to send a scsi command down to a target device + * as part of the error recovery process. If @copy_sense is 0 the command + * sent must be one that does not transfer any data. If @copy_sense is 1 + * the command must be REQUEST_SENSE and this functions copies out the + * sense buffer it got into @scmd->sense_buffer. * * Return value: * SUCCESS or FAILED or NEEDS_RETRY @@ -469,6 +478,7 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, DECLARE_COMPLETION_ONSTACK(done); unsigned long timeleft; unsigned long flags; + struct scatterlist sgl; unsigned char old_cmnd[MAX_COMMAND_SIZE]; enum dma_data_direction old_data_direction; unsigned short old_use_sg; @@ -500,19 +510,24 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, if (shost->hostt->unchecked_isa_dma) gfp_mask |= __GFP_DMA; - scmd->sc_data_direction = DMA_FROM_DEVICE; - scmd->request_bufflen = 252; - scmd->request_buffer = kzalloc(scmd->request_bufflen, gfp_mask); - if (!scmd->request_buffer) + sgl.page = alloc_page(gfp_mask); + if (!sgl.page) return FAILED; + sgl.offset = 0; + sgl.length = 252; + + scmd->sc_data_direction = DMA_FROM_DEVICE; + scmd->request_bufflen = sgl.length; + scmd->request_buffer = &sgl; + scmd->use_sg = 1; } else { scmd->request_buffer = NULL; scmd->request_bufflen = 0; scmd->sc_data_direction = DMA_NONE; + scmd->use_sg = 0; } scmd->underflow = 0; - scmd->use_sg = 0; scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]); if (sdev->scsi_level <= SCSI_2) @@ -583,7 +598,7 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, memcpy(scmd->sense_buffer, scmd->request_buffer, sizeof(scmd->sense_buffer)); } - kfree(scmd->request_buffer); + __free_page(sgl.page); } From 3b00315799d78f76531b71435fbc2643cd71ae4c Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 4 Nov 2006 20:10:55 +0100 Subject: [PATCH 07/73] [SCSI] untangle scsi_prep_fn I wanted to add some BUG checks to scsi_prep_fn to make sure no one sends us a non-sg command, but this function is a horrible mess. So I decided to detangle the function and document what the valid cases are. While doing that I found that REQ_TYPE_SPECIAL commands aren't used by the SCSI layer anymore and we can get rid of the code handling them. The new structure of scsi_prep_fn is: (1) check if we're allowed to send this command (2) big switch on cmd_type. For the two valid types call into a function to set the command up, else error (3) code to handle error cases Because FS and BLOCK_PC commands are handled entirely separate after the patch this introduces a tiny amount of code duplication. This improves readabiulity though and will help to avoid the bidi command overhead for FS commands so it's a good thing. I've tested this on both sata and mptsas. Signed-off-by: Christoph Hellwig Signed-off-by: James Bottomley --- drivers/scsi/scsi_lib.c | 319 +++++++++++++++++++++------------------- 1 file changed, 169 insertions(+), 150 deletions(-) diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 743f67ed7640..ee35a62bb7a2 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -995,25 +995,14 @@ static int scsi_init_io(struct scsi_cmnd *cmd) int count; /* - * if this is a rq->data based REQ_BLOCK_PC, setup for a non-sg xfer - */ - if (blk_pc_request(req) && !req->bio) { - cmd->request_bufflen = req->data_len; - cmd->request_buffer = req->data; - req->buffer = req->data; - cmd->use_sg = 0; - return 0; - } - - /* - * we used to not use scatter-gather for single segment request, + * We used to not use scatter-gather for single segment request, * but now we do (it makes highmem I/O easier to support without * kmapping pages) */ cmd->use_sg = req->nr_phys_segments; /* - * if sg table allocation fails, requeue request later. + * If sg table allocation fails, requeue request later. */ sgpnt = scsi_alloc_sgtable(cmd, GFP_ATOMIC); if (unlikely(!sgpnt)) { @@ -1021,24 +1010,21 @@ static int scsi_init_io(struct scsi_cmnd *cmd) return BLKPREP_DEFER; } + req->buffer = NULL; cmd->request_buffer = (char *) sgpnt; - cmd->request_bufflen = req->nr_sectors << 9; if (blk_pc_request(req)) cmd->request_bufflen = req->data_len; - req->buffer = NULL; + else + cmd->request_bufflen = req->nr_sectors << 9; /* * Next, walk the list, and fill in the addresses and sizes of * each segment. */ count = blk_rq_map_sg(req->q, req, cmd->request_buffer); - - /* - * mapped well, send it off - */ if (likely(count <= cmd->use_sg)) { cmd->use_sg = count; - return 0; + return BLKPREP_OK; } printk(KERN_ERR "Incorrect number of segments after building list\n"); @@ -1068,6 +1054,27 @@ static int scsi_issue_flush_fn(request_queue_t *q, struct gendisk *disk, return -EOPNOTSUPP; } +static struct scsi_cmnd *scsi_get_cmd_from_req(struct scsi_device *sdev, + struct request *req) +{ + struct scsi_cmnd *cmd; + + if (!req->special) { + cmd = scsi_get_command(sdev, GFP_ATOMIC); + if (unlikely(!cmd)) + return NULL; + req->special = cmd; + } else { + cmd = req->special; + } + + /* pull a tag out of the request if we have one */ + cmd->tag = req->tag; + cmd->request = req; + + return cmd; +} + static void scsi_blk_pc_done(struct scsi_cmnd *cmd) { BUG_ON(!blk_pc_request(cmd->request)); @@ -1080,9 +1087,37 @@ static void scsi_blk_pc_done(struct scsi_cmnd *cmd) scsi_io_completion(cmd, cmd->request_bufflen); } -static void scsi_setup_blk_pc_cmnd(struct scsi_cmnd *cmd) +static int scsi_setup_blk_pc_cmnd(struct scsi_device *sdev, struct request *req) { - struct request *req = cmd->request; + struct scsi_cmnd *cmd; + + cmd = scsi_get_cmd_from_req(sdev, req); + if (unlikely(!cmd)) + return BLKPREP_DEFER; + + /* + * BLOCK_PC requests may transfer data, in which case they must + * a bio attached to them. Or they might contain a SCSI command + * that does not transfer data, in which case they may optionally + * submit a request without an attached bio. + */ + if (req->bio) { + int ret; + + BUG_ON(!req->nr_phys_segments); + + ret = scsi_init_io(cmd); + if (unlikely(ret)) + return ret; + } else { + BUG_ON(req->data_len); + BUG_ON(req->data); + + cmd->request_bufflen = 0; + cmd->request_buffer = NULL; + cmd->use_sg = 0; + req->buffer = NULL; + } BUG_ON(sizeof(req->cmd) > sizeof(cmd->cmnd)); memcpy(cmd->cmnd, req->cmd, sizeof(cmd->cmnd)); @@ -1098,154 +1133,138 @@ static void scsi_setup_blk_pc_cmnd(struct scsi_cmnd *cmd) cmd->allowed = req->retries; cmd->timeout_per_command = req->timeout; cmd->done = scsi_blk_pc_done; + return BLKPREP_OK; +} + +/* + * Setup a REQ_TYPE_FS command. These are simple read/write request + * from filesystems that still need to be translated to SCSI CDBs from + * the ULD. + */ +static int scsi_setup_fs_cmnd(struct scsi_device *sdev, struct request *req) +{ + struct scsi_cmnd *cmd; + struct scsi_driver *drv; + int ret; + + /* + * Filesystem requests must transfer data. + */ + BUG_ON(!req->nr_phys_segments); + + cmd = scsi_get_cmd_from_req(sdev, req); + if (unlikely(!cmd)) + return BLKPREP_DEFER; + + ret = scsi_init_io(cmd); + if (unlikely(ret)) + return ret; + + /* + * Initialize the actual SCSI command for this request. + */ + drv = *(struct scsi_driver **)req->rq_disk->private_data; + if (unlikely(!drv->init_command(cmd))) { + scsi_release_buffers(cmd); + scsi_put_command(cmd); + return BLKPREP_KILL; + } + + return BLKPREP_OK; } static int scsi_prep_fn(struct request_queue *q, struct request *req) { struct scsi_device *sdev = q->queuedata; - struct scsi_cmnd *cmd; - int specials_only = 0; + int ret = BLKPREP_OK; /* - * Just check to see if the device is online. If it isn't, we - * refuse to process any commands. The device must be brought - * online before trying any recovery commands + * If the device is not in running state we will reject some + * or all commands. */ - if (unlikely(!scsi_device_online(sdev))) { - sdev_printk(KERN_ERR, sdev, - "rejecting I/O to offline device\n"); - goto kill; - } if (unlikely(sdev->sdev_state != SDEV_RUNNING)) { - /* OK, we're not in a running state don't prep - * user commands */ - if (sdev->sdev_state == SDEV_DEL) { - /* Device is fully deleted, no commands - * at all allowed down */ + switch (sdev->sdev_state) { + case SDEV_OFFLINE: + /* + * If the device is offline we refuse to process any + * commands. The device must be brought online + * before trying any recovery commands. + */ + sdev_printk(KERN_ERR, sdev, + "rejecting I/O to offline device\n"); + ret = BLKPREP_KILL; + break; + case SDEV_DEL: + /* + * If the device is fully deleted, we refuse to + * process any commands as well. + */ sdev_printk(KERN_ERR, sdev, "rejecting I/O to dead device\n"); - goto kill; + ret = BLKPREP_KILL; + break; + case SDEV_QUIESCE: + case SDEV_BLOCK: + /* + * If the devices is blocked we defer normal commands. + */ + if (!(req->cmd_flags & REQ_PREEMPT)) + ret = BLKPREP_DEFER; + break; + default: + /* + * For any other not fully online state we only allow + * special commands. In particular any user initiated + * command is not allowed. + */ + if (!(req->cmd_flags & REQ_PREEMPT)) + ret = BLKPREP_KILL; + break; } - /* OK, we only allow special commands (i.e. not - * user initiated ones */ - specials_only = sdev->sdev_state; + + if (ret != BLKPREP_OK) + goto out; } - /* - * Find the actual device driver associated with this command. - * The SPECIAL requests are things like character device or - * ioctls, which did not originate from ll_rw_blk. Note that - * the special field is also used to indicate the cmd for - * the remainder of a partially fulfilled request that can - * come up when there is a medium error. We have to treat - * these two cases differently. We differentiate by looking - * at request->cmd, as this tells us the real story. - */ - if (blk_special_request(req) && req->special) - cmd = req->special; - else if (blk_pc_request(req) || blk_fs_request(req)) { - if (unlikely(specials_only) && !(req->cmd_flags & REQ_PREEMPT)){ - if (specials_only == SDEV_QUIESCE || - specials_only == SDEV_BLOCK) - goto defer; - - sdev_printk(KERN_ERR, sdev, - "rejecting I/O to device being removed\n"); - goto kill; - } - + switch (req->cmd_type) { + case REQ_TYPE_BLOCK_PC: + ret = scsi_setup_blk_pc_cmnd(sdev, req); + break; + case REQ_TYPE_FS: + ret = scsi_setup_fs_cmnd(sdev, req); + break; + default: /* - * Now try and find a command block that we can use. - */ - if (!req->special) { - cmd = scsi_get_command(sdev, GFP_ATOMIC); - if (unlikely(!cmd)) - goto defer; - } else - cmd = req->special; - - /* pull a tag out of the request if we have one */ - cmd->tag = req->tag; - } else { - blk_dump_rq_flags(req, "SCSI bad req"); - goto kill; - } - - /* note the overloading of req->special. When the tag - * is active it always means cmd. If the tag goes - * back for re-queueing, it may be reset */ - req->special = cmd; - cmd->request = req; - - /* - * FIXME: drop the lock here because the functions below - * expect to be called without the queue lock held. Also, - * previously, we dequeued the request before dropping the - * lock. We hope REQ_STARTED prevents anything untoward from - * happening now. - */ - if (blk_fs_request(req) || blk_pc_request(req)) { - int ret; - - /* - * This will do a couple of things: - * 1) Fill in the actual SCSI command. - * 2) Fill in any other upper-level specific fields - * (timeout). + * All other command types are not supported. * - * If this returns 0, it means that the request failed - * (reading past end of disk, reading offline device, - * etc). This won't actually talk to the device, but - * some kinds of consistency checking may cause the - * request to be rejected immediately. + * Note that these days the SCSI subsystem does not use + * REQ_TYPE_SPECIAL requests anymore. These are only used + * (directly or via blk_insert_request) by non-SCSI drivers. */ - - /* - * This sets up the scatter-gather table (allocating if - * required). - */ - ret = scsi_init_io(cmd); - switch(ret) { - /* For BLKPREP_KILL/DEFER the cmd was released */ - case BLKPREP_KILL: - goto kill; - case BLKPREP_DEFER: - goto defer; - } - - /* - * Initialize the actual SCSI command for this request. - */ - if (blk_pc_request(req)) { - scsi_setup_blk_pc_cmnd(cmd); - } else if (req->rq_disk) { - struct scsi_driver *drv; - - drv = *(struct scsi_driver **)req->rq_disk->private_data; - if (unlikely(!drv->init_command(cmd))) { - scsi_release_buffers(cmd); - scsi_put_command(cmd); - goto kill; - } - } + blk_dump_rq_flags(req, "SCSI bad req"); + ret = BLKPREP_KILL; + break; } - /* - * The request is now prepped, no need to come back here - */ - req->cmd_flags |= REQ_DONTPREP; - return BLKPREP_OK; + out: + switch (ret) { + case BLKPREP_KILL: + req->errors = DID_NO_CONNECT << 16; + break; + case BLKPREP_DEFER: + /* + * If we defer, the elv_next_request() returns NULL, but the + * queue must be restarted, so we plug here if no returning + * command will automatically do that. + */ + if (sdev->device_busy == 0) + blk_plug_device(q); + break; + default: + req->cmd_flags |= REQ_DONTPREP; + } - defer: - /* If we defer, the elv_next_request() returns NULL, but the - * queue must be restarted, so we plug here if no returning - * command will automatically do that. */ - if (sdev->device_busy == 0) - blk_plug_device(q); - return BLKPREP_DEFER; - kill: - req->errors = DID_NO_CONNECT << 16; - return BLKPREP_KILL; + return ret; } /* From 4f777ed26086452737ea52597cf8de26137090d5 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 4 Nov 2006 20:11:36 +0100 Subject: [PATCH 08/73] [SCSI] kill scsi_assign_lock scsi_assign_lock has been unused for a long time and is a bad idea in general, so kill it. Signed-off-by: Christoph Hellwig Signed-off-by: James Bottomley --- Documentation/scsi/scsi_mid_low_api.txt | 29 ++++--------------------- drivers/scsi/hosts.c | 4 ++-- include/scsi/scsi_host.h | 5 ----- 3 files changed, 6 insertions(+), 32 deletions(-) diff --git a/Documentation/scsi/scsi_mid_low_api.txt b/Documentation/scsi/scsi_mid_low_api.txt index 75a535a975c3..6f70f2b9327e 100644 --- a/Documentation/scsi/scsi_mid_low_api.txt +++ b/Documentation/scsi/scsi_mid_low_api.txt @@ -375,7 +375,6 @@ Summary: scsi_add_device - creates new scsi device (lu) instance scsi_add_host - perform sysfs registration and set up transport class scsi_adjust_queue_depth - change the queue depth on a SCSI device - scsi_assign_lock - replace default host_lock with given lock scsi_bios_ptable - return copy of block device's partition table scsi_block_requests - prevent further commands being queued to given host scsi_deactivate_tcq - turn off tag command queueing @@ -488,20 +487,6 @@ void scsi_adjust_queue_depth(struct scsi_device * sdev, int tagged, int tags) -/** - * scsi_assign_lock - replace default host_lock with given lock - * @shost: a pointer to a scsi host instance - * @lock: pointer to lock to replace host_lock for this host - * - * Returns nothing - * - * Might block: no - * - * Defined in: include/scsi/scsi_host.h . - **/ -void scsi_assign_lock(struct Scsi_Host *shost, spinlock_t *lock) - - /** * scsi_bios_ptable - return copy of block device's partition table * @dev: pointer to block device @@ -1366,17 +1351,11 @@ Locks Each struct Scsi_Host instance has a spin_lock called struct Scsi_Host::default_lock which is initialized in scsi_host_alloc() [found in hosts.c]. Within the same function the struct Scsi_Host::host_lock pointer -is initialized to point at default_lock with the scsi_assign_lock() function. -Thereafter lock and unlock operations performed by the mid level use the -struct Scsi_Host::host_lock pointer. +is initialized to point at default_lock. Thereafter lock and unlock +operations performed by the mid level use the struct Scsi_Host::host_lock +pointer. Previously drivers could override the host_lock pointer but +this is not allowed anymore. -LLDs can override the use of struct Scsi_Host::default_lock by -using scsi_assign_lock(). The earliest opportunity to do this would -be in the detect() function after it has invoked scsi_register(). It -could be replaced by a coarser grain lock (e.g. per driver) or a -lock of equal granularity (i.e. per host). Using finer grain locks -(e.g. per SCSI device) may be possible by juggling locks in -queuecommand(). Autosense ========= diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 68ef1636678d..2ffdc9e0532d 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -301,8 +301,8 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) if (!shost) return NULL; - spin_lock_init(&shost->default_lock); - scsi_assign_lock(shost, &shost->default_lock); + shost->host_lock = &shost->default_lock; + spin_lock_init(shost->host_lock); shost->shost_state = SHOST_CREATED; INIT_LIST_HEAD(&shost->__devices); INIT_LIST_HEAD(&shost->__targets); diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index ba5b3eb6b43f..e618e711ea57 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -651,11 +651,6 @@ extern const char *scsi_host_state_name(enum scsi_host_state); extern u64 scsi_calculate_bounce_limit(struct Scsi_Host *); -static inline void scsi_assign_lock(struct Scsi_Host *shost, spinlock_t *lock) -{ - shost->host_lock = lock; -} - static inline struct device *scsi_get_device(struct Scsi_Host *shost) { return shost->shost_gendev.parent; From fd44bab5c709bbcd30fbebe9ad45a76c58cd32a7 Mon Sep 17 00:00:00 2001 From: Luben Tuikov Date: Wed, 15 Nov 2006 12:47:08 -0800 Subject: [PATCH 09/73] [SCSI] sd: clearer output of disk cache state Signed-off-by: Luben Tuikov Signed-off-by: James Bottomley --- drivers/scsi/sd.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 152f4ff4ed7e..f6a452846fab 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1467,7 +1467,6 @@ sd_read_cache_type(struct scsi_disk *sdkp, char *diskname, res = sd_do_mode_sense(sdp, dbd, modepage, buffer, len, &data, &sshdr); if (scsi_status_is_good(res)) { - int ct = 0; int offset = data.header_length + data.block_descriptor_length; if (offset >= SD_BUF_SIZE - 2) { @@ -1496,11 +1495,13 @@ sd_read_cache_type(struct scsi_disk *sdkp, char *diskname, sdkp->DPOFUA = 0; } - ct = sdkp->RCD + 2*sdkp->WCE; - - printk(KERN_NOTICE "SCSI device %s: drive cache: %s%s\n", - diskname, sd_cache_types[ct], - sdkp->DPOFUA ? " w/ FUA" : ""); + printk(KERN_NOTICE "SCSI device %s: " + "write cache: %s, read cache: %s, %s\n", + diskname, + sdkp->WCE ? "enabled" : "disabled", + sdkp->RCD ? "disabled" : "enabled", + sdkp->DPOFUA ? "supports DPO and FUA" + : "doesn't support DPO or FUA"); return; } From 42252854011b31fe63ffeca44ac941e6fe8277ae Mon Sep 17 00:00:00 2001 From: Kai Makisara Date: Tue, 7 Nov 2006 21:56:38 +0200 Subject: [PATCH 10/73] [SCSI] st: log message changes Printk -> sdev_printk change originally from Luben Tuikov . Loglevel changes prompted by Matthew Wilcox . Signed-off-by: Kai Makisara Signed-off-by: James Bottomley --- drivers/scsi/st.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 3babdc76b3fb..febfac97ad38 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -9,7 +9,7 @@ Steve Hirsch, Andreas Koppenh"ofer, Michael Leodolter, Eyal Lebedinsky, Michael Schaefer, J"org Weule, and Eric Youngdale. - Copyright 1992 - 2005 Kai Makisara + Copyright 1992 - 2006 Kai Makisara email Kai.Makisara@kolumbus.fi Some small formal changes - aeb, 950809 @@ -17,7 +17,7 @@ Last modified: 18-JAN-1998 Richard Gooch Devfs support */ -static const char *verstr = "20050830"; +static const char *verstr = "20061107"; #include @@ -999,7 +999,7 @@ static int check_tape(struct scsi_tape *STp, struct file *filp) STp->min_block = ((STp->buffer)->b_data[4] << 8) | (STp->buffer)->b_data[5]; if ( DEB( debugging || ) !STp->inited) - printk(KERN_WARNING + printk(KERN_INFO "%s: Block limits %d - %d bytes.\n", name, STp->min_block, STp->max_block); } else { @@ -1221,7 +1221,7 @@ static int st_flush(struct file *filp, fl_owner_t id) } DEBC( if (STp->nbr_requests) - printk(KERN_WARNING "%s: Number of r/w requests %d, dio used in %d, pages %d (%d).\n", + printk(KERN_DEBUG "%s: Number of r/w requests %d, dio used in %d, pages %d (%d).\n", name, STp->nbr_requests, STp->nbr_dio, STp->nbr_pages, STp->nbr_combinable)); if (STps->rw == ST_WRITING && !STp->pos_unknown) { @@ -4053,11 +4053,11 @@ static int st_probe(struct device *dev) goto out_free_tape; } - sdev_printk(KERN_WARNING, SDp, + sdev_printk(KERN_NOTICE, SDp, "Attached scsi tape %s\n", tape_name(tpnt)); - printk(KERN_WARNING "%s: try direct i/o: %s (alignment %d B)\n", - tape_name(tpnt), tpnt->try_dio ? "yes" : "no", - queue_dma_alignment(SDp->request_queue) + 1); + sdev_printk(KERN_INFO, SDp, "%s: try direct i/o: %s (alignment %d B)\n", + tape_name(tpnt), tpnt->try_dio ? "yes" : "no", + queue_dma_alignment(SDp->request_queue) + 1); return 0; From 08157cd0787004e2ebf9ee8cc92257244da53848 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Wed, 8 Nov 2006 19:56:19 -0800 Subject: [PATCH 11/73] [SCSI] SCSI/aha1740: handle SCSI API errors Signed-off-by: Jeff Garzik Signed-off-by: Andrew Morton Signed-off-by: James Bottomley --- drivers/scsi/aha1740.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/aha1740.c b/drivers/scsi/aha1740.c index c3c38a7e8d32..d7af9c63a04d 100644 --- a/drivers/scsi/aha1740.c +++ b/drivers/scsi/aha1740.c @@ -586,7 +586,7 @@ static struct scsi_host_template aha1740_template = { static int aha1740_probe (struct device *dev) { - int slotbase; + int slotbase, rc; unsigned int irq_level, irq_type, translation; struct Scsi_Host *shpnt; struct aha1740_hostdata *host; @@ -641,10 +641,16 @@ static int aha1740_probe (struct device *dev) } eisa_set_drvdata (edev, shpnt); - scsi_add_host (shpnt, dev); /* XXX handle failure */ + + rc = scsi_add_host (shpnt, dev); + if (rc) + goto err_irq; + scsi_scan_host (shpnt); return 0; + err_irq: + free_irq(irq_level, shpnt); err_unmap: dma_unmap_single (&edev->dev, host->ecb_dma_addr, sizeof (host->ecb), DMA_BIDIRECTIONAL); From bf4713418b9d8543e7b64bf6c742f1959828033e Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 8 Nov 2006 19:56:24 -0800 Subject: [PATCH 12/73] [SCSI] revert "[SCSI] ips soft lockup during reset/initialization" Revert 15084a4a63bc300c18b28a8a9afac870c552abce - it caused a scheduling-inside-spinlock bug. Cc: Nishanth Aravamudan Cc: Nick Piggin Cc: Jack Hammer Signed-off-by: Andrew Morton Signed-off-by: James Bottomley --- drivers/scsi/ips.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c index f06a06ae6092..8b704f73055a 100644 --- a/drivers/scsi/ips.c +++ b/drivers/scsi/ips.c @@ -5001,7 +5001,7 @@ ips_init_copperhead(ips_ha_t * ha) break; /* Delay for 1 Second */ - msleep(IPS_ONE_SEC); + MDELAY(IPS_ONE_SEC); } if (j >= 45) @@ -5027,7 +5027,7 @@ ips_init_copperhead(ips_ha_t * ha) break; /* Delay for 1 Second */ - msleep(IPS_ONE_SEC); + MDELAY(IPS_ONE_SEC); } if (j >= 240) @@ -5045,7 +5045,7 @@ ips_init_copperhead(ips_ha_t * ha) break; /* Delay for 1 Second */ - msleep(IPS_ONE_SEC); + MDELAY(IPS_ONE_SEC); } if (i >= 240) @@ -5095,7 +5095,7 @@ ips_init_copperhead_memio(ips_ha_t * ha) break; /* Delay for 1 Second */ - msleep(IPS_ONE_SEC); + MDELAY(IPS_ONE_SEC); } if (j >= 45) @@ -5121,7 +5121,7 @@ ips_init_copperhead_memio(ips_ha_t * ha) break; /* Delay for 1 Second */ - msleep(IPS_ONE_SEC); + MDELAY(IPS_ONE_SEC); } if (j >= 240) @@ -5139,7 +5139,7 @@ ips_init_copperhead_memio(ips_ha_t * ha) break; /* Delay for 1 Second */ - msleep(IPS_ONE_SEC); + MDELAY(IPS_ONE_SEC); } if (i >= 240) @@ -5191,7 +5191,7 @@ ips_init_morpheus(ips_ha_t * ha) break; /* Delay for 1 Second */ - msleep(IPS_ONE_SEC); + MDELAY(IPS_ONE_SEC); } if (i >= 45) { @@ -5217,7 +5217,7 @@ ips_init_morpheus(ips_ha_t * ha) if (Post != 0x4F00) break; /* Delay for 1 Second */ - msleep(IPS_ONE_SEC); + MDELAY(IPS_ONE_SEC); } if (i >= 120) { @@ -5247,7 +5247,7 @@ ips_init_morpheus(ips_ha_t * ha) break; /* Delay for 1 Second */ - msleep(IPS_ONE_SEC); + MDELAY(IPS_ONE_SEC); } if (i >= 240) { @@ -5307,12 +5307,12 @@ ips_reset_copperhead(ips_ha_t * ha) outb(IPS_BIT_RST, ha->io_addr + IPS_REG_SCPR); /* Delay for 1 Second */ - msleep(IPS_ONE_SEC); + MDELAY(IPS_ONE_SEC); outb(0, ha->io_addr + IPS_REG_SCPR); /* Delay for 1 Second */ - msleep(IPS_ONE_SEC); + MDELAY(IPS_ONE_SEC); if ((*ha->func.init) (ha)) break; @@ -5352,12 +5352,12 @@ ips_reset_copperhead_memio(ips_ha_t * ha) writeb(IPS_BIT_RST, ha->mem_ptr + IPS_REG_SCPR); /* Delay for 1 Second */ - msleep(IPS_ONE_SEC); + MDELAY(IPS_ONE_SEC); writeb(0, ha->mem_ptr + IPS_REG_SCPR); /* Delay for 1 Second */ - msleep(IPS_ONE_SEC); + MDELAY(IPS_ONE_SEC); if ((*ha->func.init) (ha)) break; @@ -5398,7 +5398,7 @@ ips_reset_morpheus(ips_ha_t * ha) writel(0x80000000, ha->mem_ptr + IPS_REG_I960_IDR); /* Delay for 5 Seconds */ - msleep(5 * IPS_ONE_SEC); + MDELAY(5 * IPS_ONE_SEC); /* Do a PCI config read to wait for adapter */ pci_read_config_byte(ha->pcidev, 4, &junk); From db3a8815fb03f9985713b4ab29e208b7074f939c Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Wed, 8 Nov 2006 19:56:20 -0800 Subject: [PATCH 13/73] [SCSI] minor bug fixes and cleanups BusLogic: use kzalloc(), remove cast to/from void* aic7xxx_old: fix typo in cast NCR53c406a: ifdef out static built code fd_mcs: ifdef out static built code ncr53c8xx: ifdef out static built code Signed-off-by: Jeff Garzik Signed-off-by: Andrew Morton Signed-off-by: James Bottomley --- drivers/scsi/BusLogic.c | 12 ++++++------ drivers/scsi/NCR53c406a.c | 5 +++++ drivers/scsi/fd_mcs.c | 2 ++ drivers/scsi/ncr53c8xx.c | 19 +++++++++++-------- 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/drivers/scsi/BusLogic.c b/drivers/scsi/BusLogic.c index 7c59bba98798..689dc4cc789c 100644 --- a/drivers/scsi/BusLogic.c +++ b/drivers/scsi/BusLogic.c @@ -2186,21 +2186,21 @@ static int __init BusLogic_init(void) if (BusLogic_ProbeOptions.NoProbe) return -ENODEV; - BusLogic_ProbeInfoList = (struct BusLogic_ProbeInfo *) - kmalloc(BusLogic_MaxHostAdapters * sizeof(struct BusLogic_ProbeInfo), GFP_ATOMIC); + BusLogic_ProbeInfoList = + kzalloc(BusLogic_MaxHostAdapters * sizeof(struct BusLogic_ProbeInfo), GFP_KERNEL); if (BusLogic_ProbeInfoList == NULL) { BusLogic_Error("BusLogic: Unable to allocate Probe Info List\n", NULL); return -ENOMEM; } - memset(BusLogic_ProbeInfoList, 0, BusLogic_MaxHostAdapters * sizeof(struct BusLogic_ProbeInfo)); - PrototypeHostAdapter = (struct BusLogic_HostAdapter *) - kmalloc(sizeof(struct BusLogic_HostAdapter), GFP_ATOMIC); + + PrototypeHostAdapter = + kzalloc(sizeof(struct BusLogic_HostAdapter), GFP_KERNEL); if (PrototypeHostAdapter == NULL) { kfree(BusLogic_ProbeInfoList); BusLogic_Error("BusLogic: Unable to allocate Prototype " "Host Adapter\n", NULL); return -ENOMEM; } - memset(PrototypeHostAdapter, 0, sizeof(struct BusLogic_HostAdapter)); + #ifdef MODULE if (BusLogic != NULL) BusLogic_Setup(BusLogic); diff --git a/drivers/scsi/NCR53c406a.c b/drivers/scsi/NCR53c406a.c index d4613815f685..8578555d58fd 100644 --- a/drivers/scsi/NCR53c406a.c +++ b/drivers/scsi/NCR53c406a.c @@ -220,9 +220,11 @@ static void *addresses[] = { static unsigned short ports[] = { 0x230, 0x330, 0x280, 0x290, 0x330, 0x340, 0x300, 0x310, 0x348, 0x350 }; #define PORT_COUNT ARRAY_SIZE(ports) +#ifndef MODULE /* possible interrupt channels */ static unsigned short intrs[] = { 10, 11, 12, 15 }; #define INTR_COUNT ARRAY_SIZE(intrs) +#endif /* !MODULE */ /* signatures for NCR 53c406a based controllers */ #if USE_BIOS @@ -605,6 +607,7 @@ static int NCR53c406a_release(struct Scsi_Host *shost) return 0; } +#ifndef MODULE /* called from init/main.c */ static int __init NCR53c406a_setup(char *str) { @@ -661,6 +664,8 @@ static int __init NCR53c406a_setup(char *str) __setup("ncr53c406a=", NCR53c406a_setup); +#endif /* !MODULE */ + static const char *NCR53c406a_info(struct Scsi_Host *SChost) { DEB(printk("NCR53c406a_info called\n")); diff --git a/drivers/scsi/fd_mcs.c b/drivers/scsi/fd_mcs.c index ef8285c326e4..668569e8856b 100644 --- a/drivers/scsi/fd_mcs.c +++ b/drivers/scsi/fd_mcs.c @@ -294,6 +294,7 @@ static struct Scsi_Host *hosts[FD_MAX_HOSTS + 1] = { NULL }; static int user_fifo_count = 0; static int user_fifo_size = 0; +#ifndef MODULE static int __init fd_mcs_setup(char *str) { static int done_setup = 0; @@ -311,6 +312,7 @@ static int __init fd_mcs_setup(char *str) } __setup("fd_mcs=", fd_mcs_setup); +#endif /* !MODULE */ static void print_banner(struct Scsi_Host *shpnt) { diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c index 6cc2bc2f62be..5a88fa0b2fc3 100644 --- a/drivers/scsi/ncr53c8xx.c +++ b/drivers/scsi/ncr53c8xx.c @@ -589,10 +589,12 @@ static int __map_scsi_sg_data(struct device *dev, struct scsi_cmnd *cmd) static struct ncr_driver_setup driver_setup = SCSI_NCR_DRIVER_SETUP; +#ifndef MODULE #ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT static struct ncr_driver_setup driver_safe_setup __initdata = SCSI_NCR_DRIVER_SAFE_SETUP; #endif +#endif /* !MODULE */ #define initverbose (driver_setup.verbose) #define bootverbose (np->verbose) @@ -641,6 +643,13 @@ static struct ncr_driver_setup #define OPT_IARB 26 #endif +#ifdef MODULE +#define ARG_SEP ' ' +#else +#define ARG_SEP ',' +#endif + +#ifndef MODULE static char setup_token[] __initdata = "tags:" "mpar:" "spar:" "disc:" @@ -660,12 +669,6 @@ static char setup_token[] __initdata = #endif ; /* DONNOT REMOVE THIS ';' */ -#ifdef MODULE -#define ARG_SEP ' ' -#else -#define ARG_SEP ',' -#endif - static int __init get_setup_token(char *p) { char *cur = setup_token; @@ -682,7 +685,6 @@ static int __init get_setup_token(char *p) return 0; } - static int __init sym53c8xx__setup(char *str) { #ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT @@ -804,6 +806,7 @@ static int __init sym53c8xx__setup(char *str) #endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */ return 1; } +#endif /* !MODULE */ /*=================================================================== ** @@ -8321,12 +8324,12 @@ char *ncr53c8xx; /* command line passed by insmod */ module_param(ncr53c8xx, charp, 0); #endif +#ifndef MODULE static int __init ncr53c8xx_setup(char *str) { return sym53c8xx__setup(str); } -#ifndef MODULE __setup("ncr53c8xx=", ncr53c8xx_setup); #endif From e5f82ab8342b527231cfcccde1fe265666e86b7b Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Wed, 8 Nov 2006 19:55:50 -0800 Subject: [PATCH 14/73] [SCSI] qla2xxx: make some functions static This patch makes some needlessly global functions static. Signed-off-by: Adrian Bunk Acked-by: Andrew Vasquez Signed-off-by: Andrew Morton Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_os.c | 12 ++++++------ drivers/scsi/qla2xxx/qla_sup.c | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 3f20d765563e..5e70c49fdf84 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -274,7 +274,7 @@ qla24xx_pci_info_str(struct scsi_qla_host *ha, char *str) return str; } -char * +static char * qla2x00_fw_version_str(struct scsi_qla_host *ha, char *str) { char un_str[10]; @@ -312,7 +312,7 @@ qla2x00_fw_version_str(struct scsi_qla_host *ha, char *str) return (str); } -char * +static char * qla24xx_fw_version_str(struct scsi_qla_host *ha, char *str) { sprintf(str, "%d.%02d.%02d ", ha->fw_major_version, @@ -621,7 +621,7 @@ qla2x00_block_error_handler(struct scsi_cmnd *cmnd) * Note: * Only return FAILED if command not returned by firmware. **************************************************************************/ -int +static int qla2xxx_eh_abort(struct scsi_cmnd *cmd) { scsi_qla_host_t *ha = to_qla_host(cmd->device->host); @@ -758,7 +758,7 @@ qla2x00_eh_wait_for_pending_target_commands(scsi_qla_host_t *ha, unsigned int t) * SUCCESS/FAILURE (defined as macro in scsi.h). * **************************************************************************/ -int +static int qla2xxx_eh_device_reset(struct scsi_cmnd *cmd) { scsi_qla_host_t *ha = to_qla_host(cmd->device->host); @@ -889,7 +889,7 @@ qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *ha) * SUCCESS/FAILURE (defined as macro in scsi.h). * **************************************************************************/ -int +static int qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd) { scsi_qla_host_t *ha = to_qla_host(cmd->device->host); @@ -950,7 +950,7 @@ eh_bus_reset_done: * * Note: **************************************************************************/ -int +static int qla2xxx_eh_host_reset(struct scsi_cmnd *cmd) { scsi_qla_host_t *ha = to_qla_host(cmd->device->host); diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index c71dbd5bd543..15390ad87456 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -449,7 +449,7 @@ nvram_data_to_access_addr(uint32_t naddr) return FARX_ACCESS_NVRAM_DATA | naddr; } -uint32_t +static uint32_t qla24xx_read_flash_dword(scsi_qla_host_t *ha, uint32_t addr) { int rval; @@ -490,7 +490,7 @@ qla24xx_read_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr, return dwptr; } -int +static int qla24xx_write_flash_dword(scsi_qla_host_t *ha, uint32_t addr, uint32_t data) { int rval; @@ -512,7 +512,7 @@ qla24xx_write_flash_dword(scsi_qla_host_t *ha, uint32_t addr, uint32_t data) return rval; } -void +static void qla24xx_get_flash_manufacturer(scsi_qla_host_t *ha, uint8_t *man_id, uint8_t *flash_id) { @@ -537,7 +537,7 @@ qla24xx_get_flash_manufacturer(scsi_qla_host_t *ha, uint8_t *man_id, } } -int +static int qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr, uint32_t dwords) { From 297295ae6653af1356dd35a8edc411e2715aec57 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 8 Nov 2006 19:56:26 -0800 Subject: [PATCH 15/73] [SCSI] ips: fix soft lockup during reset initialization Resetting the adapter causes the ServeRAID driver to exceed the max time allowed by the softlock watchdog. Resetting the hardware can easily require 30 or more seconds. To avoid the "BUG: soft lockup detected on CPU#0!" result, this patch adds a touch_nmi_watchdog() to the driver's MDELAY macro. Cc: Nishanth Aravamudan Cc: Nick Piggin Acked-by: Jack Hammer Signed-off-by: Andrew Morton Signed-off-by: James Bottomley --- drivers/scsi/ips.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/ips.h b/drivers/scsi/ips.h index 34680f3dd452..b726dcc424b1 100644 --- a/drivers/scsi/ips.h +++ b/drivers/scsi/ips.h @@ -51,6 +51,7 @@ #define _IPS_H_ #include +#include #include #include @@ -116,9 +117,11 @@ dev_printk(level , &((pcidev)->dev) , format , ## arg) #endif - #ifndef MDELAY - #define MDELAY mdelay - #endif + #define MDELAY(n) \ + do { \ + mdelay(n); \ + touch_nmi_watchdog(); \ + } while (0) #ifndef min #define min(x,y) ((x) < (y) ? x : y) From 667c667f86c87cd4f72296d650e5e65f2d89cb31 Mon Sep 17 00:00:00 2001 From: Henne Date: Wed, 8 Nov 2006 19:56:28 -0800 Subject: [PATCH 16/73] [SCSI] scsi: t128 scsi_cmnd convertion Changes the obsolete Scsi_Cmnd to struct scsi_cmnd and remove the trailing whitespaces. Signed-off-by: Henrik Kretzschmar Signed-off-by: Andrew Morton Signed-off-by: James Bottomley --- drivers/scsi/t128.h | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/drivers/scsi/t128.h b/drivers/scsi/t128.h index 646e840266e2..76a069b7ac0b 100644 --- a/drivers/scsi/t128.h +++ b/drivers/scsi/t128.h @@ -8,20 +8,20 @@ * drew@colorado.edu * +1 (303) 440-4894 * - * DISTRIBUTION RELEASE 3. + * DISTRIBUTION RELEASE 3. * - * For more information, please consult + * For more information, please consult * * Trantor Systems, Ltd. * T128/T128F/T228 SCSI Host Adapter * Hardware Specifications - * - * Trantor Systems, Ltd. + * + * Trantor Systems, Ltd. * 5415 Randall Place * Fremont, CA 94538 * 1+ (415) 770-1400, FAX 1+ (415) 770-9910 - * - * and + * + * and * * NCR 5380 Family * SCSI Protocol Controller @@ -48,15 +48,15 @@ #define TDEBUG_TRANSFER 0x2 /* - * The trantor boards are memory mapped. They use an NCR5380 or + * The trantor boards are memory mapped. They use an NCR5380 or * equivalent (my sample board had part second sourced from ZILOG). - * NCR's recommended "Pseudo-DMA" architecture is used, where + * NCR's recommended "Pseudo-DMA" architecture is used, where * a PAL drives the DMA signals on the 5380 allowing fast, blind - * transfers with proper handshaking. + * transfers with proper handshaking. */ /* - * Note : a boot switch is provided for the purpose of informing the + * Note : a boot switch is provided for the purpose of informing the * firmware to boot or not boot from attached SCSI devices. So, I imagine * there are fewer people who've yanked the ROM like they do on the Seagate * to make bootup faster, and I'll probably use this for autodetection. @@ -92,19 +92,20 @@ #define T_DATA_REG_OFFSET 0x1e00 /* rw 512 bytes long */ #ifndef ASM -static int t128_abort(Scsi_Cmnd *); +static int t128_abort(struct scsi_cmnd *); static int t128_biosparam(struct scsi_device *, struct block_device *, sector_t, int*); static int t128_detect(struct scsi_host_template *); -static int t128_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); -static int t128_bus_reset(Scsi_Cmnd *); +static int t128_queue_command(struct scsi_cmnd *, + void (*done)(struct scsi_cmnd *)); +static int t128_bus_reset(struct scsi_cmnd *); #ifndef CMD_PER_LUN #define CMD_PER_LUN 2 #endif #ifndef CAN_QUEUE -#define CAN_QUEUE 32 +#define CAN_QUEUE 32 #endif #ifndef HOSTS_C @@ -120,7 +121,7 @@ static int t128_bus_reset(Scsi_Cmnd *); #define T128_address(reg) (base + T_5380_OFFSET + ((reg) * 0x20)) -#if !(TDEBUG & TDEBUG_TRANSFER) +#if !(TDEBUG & TDEBUG_TRANSFER) #define NCR5380_read(reg) readb(T128_address(reg)) #define NCR5380_write(reg, value) writeb((value),(T128_address(reg))) #else @@ -129,7 +130,7 @@ static int t128_bus_reset(Scsi_Cmnd *); , instance->hostno, (reg), T128_address(reg))), readb(T128_address(reg))) #define NCR5380_write(reg, value) { \ - printk("scsi%d : write %02x to register %d at address %08x\n", \ + printk("scsi%d : write %02x to register %d at address %08x\n", \ instance->hostno, (value), (reg), T128_address(reg)); \ writeb((value), (T128_address(reg))); \ } @@ -142,10 +143,10 @@ static int t128_bus_reset(Scsi_Cmnd *); #define NCR5380_bus_reset t128_bus_reset #define NCR5380_proc_info t128_proc_info -/* 15 14 12 10 7 5 3 +/* 15 14 12 10 7 5 3 1101 0100 1010 1000 */ - -#define T128_IRQS 0xc4a8 + +#define T128_IRQS 0xc4a8 #endif /* else def HOSTS_C */ #endif /* ndef ASM */ From 85e04e371b5a321b5df2bc3f8e0099a64fb087d7 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Thu, 9 Nov 2006 11:55:41 +0100 Subject: [PATCH 17/73] [SCSI] block: convert jiffies to msecs in scsi_ioctl() Use the proper conversion function for convert jiffies to msecs in sg_io(). Signed-off-by: Hannes Reinecke Signed-off-by: James Bottomley --- block/scsi_ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index 2dc326421a24..ac63964b7242 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c @@ -304,7 +304,7 @@ static int sg_io(struct file *file, request_queue_t *q, if (rq->bio) blk_queue_bounce(q, &rq->bio); - rq->timeout = (hdr->timeout * HZ) / 1000; + rq->timeout = jiffies_to_msecs(hdr->timeout); if (!rq->timeout) rq->timeout = q->sg_timeout; if (!rq->timeout) From 504fb37a0801d843bc1907c1a1f9c719c3509863 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Thu, 9 Nov 2006 10:09:35 +0100 Subject: [PATCH 18/73] [SCSI] fix module unload induced compile warning Signed-off-by: Ingo Molnar Signed-off-by: James Bottomley --- drivers/scsi/scsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index c59f31533ab4..780d6dc92b42 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -871,9 +871,9 @@ EXPORT_SYMBOL(scsi_device_get); */ void scsi_device_put(struct scsi_device *sdev) { +#ifdef CONFIG_MODULE_UNLOAD struct module *module = sdev->host->hostt->module; -#ifdef CONFIG_MODULE_UNLOAD /* The module refcount will be zero if scsi_device_get() * was called from a module removal routine */ if (module && module_refcount(module) != 0) From dea22214790d1306f3a3444db13d2c726037b189 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 7 Nov 2006 17:28:55 -0800 Subject: [PATCH 19/73] [PATCH] aic94xx: handle REQ_DEVICE_RESET This patch implements a REQ_DEVICE_RESET handler for the aic94xx driver. Like the earlier REQ_TASK_ABORT patch, this patch defers the device reset to the Scsi_Host's workqueue, which has the added benefit of ensuring that the device reset does not happen at the same time that the abort tmfs are being processed. After the phy reset, the busted drive should go away and be re-detected later, which is indeed what I've seen on both a x260 and a x206m. Signed-off-by: Darrick J. Wong Signed-off-by: James Bottomley --- drivers/scsi/aic94xx/aic94xx_scb.c | 51 +++++++++++++++++++++++++---- drivers/scsi/libsas/sas_init.c | 2 +- drivers/scsi/libsas/sas_scsi_host.c | 1 + include/scsi/libsas.h | 1 + include/scsi/scsi_transport_sas.h | 2 ++ 5 files changed, 49 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/aic94xx/aic94xx_scb.c b/drivers/scsi/aic94xx/aic94xx_scb.c index 1911c5d17875..a014418d670e 100644 --- a/drivers/scsi/aic94xx/aic94xx_scb.c +++ b/drivers/scsi/aic94xx/aic94xx_scb.c @@ -343,6 +343,27 @@ void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id) } } +/* hard reset a phy later */ +static void do_phy_reset_later(void *data) +{ + struct sas_phy *sas_phy = data; + int error; + + ASD_DPRINTK("%s: About to hard reset phy %d\n", __FUNCTION__, + sas_phy->identify.phy_identifier); + /* Reset device port */ + error = sas_phy_reset(sas_phy, 1); + if (error) + ASD_DPRINTK("%s: Hard reset of phy %d failed (%d).\n", + __FUNCTION__, sas_phy->identify.phy_identifier, error); +} + +static void phy_reset_later(struct sas_phy *sas_phy, struct Scsi_Host *shost) +{ + INIT_WORK(&sas_phy->reset_work, do_phy_reset_later, sas_phy); + queue_work(shost->work_q, &sas_phy->reset_work); +} + /* start up the ABORT TASK tmf... */ static void task_kill_later(struct asd_ascb *ascb) { @@ -402,7 +423,9 @@ static void escb_tasklet_complete(struct asd_ascb *ascb, goto out; } case REQ_DEVICE_RESET: { - struct asd_ascb *a, *b; + struct Scsi_Host *shost = sas_ha->core.shost; + struct sas_phy *dev_phy; + struct asd_ascb *a; u16 conn_handle; conn_handle = *((u16*)(&dl->status_block[1])); @@ -412,17 +435,31 @@ static void escb_tasklet_complete(struct asd_ascb *ascb, dl->status_block[3]); /* Kill all pending tasks and reset the device */ - list_for_each_entry_safe(a, b, &asd_ha->seq.pend_q, list) { - struct sas_task *task = a->uldd_task; - struct domain_device *dev = task->dev; + dev_phy = NULL; + list_for_each_entry(a, &asd_ha->seq.pend_q, list) { + struct sas_task *task; + struct domain_device *dev; u16 x; - x = *((u16*)(&dev->lldd_dev)); - if (x == conn_handle) + task = a->uldd_task; + if (!task) + continue; + dev = task->dev; + + x = (u16)dev->lldd_dev; + if (x == conn_handle) { + dev_phy = dev->port->phy; task_kill_later(a); + } } - /* FIXME: Reset device port (huh?) */ + /* Reset device port */ + if (!dev_phy) { + ASD_DPRINTK("%s: No pending commands; can't reset.\n", + __FUNCTION__); + goto out; + } + phy_reset_later(dev_phy, shost); goto out; } case SIGNAL_NCQ_ERROR: diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index a2b479a65908..0fb347b4b1a2 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -144,7 +144,7 @@ static int sas_get_linkerrors(struct sas_phy *phy) return sas_smp_get_phy_events(phy); } -static int sas_phy_reset(struct sas_phy *phy, int hard_reset) +int sas_phy_reset(struct sas_phy *phy, int hard_reset) { int ret; enum phy_func reset_type; diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index c5fd37522728..e064aac06b90 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -865,3 +865,4 @@ EXPORT_SYMBOL_GPL(sas_change_queue_depth); EXPORT_SYMBOL_GPL(sas_change_queue_type); EXPORT_SYMBOL_GPL(sas_bios_param); EXPORT_SYMBOL_GPL(sas_task_abort); +EXPORT_SYMBOL_GPL(sas_phy_reset); diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index a1fc20a47c50..29f6e1af1bf9 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -597,6 +597,7 @@ struct sas_domain_function_template { extern int sas_register_ha(struct sas_ha_struct *); extern int sas_unregister_ha(struct sas_ha_struct *); +int sas_phy_reset(struct sas_phy *phy, int hard_reset); extern int sas_queuecommand(struct scsi_cmnd *, void (*scsi_done)(struct scsi_cmnd *)); extern int sas_target_alloc(struct scsi_target *); diff --git a/include/scsi/scsi_transport_sas.h b/include/scsi/scsi_transport_sas.h index 53024377f3b8..59633a82de47 100644 --- a/include/scsi/scsi_transport_sas.h +++ b/include/scsi/scsi_transport_sas.h @@ -73,6 +73,8 @@ struct sas_phy { /* for the list of phys belonging to a port */ struct list_head port_siblings; + + struct work_struct reset_work; }; #define dev_to_phy(d) \ From 7b4feee973ca7be63345b92a987ef7ef879b179b Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 14 Nov 2006 18:02:07 -0800 Subject: [PATCH 20/73] [PATCH] aic94xx: delete ascb timers when freeing queues When the aic94xx driver creates ascbs, each ascb is initialized with a timeout timer. If there are any ascbs left over when the driver is being torn down, these timers need to be deleted. In particular, we seem to hit this case when ascbs are issued yet never end up on the done list. Right now there's a sequencer bug that results in this happening every so often. CONTROL PHY commands are typically sent when things are really messed up with the sequencer; however, any other leftover ascb should produce loud warnings. Signed-off-by: Darrick J. Wong Signed-off-by: James Bottomley --- drivers/scsi/aic94xx/aic94xx_init.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c index 99743ca29ca1..3a5bbba3976e 100644 --- a/drivers/scsi/aic94xx/aic94xx_init.c +++ b/drivers/scsi/aic94xx/aic94xx_init.c @@ -724,6 +724,15 @@ static void asd_free_queues(struct asd_ha_struct *asd_ha) list_for_each_safe(pos, n, &pending) { struct asd_ascb *ascb = list_entry(pos, struct asd_ascb, list); + /* + * Delete unexpired ascb timers. This may happen if we issue + * a CONTROL PHY scb to an adapter and rmmod before the scb + * times out. Apparently we don't wait for the CONTROL PHY + * to complete, so it doesn't matter if we kill the timer. + */ + del_timer_sync(&ascb->timer); + WARN_ON(ascb->scb->header.opcode != CONTROL_PHY); + list_del_init(pos); ASD_DPRINTK("freeing from pending\n"); asd_ascb_free(ascb); From f26b90440cd74c78fe10c9bd5160809704a9627c Mon Sep 17 00:00:00 2001 From: David C Somayajulu Date: Wed, 15 Nov 2006 16:41:09 -0800 Subject: [PATCH 21/73] [PATCH] qla4xxx: bug fix: driver hardware semaphore needs to be grabbed before soft reset On qla4xxx, the driver needs to grab the drvr semaphore provided by the hardware, prior to issuing a reset. This patches takes care of a couple of places where it was not being done. In addition there is minor clean up. Signed-off-by: David Somayajulu Signed-off-by: James Bottomley --- drivers/scsi/qla4xxx/ql4_glbl.h | 1 + drivers/scsi/qla4xxx/ql4_init.c | 2 +- drivers/scsi/qla4xxx/ql4_os.c | 42 +++++++++--------------------- drivers/scsi/qla4xxx/ql4_version.h | 7 +---- 4 files changed, 15 insertions(+), 37 deletions(-) diff --git a/drivers/scsi/qla4xxx/ql4_glbl.h b/drivers/scsi/qla4xxx/ql4_glbl.h index 1b221ff0f6f7..eeefdcb1edb5 100644 --- a/drivers/scsi/qla4xxx/ql4_glbl.h +++ b/drivers/scsi/qla4xxx/ql4_glbl.h @@ -8,6 +8,7 @@ #ifndef __QLA4x_GBL_H #define __QLA4x_GBL_H +int ql4xxx_lock_drvr_wait(struct scsi_qla_host *a); int qla4xxx_send_tgts(struct scsi_qla_host *ha, char *ip, uint16_t port); int qla4xxx_send_command_to_isp(struct scsi_qla_host *ha, struct srb * srb); int qla4xxx_initialize_adapter(struct scsi_qla_host * ha, diff --git a/drivers/scsi/qla4xxx/ql4_init.c b/drivers/scsi/qla4xxx/ql4_init.c index bb3a1c11f44c..9e81b810dc88 100644 --- a/drivers/scsi/qla4xxx/ql4_init.c +++ b/drivers/scsi/qla4xxx/ql4_init.c @@ -978,7 +978,7 @@ static int qla4xxx_start_firmware_from_flash(struct scsi_qla_host *ha) return status; } -static int ql4xxx_lock_drvr_wait(struct scsi_qla_host *a) +int ql4xxx_lock_drvr_wait(struct scsi_qla_host *a) { #define QL4_LOCK_DRVR_WAIT 300 #define QL4_LOCK_DRVR_SLEEP 100 diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 5b8db6109536..bab434ee774b 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -919,18 +919,11 @@ static int qla4xxx_recover_adapter(struct scsi_qla_host *ha, if (status == QLA_SUCCESS) { DEBUG2(printk("scsi%ld: %s - Performing soft reset..\n", ha->host_no, __func__)); - status = qla4xxx_soft_reset(ha); - } - /* FIXMEkaren: Do we want to keep interrupts enabled and process - AENs after soft reset */ - - /* If firmware (SOFT) reset failed, or if all outstanding - * commands have not returned, then do a HARD reset. - */ - if (status == QLA_ERROR) { - DEBUG2(printk("scsi%ld: %s - Performing hard reset..\n", - ha->host_no, __func__)); - status = qla4xxx_hard_reset(ha); + qla4xxx_flush_active_srbs(ha); + if (ql4xxx_lock_drvr_wait(ha) == QLA_SUCCESS) + status = qla4xxx_soft_reset(ha); + else + status = QLA_ERROR; } /* Flush any pending ddb changed AENs */ @@ -1016,13 +1009,9 @@ static void qla4xxx_do_dpc(void *data) struct scsi_qla_host *ha = (struct scsi_qla_host *) data; struct ddb_entry *ddb_entry, *dtemp; - DEBUG2(printk("scsi%ld: %s: DPC handler waking up.\n", - ha->host_no, __func__)); - - DEBUG2(printk("scsi%ld: %s: ha->flags = 0x%08lx\n", - ha->host_no, __func__, ha->flags)); - DEBUG2(printk("scsi%ld: %s: ha->dpc_flags = 0x%08lx\n", - ha->host_no, __func__, ha->dpc_flags)); + DEBUG2(printk("scsi%ld: %s: DPC handler waking up." + "flags = 0x%08lx, dpc_flags = 0x%08lx\n", + ha->host_no, __func__, ha->flags, ha->dpc_flags)); /* Initialization not yet finished. Don't do anything yet. */ if (!test_bit(AF_INIT_DONE, &ha->flags)) @@ -1032,16 +1021,8 @@ static void qla4xxx_do_dpc(void *data) test_bit(DPC_RESET_HA, &ha->dpc_flags) || test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags) || test_bit(DPC_RESET_HA_DESTROY_DDB_LIST, &ha->dpc_flags)) { - if (test_bit(DPC_RESET_HA_DESTROY_DDB_LIST, &ha->dpc_flags)) - /* - * dg 09/23 Never initialize ddb list - * once we up and running - * qla4xxx_recover_adapter(ha, - * REBUILD_DDB_LIST); - */ - qla4xxx_recover_adapter(ha, PRESERVE_DDB_LIST); - - if (test_bit(DPC_RESET_HA, &ha->dpc_flags)) + if (test_bit(DPC_RESET_HA_DESTROY_DDB_LIST, &ha->dpc_flags) || + test_bit(DPC_RESET_HA, &ha->dpc_flags)) qla4xxx_recover_adapter(ha, PRESERVE_DDB_LIST); if (test_and_clear_bit(DPC_RESET_HA_INTR, &ha->dpc_flags)) { @@ -1122,7 +1103,8 @@ static void qla4xxx_free_adapter(struct scsi_qla_host *ha) destroy_workqueue(ha->dpc_thread); /* Issue Soft Reset to put firmware in unknown state */ - qla4xxx_soft_reset(ha); + if (ql4xxx_lock_drvr_wait(ha) == QLA_SUCCESS) + qla4xxx_soft_reset(ha); /* Remove timer thread, if present */ if (ha->timer_active) diff --git a/drivers/scsi/qla4xxx/ql4_version.h b/drivers/scsi/qla4xxx/ql4_version.h index b3fe7e68988e..d05048b4e88a 100644 --- a/drivers/scsi/qla4xxx/ql4_version.h +++ b/drivers/scsi/qla4xxx/ql4_version.h @@ -5,9 +5,4 @@ * See LICENSE.qla4xxx for copyright and licensing details. */ -#define QLA4XXX_DRIVER_VERSION "5.00.05b9-k" - -#define QL4_DRIVER_MAJOR_VER 5 -#define QL4_DRIVER_MINOR_VER 0 -#define QL4_DRIVER_PATCH_VER 5 -#define QL4_DRIVER_BETA_VER 9 +#define QLA4XXX_DRIVER_VERSION "5.00.06-k" From e138a5d2356729b8752e88520cc1525fae9794ac Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Wed, 22 Nov 2006 11:54:15 -0600 Subject: [PATCH 22/73] [SCSI] aic94xx: fix pointer to integer conversion warning Signed-off-by: James Bottomley --- drivers/scsi/aic94xx/aic94xx_scb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/aic94xx/aic94xx_scb.c b/drivers/scsi/aic94xx/aic94xx_scb.c index a014418d670e..52c6ea4fbf71 100644 --- a/drivers/scsi/aic94xx/aic94xx_scb.c +++ b/drivers/scsi/aic94xx/aic94xx_scb.c @@ -446,7 +446,7 @@ static void escb_tasklet_complete(struct asd_ascb *ascb, continue; dev = task->dev; - x = (u16)dev->lldd_dev; + x = (unsigned long)dev->lldd_dev; if (x == conn_handle) { dev_phy = dev->port->phy; task_kill_later(a); From d915058f48745c0d5c4582566e5aa63867264f81 Mon Sep 17 00:00:00 2001 From: David C Somayajulu Date: Wed, 15 Nov 2006 17:38:40 -0800 Subject: [PATCH 23/73] [SCSI] qla4xxx: add support for qla4032 This patch provides the following: 1. adds support for the next version of Qlogic's iSCSI HBA, qla4032 (PCI Device ID 4032). 2. removes dead code related to topcat chip and renames qla4010_soft_reset to qla4xxx_soft_reset (minor changes). Signed-off-by: David Somayajulu Signed-off-by: James Bottomley --- drivers/scsi/qla4xxx/ql4_dbg.c | 4 +- drivers/scsi/qla4xxx/ql4_def.h | 105 ++++++++++++++++------------- drivers/scsi/qla4xxx/ql4_fw.h | 7 +- drivers/scsi/qla4xxx/ql4_glbl.h | 2 +- drivers/scsi/qla4xxx/ql4_init.c | 45 +++---------- drivers/scsi/qla4xxx/ql4_inline.h | 4 +- drivers/scsi/qla4xxx/ql4_iocb.c | 6 ++ drivers/scsi/qla4xxx/ql4_isr.c | 1 + drivers/scsi/qla4xxx/ql4_nvram.c | 6 +- drivers/scsi/qla4xxx/ql4_nvram.h | 4 +- drivers/scsi/qla4xxx/ql4_os.c | 75 +++------------------ drivers/scsi/qla4xxx/ql4_version.h | 2 +- 12 files changed, 98 insertions(+), 163 deletions(-) diff --git a/drivers/scsi/qla4xxx/ql4_dbg.c b/drivers/scsi/qla4xxx/ql4_dbg.c index 752031fadfef..7b4e077a39c1 100644 --- a/drivers/scsi/qla4xxx/ql4_dbg.c +++ b/drivers/scsi/qla4xxx/ql4_dbg.c @@ -71,7 +71,7 @@ void __dump_registers(struct scsi_qla_host *ha) readw(&ha->reg->u1.isp4010.nvram)); } - else if (is_qla4022(ha)) { + else if (is_qla4022(ha) | is_qla4032(ha)) { printk(KERN_INFO "0x%02X intr_mask = 0x%08X\n", (uint8_t) offsetof(struct isp_reg, u1.isp4022.intr_mask), @@ -119,7 +119,7 @@ void __dump_registers(struct scsi_qla_host *ha) readw(&ha->reg->u2.isp4010.port_err_status)); } - else if (is_qla4022(ha)) { + else if (is_qla4022(ha) | is_qla4032(ha)) { printk(KERN_INFO "Page 0 Registers:\n"); printk(KERN_INFO "0x%02X ext_hw_conf = 0x%08X\n", (uint8_t) offsetof(struct isp_reg, diff --git a/drivers/scsi/qla4xxx/ql4_def.h b/drivers/scsi/qla4xxx/ql4_def.h index a7f6c7b1c590..4249e52a5592 100644 --- a/drivers/scsi/qla4xxx/ql4_def.h +++ b/drivers/scsi/qla4xxx/ql4_def.h @@ -40,7 +40,11 @@ #ifndef PCI_DEVICE_ID_QLOGIC_ISP4022 #define PCI_DEVICE_ID_QLOGIC_ISP4022 0x4022 -#endif /* */ +#endif + +#ifndef PCI_DEVICE_ID_QLOGIC_ISP4032 +#define PCI_DEVICE_ID_QLOGIC_ISP4032 0x4032 +#endif #define QLA_SUCCESS 0 #define QLA_ERROR 1 @@ -277,7 +281,6 @@ struct scsi_qla_host { #define AF_INTERRUPTS_ON 6 /* 0x00000040 Not Used */ #define AF_GET_CRASH_RECORD 7 /* 0x00000080 */ #define AF_LINK_UP 8 /* 0x00000100 */ -#define AF_TOPCAT_CHIP_PRESENT 9 /* 0x00000200 */ #define AF_IRQ_ATTACHED 10 /* 0x00000400 */ #define AF_ISNS_CMD_IN_PROCESS 12 /* 0x00001000 */ #define AF_ISNS_CMD_DONE 13 /* 0x00002000 */ @@ -317,16 +320,17 @@ struct scsi_qla_host { /* NVRAM registers */ struct eeprom_data *nvram; spinlock_t hardware_lock ____cacheline_aligned; - spinlock_t list_lock; uint32_t eeprom_cmd_data; /* Counters for general statistics */ + uint64_t isr_count; uint64_t adapter_error_count; uint64_t device_error_count; uint64_t total_io_count; uint64_t total_mbytes_xferred; uint64_t link_failure_count; uint64_t invalid_crc_count; + uint32_t bytes_xfered; uint32_t spurious_int_count; uint32_t aborted_io_count; uint32_t io_timeout_count; @@ -438,6 +442,11 @@ static inline int is_qla4022(struct scsi_qla_host *ha) return ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP4022; } +static inline int is_qla4032(struct scsi_qla_host *ha) +{ + return ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP4032; +} + static inline int adapter_up(struct scsi_qla_host *ha) { return (test_bit(AF_ONLINE, &ha->flags) != 0) && @@ -451,58 +460,58 @@ static inline struct scsi_qla_host* to_qla_host(struct Scsi_Host *shost) static inline void __iomem* isp_semaphore(struct scsi_qla_host *ha) { - return (is_qla4022(ha) ? - &ha->reg->u1.isp4022.semaphore : - &ha->reg->u1.isp4010.nvram); + return (is_qla4010(ha) ? + &ha->reg->u1.isp4010.nvram : + &ha->reg->u1.isp4022.semaphore); } static inline void __iomem* isp_nvram(struct scsi_qla_host *ha) { - return (is_qla4022(ha) ? - &ha->reg->u1.isp4022.nvram : - &ha->reg->u1.isp4010.nvram); + return (is_qla4010(ha) ? + &ha->reg->u1.isp4010.nvram : + &ha->reg->u1.isp4022.nvram); } static inline void __iomem* isp_ext_hw_conf(struct scsi_qla_host *ha) { - return (is_qla4022(ha) ? - &ha->reg->u2.isp4022.p0.ext_hw_conf : - &ha->reg->u2.isp4010.ext_hw_conf); + return (is_qla4010(ha) ? + &ha->reg->u2.isp4010.ext_hw_conf : + &ha->reg->u2.isp4022.p0.ext_hw_conf); } static inline void __iomem* isp_port_status(struct scsi_qla_host *ha) { - return (is_qla4022(ha) ? - &ha->reg->u2.isp4022.p0.port_status : - &ha->reg->u2.isp4010.port_status); + return (is_qla4010(ha) ? + &ha->reg->u2.isp4010.port_status : + &ha->reg->u2.isp4022.p0.port_status); } static inline void __iomem* isp_port_ctrl(struct scsi_qla_host *ha) { - return (is_qla4022(ha) ? - &ha->reg->u2.isp4022.p0.port_ctrl : - &ha->reg->u2.isp4010.port_ctrl); + return (is_qla4010(ha) ? + &ha->reg->u2.isp4010.port_ctrl : + &ha->reg->u2.isp4022.p0.port_ctrl); } static inline void __iomem* isp_port_error_status(struct scsi_qla_host *ha) { - return (is_qla4022(ha) ? - &ha->reg->u2.isp4022.p0.port_err_status : - &ha->reg->u2.isp4010.port_err_status); + return (is_qla4010(ha) ? + &ha->reg->u2.isp4010.port_err_status : + &ha->reg->u2.isp4022.p0.port_err_status); } static inline void __iomem * isp_gp_out(struct scsi_qla_host *ha) { - return (is_qla4022(ha) ? - &ha->reg->u2.isp4022.p0.gp_out : - &ha->reg->u2.isp4010.gp_out); + return (is_qla4010(ha) ? + &ha->reg->u2.isp4010.gp_out : + &ha->reg->u2.isp4022.p0.gp_out); } static inline int eeprom_ext_hw_conf_offset(struct scsi_qla_host *ha) { - return (is_qla4022(ha) ? - offsetof(struct eeprom_data, isp4022.ext_hw_conf) / 2 : - offsetof(struct eeprom_data, isp4010.ext_hw_conf) / 2); + return (is_qla4010(ha) ? + offsetof(struct eeprom_data, isp4010.ext_hw_conf) / 2 : + offsetof(struct eeprom_data, isp4022.ext_hw_conf) / 2); } int ql4xxx_sem_spinlock(struct scsi_qla_host * ha, u32 sem_mask, u32 sem_bits); @@ -511,59 +520,59 @@ int ql4xxx_sem_lock(struct scsi_qla_host * ha, u32 sem_mask, u32 sem_bits); static inline int ql4xxx_lock_flash(struct scsi_qla_host *a) { - if (is_qla4022(a)) + if (is_qla4010(a)) + return ql4xxx_sem_spinlock(a, QL4010_FLASH_SEM_MASK, + QL4010_FLASH_SEM_BITS); + else return ql4xxx_sem_spinlock(a, QL4022_FLASH_SEM_MASK, (QL4022_RESOURCE_BITS_BASE_CODE | (a->mac_index)) << 13); - else - return ql4xxx_sem_spinlock(a, QL4010_FLASH_SEM_MASK, - QL4010_FLASH_SEM_BITS); } static inline void ql4xxx_unlock_flash(struct scsi_qla_host *a) { - if (is_qla4022(a)) - ql4xxx_sem_unlock(a, QL4022_FLASH_SEM_MASK); - else + if (is_qla4010(a)) ql4xxx_sem_unlock(a, QL4010_FLASH_SEM_MASK); + else + ql4xxx_sem_unlock(a, QL4022_FLASH_SEM_MASK); } static inline int ql4xxx_lock_nvram(struct scsi_qla_host *a) { - if (is_qla4022(a)) + if (is_qla4010(a)) + return ql4xxx_sem_spinlock(a, QL4010_NVRAM_SEM_MASK, + QL4010_NVRAM_SEM_BITS); + else return ql4xxx_sem_spinlock(a, QL4022_NVRAM_SEM_MASK, (QL4022_RESOURCE_BITS_BASE_CODE | (a->mac_index)) << 10); - else - return ql4xxx_sem_spinlock(a, QL4010_NVRAM_SEM_MASK, - QL4010_NVRAM_SEM_BITS); } static inline void ql4xxx_unlock_nvram(struct scsi_qla_host *a) { - if (is_qla4022(a)) - ql4xxx_sem_unlock(a, QL4022_NVRAM_SEM_MASK); - else + if (is_qla4010(a)) ql4xxx_sem_unlock(a, QL4010_NVRAM_SEM_MASK); + else + ql4xxx_sem_unlock(a, QL4022_NVRAM_SEM_MASK); } static inline int ql4xxx_lock_drvr(struct scsi_qla_host *a) { - if (is_qla4022(a)) + if (is_qla4010(a)) + return ql4xxx_sem_lock(a, QL4010_DRVR_SEM_MASK, + QL4010_DRVR_SEM_BITS); + else return ql4xxx_sem_lock(a, QL4022_DRVR_SEM_MASK, (QL4022_RESOURCE_BITS_BASE_CODE | (a->mac_index)) << 1); - else - return ql4xxx_sem_lock(a, QL4010_DRVR_SEM_MASK, - QL4010_DRVR_SEM_BITS); } static inline void ql4xxx_unlock_drvr(struct scsi_qla_host *a) { - if (is_qla4022(a)) - ql4xxx_sem_unlock(a, QL4022_DRVR_SEM_MASK); - else + if (is_qla4010(a)) ql4xxx_sem_unlock(a, QL4010_DRVR_SEM_MASK); + else + ql4xxx_sem_unlock(a, QL4022_DRVR_SEM_MASK); } /*---------------------------------------------------------------------------*/ diff --git a/drivers/scsi/qla4xxx/ql4_fw.h b/drivers/scsi/qla4xxx/ql4_fw.h index 427489de64bc..4eea8c571916 100644 --- a/drivers/scsi/qla4xxx/ql4_fw.h +++ b/drivers/scsi/qla4xxx/ql4_fw.h @@ -296,7 +296,6 @@ static inline uint32_t clr_rmask(uint32_t val) /* ISP Semaphore definitions */ /* ISP General Purpose Output definitions */ -#define GPOR_TOPCAT_RESET 0x00000004 /* shadow registers (DMA'd from HA to system memory. read only) */ struct shadow_regs { @@ -339,10 +338,13 @@ union external_hw_config_reg { /* Mailbox command definitions */ #define MBOX_CMD_ABOUT_FW 0x0009 #define MBOX_CMD_LUN_RESET 0x0016 +#define MBOX_CMD_GET_MANAGEMENT_DATA 0x001E #define MBOX_CMD_GET_FW_STATUS 0x001F #define MBOX_CMD_SET_ISNS_SERVICE 0x0021 #define ISNS_DISABLE 0 #define ISNS_ENABLE 1 +#define MBOX_CMD_COPY_FLASH 0x0024 +#define MBOX_CMD_WRITE_FLASH 0x0025 #define MBOX_CMD_READ_FLASH 0x0026 #define MBOX_CMD_CLEAR_DATABASE_ENTRY 0x0031 #define MBOX_CMD_CONN_CLOSE_SESS_LOGOUT 0x0056 @@ -360,10 +362,13 @@ union external_hw_config_reg { #define DDB_DS_SESSION_FAILED 0x06 #define DDB_DS_LOGIN_IN_PROCESS 0x07 #define MBOX_CMD_GET_FW_STATE 0x0069 +#define MBOX_CMD_GET_INIT_FW_CTRL_BLOCK_DEFAULTS 0x006A +#define MBOX_CMD_RESTORE_FACTORY_DEFAULTS 0x0087 /* Mailbox 1 */ #define FW_STATE_READY 0x0000 #define FW_STATE_CONFIG_WAIT 0x0001 +#define FW_STATE_WAIT_LOGIN 0x0002 #define FW_STATE_ERROR 0x0004 #define FW_STATE_DHCP_IN_PROGRESS 0x0008 diff --git a/drivers/scsi/qla4xxx/ql4_glbl.h b/drivers/scsi/qla4xxx/ql4_glbl.h index eeefdcb1edb5..2122967bbf0b 100644 --- a/drivers/scsi/qla4xxx/ql4_glbl.h +++ b/drivers/scsi/qla4xxx/ql4_glbl.h @@ -76,4 +76,4 @@ int qla4xxx_process_ddb_changed(struct scsi_qla_host * ha, extern int ql4xextended_error_logging; extern int ql4xdiscoverywait; extern int ql4xdontresethba; -#endif /* _QLA4x_GBL_H */ +#endif /* _QLA4x_GBL_H */ diff --git a/drivers/scsi/qla4xxx/ql4_init.c b/drivers/scsi/qla4xxx/ql4_init.c index 9e81b810dc88..cc210f297a78 100644 --- a/drivers/scsi/qla4xxx/ql4_init.c +++ b/drivers/scsi/qla4xxx/ql4_init.c @@ -259,10 +259,16 @@ static int qla4xxx_fw_ready(struct scsi_qla_host *ha) "seconds expired= %d\n", ha->host_no, __func__, ha->firmware_state, ha->addl_fw_state, timeout_count)); + if (is_qla4032(ha) && + !(ha->addl_fw_state & FW_ADDSTATE_LINK_UP) && + (timeout_count < ADAPTER_INIT_TOV - 5)) { + break; + } + msleep(1000); } /* end of for */ - if (timeout_count <= 0) + if (timeout_count == 0) DEBUG2(printk("scsi%ld: %s: FW Initialization timed out!\n", ha->host_no, __func__)); @@ -806,32 +812,6 @@ int qla4xxx_relogin_device(struct scsi_qla_host *ha, return QLA_SUCCESS; } -/** - * qla4010_get_topcat_presence - check if it is QLA4040 TopCat Chip - * @ha: Pointer to host adapter structure. - * - **/ -static int qla4010_get_topcat_presence(struct scsi_qla_host *ha) -{ - unsigned long flags; - uint16_t topcat; - - if (ql4xxx_lock_nvram(ha) != QLA_SUCCESS) - return QLA_ERROR; - spin_lock_irqsave(&ha->hardware_lock, flags); - topcat = rd_nvram_word(ha, offsetof(struct eeprom_data, - isp4010.topcat)); - spin_unlock_irqrestore(&ha->hardware_lock, flags); - - if ((topcat & TOPCAT_MASK) == TOPCAT_PRESENT) - set_bit(AF_TOPCAT_CHIP_PRESENT, &ha->flags); - else - clear_bit(AF_TOPCAT_CHIP_PRESENT, &ha->flags); - ql4xxx_unlock_nvram(ha); - return QLA_SUCCESS; -} - - static int qla4xxx_config_nvram(struct scsi_qla_host *ha) { unsigned long flags; @@ -866,7 +846,7 @@ static int qla4xxx_config_nvram(struct scsi_qla_host *ha) /* set defaults */ if (is_qla4010(ha)) extHwConfig.Asuint32_t = 0x1912; - else if (is_qla4022(ha)) + else if (is_qla4022(ha) | is_qla4032(ha)) extHwConfig.Asuint32_t = 0x0023; } DEBUG(printk("scsi%ld: %s: Setting extHwConfig to 0xFFFF%04x\n", @@ -927,7 +907,7 @@ static int qla4xxx_start_firmware_from_flash(struct scsi_qla_host *ha) spin_lock_irqsave(&ha->hardware_lock, flags); writel(jiffies, &ha->reg->mailbox[7]); - if (is_qla4022(ha)) + if (is_qla4022(ha) | is_qla4032(ha)) writel(set_rmask(NVR_WRITE_ENABLE), &ha->reg->u1.isp4022.nvram); @@ -1018,12 +998,7 @@ static int qla4xxx_start_firmware(struct scsi_qla_host *ha) int soft_reset = 1; int config_chip = 0; - if (is_qla4010(ha)){ - if (qla4010_get_topcat_presence(ha) != QLA_SUCCESS) - return QLA_ERROR; - } - - if (is_qla4022(ha)) + if (is_qla4022(ha) | is_qla4032(ha)) ql4xxx_set_mac_number(ha); if (ql4xxx_lock_drvr_wait(ha) != QLA_SUCCESS) diff --git a/drivers/scsi/qla4xxx/ql4_inline.h b/drivers/scsi/qla4xxx/ql4_inline.h index 0d61797af7da..6375eb017dd3 100644 --- a/drivers/scsi/qla4xxx/ql4_inline.h +++ b/drivers/scsi/qla4xxx/ql4_inline.h @@ -38,7 +38,7 @@ qla4xxx_lookup_ddb_by_fw_index(struct scsi_qla_host *ha, uint32_t fw_ddb_index) static inline void __qla4xxx_enable_intrs(struct scsi_qla_host *ha) { - if (is_qla4022(ha)) { + if (is_qla4022(ha) | is_qla4032(ha)) { writel(set_rmask(IMR_SCSI_INTR_ENABLE), &ha->reg->u1.isp4022.intr_mask); readl(&ha->reg->u1.isp4022.intr_mask); @@ -52,7 +52,7 @@ __qla4xxx_enable_intrs(struct scsi_qla_host *ha) static inline void __qla4xxx_disable_intrs(struct scsi_qla_host *ha) { - if (is_qla4022(ha)) { + if (is_qla4022(ha) | is_qla4032(ha)) { writel(clr_rmask(IMR_SCSI_INTR_ENABLE), &ha->reg->u1.isp4022.intr_mask); readl(&ha->reg->u1.isp4022.intr_mask); diff --git a/drivers/scsi/qla4xxx/ql4_iocb.c b/drivers/scsi/qla4xxx/ql4_iocb.c index c0a254b89a30..d41ce380eedc 100644 --- a/drivers/scsi/qla4xxx/ql4_iocb.c +++ b/drivers/scsi/qla4xxx/ql4_iocb.c @@ -294,6 +294,12 @@ int qla4xxx_send_command_to_isp(struct scsi_qla_host *ha, struct srb * srb) cmd_entry->control_flags = CF_WRITE; else if (cmd->sc_data_direction == DMA_FROM_DEVICE) cmd_entry->control_flags = CF_READ; + + ha->bytes_xfered += cmd->request_bufflen; + if (ha->bytes_xfered & ~0xFFFFF){ + ha->total_mbytes_xferred += ha->bytes_xfered >> 20; + ha->bytes_xfered &= 0xFFFFF; + } } /* Set tagged queueing control flags */ diff --git a/drivers/scsi/qla4xxx/ql4_isr.c b/drivers/scsi/qla4xxx/ql4_isr.c index 1e283321a59d..ef975e0dc87f 100644 --- a/drivers/scsi/qla4xxx/ql4_isr.c +++ b/drivers/scsi/qla4xxx/ql4_isr.c @@ -627,6 +627,7 @@ irqreturn_t qla4xxx_intr_handler(int irq, void *dev_id) spin_lock_irqsave(&ha->hardware_lock, flags); + ha->isr_count++; /* * Repeatedly service interrupts up to a maximum of * MAX_REQS_SERVICED_PER_INTR diff --git a/drivers/scsi/qla4xxx/ql4_nvram.c b/drivers/scsi/qla4xxx/ql4_nvram.c index e3957ca5b645..fc5020395407 100644 --- a/drivers/scsi/qla4xxx/ql4_nvram.c +++ b/drivers/scsi/qla4xxx/ql4_nvram.c @@ -9,13 +9,13 @@ static inline int eeprom_size(struct scsi_qla_host *ha) { - return is_qla4022(ha) ? FM93C86A_SIZE_16 : FM93C66A_SIZE_16; + return is_qla4010(ha) ? FM93C66A_SIZE_16 : FM93C86A_SIZE_16; } static inline int eeprom_no_addr_bits(struct scsi_qla_host *ha) { - return is_qla4022(ha) ? FM93C86A_NO_ADDR_BITS_16 : - FM93C56A_NO_ADDR_BITS_16; + return is_qla4010(ha) ? FM93C56A_NO_ADDR_BITS_16 : + FM93C86A_NO_ADDR_BITS_16 ; } static inline int eeprom_no_data_bits(struct scsi_qla_host *ha) diff --git a/drivers/scsi/qla4xxx/ql4_nvram.h b/drivers/scsi/qla4xxx/ql4_nvram.h index 08e2aed8c6cc..b47b4fc59d83 100644 --- a/drivers/scsi/qla4xxx/ql4_nvram.h +++ b/drivers/scsi/qla4xxx/ql4_nvram.h @@ -134,9 +134,7 @@ struct eeprom_data { u16 phyConfig; /* x36 */ #define PHY_CONFIG_PHY_ADDR_MASK 0x1f #define PHY_CONFIG_ENABLE_FW_MANAGEMENT_MASK 0x20 - u16 topcat; /* x38 */ -#define TOPCAT_PRESENT 0x0100 -#define TOPCAT_MASK 0xFF00 + u16 reserved_56; /* x38 */ #define EEPROM_UNUSED_1_SIZE 2 u8 unused_1[EEPROM_UNUSED_1_SIZE]; /* x3A */ diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index bab434ee774b..db9d88e7bee7 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -708,10 +708,10 @@ static int qla4xxx_cmd_wait(struct scsi_qla_host *ha) } /** - * qla4010_soft_reset - performs soft reset. + * qla4xxx_soft_reset - performs soft reset. * @ha: Pointer to host adapter structure. **/ -static int qla4010_soft_reset(struct scsi_qla_host *ha) +int qla4xxx_soft_reset(struct scsi_qla_host *ha) { uint32_t max_wait_time; unsigned long flags = 0; @@ -816,29 +816,6 @@ static int qla4010_soft_reset(struct scsi_qla_host *ha) return status; } -/** - * qla4xxx_topcat_reset - performs hard reset of TopCat Chip. - * @ha: Pointer to host adapter structure. - **/ -static int qla4xxx_topcat_reset(struct scsi_qla_host *ha) -{ - unsigned long flags; - - ql4xxx_lock_nvram(ha); - spin_lock_irqsave(&ha->hardware_lock, flags); - writel(set_rmask(GPOR_TOPCAT_RESET), isp_gp_out(ha)); - readl(isp_gp_out(ha)); - mdelay(1); - - writel(clr_rmask(GPOR_TOPCAT_RESET), isp_gp_out(ha)); - readl(isp_gp_out(ha)); - spin_unlock_irqrestore(&ha->hardware_lock, flags); - mdelay(2523); - - ql4xxx_unlock_nvram(ha); - return QLA_SUCCESS; -} - /** * qla4xxx_flush_active_srbs - returns all outstanding i/o requests to O.S. * @ha: Pointer to host adapter structure. @@ -866,26 +843,6 @@ static void qla4xxx_flush_active_srbs(struct scsi_qla_host *ha) } -/** - * qla4xxx_hard_reset - performs HBA Hard Reset - * @ha: Pointer to host adapter structure. - **/ -static int qla4xxx_hard_reset(struct scsi_qla_host *ha) -{ - /* The QLA4010 really doesn't have an equivalent to a hard reset */ - qla4xxx_flush_active_srbs(ha); - if (test_bit(AF_TOPCAT_CHIP_PRESENT, &ha->flags)) { - int status = QLA_ERROR; - - if ((qla4010_soft_reset(ha) == QLA_SUCCESS) && - (qla4xxx_topcat_reset(ha) == QLA_SUCCESS) && - (qla4010_soft_reset(ha) == QLA_SUCCESS)) - status = QLA_SUCCESS; - return status; - } else - return qla4010_soft_reset(ha); -} - /** * qla4xxx_recover_adapter - recovers adapter after a fatal error * @ha: Pointer to host adapter structure. @@ -1243,7 +1200,6 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev, init_waitqueue_head(&ha->mailbox_wait_queue); spin_lock_init(&ha->hardware_lock); - spin_lock_init(&ha->list_lock); /* Allocate dma buffers */ if (qla4xxx_mem_alloc(ha)) { @@ -1449,27 +1405,6 @@ struct srb * qla4xxx_del_from_active_array(struct scsi_qla_host *ha, uint32_t in return srb; } -/** - * qla4xxx_soft_reset - performs a SOFT RESET of hba. - * @ha: Pointer to host adapter structure. - **/ -int qla4xxx_soft_reset(struct scsi_qla_host *ha) -{ - - DEBUG2(printk(KERN_WARNING "scsi%ld: %s: chip reset!\n", ha->host_no, - __func__)); - if (test_bit(AF_TOPCAT_CHIP_PRESENT, &ha->flags)) { - int status = QLA_ERROR; - - if ((qla4010_soft_reset(ha) == QLA_SUCCESS) && - (qla4xxx_topcat_reset(ha) == QLA_SUCCESS) && - (qla4010_soft_reset(ha) == QLA_SUCCESS) ) - status = QLA_SUCCESS; - return status; - } else - return qla4010_soft_reset(ha); -} - /** * qla4xxx_eh_wait_on_command - waits for command to be returned by firmware * @ha: actual ha whose done queue will contain the comd returned by firmware. @@ -1668,6 +1603,12 @@ static struct pci_device_id qla4xxx_pci_tbl[] = { .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, }, + { + .vendor = PCI_VENDOR_ID_QLOGIC, + .device = PCI_DEVICE_ID_QLOGIC_ISP4032, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, {0, 0}, }; MODULE_DEVICE_TABLE(pci, qla4xxx_pci_tbl); diff --git a/drivers/scsi/qla4xxx/ql4_version.h b/drivers/scsi/qla4xxx/ql4_version.h index d05048b4e88a..454e19c8ad68 100644 --- a/drivers/scsi/qla4xxx/ql4_version.h +++ b/drivers/scsi/qla4xxx/ql4_version.h @@ -5,4 +5,4 @@ * See LICENSE.qla4xxx for copyright and licensing details. */ -#define QLA4XXX_DRIVER_VERSION "5.00.06-k" +#define QLA4XXX_DRIVER_VERSION "5.00.07-k" From b448de473139e40a9ecc4e34946ffec5a65807ec Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Mon, 20 Nov 2006 03:23:49 +0100 Subject: [PATCH 24/73] [SCSI] megaraid_sas: make 2 functions static This patch makes two needlessly global functions static. Signed-off-by: Adrian Bunk Acked-by: "Patro, Sumant" Signed-off-by: James Bottomley --- drivers/scsi/megaraid/megaraid_sas.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/megaraid/megaraid_sas.c b/drivers/scsi/megaraid/megaraid_sas.c index 7e4262f2af96..046223b4ae57 100644 --- a/drivers/scsi/megaraid/megaraid_sas.c +++ b/drivers/scsi/megaraid/megaraid_sas.c @@ -517,7 +517,7 @@ megasas_make_sgl64(struct megasas_instance *instance, struct scsi_cmnd *scp, * Returns the number of frames required for numnber of sge's (sge_count) */ -u32 megasas_get_frame_count(u8 sge_count) +static u32 megasas_get_frame_count(u8 sge_count) { int num_cnt; int sge_bytes; @@ -1733,7 +1733,7 @@ megasas_get_ctrl_info(struct megasas_instance *instance, * * Tasklet to complete cmds */ -void megasas_complete_cmd_dpc(unsigned long instance_addr) +static void megasas_complete_cmd_dpc(unsigned long instance_addr) { u32 producer; u32 consumer; From 7feb6b3fbb48ceaceb7a66a9784106abef48e454 Mon Sep 17 00:00:00 2001 From: David C Somayajulu Date: Mon, 20 Nov 2006 16:20:22 -0800 Subject: [PATCH 25/73] [SCSI] qla4xxx: fix for timing issue for nvram accesses. This patch fixes a timing issue related to nvram accesses in qla4xxx driver for some cpu/slot speed combination. Signed-off-by: David Somayajulu Signed-off-by: James Bottomley --- drivers/scsi/qla4xxx/ql4_nvram.c | 64 ++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/drivers/scsi/qla4xxx/ql4_nvram.c b/drivers/scsi/qla4xxx/ql4_nvram.c index fc5020395407..58afd135aa1d 100644 --- a/drivers/scsi/qla4xxx/ql4_nvram.c +++ b/drivers/scsi/qla4xxx/ql4_nvram.c @@ -7,6 +7,13 @@ #include "ql4_def.h" +static inline void eeprom_cmd(uint32_t cmd, struct scsi_qla_host *ha) +{ + writel(cmd, isp_nvram(ha)); + readl(isp_nvram(ha)); + udelay(1); +} + static inline int eeprom_size(struct scsi_qla_host *ha) { return is_qla4010(ha) ? FM93C66A_SIZE_16 : FM93C86A_SIZE_16; @@ -28,8 +35,7 @@ static int fm93c56a_select(struct scsi_qla_host * ha) DEBUG5(printk(KERN_ERR "fm93c56a_select:\n")); ha->eeprom_cmd_data = AUBURN_EEPROM_CS_1 | 0x000f0000; - writel(ha->eeprom_cmd_data, isp_nvram(ha)); - readl(isp_nvram(ha)); + eeprom_cmd(ha->eeprom_cmd_data, ha); return 1; } @@ -41,12 +47,13 @@ static int fm93c56a_cmd(struct scsi_qla_host * ha, int cmd, int addr) int previousBit; /* Clock in a zero, then do the start bit. */ - writel(ha->eeprom_cmd_data | AUBURN_EEPROM_DO_1, isp_nvram(ha)); - writel(ha->eeprom_cmd_data | AUBURN_EEPROM_DO_1 | - AUBURN_EEPROM_CLK_RISE, isp_nvram(ha)); - writel(ha->eeprom_cmd_data | AUBURN_EEPROM_DO_1 | - AUBURN_EEPROM_CLK_FALL, isp_nvram(ha)); - readl(isp_nvram(ha)); + eeprom_cmd(ha->eeprom_cmd_data | AUBURN_EEPROM_DO_1, ha); + + eeprom_cmd(ha->eeprom_cmd_data | AUBURN_EEPROM_DO_1 | + AUBURN_EEPROM_CLK_RISE, ha); + eeprom_cmd(ha->eeprom_cmd_data | AUBURN_EEPROM_DO_1 | + AUBURN_EEPROM_CLK_FALL, ha); + mask = 1 << (FM93C56A_CMD_BITS - 1); /* Force the previous data bit to be different. */ @@ -60,14 +67,14 @@ static int fm93c56a_cmd(struct scsi_qla_host * ha, int cmd, int addr) * If the bit changed, then change the DO state to * match. */ - writel(ha->eeprom_cmd_data | dataBit, isp_nvram(ha)); + eeprom_cmd(ha->eeprom_cmd_data | dataBit, ha); previousBit = dataBit; } - writel(ha->eeprom_cmd_data | dataBit | - AUBURN_EEPROM_CLK_RISE, isp_nvram(ha)); - writel(ha->eeprom_cmd_data | dataBit | - AUBURN_EEPROM_CLK_FALL, isp_nvram(ha)); - readl(isp_nvram(ha)); + eeprom_cmd(ha->eeprom_cmd_data | dataBit | + AUBURN_EEPROM_CLK_RISE, ha); + eeprom_cmd(ha->eeprom_cmd_data | dataBit | + AUBURN_EEPROM_CLK_FALL, ha); + cmd = cmd << 1; } mask = 1 << (eeprom_no_addr_bits(ha) - 1); @@ -82,14 +89,15 @@ static int fm93c56a_cmd(struct scsi_qla_host * ha, int cmd, int addr) * If the bit changed, then change the DO state to * match. */ - writel(ha->eeprom_cmd_data | dataBit, isp_nvram(ha)); + eeprom_cmd(ha->eeprom_cmd_data | dataBit, ha); + previousBit = dataBit; } - writel(ha->eeprom_cmd_data | dataBit | - AUBURN_EEPROM_CLK_RISE, isp_nvram(ha)); - writel(ha->eeprom_cmd_data | dataBit | - AUBURN_EEPROM_CLK_FALL, isp_nvram(ha)); - readl(isp_nvram(ha)); + eeprom_cmd(ha->eeprom_cmd_data | dataBit | + AUBURN_EEPROM_CLK_RISE, ha); + eeprom_cmd(ha->eeprom_cmd_data | dataBit | + AUBURN_EEPROM_CLK_FALL, ha); + addr = addr << 1; } return 1; @@ -98,8 +106,7 @@ static int fm93c56a_cmd(struct scsi_qla_host * ha, int cmd, int addr) static int fm93c56a_deselect(struct scsi_qla_host * ha) { ha->eeprom_cmd_data = AUBURN_EEPROM_CS_0 | 0x000f0000; - writel(ha->eeprom_cmd_data, isp_nvram(ha)); - readl(isp_nvram(ha)); + eeprom_cmd(ha->eeprom_cmd_data, ha); return 1; } @@ -112,12 +119,13 @@ static int fm93c56a_datain(struct scsi_qla_host * ha, unsigned short *value) /* Read the data bits * The first bit is a dummy. Clock right over it. */ for (i = 0; i < eeprom_no_data_bits(ha); i++) { - writel(ha->eeprom_cmd_data | - AUBURN_EEPROM_CLK_RISE, isp_nvram(ha)); - writel(ha->eeprom_cmd_data | - AUBURN_EEPROM_CLK_FALL, isp_nvram(ha)); - dataBit = - (readw(isp_nvram(ha)) & AUBURN_EEPROM_DI_1) ? 1 : 0; + eeprom_cmd(ha->eeprom_cmd_data | + AUBURN_EEPROM_CLK_RISE, ha); + eeprom_cmd(ha->eeprom_cmd_data | + AUBURN_EEPROM_CLK_FALL, ha); + + dataBit = (readw(isp_nvram(ha)) & AUBURN_EEPROM_DI_1) ? 1 : 0; + data = (data << 1) | dataBit; } From 73d98ff0fa8d9074792b7273f57d9f92810104c6 Mon Sep 17 00:00:00 2001 From: Brian King Date: Tue, 21 Nov 2006 10:27:58 -0600 Subject: [PATCH 26/73] [SCSI] ipr: SATA reset - wait for host reset completion If an ipr adapter hits a fatal microcode error requiring a reset while a SATA device is going through EH, it can result in a command getting issued to the ipr adapter while it is getting reset, which can cause PCI bus errors. Wait for any outstanding adapter reset to finish prior to issuing a SATA device reset. Signed-off-by: James Bottomley --- drivers/scsi/ipr.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 2dde821025f3..c983f0f07584 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -3573,6 +3573,12 @@ static int ipr_sata_reset(struct ata_port *ap, unsigned int *classes) ENTER; spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + while(ioa_cfg->in_reset_reload) { + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload); + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + } + res = sata_port->res; if (res) { rc = ipr_device_reset(ioa_cfg, res); @@ -4776,6 +4782,12 @@ static void ipr_ata_post_internal(struct ata_queued_cmd *qc) unsigned long flags; spin_lock_irqsave(ioa_cfg->host->host_lock, flags); + while(ioa_cfg->in_reset_reload) { + spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags); + wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload); + spin_lock_irqsave(ioa_cfg->host->host_lock, flags); + } + list_for_each_entry(ipr_cmd, &ioa_cfg->pending_q, queue) { if (ipr_cmd->qc == qc) { ipr_device_reset(ioa_cfg, sata_port->res); From 04d9768ff8847b0ae8c41ac3cfb3c5bb2e31699a Mon Sep 17 00:00:00 2001 From: Brian King Date: Tue, 21 Nov 2006 10:28:04 -0600 Subject: [PATCH 27/73] [SCSI] ipr: Stop issuing cancel all to disk arrays The ipr disk array devices do not support a cancel all requests primitive, so change the ipr driver to never send it. Signed-off-by: James Bottomley --- drivers/scsi/ipr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index c983f0f07584..244173adf8b5 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -3776,7 +3776,7 @@ static int ipr_cancel_op(struct scsi_cmnd * scsi_cmd) */ if (ioa_cfg->in_reset_reload || ioa_cfg->ioa_is_dead) return FAILED; - if (!res || (!ipr_is_gscsi(res) && !ipr_is_vset_device(res))) + if (!res || !ipr_is_gscsi(res)) return FAILED; list_for_each_entry(ipr_cmd, &ioa_cfg->pending_q, queue) { From 60e7486b190998745b548599c843f090d5624ae3 Mon Sep 17 00:00:00 2001 From: Brian King Date: Tue, 21 Nov 2006 10:28:10 -0600 Subject: [PATCH 28/73] [SCSI] ipr: PCI IDs for new SAS adapters Adds PCI IDs for some new ipr SAS adapters. Signed-off-by: James Bottomley --- drivers/scsi/ipr.c | 18 +++++++++++++++++- drivers/scsi/ipr.h | 6 ++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 244173adf8b5..f1e01b6ff7c0 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -98,7 +98,7 @@ static DEFINE_SPINLOCK(ipr_driver_lock); /* This table describes the differences between DMA controller chips */ static const struct ipr_chip_cfg_t ipr_chip_cfg[] = { - { /* Gemstone, Citrine, and Obsidian */ + { /* Gemstone, Citrine, Obsidian, and Obsidian-E */ .mailbox = 0x0042C, .cache_line_size = 0x20, { @@ -135,6 +135,7 @@ static const struct ipr_chip_t ipr_chip[] = { { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CITRINE, &ipr_chip_cfg[0] }, { PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN, &ipr_chip_cfg[0] }, { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN, &ipr_chip_cfg[0] }, + { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN_E, &ipr_chip_cfg[0] }, { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_SNIPE, &ipr_chip_cfg[1] }, { PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_SCAMP, &ipr_chip_cfg[1] } }; @@ -7363,12 +7364,24 @@ static struct pci_device_id ipr_pci_table[] __devinitdata = { { PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN, PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_572B, 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] }, + { PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN, + PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_575C, + 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] }, { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN, PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_572A, 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] }, { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN, PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_572B, 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] }, + { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN, + PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_575C, + 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] }, + { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN, + PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57B8, + 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] }, + { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN_E, + PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57B7, + 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] }, { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_SNIPE, PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_2780, 0, 0, (kernel_ulong_t)&ipr_chip_cfg[1] }, @@ -7378,6 +7391,9 @@ static struct pci_device_id ipr_pci_table[] __devinitdata = { { PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_SCAMP, PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_571F, 0, 0, (kernel_ulong_t)&ipr_chip_cfg[1] }, + { PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_SCAMP, + PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_572F, + 0, 0, (kernel_ulong_t)&ipr_chip_cfg[1] }, { } }; MODULE_DEVICE_TABLE(pci, ipr_pci_table); diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h index 6d035283af08..75669251fb63 100644 --- a/drivers/scsi/ipr.h +++ b/drivers/scsi/ipr.h @@ -54,6 +54,8 @@ */ #define IPR_NUM_BASE_CMD_BLKS 100 +#define PCI_DEVICE_ID_IBM_OBSIDIAN_E 0x0339 + #define IPR_SUBS_DEV_ID_2780 0x0264 #define IPR_SUBS_DEV_ID_5702 0x0266 #define IPR_SUBS_DEV_ID_5703 0x0278 @@ -66,7 +68,11 @@ #define IPR_SUBS_DEV_ID_571F 0x02D5 #define IPR_SUBS_DEV_ID_572A 0x02C1 #define IPR_SUBS_DEV_ID_572B 0x02C2 +#define IPR_SUBS_DEV_ID_572F 0x02C3 #define IPR_SUBS_DEV_ID_575B 0x030D +#define IPR_SUBS_DEV_ID_575C 0x0338 +#define IPR_SUBS_DEV_ID_57B7 0x0360 +#define IPR_SUBS_DEV_ID_57B8 0x02C2 #define IPR_NAME "ipr" From efbbdd846d41221396c094ff6df35246bdcf889e Mon Sep 17 00:00:00 2001 From: Brian King Date: Tue, 21 Nov 2006 10:28:16 -0600 Subject: [PATCH 29/73] [SCSI] ipr: Set default ipr Kconfig values Both SCSI_IPR_TRACE and SCSI_IPR_DUMP should be defaulted to yes when SCSI_IPR is enabled. Signed-off-by: Brian King Signed-off-by: James Bottomley --- drivers/scsi/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 9540eb8efdcb..5881464a938d 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1026,6 +1026,7 @@ config SCSI_IPR config SCSI_IPR_TRACE bool "enable driver internal trace" depends on SCSI_IPR + default y help If you say Y here, the driver will trace all commands issued to the adapter. Performance impact is minimal. Trace can be @@ -1034,6 +1035,7 @@ config SCSI_IPR_TRACE config SCSI_IPR_DUMP bool "enable adapter dump support" depends on SCSI_IPR + default y help If you say Y here, the driver will support adapter crash dump. If you enable this support, the iprdump daemon can be used From 7402ecef94f416dcf03af7435c404a4079683d96 Mon Sep 17 00:00:00 2001 From: Brian King Date: Tue, 21 Nov 2006 10:28:23 -0600 Subject: [PATCH 30/73] [SCSI] ipr: Remove ipr_scsi_timed_out Remove ipr's usage of the scsi transport eh_timed_out for handling SATA timeouts. This was only needed in order to set some flags on the qc prior to calling ata_do_eh. Signed-off-by: James Bottomley --- drivers/scsi/ipr.c | 40 ++++------------------------------------ 1 file changed, 4 insertions(+), 36 deletions(-) diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index f1e01b6ff7c0..e32c3f8848ba 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -79,7 +79,6 @@ #include #include #include -#include #include "ipr.h" /* @@ -3643,6 +3642,10 @@ static int __ipr_eh_dev_reset(struct scsi_cmnd * scsi_cmd) if (ipr_cmd->ioarcb.res_handle == res->cfgte.res_handle) { if (ipr_cmd->scsi_cmd) ipr_cmd->done = ipr_scsi_eh_done; + if (ipr_cmd->qc && !(ipr_cmd->qc->flags & ATA_QCFLAG_FAILED)) { + ipr_cmd->qc->err_mask |= AC_ERR_TIMEOUT; + ipr_cmd->qc->flags |= ATA_QCFLAG_FAILED; + } } } @@ -4655,40 +4658,6 @@ static const char * ipr_ioa_info(struct Scsi_Host *host) return buffer; } -/** - * ipr_scsi_timed_out - Handle scsi command timeout - * @scsi_cmd: scsi command struct - * - * Return value: - * EH_NOT_HANDLED - **/ -enum scsi_eh_timer_return ipr_scsi_timed_out(struct scsi_cmnd *scsi_cmd) -{ - struct ipr_ioa_cfg *ioa_cfg; - struct ipr_cmnd *ipr_cmd; - unsigned long flags; - - ENTER; - spin_lock_irqsave(scsi_cmd->device->host->host_lock, flags); - ioa_cfg = (struct ipr_ioa_cfg *)scsi_cmd->device->host->hostdata; - - list_for_each_entry(ipr_cmd, &ioa_cfg->pending_q, queue) { - if (ipr_cmd->qc && ipr_cmd->qc->scsicmd == scsi_cmd) { - ipr_cmd->qc->err_mask |= AC_ERR_TIMEOUT; - ipr_cmd->qc->flags |= ATA_QCFLAG_FAILED; - break; - } - } - - spin_unlock_irqrestore(scsi_cmd->device->host->host_lock, flags); - LEAVE; - return EH_NOT_HANDLED; -} - -static struct scsi_transport_template ipr_transport_template = { - .eh_timed_out = ipr_scsi_timed_out -}; - static struct scsi_host_template driver_template = { .module = THIS_MODULE, .name = "IPR", @@ -7030,7 +6999,6 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev, ioa_cfg = (struct ipr_ioa_cfg *)host->hostdata; memset(ioa_cfg, 0, sizeof(struct ipr_ioa_cfg)); - host->transportt = &ipr_transport_template; ata_host_init(&ioa_cfg->ata_host, &pdev->dev, sata_port_info.flags, &ipr_sata_ops); From 9d66bdf81f97673b6e330a26438fcaea38c26cd5 Mon Sep 17 00:00:00 2001 From: Brian King Date: Tue, 21 Nov 2006 10:28:29 -0600 Subject: [PATCH 31/73] [SCSI] ipr: Remove debug trace points from dump code Remove some debug trace points that clutter up the log when ipr debugging is turned on. Signed-off-by: James Bottomley --- drivers/scsi/ipr.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index e32c3f8848ba..e53cfbd0e076 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -2969,7 +2969,6 @@ static int ipr_alloc_dump(struct ipr_ioa_cfg *ioa_cfg) struct ipr_dump *dump; unsigned long lock_flags = 0; - ENTER; dump = kzalloc(sizeof(struct ipr_dump), GFP_KERNEL); if (!dump) { @@ -2996,7 +2995,6 @@ static int ipr_alloc_dump(struct ipr_ioa_cfg *ioa_cfg) } spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); - LEAVE; return 0; } From 49dc6a18185c12bae4980d17512fbe54ca6bae54 Mon Sep 17 00:00:00 2001 From: Brian King Date: Tue, 21 Nov 2006 10:28:35 -0600 Subject: [PATCH 32/73] [SCSI] ipr: Add support for logging SAS fabric errors Adds support for logging SAS fabric errors logged by the ipr firmware. Signed-off-by: Brian King Signed-off-by: James Bottomley --- drivers/scsi/ipr.c | 225 +++++++++++++++++++++++++++++++++++++++++++-- drivers/scsi/ipr.h | 72 +++++++++++++++ 2 files changed, 290 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index e53cfbd0e076..94345e79ef1e 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -1321,6 +1321,219 @@ static void ipr_log_dual_ioa_error(struct ipr_ioa_cfg *ioa_cfg, offsetof(struct ipr_hostrcb_type_07_error, data))); } +static const struct { + u8 active; + char *desc; +} path_active_desc[] = { + { IPR_PATH_NO_INFO, "Path" }, + { IPR_PATH_ACTIVE, "Active path" }, + { IPR_PATH_NOT_ACTIVE, "Inactive path" } +}; + +static const struct { + u8 state; + char *desc; +} path_state_desc[] = { + { IPR_PATH_STATE_NO_INFO, "has no path state information available" }, + { IPR_PATH_HEALTHY, "is healthy" }, + { IPR_PATH_DEGRADED, "is degraded" }, + { IPR_PATH_FAILED, "is failed" } +}; + +/** + * ipr_log_fabric_path - Log a fabric path error + * @hostrcb: hostrcb struct + * @fabric: fabric descriptor + * + * Return value: + * none + **/ +static void ipr_log_fabric_path(struct ipr_hostrcb *hostrcb, + struct ipr_hostrcb_fabric_desc *fabric) +{ + int i, j; + u8 path_state = fabric->path_state; + u8 active = path_state & IPR_PATH_ACTIVE_MASK; + u8 state = path_state & IPR_PATH_STATE_MASK; + + for (i = 0; i < ARRAY_SIZE(path_active_desc); i++) { + if (path_active_desc[i].active != active) + continue; + + for (j = 0; j < ARRAY_SIZE(path_state_desc); j++) { + if (path_state_desc[j].state != state) + continue; + + if (fabric->cascaded_expander == 0xff && fabric->phy == 0xff) { + ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d\n", + path_active_desc[i].desc, path_state_desc[j].desc, + fabric->ioa_port); + } else if (fabric->cascaded_expander == 0xff) { + ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d, Phy=%d\n", + path_active_desc[i].desc, path_state_desc[j].desc, + fabric->ioa_port, fabric->phy); + } else if (fabric->phy == 0xff) { + ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d, Cascade=%d\n", + path_active_desc[i].desc, path_state_desc[j].desc, + fabric->ioa_port, fabric->cascaded_expander); + } else { + ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d, Cascade=%d, Phy=%d\n", + path_active_desc[i].desc, path_state_desc[j].desc, + fabric->ioa_port, fabric->cascaded_expander, fabric->phy); + } + return; + } + } + + ipr_err("Path state=%02X IOA Port=%d Cascade=%d Phy=%d\n", path_state, + fabric->ioa_port, fabric->cascaded_expander, fabric->phy); +} + +static const struct { + u8 type; + char *desc; +} path_type_desc[] = { + { IPR_PATH_CFG_IOA_PORT, "IOA port" }, + { IPR_PATH_CFG_EXP_PORT, "Expander port" }, + { IPR_PATH_CFG_DEVICE_PORT, "Device port" }, + { IPR_PATH_CFG_DEVICE_LUN, "Device LUN" } +}; + +static const struct { + u8 status; + char *desc; +} path_status_desc[] = { + { IPR_PATH_CFG_NO_PROB, "Functional" }, + { IPR_PATH_CFG_DEGRADED, "Degraded" }, + { IPR_PATH_CFG_FAILED, "Failed" }, + { IPR_PATH_CFG_SUSPECT, "Suspect" }, + { IPR_PATH_NOT_DETECTED, "Missing" }, + { IPR_PATH_INCORRECT_CONN, "Incorrectly connected" } +}; + +static const char *link_rate[] = { + "unknown", + "disabled", + "phy reset problem", + "spinup hold", + "port selector", + "unknown", + "unknown", + "unknown", + "1.5Gbps", + "3.0Gbps", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown" +}; + +/** + * ipr_log_path_elem - Log a fabric path element. + * @hostrcb: hostrcb struct + * @cfg: fabric path element struct + * + * Return value: + * none + **/ +static void ipr_log_path_elem(struct ipr_hostrcb *hostrcb, + struct ipr_hostrcb_config_element *cfg) +{ + int i, j; + u8 type = cfg->type_status & IPR_PATH_CFG_TYPE_MASK; + u8 status = cfg->type_status & IPR_PATH_CFG_STATUS_MASK; + + if (type == IPR_PATH_CFG_NOT_EXIST) + return; + + for (i = 0; i < ARRAY_SIZE(path_type_desc); i++) { + if (path_type_desc[i].type != type) + continue; + + for (j = 0; j < ARRAY_SIZE(path_status_desc); j++) { + if (path_status_desc[j].status != status) + continue; + + if (type == IPR_PATH_CFG_IOA_PORT) { + ipr_hcam_err(hostrcb, "%s %s: Phy=%d, Link rate=%s, WWN=%08X%08X\n", + path_status_desc[j].desc, path_type_desc[i].desc, + cfg->phy, link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK], + be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1])); + } else { + if (cfg->cascaded_expander == 0xff && cfg->phy == 0xff) { + ipr_hcam_err(hostrcb, "%s %s: Link rate=%s, WWN=%08X%08X\n", + path_status_desc[j].desc, path_type_desc[i].desc, + link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK], + be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1])); + } else if (cfg->cascaded_expander == 0xff) { + ipr_hcam_err(hostrcb, "%s %s: Phy=%d, Link rate=%s, " + "WWN=%08X%08X\n", path_status_desc[j].desc, + path_type_desc[i].desc, cfg->phy, + link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK], + be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1])); + } else if (cfg->phy == 0xff) { + ipr_hcam_err(hostrcb, "%s %s: Cascade=%d, Link rate=%s, " + "WWN=%08X%08X\n", path_status_desc[j].desc, + path_type_desc[i].desc, cfg->cascaded_expander, + link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK], + be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1])); + } else { + ipr_hcam_err(hostrcb, "%s %s: Cascade=%d, Phy=%d, Link rate=%s " + "WWN=%08X%08X\n", path_status_desc[j].desc, + path_type_desc[i].desc, cfg->cascaded_expander, cfg->phy, + link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK], + be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1])); + } + } + return; + } + } + + ipr_hcam_err(hostrcb, "Path element=%02X: Cascade=%d Phy=%d Link rate=%s " + "WWN=%08X%08X\n", cfg->type_status, cfg->cascaded_expander, cfg->phy, + link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK], + be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1])); +} + +/** + * ipr_log_fabric_error - Log a fabric error. + * @ioa_cfg: ioa config struct + * @hostrcb: hostrcb struct + * + * Return value: + * none + **/ +static void ipr_log_fabric_error(struct ipr_ioa_cfg *ioa_cfg, + struct ipr_hostrcb *hostrcb) +{ + struct ipr_hostrcb_type_20_error *error; + struct ipr_hostrcb_fabric_desc *fabric; + struct ipr_hostrcb_config_element *cfg; + int i, add_len; + + error = &hostrcb->hcam.u.error.u.type_20_error; + error->failure_reason[sizeof(error->failure_reason) - 1] = '\0'; + ipr_hcam_err(hostrcb, "%s\n", error->failure_reason); + + add_len = be32_to_cpu(hostrcb->hcam.length) - + (offsetof(struct ipr_hostrcb_error, u) + + offsetof(struct ipr_hostrcb_type_20_error, desc)); + + for (i = 0, fabric = error->desc; i < error->num_entries; i++) { + ipr_log_fabric_path(hostrcb, fabric); + for_each_fabric_cfg(fabric, cfg) + ipr_log_path_elem(hostrcb, cfg); + + add_len -= be16_to_cpu(fabric->length); + fabric = (struct ipr_hostrcb_fabric_desc *) + ((unsigned long)fabric + be16_to_cpu(fabric->length)); + } + + ipr_log_hex_data((u32 *)fabric, add_len); +} + /** * ipr_log_generic_error - Log an adapter error. * @ioa_cfg: ioa config struct @@ -1394,13 +1607,7 @@ static void ipr_handle_log_data(struct ipr_ioa_cfg *ioa_cfg, if (!ipr_error_table[error_index].log_hcam) return; - if (ipr_is_device(&hostrcb->hcam.u.error.failing_dev_res_addr)) { - ipr_ra_err(ioa_cfg, hostrcb->hcam.u.error.failing_dev_res_addr, - "%s\n", ipr_error_table[error_index].error); - } else { - dev_err(&ioa_cfg->pdev->dev, "%s\n", - ipr_error_table[error_index].error); - } + ipr_hcam_err(hostrcb, "%s\n", ipr_error_table[error_index].error); /* Set indication we have logged an error */ ioa_cfg->errors_logged++; @@ -1437,6 +1644,9 @@ static void ipr_handle_log_data(struct ipr_ioa_cfg *ioa_cfg, case IPR_HOST_RCB_OVERLAY_ID_17: ipr_log_enhanced_dual_ioa_error(ioa_cfg, hostrcb); break; + case IPR_HOST_RCB_OVERLAY_ID_20: + ipr_log_fabric_error(ioa_cfg, hostrcb); + break; case IPR_HOST_RCB_OVERLAY_ID_1: case IPR_HOST_RCB_OVERLAY_ID_DEFAULT: default: @@ -6812,6 +7022,7 @@ static int __devinit ipr_alloc_mem(struct ipr_ioa_cfg *ioa_cfg) ioa_cfg->hostrcb[i]->hostrcb_dma = ioa_cfg->hostrcb_dma[i] + offsetof(struct ipr_hostrcb, hcam); + ioa_cfg->hostrcb[i]->ioa_cfg = ioa_cfg; list_add_tail(&ioa_cfg->hostrcb[i]->queue, &ioa_cfg->hostrcb_free_q); } diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h index 75669251fb63..44b15e4b6edf 100644 --- a/drivers/scsi/ipr.h +++ b/drivers/scsi/ipr.h @@ -737,6 +737,64 @@ struct ipr_hostrcb_type_17_error { u32 data[476]; }__attribute__((packed, aligned (4))); +struct ipr_hostrcb_config_element { + u8 type_status; +#define IPR_PATH_CFG_TYPE_MASK 0xF0 +#define IPR_PATH_CFG_NOT_EXIST 0x00 +#define IPR_PATH_CFG_IOA_PORT 0x10 +#define IPR_PATH_CFG_EXP_PORT 0x20 +#define IPR_PATH_CFG_DEVICE_PORT 0x30 +#define IPR_PATH_CFG_DEVICE_LUN 0x40 + +#define IPR_PATH_CFG_STATUS_MASK 0x0F +#define IPR_PATH_CFG_NO_PROB 0x00 +#define IPR_PATH_CFG_DEGRADED 0x01 +#define IPR_PATH_CFG_FAILED 0x02 +#define IPR_PATH_CFG_SUSPECT 0x03 +#define IPR_PATH_NOT_DETECTED 0x04 +#define IPR_PATH_INCORRECT_CONN 0x05 + + u8 cascaded_expander; + u8 phy; + u8 link_rate; +#define IPR_PHY_LINK_RATE_MASK 0x0F + + __be32 wwid[2]; +}__attribute__((packed, aligned (4))); + +struct ipr_hostrcb_fabric_desc { + __be16 length; + u8 ioa_port; + u8 cascaded_expander; + u8 phy; + u8 path_state; +#define IPR_PATH_ACTIVE_MASK 0xC0 +#define IPR_PATH_NO_INFO 0x00 +#define IPR_PATH_ACTIVE 0x40 +#define IPR_PATH_NOT_ACTIVE 0x80 + +#define IPR_PATH_STATE_MASK 0x0F +#define IPR_PATH_STATE_NO_INFO 0x00 +#define IPR_PATH_HEALTHY 0x01 +#define IPR_PATH_DEGRADED 0x02 +#define IPR_PATH_FAILED 0x03 + + __be16 num_entries; + struct ipr_hostrcb_config_element elem[1]; +}__attribute__((packed, aligned (4))); + +#define for_each_fabric_cfg(fabric, cfg) \ + for (cfg = (fabric)->elem; \ + cfg < ((fabric)->elem + be16_to_cpu((fabric)->num_entries)); \ + cfg++) + +struct ipr_hostrcb_type_20_error { + u8 failure_reason[64]; + u8 reserved[3]; + u8 num_entries; + struct ipr_hostrcb_fabric_desc desc[1]; +}__attribute__((packed, aligned (4))); + struct ipr_hostrcb_error { __be32 failing_dev_ioasc; struct ipr_res_addr failing_dev_res_addr; @@ -753,6 +811,7 @@ struct ipr_hostrcb_error { struct ipr_hostrcb_type_13_error type_13_error; struct ipr_hostrcb_type_14_error type_14_error; struct ipr_hostrcb_type_17_error type_17_error; + struct ipr_hostrcb_type_20_error type_20_error; } u; }__attribute__((packed, aligned (4))); @@ -792,6 +851,7 @@ struct ipr_hcam { #define IPR_HOST_RCB_OVERLAY_ID_14 0x14 #define IPR_HOST_RCB_OVERLAY_ID_16 0x16 #define IPR_HOST_RCB_OVERLAY_ID_17 0x17 +#define IPR_HOST_RCB_OVERLAY_ID_20 0x20 #define IPR_HOST_RCB_OVERLAY_ID_DEFAULT 0xFF u8 reserved1[3]; @@ -811,6 +871,7 @@ struct ipr_hostrcb { struct ipr_hcam hcam; dma_addr_t hostrcb_dma; struct list_head queue; + struct ipr_ioa_cfg *ioa_cfg; }; /* IPR smart dump table structures */ @@ -1289,6 +1350,17 @@ struct ipr_ucode_image_header { } \ } +#define ipr_hcam_err(hostrcb, fmt, ...) \ +{ \ + if (ipr_is_device(&(hostrcb)->hcam.u.error.failing_dev_res_addr)) { \ + ipr_ra_err((hostrcb)->ioa_cfg, \ + (hostrcb)->hcam.u.error.failing_dev_res_addr, \ + fmt, ##__VA_ARGS__); \ + } else { \ + dev_err(&(hostrcb)->ioa_cfg->pdev->dev, fmt, ##__VA_ARGS__); \ + } \ +} + #define ipr_trace ipr_dbg("%s: %s: Line: %d\n",\ __FILE__, __FUNCTION__, __LINE__) From ac719abab0fbe2b83dc99351f6605645aef814ab Mon Sep 17 00:00:00 2001 From: Brian King Date: Tue, 21 Nov 2006 10:28:42 -0600 Subject: [PATCH 33/73] [SCSI] ipr: Reduce default error log size Since the default error log size has increased on SAS adapters, prevent ipr from logging this additional data unless requested to do so by the user set log level in order to prevent flooding the logs. Signed-off-by: Brian King Signed-off-by: James Bottomley --- drivers/scsi/ipr.c | 14 +++++++++----- drivers/scsi/ipr.h | 1 + 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 94345e79ef1e..3c32e08e71c1 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -1249,19 +1249,23 @@ static void ipr_log_array_error(struct ipr_ioa_cfg *ioa_cfg, /** * ipr_log_hex_data - Log additional hex IOA error data. + * @ioa_cfg: ioa config struct * @data: IOA error data * @len: data length * * Return value: * none **/ -static void ipr_log_hex_data(u32 *data, int len) +static void ipr_log_hex_data(struct ipr_ioa_cfg *ioa_cfg, u32 *data, int len) { int i; if (len == 0) return; + if (ioa_cfg->log_level <= IPR_DEFAULT_LOG_LEVEL) + len = min_t(int, len, IPR_DEFAULT_MAX_ERROR_DUMP); + for (i = 0; i < len / 4; i += 4) { ipr_err("%08X: %08X %08X %08X %08X\n", i*4, be32_to_cpu(data[i]), @@ -1290,7 +1294,7 @@ static void ipr_log_enhanced_dual_ioa_error(struct ipr_ioa_cfg *ioa_cfg, ipr_err("%s\n", error->failure_reason); ipr_err("Remote Adapter VPD:\n"); ipr_log_ext_vpd(&error->vpd); - ipr_log_hex_data(error->data, + ipr_log_hex_data(ioa_cfg, error->data, be32_to_cpu(hostrcb->hcam.length) - (offsetof(struct ipr_hostrcb_error, u) + offsetof(struct ipr_hostrcb_type_17_error, data))); @@ -1315,7 +1319,7 @@ static void ipr_log_dual_ioa_error(struct ipr_ioa_cfg *ioa_cfg, ipr_err("%s\n", error->failure_reason); ipr_err("Remote Adapter VPD:\n"); ipr_log_vpd(&error->vpd); - ipr_log_hex_data(error->data, + ipr_log_hex_data(ioa_cfg, error->data, be32_to_cpu(hostrcb->hcam.length) - (offsetof(struct ipr_hostrcb_error, u) + offsetof(struct ipr_hostrcb_type_07_error, data))); @@ -1531,7 +1535,7 @@ static void ipr_log_fabric_error(struct ipr_ioa_cfg *ioa_cfg, ((unsigned long)fabric + be16_to_cpu(fabric->length)); } - ipr_log_hex_data((u32 *)fabric, add_len); + ipr_log_hex_data(ioa_cfg, (u32 *)fabric, add_len); } /** @@ -1545,7 +1549,7 @@ static void ipr_log_fabric_error(struct ipr_ioa_cfg *ioa_cfg, static void ipr_log_generic_error(struct ipr_ioa_cfg *ioa_cfg, struct ipr_hostrcb *hostrcb) { - ipr_log_hex_data(hostrcb->hcam.u.raw.data, + ipr_log_hex_data(ioa_cfg, hostrcb->hcam.u.raw.data, be32_to_cpu(hostrcb->hcam.length)); } diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h index 44b15e4b6edf..397e502bd0f1 100644 --- a/drivers/scsi/ipr.h +++ b/drivers/scsi/ipr.h @@ -104,6 +104,7 @@ #define IPR_IOASC_IOA_WAS_RESET 0x10000001 #define IPR_IOASC_PCI_ACCESS_ERROR 0x10000002 +#define IPR_DEFAULT_MAX_ERROR_DUMP 984 #define IPR_NUM_LOG_HCAMS 2 #define IPR_NUM_CFG_CHG_HCAMS 2 #define IPR_NUM_HCAMS (IPR_NUM_LOG_HCAMS + IPR_NUM_CFG_CHG_HCAMS) From bd705f2d43d34c0309dfa48117bed7167024247b Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 21 Nov 2006 10:28:48 -0600 Subject: [PATCH 34/73] [SCSI] ipr: Make ipr_ioctl static This patch makes ipr_ioctl static. Signed-off-by: Adrian Bunk Signed-off-by: Brian King Signed-off-by: James Bottomley --- drivers/scsi/ipr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 3c32e08e71c1..2d83fbb806a5 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -4837,7 +4837,7 @@ static int ipr_queuecommand(struct scsi_cmnd *scsi_cmd, * Return value: * 0 on success / other on failure **/ -int ipr_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) +static int ipr_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) { struct ipr_resource_entry *res; From 51d23da95c3b71ce70587af8fc8244a05722c191 Mon Sep 17 00:00:00 2001 From: Brian King Date: Tue, 21 Nov 2006 10:28:55 -0600 Subject: [PATCH 35/73] [SCSI] ipr: Driver version 2.3.0 Bump driver version. Signed-off-by: James Bottomley --- drivers/scsi/ipr.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h index 397e502bd0f1..9f62a1d4d511 100644 --- a/drivers/scsi/ipr.h +++ b/drivers/scsi/ipr.h @@ -37,8 +37,8 @@ /* * Literals */ -#define IPR_DRIVER_VERSION "2.2.0" -#define IPR_DRIVER_DATE "(September 25, 2006)" +#define IPR_DRIVER_VERSION "2.3.0" +#define IPR_DRIVER_DATE "(November 8, 2006)" /* * IPR_MAX_CMD_PER_LUN: This defines the maximum number of outstanding From 33524b70e8f3dd55a4ba78ad81742c7814e7b0ed Mon Sep 17 00:00:00 2001 From: Mark Haverkamp Date: Tue, 21 Nov 2006 10:40:08 -0800 Subject: [PATCH 36/73] [SCSI] aacraid: Detect Blinkled at startup Received from Mark Salyzyn: Blinkled at startup is useful for catching Adapters in a lot of pain, in a BlinkLED assert, quickly; rather than waiting several minutes for commands to timeout. Signed-off-by: Mark Haverkamp Signed-off-by: James Bottomley --- drivers/scsi/aacraid/commsup.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c index 19e42ac07cb2..0fd462a876f9 100644 --- a/drivers/scsi/aacraid/commsup.c +++ b/drivers/scsi/aacraid/commsup.c @@ -518,6 +518,7 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size, */ unsigned long count = 36000000L; /* 3 minutes */ while (down_trylock(&fibptr->event_wait)) { + int blink; if (--count == 0) { spin_lock_irqsave(q->lock, qflags); q->numpending--; @@ -530,6 +531,14 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size, } return -ETIMEDOUT; } + if ((blink = aac_adapter_check_health(dev)) > 0) { + if (wait == -1) { + printk(KERN_ERR "aacraid: aac_fib_send: adapter blinkLED 0x%x.\n" + "Usually a result of a serious unrecoverable hardware problem\n", + blink); + } + return -EFAULT; + } udelay(5); } } else if (down_interruptible(&fibptr->event_wait)) { From d18b448fc2caf0d719bd4bd34fb1856be89c8ef7 Mon Sep 17 00:00:00 2001 From: Mark Haverkamp Date: Tue, 21 Nov 2006 10:40:31 -0800 Subject: [PATCH 37/73] [SCSI] aacraid: Abort management FIBs Received from Mark Salyzyn: Add code to abort outstanding management ioctl fibs when the blinkLED recovery is performed. This code is 'clunky' and does not have any real feedback in that the reset could progress before the user application has gotten it's notification of command completion. We put a schedule() call to delay just the right amount for most cases, because we tried a spin and still managed to find cases where we would spin forever waiting for the management application to acknowledge the impending doom surrounding the cause of the BlinkLED. Will cause an oops in the context of the management application if we proceed too quickly. I view this as the lesser of many evils since currently if there are outstanding management ioctls during a need to reset/recover the adapter, the management application just locks up and waits forever. The best practices fix for this problem not going to be simple or easy (at least the fixes I imagine today); and we found a balance between the needs of the driver to proceed, and the applications that locked or confused that would hold back the driver. I just do not like the idea of a kernel oops in an application to deal with low priority, sluggish or misbehaving applications. Signed-off-by: Mark Haverkamp Signed-off-by: James Bottomley --- drivers/scsi/aacraid/commsup.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c index 0fd462a876f9..4893a6d06a33 100644 --- a/drivers/scsi/aacraid/commsup.c +++ b/drivers/scsi/aacraid/commsup.c @@ -1102,6 +1102,20 @@ static int _aac_reset_adapter(struct aac_dev *aac) goto out; } + /* + * Loop through the fibs, close the synchronous FIBS + */ + for (index = 0; index < (aac->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB); index++) { + struct fib *fib = &aac->fibs[index]; + if (!(fib->hw_fib->header.XferState & cpu_to_le32(NoResponseExpected | Async)) && + (fib->hw_fib->header.XferState & cpu_to_le32(ResponseExpected))) { + unsigned long flagv; + spin_lock_irqsave(&fib->event_lock, flagv); + up(&fib->event_wait); + spin_unlock_irqrestore(&fib->event_lock, flagv); + schedule(); + } + } index = aac->cardtype; /* From 902762831c0068ba0c8c8fca801e505a9881aa01 Mon Sep 17 00:00:00 2001 From: Mark Haverkamp Date: Tue, 21 Nov 2006 10:40:50 -0800 Subject: [PATCH 38/73] [SCSI] aacraid: Driver version update Received from Mark Salyzyn: Version patch, update to reflect a rough estimate of the Adaptec build (2423) that coincides with the sources on kernel.org. Signed-off-by: Mark Haverkamp Signed-off-by: James Bottomley --- drivers/scsi/aacraid/aacraid.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h index eb3ed91bac79..4f8b4c53d435 100644 --- a/drivers/scsi/aacraid/aacraid.h +++ b/drivers/scsi/aacraid/aacraid.h @@ -11,8 +11,8 @@ *----------------------------------------------------------------------------*/ #ifndef AAC_DRIVER_BUILD -# define AAC_DRIVER_BUILD 2409 -# define AAC_DRIVER_BRANCH "-mh2" +# define AAC_DRIVER_BUILD 2423 +# define AAC_DRIVER_BRANCH "-mh3" #endif #define MAXIMUM_NUM_CONTAINERS 32 From 21db1882f79a1ad5977cae6766376a63f60ec414 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 22 Nov 2006 13:24:52 -0700 Subject: [PATCH 39/73] [SCSI] Add Kconfig option for asynchronous SCSI scanning Without this patch, the user has to add a kernel command line parameter to get asynchronous SCSI scanning. Now they can select the default at compile time and still override it at boot time if they need to. Signed-off-by: Matthew Wilcox Signed-off-by: James Bottomley --- drivers/scsi/Kconfig | 17 +++++++++++++++++ drivers/scsi/scsi_scan.c | 8 +++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 5881464a938d..8442be1bb162 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -216,6 +216,23 @@ config SCSI_LOGGING there should be no noticeable performance impact as long as you have logging turned off. +config SCSI_SCAN_ASYNC + bool "Asynchronous SCSI scanning" + depends on SCSI + help + The SCSI subsystem can probe for devices while the rest of the + system continues booting, and even probe devices on different + busses in parallel, leading to a significant speed-up. + If you have built SCSI as modules, enabling this option can + be a problem as the devices may not have been found by the + time your system expects them to have been. You can load the + scsi_wait_scan module to ensure that all scans have completed. + If you build your SCSI drivers into the kernel, then everything + will work fine if you say Y here. + + You can override this choice by specifying scsi_mod.scan="sync" + or "async" on the kernel's command line. + menu "SCSI Transports" depends on SCSI diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index aa1b1e0e9d22..d91d268dd331 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -89,7 +89,13 @@ module_param_named(max_luns, max_scsi_luns, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(max_luns, "last scsi LUN (should be between 1 and 2^32-1)"); -static char scsi_scan_type[6] = "sync"; +#ifdef CONFIG_SCSI_SCAN_ASYNC +#define SCSI_SCAN_TYPE_DEFAULT "async" +#else +#define SCSI_SCAN_TYPE_DEFAULT "sync" +#endif + +static char scsi_scan_type[6] = SCSI_SCAN_TYPE_DEFAULT; module_param_string(scan, scsi_scan_type, sizeof(scsi_scan_type), S_IRUGO); MODULE_PARM_DESC(scan, "sync, async or none"); From 93b45af5c6b9d41bbe9da11442291ac4eefc15b4 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 22 Nov 2006 13:24:53 -0700 Subject: [PATCH 40/73] [SCSI] fix missing check for no scanning Drivers that called scsi_scan_target() instead of scsi_scan_host() were still adding devices; this needs to be under the control of userspace, not the driver. Signed-off-by: Matthew Wilcox Signed-off-by: James Bottomley --- drivers/scsi/scsi_scan.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index d91d268dd331..3ccaa4be92d8 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -1549,6 +1549,9 @@ void scsi_scan_target(struct device *parent, unsigned int channel, { struct Scsi_Host *shost = dev_to_shost(parent); + if (strncmp(scsi_scan_type, "none", 4) == 0) + return; + if (!shost->async_scan) scsi_complete_async_scans(); From 1aa8fab2acf1cb8b341131b726773fcff0abc707 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 22 Nov 2006 13:24:54 -0700 Subject: [PATCH 41/73] [SCSI] Make scsi_scan_host work for drivers which find their own targets If a driver can find its own targets, it can now fill in scan_finished and (optionally) scan_start in the scsi_host_template. Then, when it calls scsi_scan_host(), it will be called back (from a thread if asynchronous discovery is enabled), first to start the scan, and then at intervals to check if the scan is completed. Also make scsi_prep_async_scan and scsi_finish_async_scan static. Signed-off-by: Matthew Wilcox Signed-off-by: James Bottomley --- drivers/scsi/scsi_scan.c | 27 ++++++++++++++++++++------- include/scsi/scsi_host.h | 18 ++++++++++++++++++ 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 3ccaa4be92d8..4d656148bd67 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -1642,7 +1642,7 @@ static void scsi_sysfs_add_devices(struct Scsi_Host *shost) * that other asynchronous scans started after this one won't affect the * ordering of the discovered devices. */ -struct async_scan_data *scsi_prep_async_scan(struct Scsi_Host *shost) +static struct async_scan_data *scsi_prep_async_scan(struct Scsi_Host *shost) { struct async_scan_data *data; @@ -1686,7 +1686,7 @@ struct async_scan_data *scsi_prep_async_scan(struct Scsi_Host *shost) * This function announces all the devices it has found to the rest * of the system. */ -void scsi_finish_async_scan(struct async_scan_data *data) +static void scsi_finish_async_scan(struct async_scan_data *data) { struct Scsi_Host *shost; @@ -1719,12 +1719,25 @@ void scsi_finish_async_scan(struct async_scan_data *data) kfree(data); } +static void do_scsi_scan_host(struct Scsi_Host *shost) +{ + if (shost->hostt->scan_finished) { + unsigned long start = jiffies; + if (shost->hostt->scan_start) + shost->hostt->scan_start(shost); + + while (!shost->hostt->scan_finished(shost, jiffies - start)) + msleep(10); + } else { + scsi_scan_host_selected(shost, SCAN_WILD_CARD, SCAN_WILD_CARD, + SCAN_WILD_CARD, 0); + } +} + static int do_scan_async(void *_data) { struct async_scan_data *data = _data; - scsi_scan_host_selected(data->shost, SCAN_WILD_CARD, SCAN_WILD_CARD, - SCAN_WILD_CARD, 0); - + do_scsi_scan_host(data->shost); scsi_finish_async_scan(data); return 0; } @@ -1742,10 +1755,10 @@ void scsi_scan_host(struct Scsi_Host *shost) data = scsi_prep_async_scan(shost); if (!data) { - scsi_scan_host_selected(shost, SCAN_WILD_CARD, SCAN_WILD_CARD, - SCAN_WILD_CARD, 0); + do_scsi_scan_host(shost); return; } + kthread_run(do_scan_async, data, "scsi_scan_%d", shost->host_no); } EXPORT_SYMBOL(scsi_scan_host); diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index e618e711ea57..6d8945d71c65 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -240,6 +240,24 @@ struct scsi_host_template { */ void (* target_destroy)(struct scsi_target *); + /* + * If a host has the ability to discover targets on its own instead + * of scanning the entire bus, it can fill in this function and + * call scsi_scan_host(). This function will be called periodically + * until it returns 1 with the scsi_host and the elapsed time of + * the scan in jiffies. + * + * Status: OPTIONAL + */ + int (* scan_finished)(struct Scsi_Host *, unsigned long); + + /* + * If the host wants to be called before the scan starts, but + * after the midlayer has set up ready for the scan, it can fill + * in this function. + */ + void (* scan_start)(struct Scsi_Host *); + /* * fill in this function to allow the queue depth of this host * to be changeable (on a per device basis). returns either From d19044c32baadeb80e135027124a9e845c6f057c Mon Sep 17 00:00:00 2001 From: Andrew Vasquez Date: Wed, 22 Nov 2006 08:22:19 -0800 Subject: [PATCH 42/73] [SCSI] qla2xxx: defer topology discovery to DPC thread during initialization. Modify intialization semantics: - perform basic hardware configuration only (as usual) - allocate resources - load and execute firmware - defer link (transport) negotiations to the DPC thread - again the code in qla2x00_initialize_adapter() to stall probe() completion was needed for legacy-style scanning. - DPC thread stalls until probe() complete. - before probe() completes, set DPC flags to perform loop-resync logic (similar to what's done during cable-insertion/removal). Benefits: user does not have to wait 20+ seconds in case the FC cable is unplugged during driver load, code consolidation (removal of redundant link negotiation logic during initialize_adaoter()), and finilly, the driver no longer needs to defer the fc_remote_port_add() calls to hold off lun-scanning prior to returning from the probe() function. Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_init.c | 94 ++++----------------------------- drivers/scsi/qla2xxx/qla_os.c | 29 +++------- 2 files changed, 16 insertions(+), 107 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 08cb5e3fb553..a823f0bc519d 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -59,9 +59,6 @@ int qla2x00_initialize_adapter(scsi_qla_host_t *ha) { int rval; - uint8_t restart_risc = 0; - uint8_t retry; - uint32_t wait_time; /* Clear adapter flags. */ ha->flags.online = 0; @@ -104,87 +101,15 @@ qla2x00_initialize_adapter(scsi_qla_host_t *ha) qla_printk(KERN_INFO, ha, "Verifying loaded RISC code...\n"); - retry = 10; - /* - * Try to configure the loop. - */ - do { - restart_risc = 0; - - /* If firmware needs to be loaded */ - if (qla2x00_isp_firmware(ha) != QLA_SUCCESS) { - if ((rval = ha->isp_ops.chip_diag(ha)) == QLA_SUCCESS) { - rval = qla2x00_setup_chip(ha); - } - } - - if (rval == QLA_SUCCESS && - (rval = qla2x00_init_rings(ha)) == QLA_SUCCESS) { -check_fw_ready_again: - /* - * Wait for a successful LIP up to a maximum - * of (in seconds): RISC login timeout value, - * RISC retry count value, and port down retry - * value OR a minimum of 4 seconds OR If no - * cable, only 5 seconds. - */ - rval = qla2x00_fw_ready(ha); - if (rval == QLA_SUCCESS) { - clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags); - - /* Issue a marker after FW becomes ready. */ - qla2x00_marker(ha, 0, 0, MK_SYNC_ALL); - - /* - * Wait at most MAX_TARGET RSCNs for a stable - * link. - */ - wait_time = 256; - do { - clear_bit(LOOP_RESYNC_NEEDED, - &ha->dpc_flags); - rval = qla2x00_configure_loop(ha); - - if (test_and_clear_bit(ISP_ABORT_NEEDED, - &ha->dpc_flags)) { - restart_risc = 1; - break; - } - - /* - * If loop state change while we were - * discoverying devices then wait for - * LIP to complete - */ - - if (atomic_read(&ha->loop_state) != - LOOP_READY && retry--) { - goto check_fw_ready_again; - } - wait_time--; - } while (!atomic_read(&ha->loop_down_timer) && - retry && - wait_time && - (test_bit(LOOP_RESYNC_NEEDED, - &ha->dpc_flags))); - - if (wait_time == 0) - rval = QLA_FUNCTION_FAILED; - } else if (ha->device_flags & DFLG_NO_CABLE) - /* If no cable, then all is good. */ - rval = QLA_SUCCESS; - } - } while (restart_risc && retry--); - - if (rval == QLA_SUCCESS) { - clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags); - qla2x00_marker(ha, 0, 0, MK_SYNC_ALL); - ha->marker_needed = 0; - - ha->flags.online = 1; - } else { - DEBUG2_3(printk("%s(): **** FAILED ****\n", __func__)); + if (qla2x00_isp_firmware(ha) != QLA_SUCCESS) { + rval = ha->isp_ops.chip_diag(ha); + if (rval) + return (rval); + rval = qla2x00_setup_chip(ha); + if (rval) + return (rval); } + rval = qla2x00_init_rings(ha); return (rval); } @@ -2208,8 +2133,7 @@ qla2x00_update_fcport(scsi_qla_host_t *ha, fc_port_t *fcport) atomic_set(&fcport->state, FCS_ONLINE); - if (ha->flags.init_done) - qla2x00_reg_remote_port(ha, fcport); + qla2x00_reg_remote_port(ha, fcport); } void diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 3eb4cd2cbc78..1cc8febcbadb 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -1377,10 +1377,8 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) struct Scsi_Host *host; scsi_qla_host_t *ha; unsigned long flags = 0; - unsigned long wait_switch = 0; char pci_info[20]; char fw_str[30]; - fc_port_t *fcport; struct scsi_host_template *sht; if (pci_enable_device(pdev)) @@ -1631,24 +1629,15 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->isp_ops.enable_intrs(ha); - /* v2.19.5b6 */ - /* - * Wait around max loop_reset_delay secs for the devices to come - * on-line. We don't want Linux scanning before we are ready. - * - */ - for (wait_switch = jiffies + (ha->loop_reset_delay * HZ); - time_before(jiffies,wait_switch) && - !(ha->device_flags & (DFLG_NO_CABLE | DFLG_FABRIC_DEVICES)) - && (ha->device_flags & SWITCH_FOUND) ;) { - - qla2x00_check_fabric_devices(ha); - - msleep(10); - } - pci_set_drvdata(pdev, ha); + + /* Start link scan. */ + set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags); + set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags); + set_bit(RSCN_UPDATE, &ha->dpc_flags); ha->flags.init_done = 1; + ha->flags.online = 1; + num_hosts++; ret = scsi_add_host(host, &pdev->dev); @@ -1669,10 +1658,6 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->flags.enable_64bit_addressing ? '+': '-', ha->host_no, ha->isp_ops.fw_version_str(ha, fw_str)); - /* Go with fc_rport registration. */ - list_for_each_entry(fcport, &ha->fcports, list) - qla2x00_reg_remote_port(ha, fcport); - return 0; probe_failed: From 1e99e33ad94aa363e01cdfc2fc9df93338907fe6 Mon Sep 17 00:00:00 2001 From: Andrew Vasquez Date: Wed, 22 Nov 2006 08:24:48 -0800 Subject: [PATCH 43/73] [SCSI] qla2xxx: add asynchronous scsi scanning support. Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_os.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 1cc8febcbadb..cbe0cad83b68 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -95,6 +95,8 @@ MODULE_PARM_DESC(ql2xqfullrampup, */ static int qla2xxx_slave_configure(struct scsi_device * device); static int qla2xxx_slave_alloc(struct scsi_device *); +static int qla2xxx_scan_finished(struct Scsi_Host *, unsigned long time); +static void qla2xxx_scan_start(struct Scsi_Host *); static void qla2xxx_slave_destroy(struct scsi_device *); static int qla2x00_queuecommand(struct scsi_cmnd *cmd, void (*fn)(struct scsi_cmnd *)); @@ -124,6 +126,8 @@ static struct scsi_host_template qla2x00_driver_template = { .slave_alloc = qla2xxx_slave_alloc, .slave_destroy = qla2xxx_slave_destroy, + .scan_finished = qla2xxx_scan_finished, + .scan_start = qla2xxx_scan_start, .change_queue_depth = qla2x00_change_queue_depth, .change_queue_type = qla2x00_change_queue_type, .this_id = -1, @@ -1366,6 +1370,29 @@ qla24xx_disable_intrs(scsi_qla_host_t *ha) spin_unlock_irqrestore(&ha->hardware_lock, flags); } +static void +qla2xxx_scan_start(struct Scsi_Host *shost) +{ + scsi_qla_host_t *ha = (scsi_qla_host_t *)shost->hostdata; + + set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags); + set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags); + set_bit(RSCN_UPDATE, &ha->dpc_flags); +} + +static int +qla2xxx_scan_finished(struct Scsi_Host *shost, unsigned long time) +{ + scsi_qla_host_t *ha = (scsi_qla_host_t *)shost->hostdata; + + if (!ha->host) + return 1; + if (time > ha->loop_reset_delay * HZ) + return 1; + + return atomic_read(&ha->loop_state) == LOOP_READY; +} + /* * PCI driver interface */ @@ -1631,10 +1658,6 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) pci_set_drvdata(pdev, ha); - /* Start link scan. */ - set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags); - set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags); - set_bit(RSCN_UPDATE, &ha->dpc_flags); ha->flags.init_done = 1; ha->flags.online = 1; @@ -1644,6 +1667,8 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) if (ret) goto probe_failed; + scsi_scan_host(host); + qla2x00_alloc_sysfs_attr(ha); qla2x00_init_host_attr(ha); From 84ad58e4efcf80c154f693d4cc8f5c913511b760 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 19 Nov 2006 18:53:43 -0800 Subject: [PATCH 44/73] [SCSI] initio: fix section mismatches with HOTPLUG=n WARNING: drivers/scsi/initio.o - Section mismatch: reference to .init.data: from .text between 'i91u_detect' (at offset 0x26e8) and 'i91uSCBPost' WARNING: drivers/scsi/initio.o - Section mismatch: reference to .init.data:i91u_pci_devices from .text between 'i91u_detect' (at offset 0x26ef) and 'i91uSCBPost' Signed-off-by: Randy Dunlap Signed-off-by: James Bottomley --- drivers/scsi/initio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/initio.c b/drivers/scsi/initio.c index afed293dd7b9..f160357e37a6 100644 --- a/drivers/scsi/initio.c +++ b/drivers/scsi/initio.c @@ -170,7 +170,7 @@ static int setup_debug = 0; static void i91uSCBPost(BYTE * pHcb, BYTE * pScb); /* PCI Devices supported by this driver */ -static struct pci_device_id i91u_pci_devices[] __devinitdata = { +static struct pci_device_id i91u_pci_devices[] = { { PCI_VENDOR_ID_INIT, I950_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { PCI_VENDOR_ID_INIT, I940_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { PCI_VENDOR_ID_INIT, I935_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, From b58d91547fb17c65ad621f3f98b1f2c228c812a5 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Thu, 16 Nov 2006 19:24:10 +0900 Subject: [PATCH 45/73] [SCSI] export scsi-ml functions needed by tgt_scsi_lib and its LLDs This patch contains the needed changes to the scsi-ml for the target mode support. Note, per the last review we moved almost all the fields we added to the scsi_cmnd to our internal data structure which we are going to try and kill off when we can replace it with support from other parts of the kernel. The one field we left on was the offset variable. This is needed to handle the case where the target gets request that is so large that it cannot execute it in one dma operation. So max_secotors or a segment limit may limit the size of the transfer. In this case our tgt core code will break up the command into managable transfers and send them to the LLD one at a time. The offset is then used to tell the LLD where in the command we are at. Is there another field on the scsi_cmd for that? Signed-off-by: Mike Christie Signed-off-by: FUJITA Tomonori Signed-off-by: James Bottomley --- drivers/scsi/hosts.c | 4 ++++ drivers/scsi/scsi.c | 43 ++++++++++++++++++++++++---------------- drivers/scsi/scsi_lib.c | 33 +++++++++++++++++++++--------- include/scsi/scsi_cmnd.h | 10 ++++++++++ include/scsi/scsi_host.h | 43 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 107 insertions(+), 26 deletions(-) diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 2ffdc9e0532d..38c3a291efac 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -263,6 +263,10 @@ static void scsi_host_dev_release(struct device *dev) kthread_stop(shost->ehandler); if (shost->work_q) destroy_workqueue(shost->work_q); + if (shost->uspace_req_q) { + kfree(shost->uspace_req_q->queuedata); + scsi_free_queue(shost->uspace_req_q); + } scsi_destroy_command_freelist(shost); if (shost->bqt) diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 780d6dc92b42..fafc00deaade 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -156,8 +156,7 @@ static struct scsi_host_cmd_pool scsi_cmd_dma_pool = { static DEFINE_MUTEX(host_cmd_pool_mutex); -static struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *shost, - gfp_t gfp_mask) +struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *shost, gfp_t gfp_mask) { struct scsi_cmnd *cmd; @@ -178,6 +177,7 @@ static struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *shost, return cmd; } +EXPORT_SYMBOL_GPL(__scsi_get_command); /* * Function: scsi_get_command() @@ -214,9 +214,29 @@ struct scsi_cmnd *scsi_get_command(struct scsi_device *dev, gfp_t gfp_mask) put_device(&dev->sdev_gendev); return cmd; -} +} EXPORT_SYMBOL(scsi_get_command); +void __scsi_put_command(struct Scsi_Host *shost, struct scsi_cmnd *cmd, + struct device *dev) +{ + unsigned long flags; + + /* changing locks here, don't need to restore the irq state */ + spin_lock_irqsave(&shost->free_list_lock, flags); + if (unlikely(list_empty(&shost->free_list))) { + list_add(&cmd->list, &shost->free_list); + cmd = NULL; + } + spin_unlock_irqrestore(&shost->free_list_lock, flags); + + if (likely(cmd != NULL)) + kmem_cache_free(shost->cmd_pool->slab, cmd); + + put_device(dev); +} +EXPORT_SYMBOL(__scsi_put_command); + /* * Function: scsi_put_command() * @@ -231,26 +251,15 @@ EXPORT_SYMBOL(scsi_get_command); void scsi_put_command(struct scsi_cmnd *cmd) { struct scsi_device *sdev = cmd->device; - struct Scsi_Host *shost = sdev->host; unsigned long flags; - + /* serious error if the command hasn't come from a device list */ spin_lock_irqsave(&cmd->device->list_lock, flags); BUG_ON(list_empty(&cmd->list)); list_del_init(&cmd->list); - spin_unlock(&cmd->device->list_lock); - /* changing locks here, don't need to restore the irq state */ - spin_lock(&shost->free_list_lock); - if (unlikely(list_empty(&shost->free_list))) { - list_add(&cmd->list, &shost->free_list); - cmd = NULL; - } - spin_unlock_irqrestore(&shost->free_list_lock, flags); + spin_unlock_irqrestore(&cmd->device->list_lock, flags); - if (likely(cmd != NULL)) - kmem_cache_free(shost->cmd_pool->slab, cmd); - - put_device(&sdev->sdev_gendev); + __scsi_put_command(cmd->device->host, cmd, &sdev->sdev_gendev); } EXPORT_SYMBOL(scsi_put_command); diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 2f12f9f12fcb..fb616c69151f 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -704,7 +704,7 @@ static struct scsi_cmnd *scsi_end_request(struct scsi_cmnd *cmd, int uptodate, return NULL; } -static struct scatterlist *scsi_alloc_sgtable(struct scsi_cmnd *cmd, gfp_t gfp_mask) +struct scatterlist *scsi_alloc_sgtable(struct scsi_cmnd *cmd, gfp_t gfp_mask) { struct scsi_host_sg_pool *sgp; struct scatterlist *sgl; @@ -745,7 +745,9 @@ static struct scatterlist *scsi_alloc_sgtable(struct scsi_cmnd *cmd, gfp_t gfp_m return sgl; } -static void scsi_free_sgtable(struct scatterlist *sgl, int index) +EXPORT_SYMBOL(scsi_alloc_sgtable); + +void scsi_free_sgtable(struct scatterlist *sgl, int index) { struct scsi_host_sg_pool *sgp; @@ -755,6 +757,8 @@ static void scsi_free_sgtable(struct scatterlist *sgl, int index) mempool_free(sgl, sgp->pool); } +EXPORT_SYMBOL(scsi_free_sgtable); + /* * Function: scsi_release_buffers() * @@ -1567,29 +1571,40 @@ u64 scsi_calculate_bounce_limit(struct Scsi_Host *shost) } EXPORT_SYMBOL(scsi_calculate_bounce_limit); -struct request_queue *scsi_alloc_queue(struct scsi_device *sdev) +struct request_queue *__scsi_alloc_queue(struct Scsi_Host *shost, + request_fn_proc *request_fn) { - struct Scsi_Host *shost = sdev->host; struct request_queue *q; - q = blk_init_queue(scsi_request_fn, NULL); + q = blk_init_queue(request_fn, NULL); if (!q) return NULL; - blk_queue_prep_rq(q, scsi_prep_fn); - blk_queue_max_hw_segments(q, shost->sg_tablesize); blk_queue_max_phys_segments(q, SCSI_MAX_PHYS_SEGMENTS); blk_queue_max_sectors(q, shost->max_sectors); blk_queue_bounce_limit(q, scsi_calculate_bounce_limit(shost)); blk_queue_segment_boundary(q, shost->dma_boundary); - blk_queue_issue_flush_fn(q, scsi_issue_flush_fn); - blk_queue_softirq_done(q, scsi_softirq_done); if (!shost->use_clustering) clear_bit(QUEUE_FLAG_CLUSTER, &q->queue_flags); return q; } +EXPORT_SYMBOL(__scsi_alloc_queue); + +struct request_queue *scsi_alloc_queue(struct scsi_device *sdev) +{ + struct request_queue *q; + + q = __scsi_alloc_queue(sdev->host, scsi_request_fn); + if (!q) + return NULL; + + blk_queue_prep_rq(q, scsi_prep_fn); + blk_queue_issue_flush_fn(q, scsi_issue_flush_fn); + blk_queue_softirq_done(q, scsi_softirq_done); + return q; +} void scsi_free_queue(struct request_queue *q) { diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h index be117f812deb..d6948d0e8cdb 100644 --- a/include/scsi/scsi_cmnd.h +++ b/include/scsi/scsi_cmnd.h @@ -8,6 +8,7 @@ struct request; struct scatterlist; +struct Scsi_Host; struct scsi_device; @@ -72,6 +73,9 @@ struct scsi_cmnd { unsigned short use_sg; /* Number of pieces of scatter-gather */ unsigned short sglist_len; /* size of malloc'd scatter-gather list */ + /* offset in cmd we are at (for multi-transfer tgt cmds) */ + unsigned offset; + unsigned underflow; /* Return error if less than this amount is transferred */ @@ -119,7 +123,10 @@ struct scsi_cmnd { }; extern struct scsi_cmnd *scsi_get_command(struct scsi_device *, gfp_t); +extern struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *, gfp_t); extern void scsi_put_command(struct scsi_cmnd *); +extern void __scsi_put_command(struct Scsi_Host *, struct scsi_cmnd *, + struct device *); extern void scsi_io_completion(struct scsi_cmnd *, unsigned int); extern void scsi_finish_command(struct scsi_cmnd *cmd); extern void scsi_req_abort_cmd(struct scsi_cmnd *cmd); @@ -128,4 +135,7 @@ extern void *scsi_kmap_atomic_sg(struct scatterlist *sg, int sg_count, size_t *offset, size_t *len); extern void scsi_kunmap_atomic_sg(void *virt); +extern struct scatterlist *scsi_alloc_sgtable(struct scsi_cmnd *, gfp_t); +extern void scsi_free_sgtable(struct scatterlist *, int); + #endif /* _SCSI_SCSI_CMND_H */ diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 6d8945d71c65..7f1f411d07af 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -7,6 +7,7 @@ #include #include +struct request_queue; struct block_device; struct completion; struct module; @@ -123,6 +124,39 @@ struct scsi_host_template { int (* queuecommand)(struct scsi_cmnd *, void (*done)(struct scsi_cmnd *)); + /* + * The transfer functions are used to queue a scsi command to + * the LLD. When the driver is finished processing the command + * the done callback is invoked. + * + * return values: see queuecommand + * + * If the LLD accepts the cmd, it should set the result to an + * appropriate value when completed before calling the done function. + * + * STATUS: REQUIRED FOR TARGET DRIVERS + */ + /* TODO: rename */ + int (* transfer_response)(struct scsi_cmnd *, + void (*done)(struct scsi_cmnd *)); + /* + * This is called to inform the LLD to transfer cmd->request_bufflen + * bytes of the cmd at cmd->offset in the cmd. The cmd->use_sg + * speciefies the number of scatterlist entried in the command + * and cmd->request_buffer contains the scatterlist. + * + * If the command cannot be processed in one transfer_data call + * becuase a scatterlist within the LLD's limits cannot be + * created then transfer_data will be called multiple times. + * It is initially called from process context, and later + * calls are from the interrup context. + */ + int (* transfer_data)(struct scsi_cmnd *, + void (*done)(struct scsi_cmnd *)); + + /* Used as callback for the completion of task management request. */ + int (* tsk_mgmt_response)(u64 mid, int result); + /* * This is an error handling strategy routine. You don't need to * define one of these if you don't want to - there is a default @@ -589,6 +623,12 @@ struct Scsi_Host { */ unsigned int max_host_blocked; + /* + * q used for scsi_tgt msgs, async events or any other requests that + * need to be processed in userspace + */ + struct request_queue *uspace_req_q; + /* legacy crap */ unsigned long base; unsigned long io_port; @@ -687,6 +727,9 @@ extern void scsi_unblock_requests(struct Scsi_Host *); extern void scsi_block_requests(struct Scsi_Host *); struct class_container; + +extern struct request_queue *__scsi_alloc_queue(struct Scsi_Host *shost, + void (*) (struct request_queue *)); /* * These two functions are used to allocate and free a pseudo device * which will connect to the host adapter itself rather than any From 5a55c2596f55e3a60f7502d0dfcfa0d20241e5ac Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Thu, 16 Nov 2006 19:24:13 +0900 Subject: [PATCH 46/73] [SCSI] scsi tgt: scsi target lib functionality The core scsi target lib functions. TODO: - mv md/dm-bio-list.h to linux/bio-list.h so md and us do not have to do that weird include. - convert scsi_tgt_cmd's work struct to James's execute code. And try to kill our scsi_tgt_cmd. - add host state checking. We do refcouting so hotplug is partially supported, but we need to add state checking to make it easier on the LLD. Signed-off-by: Mike Christie Signed-off-by: FUJITA Tomonori Signed-off-by: James Bottomley --- drivers/scsi/scsi_tgt_lib.c | 742 +++++++++++++++++++++++++++++++++++ drivers/scsi/scsi_tgt_priv.h | 25 ++ include/scsi/scsi_tgt.h | 19 + 3 files changed, 786 insertions(+) create mode 100644 drivers/scsi/scsi_tgt_lib.c create mode 100644 drivers/scsi/scsi_tgt_priv.h create mode 100644 include/scsi/scsi_tgt.h diff --git a/drivers/scsi/scsi_tgt_lib.c b/drivers/scsi/scsi_tgt_lib.c new file mode 100644 index 000000000000..39da5cd6fb6f --- /dev/null +++ b/drivers/scsi/scsi_tgt_lib.c @@ -0,0 +1,742 @@ +/* + * SCSI target lib functions + * + * Copyright (C) 2005 Mike Christie + * Copyright (C) 2005 FUJITA Tomonori + * + * 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. 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include <../drivers/md/dm-bio-list.h> + +#include "scsi_tgt_priv.h" + +static struct workqueue_struct *scsi_tgtd; +static kmem_cache_t *scsi_tgt_cmd_cache; + +/* + * TODO: this struct will be killed when the block layer supports large bios + * and James's work struct code is in + */ +struct scsi_tgt_cmd { + /* TODO replace work with James b's code */ + struct work_struct work; + /* TODO replace the lists with a large bio */ + struct bio_list xfer_done_list; + struct bio_list xfer_list; + + struct list_head hash_list; + struct request *rq; + u64 tag; + + void *buffer; + unsigned bufflen; +}; + +#define TGT_HASH_ORDER 4 +#define cmd_hashfn(tag) hash_long((unsigned long) (tag), TGT_HASH_ORDER) + +struct scsi_tgt_queuedata { + struct Scsi_Host *shost; + struct list_head cmd_hash[1 << TGT_HASH_ORDER]; + spinlock_t cmd_hash_lock; +}; + +/* + * Function: scsi_host_get_command() + * + * Purpose: Allocate and setup a scsi command block and blk request + * + * Arguments: shost - scsi host + * data_dir - dma data dir + * gfp_mask- allocator flags + * + * Returns: The allocated scsi command structure. + * + * This should be called by target LLDs to get a command. + */ +struct scsi_cmnd *scsi_host_get_command(struct Scsi_Host *shost, + enum dma_data_direction data_dir, + gfp_t gfp_mask) +{ + int write = (data_dir == DMA_TO_DEVICE); + struct request *rq; + struct scsi_cmnd *cmd; + struct scsi_tgt_cmd *tcmd; + + /* Bail if we can't get a reference to the device */ + if (!get_device(&shost->shost_gendev)) + return NULL; + + tcmd = kmem_cache_alloc(scsi_tgt_cmd_cache, GFP_ATOMIC); + if (!tcmd) + goto put_dev; + + rq = blk_get_request(shost->uspace_req_q, write, gfp_mask); + if (!rq) + goto free_tcmd; + + cmd = __scsi_get_command(shost, gfp_mask); + if (!cmd) + goto release_rq; + + memset(cmd, 0, sizeof(*cmd)); + cmd->sc_data_direction = data_dir; + cmd->jiffies_at_alloc = jiffies; + cmd->request = rq; + + rq->special = cmd; + rq->cmd_type = REQ_TYPE_SPECIAL; + rq->cmd_flags |= REQ_TYPE_BLOCK_PC; + rq->end_io_data = tcmd; + + bio_list_init(&tcmd->xfer_list); + bio_list_init(&tcmd->xfer_done_list); + tcmd->rq = rq; + + return cmd; + +release_rq: + blk_put_request(rq); +free_tcmd: + kmem_cache_free(scsi_tgt_cmd_cache, tcmd); +put_dev: + put_device(&shost->shost_gendev); + return NULL; + +} +EXPORT_SYMBOL_GPL(scsi_host_get_command); + +/* + * Function: scsi_host_put_command() + * + * Purpose: Free a scsi command block + * + * Arguments: shost - scsi host + * cmd - command block to free + * + * Returns: Nothing. + * + * Notes: The command must not belong to any lists. + */ +void scsi_host_put_command(struct Scsi_Host *shost, struct scsi_cmnd *cmd) +{ + struct request_queue *q = shost->uspace_req_q; + struct request *rq = cmd->request; + struct scsi_tgt_cmd *tcmd = rq->end_io_data; + unsigned long flags; + + kmem_cache_free(scsi_tgt_cmd_cache, tcmd); + + spin_lock_irqsave(q->queue_lock, flags); + __blk_put_request(q, rq); + spin_unlock_irqrestore(q->queue_lock, flags); + + __scsi_put_command(shost, cmd, &shost->shost_gendev); +} +EXPORT_SYMBOL_GPL(scsi_host_put_command); + +static void scsi_unmap_user_pages(struct scsi_tgt_cmd *tcmd) +{ + struct bio *bio; + + /* must call bio_endio in case bio was bounced */ + while ((bio = bio_list_pop(&tcmd->xfer_done_list))) { + bio_endio(bio, bio->bi_size, 0); + bio_unmap_user(bio); + } + + while ((bio = bio_list_pop(&tcmd->xfer_list))) { + bio_endio(bio, bio->bi_size, 0); + bio_unmap_user(bio); + } +} + +static void cmd_hashlist_del(struct scsi_cmnd *cmd) +{ + struct request_queue *q = cmd->request->q; + struct scsi_tgt_queuedata *qdata = q->queuedata; + unsigned long flags; + struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data; + + spin_lock_irqsave(&qdata->cmd_hash_lock, flags); + list_del(&tcmd->hash_list); + spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags); +} + +static void scsi_tgt_cmd_destroy(void *data) +{ + struct scsi_cmnd *cmd = data; + struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data; + + dprintk("cmd %p %d %lu\n", cmd, cmd->sc_data_direction, + rq_data_dir(cmd->request)); + /* + * We fix rq->cmd_flags here since when we told bio_map_user + * to write vm for WRITE commands, blk_rq_bio_prep set + * rq_data_dir the flags to READ. + */ + if (cmd->sc_data_direction == DMA_TO_DEVICE) + cmd->request->cmd_flags |= REQ_RW; + else + cmd->request->cmd_flags &= ~REQ_RW; + + scsi_unmap_user_pages(tcmd); + scsi_host_put_command(scsi_tgt_cmd_to_host(cmd), cmd); +} + +static void init_scsi_tgt_cmd(struct request *rq, struct scsi_tgt_cmd *tcmd, + u64 tag) +{ + struct scsi_tgt_queuedata *qdata = rq->q->queuedata; + unsigned long flags; + struct list_head *head; + + tcmd->tag = tag; + spin_lock_irqsave(&qdata->cmd_hash_lock, flags); + head = &qdata->cmd_hash[cmd_hashfn(tag)]; + list_add(&tcmd->hash_list, head); + spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags); +} + +/* + * scsi_tgt_alloc_queue - setup queue used for message passing + * shost: scsi host + * + * This should be called by the LLD after host allocation. + * And will be released when the host is released. + */ +int scsi_tgt_alloc_queue(struct Scsi_Host *shost) +{ + struct scsi_tgt_queuedata *queuedata; + struct request_queue *q; + int err, i; + + /* + * Do we need to send a netlink event or should uspace + * just respond to the hotplug event? + */ + q = __scsi_alloc_queue(shost, NULL); + if (!q) + return -ENOMEM; + + queuedata = kzalloc(sizeof(*queuedata), GFP_KERNEL); + if (!queuedata) { + err = -ENOMEM; + goto cleanup_queue; + } + queuedata->shost = shost; + q->queuedata = queuedata; + + /* + * this is a silly hack. We should probably just queue as many + * command as is recvd to userspace. uspace can then make + * sure we do not overload the HBA + */ + q->nr_requests = shost->hostt->can_queue; + /* + * We currently only support software LLDs so this does + * not matter for now. Do we need this for the cards we support? + * If so we should make it a host template value. + */ + blk_queue_dma_alignment(q, 0); + shost->uspace_req_q = q; + + for (i = 0; i < ARRAY_SIZE(queuedata->cmd_hash); i++) + INIT_LIST_HEAD(&queuedata->cmd_hash[i]); + spin_lock_init(&queuedata->cmd_hash_lock); + + return 0; + +cleanup_queue: + blk_cleanup_queue(q); + return err; +} +EXPORT_SYMBOL_GPL(scsi_tgt_alloc_queue); + +void scsi_tgt_free_queue(struct Scsi_Host *shost) +{ + int i; + unsigned long flags; + struct request_queue *q = shost->uspace_req_q; + struct scsi_cmnd *cmd; + struct scsi_tgt_queuedata *qdata = q->queuedata; + struct scsi_tgt_cmd *tcmd, *n; + LIST_HEAD(cmds); + + spin_lock_irqsave(&qdata->cmd_hash_lock, flags); + + for (i = 0; i < ARRAY_SIZE(qdata->cmd_hash); i++) { + list_for_each_entry_safe(tcmd, n, &qdata->cmd_hash[i], + hash_list) { + list_del(&tcmd->hash_list); + list_add(&tcmd->hash_list, &cmds); + } + } + + spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags); + + while (!list_empty(&cmds)) { + tcmd = list_entry(cmds.next, struct scsi_tgt_cmd, hash_list); + list_del(&tcmd->hash_list); + cmd = tcmd->rq->special; + + shost->hostt->eh_abort_handler(cmd); + scsi_tgt_cmd_destroy(cmd); + } +} +EXPORT_SYMBOL_GPL(scsi_tgt_free_queue); + +struct Scsi_Host *scsi_tgt_cmd_to_host(struct scsi_cmnd *cmd) +{ + struct scsi_tgt_queuedata *queue = cmd->request->q->queuedata; + return queue->shost; +} +EXPORT_SYMBOL_GPL(scsi_tgt_cmd_to_host); + +/* + * scsi_tgt_queue_command - queue command for userspace processing + * @cmd: scsi command + * @scsilun: scsi lun + * @tag: unique value to identify this command for tmf + */ +int scsi_tgt_queue_command(struct scsi_cmnd *cmd, struct scsi_lun *scsilun, + u64 tag) +{ + struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data; + int err; + + init_scsi_tgt_cmd(cmd->request, tcmd, tag); + err = scsi_tgt_uspace_send_cmd(cmd, scsilun, tag); + if (err) + cmd_hashlist_del(cmd); + + return err; +} +EXPORT_SYMBOL_GPL(scsi_tgt_queue_command); + +/* + * This is run from a interrpt handler normally and the unmap + * needs process context so we must queue + */ +static void scsi_tgt_cmd_done(struct scsi_cmnd *cmd) +{ + struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data; + + dprintk("cmd %p %lu\n", cmd, rq_data_dir(cmd->request)); + + scsi_tgt_uspace_send_status(cmd, tcmd->tag); + INIT_WORK(&tcmd->work, scsi_tgt_cmd_destroy, cmd); + queue_work(scsi_tgtd, &tcmd->work); +} + +static int __scsi_tgt_transfer_response(struct scsi_cmnd *cmd) +{ + struct Scsi_Host *shost = scsi_tgt_cmd_to_host(cmd); + int err; + + dprintk("cmd %p %lu\n", cmd, rq_data_dir(cmd->request)); + + err = shost->hostt->transfer_response(cmd, scsi_tgt_cmd_done); + switch (err) { + case SCSI_MLQUEUE_HOST_BUSY: + case SCSI_MLQUEUE_DEVICE_BUSY: + return -EAGAIN; + } + + return 0; +} + +static void scsi_tgt_transfer_response(struct scsi_cmnd *cmd) +{ + struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data; + int err; + + err = __scsi_tgt_transfer_response(cmd); + if (!err) + return; + + cmd->result = DID_BUS_BUSY << 16; + err = scsi_tgt_uspace_send_status(cmd, tcmd->tag); + if (err <= 0) + /* the eh will have to pick this up */ + printk(KERN_ERR "Could not send cmd %p status\n", cmd); +} + +static int scsi_tgt_init_cmd(struct scsi_cmnd *cmd, gfp_t gfp_mask) +{ + struct request *rq = cmd->request; + struct scsi_tgt_cmd *tcmd = rq->end_io_data; + int count; + + cmd->use_sg = rq->nr_phys_segments; + cmd->request_buffer = scsi_alloc_sgtable(cmd, gfp_mask); + if (!cmd->request_buffer) + return -ENOMEM; + + cmd->request_bufflen = rq->data_len; + + dprintk("cmd %p addr %p cnt %d %lu\n", cmd, tcmd->buffer, cmd->use_sg, + rq_data_dir(rq)); + count = blk_rq_map_sg(rq->q, rq, cmd->request_buffer); + if (likely(count <= cmd->use_sg)) { + cmd->use_sg = count; + return 0; + } + + eprintk("cmd %p addr %p cnt %d\n", cmd, tcmd->buffer, cmd->use_sg); + scsi_free_sgtable(cmd->request_buffer, cmd->sglist_len); + return -EINVAL; +} + +/* TODO: test this crap and replace bio_map_user with new interface maybe */ +static int scsi_map_user_pages(struct scsi_tgt_cmd *tcmd, struct scsi_cmnd *cmd, + int rw) +{ + struct request_queue *q = cmd->request->q; + struct request *rq = cmd->request; + void *uaddr = tcmd->buffer; + unsigned int len = tcmd->bufflen; + struct bio *bio; + int err; + + while (len > 0) { + dprintk("%lx %u\n", (unsigned long) uaddr, len); + bio = bio_map_user(q, NULL, (unsigned long) uaddr, len, rw); + if (IS_ERR(bio)) { + err = PTR_ERR(bio); + dprintk("fail to map %lx %u %d %x\n", + (unsigned long) uaddr, len, err, cmd->cmnd[0]); + goto unmap_bios; + } + + uaddr += bio->bi_size; + len -= bio->bi_size; + + /* + * The first bio is added and merged. We could probably + * try to add others using scsi_merge_bio() but for now + * we keep it simple. The first bio should be pretty large + * (either hitting the 1 MB bio pages limit or a queue limit) + * already but for really large IO we may want to try and + * merge these. + */ + if (!rq->bio) { + blk_rq_bio_prep(q, rq, bio); + rq->data_len = bio->bi_size; + } else + /* put list of bios to transfer in next go around */ + bio_list_add(&tcmd->xfer_list, bio); + } + + cmd->offset = 0; + err = scsi_tgt_init_cmd(cmd, GFP_KERNEL); + if (err) + goto unmap_bios; + + return 0; + +unmap_bios: + if (rq->bio) { + bio_unmap_user(rq->bio); + while ((bio = bio_list_pop(&tcmd->xfer_list))) + bio_unmap_user(bio); + } + + return err; +} + +static int scsi_tgt_transfer_data(struct scsi_cmnd *); + +static void scsi_tgt_data_transfer_done(struct scsi_cmnd *cmd) +{ + struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data; + struct bio *bio; + int err; + + /* should we free resources here on error ? */ + if (cmd->result) { +send_uspace_err: + err = scsi_tgt_uspace_send_status(cmd, tcmd->tag); + if (err <= 0) + /* the tgt uspace eh will have to pick this up */ + printk(KERN_ERR "Could not send cmd %p status\n", cmd); + return; + } + + dprintk("cmd %p request_bufflen %u bufflen %u\n", + cmd, cmd->request_bufflen, tcmd->bufflen); + + scsi_free_sgtable(cmd->request_buffer, cmd->sglist_len); + bio_list_add(&tcmd->xfer_done_list, cmd->request->bio); + + tcmd->buffer += cmd->request_bufflen; + cmd->offset += cmd->request_bufflen; + + if (!tcmd->xfer_list.head) { + scsi_tgt_transfer_response(cmd); + return; + } + + dprintk("cmd2 %p request_bufflen %u bufflen %u\n", + cmd, cmd->request_bufflen, tcmd->bufflen); + + bio = bio_list_pop(&tcmd->xfer_list); + BUG_ON(!bio); + + blk_rq_bio_prep(cmd->request->q, cmd->request, bio); + cmd->request->data_len = bio->bi_size; + err = scsi_tgt_init_cmd(cmd, GFP_ATOMIC); + if (err) { + cmd->result = DID_ERROR << 16; + goto send_uspace_err; + } + + if (scsi_tgt_transfer_data(cmd)) { + cmd->result = DID_NO_CONNECT << 16; + goto send_uspace_err; + } +} + +static int scsi_tgt_transfer_data(struct scsi_cmnd *cmd) +{ + int err; + struct Scsi_Host *host = scsi_tgt_cmd_to_host(cmd); + + err = host->hostt->transfer_data(cmd, scsi_tgt_data_transfer_done); + switch (err) { + case SCSI_MLQUEUE_HOST_BUSY: + case SCSI_MLQUEUE_DEVICE_BUSY: + return -EAGAIN; + default: + return 0; + } +} + +static int scsi_tgt_copy_sense(struct scsi_cmnd *cmd, unsigned long uaddr, + unsigned len) +{ + char __user *p = (char __user *) uaddr; + + if (copy_from_user(cmd->sense_buffer, p, + min_t(unsigned, SCSI_SENSE_BUFFERSIZE, len))) { + printk(KERN_ERR "Could not copy the sense buffer\n"); + return -EIO; + } + return 0; +} + +static int scsi_tgt_abort_cmd(struct Scsi_Host *shost, struct scsi_cmnd *cmd) +{ + int err; + + err = shost->hostt->eh_abort_handler(cmd); + if (err) + eprintk("fail to abort %p\n", cmd); + + scsi_tgt_cmd_destroy(cmd); + return err; +} + +static struct request *tgt_cmd_hash_lookup(struct request_queue *q, u64 tag) +{ + struct scsi_tgt_queuedata *qdata = q->queuedata; + struct request *rq = NULL; + struct list_head *head; + struct scsi_tgt_cmd *tcmd; + unsigned long flags; + + head = &qdata->cmd_hash[cmd_hashfn(tag)]; + spin_lock_irqsave(&qdata->cmd_hash_lock, flags); + list_for_each_entry(tcmd, head, hash_list) { + if (tcmd->tag == tag) { + rq = tcmd->rq; + list_del(&tcmd->hash_list); + break; + } + } + spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags); + + return rq; +} + +int scsi_tgt_kspace_exec(int host_no, u64 tag, int result, u32 len, + unsigned long uaddr, u8 rw) +{ + struct Scsi_Host *shost; + struct scsi_cmnd *cmd; + struct request *rq; + struct scsi_tgt_cmd *tcmd; + int err = 0; + + dprintk("%d %llu %d %u %lx %u\n", host_no, (unsigned long long) tag, + result, len, uaddr, rw); + + /* TODO: replace with a O(1) alg */ + shost = scsi_host_lookup(host_no); + if (IS_ERR(shost)) { + printk(KERN_ERR "Could not find host no %d\n", host_no); + return -EINVAL; + } + + if (!shost->uspace_req_q) { + printk(KERN_ERR "Not target scsi host %d\n", host_no); + goto done; + } + + rq = tgt_cmd_hash_lookup(shost->uspace_req_q, tag); + if (!rq) { + printk(KERN_ERR "Could not find tag %llu\n", + (unsigned long long) tag); + err = -EINVAL; + goto done; + } + cmd = rq->special; + + dprintk("cmd %p result %d len %d bufflen %u %lu %x\n", cmd, + result, len, cmd->request_bufflen, rq_data_dir(rq), cmd->cmnd[0]); + + if (result == TASK_ABORTED) { + scsi_tgt_abort_cmd(shost, cmd); + goto done; + } + /* + * store the userspace values here, the working values are + * in the request_* values + */ + tcmd = cmd->request->end_io_data; + tcmd->buffer = (void *)uaddr; + tcmd->bufflen = len; + cmd->result = result; + + if (!tcmd->bufflen || cmd->request_buffer) { + err = __scsi_tgt_transfer_response(cmd); + goto done; + } + + /* + * TODO: Do we need to handle case where request does not + * align with LLD. + */ + err = scsi_map_user_pages(rq->end_io_data, cmd, rw); + if (err) { + eprintk("%p %d\n", cmd, err); + err = -EAGAIN; + goto done; + } + + /* userspace failure */ + if (cmd->result) { + if (status_byte(cmd->result) == CHECK_CONDITION) + scsi_tgt_copy_sense(cmd, uaddr, len); + err = __scsi_tgt_transfer_response(cmd); + goto done; + } + /* ask the target LLD to transfer the data to the buffer */ + err = scsi_tgt_transfer_data(cmd); + +done: + scsi_host_put(shost); + return err; +} + +int scsi_tgt_tsk_mgmt_request(struct Scsi_Host *shost, int function, u64 tag, + struct scsi_lun *scsilun, void *data) +{ + int err; + + /* TODO: need to retry if this fails. */ + err = scsi_tgt_uspace_send_tsk_mgmt(shost->host_no, function, + tag, scsilun, data); + if (err < 0) + eprintk("The task management request lost!\n"); + return err; +} +EXPORT_SYMBOL_GPL(scsi_tgt_tsk_mgmt_request); + +int scsi_tgt_kspace_tsk_mgmt(int host_no, u64 mid, int result) +{ + struct Scsi_Host *shost; + int err = -EINVAL; + + dprintk("%d %d %llx\n", host_no, result, (unsigned long long) mid); + + shost = scsi_host_lookup(host_no); + if (IS_ERR(shost)) { + printk(KERN_ERR "Could not find host no %d\n", host_no); + return err; + } + + if (!shost->uspace_req_q) { + printk(KERN_ERR "Not target scsi host %d\n", host_no); + goto done; + } + + err = shost->hostt->tsk_mgmt_response(mid, result); +done: + scsi_host_put(shost); + return err; +} + +static int __init scsi_tgt_init(void) +{ + int err; + + scsi_tgt_cmd_cache = kmem_cache_create("scsi_tgt_cmd", + sizeof(struct scsi_tgt_cmd), + 0, 0, NULL, NULL); + if (!scsi_tgt_cmd_cache) + return -ENOMEM; + + scsi_tgtd = create_workqueue("scsi_tgtd"); + if (!scsi_tgtd) { + err = -ENOMEM; + goto free_kmemcache; + } + + err = scsi_tgt_if_init(); + if (err) + goto destroy_wq; + + return 0; + +destroy_wq: + destroy_workqueue(scsi_tgtd); +free_kmemcache: + kmem_cache_destroy(scsi_tgt_cmd_cache); + return err; +} + +static void __exit scsi_tgt_exit(void) +{ + destroy_workqueue(scsi_tgtd); + scsi_tgt_if_exit(); + kmem_cache_destroy(scsi_tgt_cmd_cache); +} + +module_init(scsi_tgt_init); +module_exit(scsi_tgt_exit); + +MODULE_DESCRIPTION("SCSI target core"); +MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/scsi_tgt_priv.h b/drivers/scsi/scsi_tgt_priv.h new file mode 100644 index 000000000000..84488c51ff62 --- /dev/null +++ b/drivers/scsi/scsi_tgt_priv.h @@ -0,0 +1,25 @@ +struct scsi_cmnd; +struct scsi_lun; +struct Scsi_Host; +struct task_struct; + +/* tmp - will replace with SCSI logging stuff */ +#define eprintk(fmt, args...) \ +do { \ + printk("%s(%d) " fmt, __FUNCTION__, __LINE__, ##args); \ +} while (0) + +#define dprintk(fmt, args...) +/* #define dprintk eprintk */ + +extern void scsi_tgt_if_exit(void); +extern int scsi_tgt_if_init(void); + +extern int scsi_tgt_uspace_send_cmd(struct scsi_cmnd *cmd, struct scsi_lun *lun, + u64 tag); +extern int scsi_tgt_uspace_send_status(struct scsi_cmnd *cmd, u64 tag); +extern int scsi_tgt_kspace_exec(int host_no, u64 tag, int result, u32 len, + unsigned long uaddr, u8 rw); +extern int scsi_tgt_uspace_send_tsk_mgmt(int host_no, int function, u64 tag, + struct scsi_lun *scsilun, void *data); +extern int scsi_tgt_kspace_tsk_mgmt(int host_no, u64 mid, int result); diff --git a/include/scsi/scsi_tgt.h b/include/scsi/scsi_tgt.h new file mode 100644 index 000000000000..4f4427937af2 --- /dev/null +++ b/include/scsi/scsi_tgt.h @@ -0,0 +1,19 @@ +/* + * SCSI target definitions + */ + +#include + +struct Scsi_Host; +struct scsi_cmnd; +struct scsi_lun; + +extern struct Scsi_Host *scsi_tgt_cmd_to_host(struct scsi_cmnd *); +extern int scsi_tgt_alloc_queue(struct Scsi_Host *); +extern void scsi_tgt_free_queue(struct Scsi_Host *); +extern int scsi_tgt_queue_command(struct scsi_cmnd *, struct scsi_lun *, u64); +extern int scsi_tgt_tsk_mgmt_request(struct Scsi_Host *, int, u64, struct scsi_lun *, + void *); +extern struct scsi_cmnd *scsi_host_get_command(struct Scsi_Host *, + enum dma_data_direction, gfp_t); +extern void scsi_host_put_command(struct Scsi_Host *, struct scsi_cmnd *); From 97f78759ea1cb7d27b61095582de78b8e7486e85 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Thu, 16 Nov 2006 19:24:16 +0900 Subject: [PATCH 47/73] [SCSI] scsi tgt: scsi target user and kernel communication interface The user-space daemon and tgt kernel module need bi-directional kernel/user high-performance interface, however, mainline provides no standard interface like that. This patch adds shared memory interface between kernel and user spaces like some other drivers do by using own character device. The user-space daemon and tgt kernel module creates shared memory via mmap and use it like ring buffer. poll (kernel to user) and write (user to kernel) system calls are used for notification. Signed-off-by: FUJITA Tomonori Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/scsi_tgt_if.c | 350 +++++++++++++++++++++++++++++++++++++ include/scsi/scsi_tgt_if.h | 90 ++++++++++ 2 files changed, 440 insertions(+) create mode 100644 drivers/scsi/scsi_tgt_if.c create mode 100644 include/scsi/scsi_tgt_if.h diff --git a/drivers/scsi/scsi_tgt_if.c b/drivers/scsi/scsi_tgt_if.c new file mode 100644 index 000000000000..28fd2223765b --- /dev/null +++ b/drivers/scsi/scsi_tgt_if.c @@ -0,0 +1,350 @@ +/* + * SCSI target kernel/user interface functions + * + * Copyright (C) 2005 FUJITA Tomonori + * Copyright (C) 2005 Mike Christie + * + * 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. 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scsi_tgt_priv.h" + +struct tgt_ring { + u32 tr_idx; + unsigned long tr_pages[TGT_RING_PAGES]; + spinlock_t tr_lock; +}; + +/* tx_ring : kernel->user, rx_ring : user->kernel */ +static struct tgt_ring tx_ring, rx_ring; +static DECLARE_WAIT_QUEUE_HEAD(tgt_poll_wait); + +static inline void tgt_ring_idx_inc(struct tgt_ring *ring) +{ + if (ring->tr_idx == TGT_MAX_EVENTS - 1) + ring->tr_idx = 0; + else + ring->tr_idx++; +} + +static struct tgt_event *tgt_head_event(struct tgt_ring *ring, u32 idx) +{ + u32 pidx, off; + + pidx = idx / TGT_EVENT_PER_PAGE; + off = idx % TGT_EVENT_PER_PAGE; + + return (struct tgt_event *) + (ring->tr_pages[pidx] + sizeof(struct tgt_event) * off); +} + +static int tgt_uspace_send_event(u32 type, struct tgt_event *p) +{ + struct tgt_event *ev; + struct tgt_ring *ring = &tx_ring; + unsigned long flags; + int err = 0; + + spin_lock_irqsave(&ring->tr_lock, flags); + + ev = tgt_head_event(ring, ring->tr_idx); + if (!ev->hdr.status) + tgt_ring_idx_inc(ring); + else + err = -BUSY; + + spin_unlock_irqrestore(&ring->tr_lock, flags); + + if (err) + return err; + + memcpy(ev, p, sizeof(*ev)); + ev->hdr.type = type; + mb(); + ev->hdr.status = 1; + + flush_dcache_page(virt_to_page(ev)); + + wake_up_interruptible(&tgt_poll_wait); + + return 0; +} + +int scsi_tgt_uspace_send_cmd(struct scsi_cmnd *cmd, struct scsi_lun *lun, u64 tag) +{ + struct Scsi_Host *shost = scsi_tgt_cmd_to_host(cmd); + struct tgt_event ev; + int err; + + memset(&ev, 0, sizeof(ev)); + ev.p.cmd_req.host_no = shost->host_no; + ev.p.cmd_req.data_len = cmd->request_bufflen; + memcpy(ev.p.cmd_req.scb, cmd->cmnd, sizeof(ev.p.cmd_req.scb)); + memcpy(ev.p.cmd_req.lun, lun, sizeof(ev.p.cmd_req.lun)); + ev.p.cmd_req.attribute = cmd->tag; + ev.p.cmd_req.tag = tag; + + dprintk("%p %d %u %x %llx\n", cmd, shost->host_no, + ev.p.cmd_req.data_len, cmd->tag, + (unsigned long long) ev.p.cmd_req.tag); + + err = tgt_uspace_send_event(TGT_KEVENT_CMD_REQ, &ev); + if (err) + eprintk("tx buf is full, could not send\n"); + + return err; +} + +int scsi_tgt_uspace_send_status(struct scsi_cmnd *cmd, u64 tag) +{ + struct Scsi_Host *shost = scsi_tgt_cmd_to_host(cmd); + struct tgt_event ev; + int err; + + memset(&ev, 0, sizeof(ev)); + ev.p.cmd_done.host_no = shost->host_no; + ev.p.cmd_done.tag = tag; + ev.p.cmd_done.result = cmd->result; + + dprintk("%p %d %llu %u %x\n", cmd, shost->host_no, + (unsigned long long) ev.p.cmd_req.tag, + ev.p.cmd_req.data_len, cmd->tag); + + err = tgt_uspace_send_event(TGT_KEVENT_CMD_DONE, &ev); + if (err) + eprintk("tx buf is full, could not send\n"); + + return err; +} + +int scsi_tgt_uspace_send_tsk_mgmt(int host_no, int function, u64 tag, + struct scsi_lun *scsilun, void *data) +{ + struct tgt_event ev; + int err; + + memset(&ev, 0, sizeof(ev)); + ev.p.tsk_mgmt_req.host_no = host_no; + ev.p.tsk_mgmt_req.function = function; + ev.p.tsk_mgmt_req.tag = tag; + memcpy(ev.p.tsk_mgmt_req.lun, scsilun, sizeof(ev.p.tsk_mgmt_req.lun)); + ev.p.tsk_mgmt_req.mid = (u64) (unsigned long) data; + + dprintk("%d %x %llx %llx\n", host_no, function, (unsigned long long) tag, + (unsigned long long) ev.p.tsk_mgmt_req.mid); + + err = tgt_uspace_send_event(TGT_KEVENT_TSK_MGMT_REQ, &ev); + if (err) + eprintk("tx buf is full, could not send\n"); + + return err; +} + +static int event_recv_msg(struct tgt_event *ev) +{ + int err = 0; + + switch (ev->hdr.type) { + case TGT_UEVENT_CMD_RSP: + err = scsi_tgt_kspace_exec(ev->p.cmd_rsp.host_no, + ev->p.cmd_rsp.tag, + ev->p.cmd_rsp.result, + ev->p.cmd_rsp.len, + ev->p.cmd_rsp.uaddr, + ev->p.cmd_rsp.rw); + break; + case TGT_UEVENT_TSK_MGMT_RSP: + err = scsi_tgt_kspace_tsk_mgmt(ev->p.tsk_mgmt_rsp.host_no, + ev->p.tsk_mgmt_rsp.mid, + ev->p.tsk_mgmt_rsp.result); + break; + default: + eprintk("unknown type %d\n", ev->hdr.type); + err = -EINVAL; + } + + return err; +} + +static ssize_t tgt_write(struct file *file, const char __user * buffer, + size_t count, loff_t * ppos) +{ + struct tgt_event *ev; + struct tgt_ring *ring = &rx_ring; + + while (1) { + ev = tgt_head_event(ring, ring->tr_idx); + /* do we need this? */ + flush_dcache_page(virt_to_page(ev)); + + if (!ev->hdr.status) + break; + + tgt_ring_idx_inc(ring); + event_recv_msg(ev); + ev->hdr.status = 0; + }; + + return count; +} + +static unsigned int tgt_poll(struct file * file, struct poll_table_struct *wait) +{ + struct tgt_event *ev; + struct tgt_ring *ring = &tx_ring; + unsigned long flags; + unsigned int mask = 0; + u32 idx; + + poll_wait(file, &tgt_poll_wait, wait); + + spin_lock_irqsave(&ring->tr_lock, flags); + + idx = ring->tr_idx ? ring->tr_idx - 1 : TGT_MAX_EVENTS - 1; + ev = tgt_head_event(ring, idx); + if (ev->hdr.status) + mask |= POLLIN | POLLRDNORM; + + spin_unlock_irqrestore(&ring->tr_lock, flags); + + return mask; +} + +static int uspace_ring_map(struct vm_area_struct *vma, unsigned long addr, + struct tgt_ring *ring) +{ + int i, err; + + for (i = 0; i < TGT_RING_PAGES; i++) { + struct page *page = virt_to_page(ring->tr_pages[i]); + err = vm_insert_page(vma, addr, page); + if (err) + return err; + addr += PAGE_SIZE; + } + + return 0; +} + +static int tgt_mmap(struct file *filp, struct vm_area_struct *vma) +{ + unsigned long addr; + int err; + + if (vma->vm_pgoff) + return -EINVAL; + + if (vma->vm_end - vma->vm_start != TGT_RING_SIZE * 2) { + eprintk("mmap size must be %lu, not %lu \n", + TGT_RING_SIZE * 2, vma->vm_end - vma->vm_start); + return -EINVAL; + } + + addr = vma->vm_start; + err = uspace_ring_map(vma, addr, &tx_ring); + if (err) + return err; + err = uspace_ring_map(vma, addr + TGT_RING_SIZE, &rx_ring); + + return err; +} + +static int tgt_open(struct inode *inode, struct file *file) +{ + tx_ring.tr_idx = rx_ring.tr_idx = 0; + + return 0; +} + +static struct file_operations tgt_fops = { + .owner = THIS_MODULE, + .open = tgt_open, + .poll = tgt_poll, + .write = tgt_write, + .mmap = tgt_mmap, +}; + +static struct miscdevice tgt_miscdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "tgt", + .fops = &tgt_fops, +}; + +static void tgt_ring_exit(struct tgt_ring *ring) +{ + int i; + + for (i = 0; i < TGT_RING_PAGES; i++) + free_page(ring->tr_pages[i]); +} + +static int tgt_ring_init(struct tgt_ring *ring) +{ + int i; + + spin_lock_init(&ring->tr_lock); + + for (i = 0; i < TGT_RING_PAGES; i++) { + ring->tr_pages[i] = get_zeroed_page(GFP_KERNEL); + if (!ring->tr_pages[i]) { + eprintk("out of memory\n"); + return -ENOMEM; + } + } + + return 0; +} + +void scsi_tgt_if_exit(void) +{ + tgt_ring_exit(&tx_ring); + tgt_ring_exit(&rx_ring); + misc_deregister(&tgt_miscdev); +} + +int scsi_tgt_if_init(void) +{ + int err; + + err = tgt_ring_init(&tx_ring); + if (err) + return err; + + err = tgt_ring_init(&rx_ring); + if (err) + goto free_tx_ring; + + err = misc_register(&tgt_miscdev); + if (err) + goto free_rx_ring; + + return 0; +free_rx_ring: + tgt_ring_exit(&rx_ring); +free_tx_ring: + tgt_ring_exit(&tx_ring); + + return err; +} diff --git a/include/scsi/scsi_tgt_if.h b/include/scsi/scsi_tgt_if.h new file mode 100644 index 000000000000..46d5e70d7215 --- /dev/null +++ b/include/scsi/scsi_tgt_if.h @@ -0,0 +1,90 @@ +/* + * SCSI target kernel/user interface + * + * Copyright (C) 2005 FUJITA Tomonori + * Copyright (C) 2005 Mike Christie + * + * 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. 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#ifndef __SCSI_TARGET_IF_H +#define __SCSI_TARGET_IF_H + +/* user -> kernel */ +#define TGT_UEVENT_CMD_RSP 0x0001 +#define TGT_UEVENT_TSK_MGMT_RSP 0x0002 + +/* kernel -> user */ +#define TGT_KEVENT_CMD_REQ 0x1001 +#define TGT_KEVENT_CMD_DONE 0x1002 +#define TGT_KEVENT_TSK_MGMT_REQ 0x1003 + +struct tgt_event_hdr { + uint16_t version; + uint16_t status; + uint16_t type; + uint16_t len; +} __attribute__ ((aligned (sizeof(uint64_t)))); + +struct tgt_event { + struct tgt_event_hdr hdr; + + union { + /* user-> kernel */ + struct { + int host_no; + uint32_t len; + int result; + aligned_u64 uaddr; + uint8_t rw; + aligned_u64 tag; + } cmd_rsp; + struct { + int host_no; + aligned_u64 mid; + int result; + } tsk_mgmt_rsp; + + + /* kernel -> user */ + struct { + int host_no; + uint32_t data_len; + uint8_t scb[16]; + uint8_t lun[8]; + int attribute; + aligned_u64 tag; + } cmd_req; + struct { + int host_no; + aligned_u64 tag; + int result; + } cmd_done; + struct { + int host_no; + int function; + aligned_u64 tag; + uint8_t lun[8]; + aligned_u64 mid; + } tsk_mgmt_req; + } p; +} __attribute__ ((aligned (sizeof(uint64_t)))); + +#define TGT_RING_SIZE (1UL << 16) +#define TGT_RING_PAGES (TGT_RING_SIZE >> PAGE_SHIFT) +#define TGT_EVENT_PER_PAGE (PAGE_SIZE / sizeof(struct tgt_event)) +#define TGT_MAX_EVENTS (TGT_EVENT_PER_PAGE * TGT_RING_PAGES) + +#endif From 0d2db302f95747908123e56fa8659c7eb2eb7498 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Thu, 16 Nov 2006 19:24:18 +0900 Subject: [PATCH 48/73] [SCSI] scsi-ml: Makefile and Kconfig changes for tgt Makefile and Kconfig for tgt. Signed-off-by: Mike Christie Signed-off-by: FUJITA Tomonori Signed-off-by: James Bottomley --- drivers/scsi/Kconfig | 7 +++++++ drivers/scsi/Makefile | 3 +++ 2 files changed, 10 insertions(+) diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 8442be1bb162..1301d5222c07 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -29,6 +29,13 @@ config SCSI However, do not compile this as a module if your root file system (the one containing the directory /) is located on a SCSI device. +config SCSI_TGT + tristate "SCSI target support" + depends on SCSI && EXPERIMENTAL + ---help--- + If you want to use SCSI target mode drivers enable this option. + If you choose M, the module will be called scsi_tgt. + config SCSI_NETLINK bool default n diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index a0a77fde3708..8f86c1b96ff7 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -21,6 +21,7 @@ CFLAGS_seagate.o = -DARBITRATE -DPARITY -DSEAGATE_USE_ASM subdir-$(CONFIG_PCMCIA) += pcmcia obj-$(CONFIG_SCSI) += scsi_mod.o +obj-$(CONFIG_SCSI_TGT) += scsi_tgt.o obj-$(CONFIG_RAID_ATTRS) += raid_class.o @@ -151,6 +152,8 @@ scsi_mod-$(CONFIG_SCSI_NETLINK) += scsi_netlink.o scsi_mod-$(CONFIG_SYSCTL) += scsi_sysctl.o scsi_mod-$(CONFIG_SCSI_PROC_FS) += scsi_proc.o +scsi_tgt-y += scsi_tgt_lib.o scsi_tgt_if.o + sd_mod-objs := sd.o sr_mod-objs := sr.o sr_ioctl.o sr_vendor.o ncr53c8xx-flags-$(CONFIG_SCSI_ZALON) \ From 46ddab7b1c7225269ad45d33fc39d0a385194360 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 27 Nov 2006 09:35:42 -0800 Subject: [PATCH 49/73] [SCSI] qla2xxx: use NULL instead of 0 Use NULL instead of 0 for pointers (sparse warning): drivers/scsi/qla2xxx/qla_attr.c:393:4: warning: Using plain integer as NULL pointer Signed-off-by: Randy Dunlap Acked-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_attr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 285c8e8ff1a0..7b18a6c7b7eb 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -390,7 +390,7 @@ static struct sysfs_entry { { "optrom_ctl", &sysfs_optrom_ctl_attr, }, { "vpd", &sysfs_vpd_attr, 1 }, { "sfp", &sysfs_sfp_attr, 1 }, - { 0 }, + { NULL }, }; void From c603d04ef6826a29f50cb151cb992f256d0cf19d Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Fri, 1 Dec 2006 11:25:12 -0600 Subject: [PATCH 50/73] [SCSI] 53c700: brown paper bag fix for auto request sense In the switch over, I forgot to set the command length, so it sends out a request sense with whatever length the prior command had (and fails badly if it wasn't 6). Signed-off-by: James Bottomley --- drivers/scsi/53c700.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/53c700.c b/drivers/scsi/53c700.c index 562432d017b0..335a25540c08 100644 --- a/drivers/scsi/53c700.c +++ b/drivers/scsi/53c700.c @@ -622,8 +622,10 @@ NCR_700_scsi_done(struct NCR_700_Host_Parameters *hostdata, dma_unmap_single(hostdata->dev, slot->dma_handle, sizeof(SCp->sense_buffer), DMA_FROM_DEVICE); /* restore the old result if the request sense was * successful */ - if(result == 0) + if (result == 0) result = cmnd[7]; + /* restore the original length */ + SCp->cmd_len = cmnd[8]; } else NCR_700_unmap(hostdata, SCp, slot); @@ -1007,6 +1009,9 @@ process_script_interrupt(__u32 dsps, __u32 dsp, struct scsi_cmnd *SCp, * of the command */ cmnd[6] = NCR_700_INTERNAL_SENSE_MAGIC; cmnd[7] = hostdata->status[0]; + cmnd[8] = SCp->cmd_len; + SCp->cmd_len = 6; /* command length for + * REQUEST_SENSE */ slot->pCmd = dma_map_single(hostdata->dev, cmnd, MAX_COMMAND_SIZE, DMA_TO_DEVICE); slot->dma_handle = dma_map_single(hostdata->dev, SCp->sense_buffer, sizeof(SCp->sense_buffer), DMA_FROM_DEVICE); slot->SG[0].ins = bS_to_host(SCRIPT_MOVE_DATA_IN | sizeof(SCp->sense_buffer)); From 26b14823441382264e6f3dfd01b8687dc18196fe Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Fri, 1 Dec 2006 03:00:50 +0900 Subject: [PATCH 51/73] [SCSI] scsi tgt: SCSI RDMA Protocol library functions libsrp provides helper functions for SRP target drivers. Some SRP target drivers would be out of drivers/scsi/ so we added an entry for libsrp in drivers/scsi/Kconfig. Signed-off-by: FUJITA Tomonori Signed-off-by: Mike Christie Signed-off-by: Santiago Leon Signed-off-by: James Bottomley --- drivers/scsi/Kconfig | 10 + drivers/scsi/Makefile | 1 + drivers/scsi/libsrp.c | 441 ++++++++++++++++++++++++++++++++++++++++++ include/scsi/libsrp.h | 77 ++++++++ 4 files changed, 529 insertions(+) create mode 100644 drivers/scsi/libsrp.c create mode 100644 include/scsi/libsrp.h diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 1301d5222c07..5570cdbf8f11 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1760,6 +1760,16 @@ config ZFCP called zfcp. If you want to compile it as a module, say M here and read . +config SCSI_SRP + tristate "SCSI RDMA Protocol helper library" + depends on SCSI && PCI + select SCSI_TGT + help + If you wish to use SRP target drivers, say Y. + + To compile this driver as a module, choose M here: the + module will be called libsrp. + endmenu source "drivers/scsi/pcmcia/Kconfig" diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 8f86c1b96ff7..00691c4c3a6f 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -126,6 +126,7 @@ obj-$(CONFIG_SCSI_FCAL) += fcal.o obj-$(CONFIG_SCSI_LASI700) += 53c700.o lasi700.o obj-$(CONFIG_SCSI_NSP32) += nsp32.o obj-$(CONFIG_SCSI_IPR) += ipr.o +obj-$(CONFIG_SCSI_SRP) += libsrp.o obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsi/ obj-$(CONFIG_SCSI_HPTIOP) += hptiop.o obj-$(CONFIG_SCSI_STEX) += stex.o diff --git a/drivers/scsi/libsrp.c b/drivers/scsi/libsrp.c new file mode 100644 index 000000000000..89403b00e042 --- /dev/null +++ b/drivers/scsi/libsrp.c @@ -0,0 +1,441 @@ +/* + * SCSI RDAM Protocol lib functions + * + * Copyright (C) 2006 FUJITA Tomonori + * + * 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. 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum srp_task_attributes { + SRP_SIMPLE_TASK = 0, + SRP_HEAD_TASK = 1, + SRP_ORDERED_TASK = 2, + SRP_ACA_TASK = 4 +}; + +/* tmp - will replace with SCSI logging stuff */ +#define eprintk(fmt, args...) \ +do { \ + printk("%s(%d) " fmt, __FUNCTION__, __LINE__, ##args); \ +} while (0) +/* #define dprintk eprintk */ +#define dprintk(fmt, args...) + +static int srp_iu_pool_alloc(struct srp_queue *q, size_t max, + struct srp_buf **ring) +{ + int i; + struct iu_entry *iue; + + q->pool = kcalloc(max, sizeof(struct iu_entry *), GFP_KERNEL); + if (!q->pool) + return -ENOMEM; + q->items = kcalloc(max, sizeof(struct iu_entry), GFP_KERNEL); + if (!q->items) + goto free_pool; + + spin_lock_init(&q->lock); + q->queue = kfifo_init((void *) q->pool, max * sizeof(void *), + GFP_KERNEL, &q->lock); + if (IS_ERR(q->queue)) + goto free_item; + + for (i = 0, iue = q->items; i < max; i++) { + __kfifo_put(q->queue, (void *) &iue, sizeof(void *)); + iue->sbuf = ring[i]; + iue++; + } + return 0; + +free_item: + kfree(q->items); +free_pool: + kfree(q->pool); + return -ENOMEM; +} + +static void srp_iu_pool_free(struct srp_queue *q) +{ + kfree(q->items); + kfree(q->pool); +} + +static struct srp_buf **srp_ring_alloc(struct device *dev, + size_t max, size_t size) +{ + int i; + struct srp_buf **ring; + + ring = kcalloc(max, sizeof(struct srp_buf *), GFP_KERNEL); + if (!ring) + return NULL; + + for (i = 0; i < max; i++) { + ring[i] = kzalloc(sizeof(struct srp_buf), GFP_KERNEL); + if (!ring[i]) + goto out; + ring[i]->buf = dma_alloc_coherent(dev, size, &ring[i]->dma, + GFP_KERNEL); + if (!ring[i]->buf) + goto out; + } + return ring; + +out: + for (i = 0; i < max && ring[i]; i++) { + if (ring[i]->buf) + dma_free_coherent(dev, size, ring[i]->buf, ring[i]->dma); + kfree(ring[i]); + } + kfree(ring); + + return NULL; +} + +static void srp_ring_free(struct device *dev, struct srp_buf **ring, size_t max, + size_t size) +{ + int i; + + for (i = 0; i < max; i++) { + dma_free_coherent(dev, size, ring[i]->buf, ring[i]->dma); + kfree(ring[i]); + } +} + +int srp_target_alloc(struct srp_target *target, struct device *dev, + size_t nr, size_t iu_size) +{ + int err; + + spin_lock_init(&target->lock); + INIT_LIST_HEAD(&target->cmd_queue); + + target->dev = dev; + target->dev->driver_data = target; + + target->srp_iu_size = iu_size; + target->rx_ring_size = nr; + target->rx_ring = srp_ring_alloc(target->dev, nr, iu_size); + if (!target->rx_ring) + return -ENOMEM; + err = srp_iu_pool_alloc(&target->iu_queue, nr, target->rx_ring); + if (err) + goto free_ring; + + return 0; + +free_ring: + srp_ring_free(target->dev, target->rx_ring, nr, iu_size); + return -ENOMEM; +} +EXPORT_SYMBOL_GPL(srp_target_alloc); + +void srp_target_free(struct srp_target *target) +{ + srp_ring_free(target->dev, target->rx_ring, target->rx_ring_size, + target->srp_iu_size); + srp_iu_pool_free(&target->iu_queue); +} +EXPORT_SYMBOL_GPL(srp_target_free); + +struct iu_entry *srp_iu_get(struct srp_target *target) +{ + struct iu_entry *iue = NULL; + + kfifo_get(target->iu_queue.queue, (void *) &iue, sizeof(void *)); + if (!iue) + return iue; + iue->target = target; + INIT_LIST_HEAD(&iue->ilist); + iue->flags = 0; + return iue; +} +EXPORT_SYMBOL_GPL(srp_iu_get); + +void srp_iu_put(struct iu_entry *iue) +{ + kfifo_put(iue->target->iu_queue.queue, (void *) &iue, sizeof(void *)); +} +EXPORT_SYMBOL_GPL(srp_iu_put); + +static int srp_direct_data(struct scsi_cmnd *sc, struct srp_direct_buf *md, + enum dma_data_direction dir, srp_rdma_t rdma_io, + int dma_map, int ext_desc) +{ + struct iu_entry *iue = NULL; + struct scatterlist *sg = NULL; + int err, nsg = 0, len; + + if (dma_map) { + iue = (struct iu_entry *) sc->SCp.ptr; + sg = sc->request_buffer; + + dprintk("%p %u %u %d\n", iue, sc->request_bufflen, + md->len, sc->use_sg); + + nsg = dma_map_sg(iue->target->dev, sg, sc->use_sg, + DMA_BIDIRECTIONAL); + if (!nsg) { + printk("fail to map %p %d\n", iue, sc->use_sg); + return 0; + } + len = min(sc->request_bufflen, md->len); + } else + len = md->len; + + err = rdma_io(sc, sg, nsg, md, 1, dir, len); + + if (dma_map) + dma_unmap_sg(iue->target->dev, sg, nsg, DMA_BIDIRECTIONAL); + + return err; +} + +static int srp_indirect_data(struct scsi_cmnd *sc, struct srp_cmd *cmd, + struct srp_indirect_buf *id, + enum dma_data_direction dir, srp_rdma_t rdma_io, + int dma_map, int ext_desc) +{ + struct iu_entry *iue = NULL; + struct srp_direct_buf *md = NULL; + struct scatterlist dummy, *sg = NULL; + dma_addr_t token = 0; + long err; + unsigned int done = 0; + int nmd, nsg = 0, len; + + if (dma_map || ext_desc) { + iue = (struct iu_entry *) sc->SCp.ptr; + sg = sc->request_buffer; + + dprintk("%p %u %u %d %d\n", + iue, sc->request_bufflen, id->len, + cmd->data_in_desc_cnt, cmd->data_out_desc_cnt); + } + + nmd = id->table_desc.len / sizeof(struct srp_direct_buf); + + if ((dir == DMA_FROM_DEVICE && nmd == cmd->data_in_desc_cnt) || + (dir == DMA_TO_DEVICE && nmd == cmd->data_out_desc_cnt)) { + md = &id->desc_list[0]; + goto rdma; + } + + if (ext_desc && dma_map) { + md = dma_alloc_coherent(iue->target->dev, id->table_desc.len, + &token, GFP_KERNEL); + if (!md) { + eprintk("Can't get dma memory %u\n", id->table_desc.len); + return -ENOMEM; + } + + sg_init_one(&dummy, md, id->table_desc.len); + sg_dma_address(&dummy) = token; + err = rdma_io(sc, &dummy, 1, &id->table_desc, 1, DMA_TO_DEVICE, + id->table_desc.len); + if (err < 0) { + eprintk("Error copying indirect table %ld\n", err); + goto free_mem; + } + } else { + eprintk("This command uses external indirect buffer\n"); + return -EINVAL; + } + +rdma: + if (dma_map) { + nsg = dma_map_sg(iue->target->dev, sg, sc->use_sg, DMA_BIDIRECTIONAL); + if (!nsg) { + eprintk("fail to map %p %d\n", iue, sc->use_sg); + goto free_mem; + } + len = min(sc->request_bufflen, id->len); + } else + len = id->len; + + err = rdma_io(sc, sg, nsg, md, nmd, dir, len); + + if (dma_map) + dma_unmap_sg(iue->target->dev, sg, nsg, DMA_BIDIRECTIONAL); + +free_mem: + if (token && dma_map) + dma_free_coherent(iue->target->dev, id->table_desc.len, md, token); + + return done; +} + +static int data_out_desc_size(struct srp_cmd *cmd) +{ + int size = 0; + u8 fmt = cmd->buf_fmt >> 4; + + switch (fmt) { + case SRP_NO_DATA_DESC: + break; + case SRP_DATA_DESC_DIRECT: + size = sizeof(struct srp_direct_buf); + break; + case SRP_DATA_DESC_INDIRECT: + size = sizeof(struct srp_indirect_buf) + + sizeof(struct srp_direct_buf) * cmd->data_out_desc_cnt; + break; + default: + eprintk("client error. Invalid data_out_format %x\n", fmt); + break; + } + return size; +} + +/* + * TODO: this can be called multiple times for a single command if it + * has very long data. + */ +int srp_transfer_data(struct scsi_cmnd *sc, struct srp_cmd *cmd, + srp_rdma_t rdma_io, int dma_map, int ext_desc) +{ + struct srp_direct_buf *md; + struct srp_indirect_buf *id; + enum dma_data_direction dir; + int offset, err = 0; + u8 format; + + offset = cmd->add_cdb_len * 4; + + dir = srp_cmd_direction(cmd); + if (dir == DMA_FROM_DEVICE) + offset += data_out_desc_size(cmd); + + if (dir == DMA_TO_DEVICE) + format = cmd->buf_fmt >> 4; + else + format = cmd->buf_fmt & ((1U << 4) - 1); + + switch (format) { + case SRP_NO_DATA_DESC: + break; + case SRP_DATA_DESC_DIRECT: + md = (struct srp_direct_buf *) + (cmd->add_data + offset); + err = srp_direct_data(sc, md, dir, rdma_io, dma_map, ext_desc); + break; + case SRP_DATA_DESC_INDIRECT: + id = (struct srp_indirect_buf *) + (cmd->add_data + offset); + err = srp_indirect_data(sc, cmd, id, dir, rdma_io, dma_map, + ext_desc); + break; + default: + eprintk("Unknown format %d %x\n", dir, format); + break; + } + + return err; +} +EXPORT_SYMBOL_GPL(srp_transfer_data); + +static int vscsis_data_length(struct srp_cmd *cmd, enum dma_data_direction dir) +{ + struct srp_direct_buf *md; + struct srp_indirect_buf *id; + int len = 0, offset = cmd->add_cdb_len * 4; + u8 fmt; + + if (dir == DMA_TO_DEVICE) + fmt = cmd->buf_fmt >> 4; + else { + fmt = cmd->buf_fmt & ((1U << 4) - 1); + offset += data_out_desc_size(cmd); + } + + switch (fmt) { + case SRP_NO_DATA_DESC: + break; + case SRP_DATA_DESC_DIRECT: + md = (struct srp_direct_buf *) (cmd->add_data + offset); + len = md->len; + break; + case SRP_DATA_DESC_INDIRECT: + id = (struct srp_indirect_buf *) (cmd->add_data + offset); + len = id->len; + break; + default: + eprintk("invalid data format %x\n", fmt); + break; + } + return len; +} + +int srp_cmd_queue(struct Scsi_Host *shost, struct srp_cmd *cmd, void *info, + u64 addr) +{ + enum dma_data_direction dir; + struct scsi_cmnd *sc; + int tag, len, err; + + switch (cmd->task_attr) { + case SRP_SIMPLE_TASK: + tag = MSG_SIMPLE_TAG; + break; + case SRP_ORDERED_TASK: + tag = MSG_ORDERED_TAG; + break; + case SRP_HEAD_TASK: + tag = MSG_HEAD_TAG; + break; + default: + eprintk("Task attribute %d not supported\n", cmd->task_attr); + tag = MSG_ORDERED_TAG; + } + + dir = srp_cmd_direction(cmd); + len = vscsis_data_length(cmd, dir); + + dprintk("%p %x %lx %d %d %d %llx\n", info, cmd->cdb[0], + cmd->lun, dir, len, tag, (unsigned long long) cmd->tag); + + sc = scsi_host_get_command(shost, dir, GFP_KERNEL); + if (!sc) + return -ENOMEM; + + sc->SCp.ptr = info; + memcpy(sc->cmnd, cmd->cdb, MAX_COMMAND_SIZE); + sc->request_bufflen = len; + sc->request_buffer = (void *) (unsigned long) addr; + sc->tag = tag; + err = scsi_tgt_queue_command(sc, (struct scsi_lun *) &cmd->lun, cmd->tag); + if (err) + scsi_host_put_command(shost, sc); + + return err; +} +EXPORT_SYMBOL_GPL(srp_cmd_queue); + +MODULE_DESCRIPTION("SCSI RDAM Protocol lib functions"); +MODULE_AUTHOR("FUJITA Tomonori"); +MODULE_LICENSE("GPL"); diff --git a/include/scsi/libsrp.h b/include/scsi/libsrp.h new file mode 100644 index 000000000000..d143171896ae --- /dev/null +++ b/include/scsi/libsrp.h @@ -0,0 +1,77 @@ +#ifndef __LIBSRP_H__ +#define __LIBSRP_H__ + +#include +#include +#include +#include + +enum iue_flags { + V_DIOVER, + V_WRITE, + V_LINKED, + V_FLYING, +}; + +struct srp_buf { + dma_addr_t dma; + void *buf; +}; + +struct srp_queue { + void *pool; + void *items; + struct kfifo *queue; + spinlock_t lock; +}; + +struct srp_target { + struct Scsi_Host *shost; + struct device *dev; + + spinlock_t lock; + struct list_head cmd_queue; + + size_t srp_iu_size; + struct srp_queue iu_queue; + size_t rx_ring_size; + struct srp_buf **rx_ring; + + void *ldata; +}; + +struct iu_entry { + struct srp_target *target; + + struct list_head ilist; + dma_addr_t remote_token; + unsigned long flags; + + struct srp_buf *sbuf; +}; + +typedef int (srp_rdma_t)(struct scsi_cmnd *, struct scatterlist *, int, + struct srp_direct_buf *, int, + enum dma_data_direction, unsigned int); +extern int srp_target_alloc(struct srp_target *, struct device *, size_t, size_t); +extern void srp_target_free(struct srp_target *); + +extern struct iu_entry *srp_iu_get(struct srp_target *); +extern void srp_iu_put(struct iu_entry *); + +extern int srp_cmd_queue(struct Scsi_Host *, struct srp_cmd *, void *, u64); +extern int srp_transfer_data(struct scsi_cmnd *, struct srp_cmd *, + srp_rdma_t, int, int); + + +static inline struct srp_target *host_to_srp_target(struct Scsi_Host *host) +{ + return (struct srp_target *) host->hostdata; +} + +static inline int srp_cmd_direction(struct srp_cmd *cmd) +{ + return (cmd->buf_fmt >> 4) ? DMA_TO_DEVICE : DMA_FROM_DEVICE; +} + +#endif From 0e5d030bef4de3e35bcca35dd9465e6672128570 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Fri, 1 Dec 2006 03:00:54 +0900 Subject: [PATCH 52/73] [SCSI] scsi tgt: IBM eServer i/pSeries virtual SCSI target driver This is IBM Virtual SCSI target driver for tgt. The driver is based on the original ibmvscsis driver: http://lkml.org/lkml/2005/10/17/99 Signed-off-by: FUJITA Tomonori Signed-off-by: Mike Christie Signed-off-by: Santiago Leon Signed-off-by: James Bottomley --- drivers/scsi/Kconfig | 14 + drivers/scsi/Makefile | 1 + drivers/scsi/ibmvscsi/Makefile | 2 + drivers/scsi/ibmvscsi/ibmvstgt.c | 958 +++++++++++++++++++++++++++++++ 4 files changed, 975 insertions(+) create mode 100644 drivers/scsi/ibmvscsi/ibmvstgt.c diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 5570cdbf8f11..1b4bfaabcfbc 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -821,6 +821,20 @@ config SCSI_IBMVSCSI To compile this driver as a module, choose M here: the module will be called ibmvscsic. +config SCSI_IBMVSCSIS + tristate "IBM Virtual SCSI Server support" + depends on PPC_PSERIES && SCSI_TGT && SCSI_SRP + help + This is the SRP target driver for IBM pSeries virtual environments. + + The userspace component needed to initialize the driver and + documentation can be found: + + http://stgt.berlios.de/ + + To compile this driver as a module, choose M here: the + module will be called ibmvstgt. + config SCSI_INITIO tristate "Initio 9100U(W) support" depends on PCI && SCSI diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 00691c4c3a6f..bd7c9888f7f4 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -128,6 +128,7 @@ obj-$(CONFIG_SCSI_NSP32) += nsp32.o obj-$(CONFIG_SCSI_IPR) += ipr.o obj-$(CONFIG_SCSI_SRP) += libsrp.o obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsi/ +obj-$(CONFIG_SCSI_IBMVSCSIS) += ibmvscsi/ obj-$(CONFIG_SCSI_HPTIOP) += hptiop.o obj-$(CONFIG_SCSI_STEX) += stex.o diff --git a/drivers/scsi/ibmvscsi/Makefile b/drivers/scsi/ibmvscsi/Makefile index 4e247b6b8700..6ac0633d5452 100644 --- a/drivers/scsi/ibmvscsi/Makefile +++ b/drivers/scsi/ibmvscsi/Makefile @@ -3,3 +3,5 @@ obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsic.o ibmvscsic-y += ibmvscsi.o ibmvscsic-$(CONFIG_PPC_ISERIES) += iseries_vscsi.o ibmvscsic-$(CONFIG_PPC_PSERIES) += rpa_vscsi.o + +obj-$(CONFIG_SCSI_IBMVSCSIS) += ibmvstgt.o diff --git a/drivers/scsi/ibmvscsi/ibmvstgt.c b/drivers/scsi/ibmvscsi/ibmvstgt.c new file mode 100644 index 000000000000..0e74174a1b37 --- /dev/null +++ b/drivers/scsi/ibmvscsi/ibmvstgt.c @@ -0,0 +1,958 @@ +/* + * IBM eServer i/pSeries Virtual SCSI Target Driver + * Copyright (C) 2003-2005 Dave Boutcher (boutcher@us.ibm.com) IBM Corp. + * Santiago Leon (santil@us.ibm.com) IBM Corp. + * Linda Xie (lxie@us.ibm.com) IBM Corp. + * + * Copyright (C) 2005-2006 FUJITA Tomonori + * + * 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. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ibmvscsi.h" + +#define INITIAL_SRP_LIMIT 16 +#define DEFAULT_MAX_SECTORS 512 + +#define TGT_NAME "ibmvstgt" + +/* + * Hypervisor calls. + */ +#define h_copy_rdma(l, sa, sb, da, db) \ + plpar_hcall_norets(H_COPY_RDMA, l, sa, sb, da, db) +#define h_send_crq(ua, l, h) \ + plpar_hcall_norets(H_SEND_CRQ, ua, l, h) +#define h_reg_crq(ua, tok, sz)\ + plpar_hcall_norets(H_REG_CRQ, ua, tok, sz); +#define h_free_crq(ua) \ + plpar_hcall_norets(H_FREE_CRQ, ua); + +/* tmp - will replace with SCSI logging stuff */ +#define eprintk(fmt, args...) \ +do { \ + printk("%s(%d) " fmt, __FUNCTION__, __LINE__, ##args); \ +} while (0) +/* #define dprintk eprintk */ +#define dprintk(fmt, args...) + +struct vio_port { + struct vio_dev *dma_dev; + + struct crq_queue crq_queue; + struct work_struct crq_work; + + unsigned long liobn; + unsigned long riobn; +}; + +static struct workqueue_struct *vtgtd; + +/* + * These are fixed for the system and come from the Open Firmware device tree. + * We just store them here to save getting them every time. + */ +static char system_id[64] = ""; +static char partition_name[97] = "UNKNOWN"; +static unsigned int partition_number = -1; + +static struct vio_port *target_to_port(struct srp_target *target) +{ + return (struct vio_port *) target->ldata; +} + +static inline union viosrp_iu *vio_iu(struct iu_entry *iue) +{ + return (union viosrp_iu *) (iue->sbuf->buf); +} + +static int send_iu(struct iu_entry *iue, uint64_t length, uint8_t format) +{ + struct srp_target *target = iue->target; + struct vio_port *vport = target_to_port(target); + long rc, rc1; + union { + struct viosrp_crq cooked; + uint64_t raw[2]; + } crq; + + /* First copy the SRP */ + rc = h_copy_rdma(length, vport->liobn, iue->sbuf->dma, + vport->riobn, iue->remote_token); + + if (rc) + eprintk("Error %ld transferring data\n", rc); + + crq.cooked.valid = 0x80; + crq.cooked.format = format; + crq.cooked.reserved = 0x00; + crq.cooked.timeout = 0x00; + crq.cooked.IU_length = length; + crq.cooked.IU_data_ptr = vio_iu(iue)->srp.rsp.tag; + + if (rc == 0) + crq.cooked.status = 0x99; /* Just needs to be non-zero */ + else + crq.cooked.status = 0x00; + + rc1 = h_send_crq(vport->dma_dev->unit_address, crq.raw[0], crq.raw[1]); + + if (rc1) { + eprintk("%ld sending response\n", rc1); + return rc1; + } + + return rc; +} + +#define SRP_RSP_SENSE_DATA_LEN 18 + +static int send_rsp(struct iu_entry *iue, struct scsi_cmnd *sc, + unsigned char status, unsigned char asc) +{ + union viosrp_iu *iu = vio_iu(iue); + uint64_t tag = iu->srp.rsp.tag; + + /* If the linked bit is on and status is good */ + if (test_bit(V_LINKED, &iue->flags) && (status == NO_SENSE)) + status = 0x10; + + memset(iu, 0, sizeof(struct srp_rsp)); + iu->srp.rsp.opcode = SRP_RSP; + iu->srp.rsp.req_lim_delta = 1; + iu->srp.rsp.tag = tag; + + if (test_bit(V_DIOVER, &iue->flags)) + iu->srp.rsp.flags |= SRP_RSP_FLAG_DIOVER; + + iu->srp.rsp.data_in_res_cnt = 0; + iu->srp.rsp.data_out_res_cnt = 0; + + iu->srp.rsp.flags &= ~SRP_RSP_FLAG_RSPVALID; + + iu->srp.rsp.resp_data_len = 0; + iu->srp.rsp.status = status; + if (status) { + uint8_t *sense = iu->srp.rsp.data; + + if (sc) { + iu->srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID; + iu->srp.rsp.sense_data_len = SCSI_SENSE_BUFFERSIZE; + memcpy(sense, sc->sense_buffer, SCSI_SENSE_BUFFERSIZE); + } else { + iu->srp.rsp.status = SAM_STAT_CHECK_CONDITION; + iu->srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID; + iu->srp.rsp.sense_data_len = SRP_RSP_SENSE_DATA_LEN; + + /* Valid bit and 'current errors' */ + sense[0] = (0x1 << 7 | 0x70); + /* Sense key */ + sense[2] = status; + /* Additional sense length */ + sense[7] = 0xa; /* 10 bytes */ + /* Additional sense code */ + sense[12] = asc; + } + } + + send_iu(iue, sizeof(iu->srp.rsp) + SRP_RSP_SENSE_DATA_LEN, + VIOSRP_SRP_FORMAT); + + return 0; +} + +static void handle_cmd_queue(struct srp_target *target) +{ + struct Scsi_Host *shost = target->shost; + struct iu_entry *iue; + struct srp_cmd *cmd; + unsigned long flags; + int err; + +retry: + spin_lock_irqsave(&target->lock, flags); + + list_for_each_entry(iue, &target->cmd_queue, ilist) { + if (!test_and_set_bit(V_FLYING, &iue->flags)) { + spin_unlock_irqrestore(&target->lock, flags); + cmd = iue->sbuf->buf; + err = srp_cmd_queue(shost, cmd, iue, 0); + if (err) { + eprintk("cannot queue cmd %p %d\n", cmd, err); + srp_iu_put(iue); + } + goto retry; + } + } + + spin_unlock_irqrestore(&target->lock, flags); +} + +static int ibmvstgt_rdma(struct scsi_cmnd *sc, struct scatterlist *sg, int nsg, + struct srp_direct_buf *md, int nmd, + enum dma_data_direction dir, unsigned int rest) +{ + struct iu_entry *iue = (struct iu_entry *) sc->SCp.ptr; + struct srp_target *target = iue->target; + struct vio_port *vport = target_to_port(target); + dma_addr_t token; + long err; + unsigned int done = 0; + int i, sidx, soff; + + sidx = soff = 0; + token = sg_dma_address(sg + sidx); + + for (i = 0; i < nmd && rest; i++) { + unsigned int mdone, mlen; + + mlen = min(rest, md[i].len); + for (mdone = 0; mlen;) { + int slen = min(sg_dma_len(sg + sidx) - soff, mlen); + + if (dir == DMA_TO_DEVICE) + err = h_copy_rdma(slen, + vport->riobn, + md[i].va + mdone, + vport->liobn, + token + soff); + else + err = h_copy_rdma(slen, + vport->liobn, + token + soff, + vport->riobn, + md[i].va + mdone); + + if (err != H_SUCCESS) { + eprintk("rdma error %d %d\n", dir, slen); + goto out; + } + + mlen -= slen; + mdone += slen; + soff += slen; + done += slen; + + if (soff == sg_dma_len(sg + sidx)) { + sidx++; + soff = 0; + token = sg_dma_address(sg + sidx); + + if (sidx > nsg) { + eprintk("out of sg %p %d %d\n", + iue, sidx, nsg); + goto out; + } + } + }; + + rest -= mlen; + } +out: + + return 0; +} + +static int ibmvstgt_transfer_data(struct scsi_cmnd *sc, + void (*done)(struct scsi_cmnd *)) +{ + struct iu_entry *iue = (struct iu_entry *) sc->SCp.ptr; + int err; + + err = srp_transfer_data(sc, &vio_iu(iue)->srp.cmd, ibmvstgt_rdma, 1, 1); + + done(sc); + + return err; +} + +static int ibmvstgt_cmd_done(struct scsi_cmnd *sc, + void (*done)(struct scsi_cmnd *)) +{ + unsigned long flags; + struct iu_entry *iue = (struct iu_entry *) sc->SCp.ptr; + struct srp_target *target = iue->target; + + dprintk("%p %p %x\n", iue, target, vio_iu(iue)->srp.cmd.cdb[0]); + + spin_lock_irqsave(&target->lock, flags); + list_del(&iue->ilist); + spin_unlock_irqrestore(&target->lock, flags); + + if (sc->result != SAM_STAT_GOOD) { + eprintk("operation failed %p %d %x\n", + iue, sc->result, vio_iu(iue)->srp.cmd.cdb[0]); + send_rsp(iue, sc, HARDWARE_ERROR, 0x00); + } else + send_rsp(iue, sc, NO_SENSE, 0x00); + + done(sc); + srp_iu_put(iue); + return 0; +} + +int send_adapter_info(struct iu_entry *iue, + dma_addr_t remote_buffer, uint16_t length) +{ + struct srp_target *target = iue->target; + struct vio_port *vport = target_to_port(target); + struct Scsi_Host *shost = target->shost; + dma_addr_t data_token; + struct mad_adapter_info_data *info; + int err; + + info = dma_alloc_coherent(target->dev, sizeof(*info), &data_token, + GFP_KERNEL); + if (!info) { + eprintk("bad dma_alloc_coherent %p\n", target); + return 1; + } + + /* Get remote info */ + err = h_copy_rdma(sizeof(*info), vport->riobn, remote_buffer, + vport->liobn, data_token); + if (err == H_SUCCESS) { + dprintk("Client connect: %s (%d)\n", + info->partition_name, info->partition_number); + } + + memset(info, 0, sizeof(*info)); + + strcpy(info->srp_version, "16.a"); + strncpy(info->partition_name, partition_name, + sizeof(info->partition_name)); + info->partition_number = partition_number; + info->mad_version = 1; + info->os_type = 2; + info->port_max_txu[0] = shost->hostt->max_sectors << 9; + + /* Send our info to remote */ + err = h_copy_rdma(sizeof(*info), vport->liobn, data_token, + vport->riobn, remote_buffer); + + dma_free_coherent(target->dev, sizeof(*info), info, data_token); + + if (err != H_SUCCESS) { + eprintk("Error sending adapter info %d\n", err); + return 1; + } + + return 0; +} + +static void process_login(struct iu_entry *iue) +{ + union viosrp_iu *iu = vio_iu(iue); + struct srp_login_rsp *rsp = &iu->srp.login_rsp; + uint64_t tag = iu->srp.rsp.tag; + + /* TODO handle case that requested size is wrong and + * buffer format is wrong + */ + memset(iu, 0, sizeof(struct srp_login_rsp)); + rsp->opcode = SRP_LOGIN_RSP; + rsp->req_lim_delta = INITIAL_SRP_LIMIT; + rsp->tag = tag; + rsp->max_it_iu_len = sizeof(union srp_iu); + rsp->max_ti_iu_len = sizeof(union srp_iu); + /* direct and indirect */ + rsp->buf_fmt = SRP_BUF_FORMAT_DIRECT | SRP_BUF_FORMAT_INDIRECT; + + send_iu(iue, sizeof(*rsp), VIOSRP_SRP_FORMAT); +} + +static inline void queue_cmd(struct iu_entry *iue) +{ + struct srp_target *target = iue->target; + unsigned long flags; + + spin_lock_irqsave(&target->lock, flags); + list_add_tail(&iue->ilist, &target->cmd_queue); + spin_unlock_irqrestore(&target->lock, flags); +} + +static int process_tsk_mgmt(struct iu_entry *iue) +{ + union viosrp_iu *iu = vio_iu(iue); + int fn; + + dprintk("%p %u\n", iue, iu->srp.tsk_mgmt.tsk_mgmt_func); + + switch (iu->srp.tsk_mgmt.tsk_mgmt_func) { + case SRP_TSK_ABORT_TASK: + fn = ABORT_TASK; + break; + case SRP_TSK_ABORT_TASK_SET: + fn = ABORT_TASK_SET; + break; + case SRP_TSK_CLEAR_TASK_SET: + fn = CLEAR_TASK_SET; + break; + case SRP_TSK_LUN_RESET: + fn = LOGICAL_UNIT_RESET; + break; + case SRP_TSK_CLEAR_ACA: + fn = CLEAR_ACA; + break; + default: + fn = 0; + } + if (fn) + scsi_tgt_tsk_mgmt_request(iue->target->shost, fn, + iu->srp.tsk_mgmt.task_tag, + (struct scsi_lun *) &iu->srp.tsk_mgmt.lun, + iue); + else + send_rsp(iue, NULL, ILLEGAL_REQUEST, 0x20); + + return !fn; +} + +static int process_mad_iu(struct iu_entry *iue) +{ + union viosrp_iu *iu = vio_iu(iue); + struct viosrp_adapter_info *info; + struct viosrp_host_config *conf; + + switch (iu->mad.empty_iu.common.type) { + case VIOSRP_EMPTY_IU_TYPE: + eprintk("%s\n", "Unsupported EMPTY MAD IU"); + break; + case VIOSRP_ERROR_LOG_TYPE: + eprintk("%s\n", "Unsupported ERROR LOG MAD IU"); + iu->mad.error_log.common.status = 1; + send_iu(iue, sizeof(iu->mad.error_log), VIOSRP_MAD_FORMAT); + break; + case VIOSRP_ADAPTER_INFO_TYPE: + info = &iu->mad.adapter_info; + info->common.status = send_adapter_info(iue, info->buffer, + info->common.length); + send_iu(iue, sizeof(*info), VIOSRP_MAD_FORMAT); + break; + case VIOSRP_HOST_CONFIG_TYPE: + conf = &iu->mad.host_config; + conf->common.status = 1; + send_iu(iue, sizeof(*conf), VIOSRP_MAD_FORMAT); + break; + default: + eprintk("Unknown type %u\n", iu->srp.rsp.opcode); + } + + return 1; +} + +static int process_srp_iu(struct iu_entry *iue) +{ + union viosrp_iu *iu = vio_iu(iue); + int done = 1; + u8 opcode = iu->srp.rsp.opcode; + + switch (opcode) { + case SRP_LOGIN_REQ: + process_login(iue); + break; + case SRP_TSK_MGMT: + done = process_tsk_mgmt(iue); + break; + case SRP_CMD: + queue_cmd(iue); + done = 0; + break; + case SRP_LOGIN_RSP: + case SRP_I_LOGOUT: + case SRP_T_LOGOUT: + case SRP_RSP: + case SRP_CRED_REQ: + case SRP_CRED_RSP: + case SRP_AER_REQ: + case SRP_AER_RSP: + eprintk("Unsupported type %u\n", opcode); + break; + default: + eprintk("Unknown type %u\n", opcode); + } + + return done; +} + +static void process_iu(struct viosrp_crq *crq, struct srp_target *target) +{ + struct vio_port *vport = target_to_port(target); + struct iu_entry *iue; + long err, done; + + iue = srp_iu_get(target); + if (!iue) { + eprintk("Error getting IU from pool, %p\n", target); + return; + } + + iue->remote_token = crq->IU_data_ptr; + + err = h_copy_rdma(crq->IU_length, vport->riobn, + iue->remote_token, vport->liobn, iue->sbuf->dma); + + if (err != H_SUCCESS) { + eprintk("%ld transferring data error %p\n", err, iue); + done = 1; + goto out; + } + + if (crq->format == VIOSRP_MAD_FORMAT) + done = process_mad_iu(iue); + else + done = process_srp_iu(iue); +out: + if (done) + srp_iu_put(iue); +} + +static irqreturn_t ibmvstgt_interrupt(int irq, void *data) +{ + struct srp_target *target = (struct srp_target *) data; + struct vio_port *vport = target_to_port(target); + + vio_disable_interrupts(vport->dma_dev); + queue_work(vtgtd, &vport->crq_work); + + return IRQ_HANDLED; +} + +static int crq_queue_create(struct crq_queue *queue, struct srp_target *target) +{ + int err; + struct vio_port *vport = target_to_port(target); + + queue->msgs = (struct viosrp_crq *) get_zeroed_page(GFP_KERNEL); + if (!queue->msgs) + goto malloc_failed; + queue->size = PAGE_SIZE / sizeof(*queue->msgs); + + queue->msg_token = dma_map_single(target->dev, queue->msgs, + queue->size * sizeof(*queue->msgs), + DMA_BIDIRECTIONAL); + + if (dma_mapping_error(queue->msg_token)) + goto map_failed; + + err = h_reg_crq(vport->dma_dev->unit_address, queue->msg_token, + PAGE_SIZE); + + /* If the adapter was left active for some reason (like kexec) + * try freeing and re-registering + */ + if (err == H_RESOURCE) { + do { + err = h_free_crq(vport->dma_dev->unit_address); + } while (err == H_BUSY || H_IS_LONG_BUSY(err)); + + err = h_reg_crq(vport->dma_dev->unit_address, queue->msg_token, + PAGE_SIZE); + } + + if (err != H_SUCCESS && err != 2) { + eprintk("Error 0x%x opening virtual adapter\n", err); + goto reg_crq_failed; + } + + err = request_irq(vport->dma_dev->irq, &ibmvstgt_interrupt, + SA_INTERRUPT, "ibmvstgt", target); + if (err) + goto req_irq_failed; + + vio_enable_interrupts(vport->dma_dev); + + h_send_crq(vport->dma_dev->unit_address, 0xC001000000000000, 0); + + queue->cur = 0; + spin_lock_init(&queue->lock); + + return 0; + +req_irq_failed: + do { + err = h_free_crq(vport->dma_dev->unit_address); + } while (err == H_BUSY || H_IS_LONG_BUSY(err)); + +reg_crq_failed: + dma_unmap_single(target->dev, queue->msg_token, + queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); +map_failed: + free_page((unsigned long) queue->msgs); + +malloc_failed: + return -ENOMEM; +} + +static void crq_queue_destroy(struct srp_target *target) +{ + struct vio_port *vport = target_to_port(target); + struct crq_queue *queue = &vport->crq_queue; + int err; + + free_irq(vport->dma_dev->irq, target); + do { + err = h_free_crq(vport->dma_dev->unit_address); + } while (err == H_BUSY || H_IS_LONG_BUSY(err)); + + dma_unmap_single(target->dev, queue->msg_token, + queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); + + free_page((unsigned long) queue->msgs); +} + +static void process_crq(struct viosrp_crq *crq, struct srp_target *target) +{ + struct vio_port *vport = target_to_port(target); + dprintk("%x %x\n", crq->valid, crq->format); + + switch (crq->valid) { + case 0xC0: + /* initialization */ + switch (crq->format) { + case 0x01: + h_send_crq(vport->dma_dev->unit_address, + 0xC002000000000000, 0); + break; + case 0x02: + break; + default: + eprintk("Unknown format %u\n", crq->format); + } + break; + case 0xFF: + /* transport event */ + break; + case 0x80: + /* real payload */ + switch (crq->format) { + case VIOSRP_SRP_FORMAT: + case VIOSRP_MAD_FORMAT: + process_iu(crq, target); + break; + case VIOSRP_OS400_FORMAT: + case VIOSRP_AIX_FORMAT: + case VIOSRP_LINUX_FORMAT: + case VIOSRP_INLINE_FORMAT: + eprintk("Unsupported format %u\n", crq->format); + break; + default: + eprintk("Unknown format %u\n", crq->format); + } + break; + default: + eprintk("unknown message type 0x%02x!?\n", crq->valid); + } +} + +static inline struct viosrp_crq *next_crq(struct crq_queue *queue) +{ + struct viosrp_crq *crq; + unsigned long flags; + + spin_lock_irqsave(&queue->lock, flags); + crq = &queue->msgs[queue->cur]; + if (crq->valid & 0x80) { + if (++queue->cur == queue->size) + queue->cur = 0; + } else + crq = NULL; + spin_unlock_irqrestore(&queue->lock, flags); + + return crq; +} + +static void handle_crq(void *data) +{ + struct srp_target *target = (struct srp_target *) data; + struct vio_port *vport = target_to_port(target); + struct viosrp_crq *crq; + int done = 0; + + while (!done) { + while ((crq = next_crq(&vport->crq_queue)) != NULL) { + process_crq(crq, target); + crq->valid = 0x00; + } + + vio_enable_interrupts(vport->dma_dev); + + crq = next_crq(&vport->crq_queue); + if (crq) { + vio_disable_interrupts(vport->dma_dev); + process_crq(crq, target); + crq->valid = 0x00; + } else + done = 1; + } + + handle_cmd_queue(target); +} + + +static int ibmvstgt_eh_abort_handler(struct scsi_cmnd *sc) +{ + unsigned long flags; + struct iu_entry *iue = (struct iu_entry *) sc->SCp.ptr; + struct srp_target *target = iue->target; + + dprintk("%p %p %x\n", iue, target, vio_iu(iue)->srp.cmd.cdb[0]); + + spin_lock_irqsave(&target->lock, flags); + list_del(&iue->ilist); + spin_unlock_irqrestore(&target->lock, flags); + + srp_iu_put(iue); + + return 0; +} + +static int ibmvstgt_tsk_mgmt_response(u64 mid, int result) +{ + struct iu_entry *iue = (struct iu_entry *) ((void *) mid); + union viosrp_iu *iu = vio_iu(iue); + unsigned char status, asc; + + eprintk("%p %d\n", iue, result); + status = NO_SENSE; + asc = 0; + + switch (iu->srp.tsk_mgmt.tsk_mgmt_func) { + case SRP_TSK_ABORT_TASK: + asc = 0x14; + if (result) + status = ABORTED_COMMAND; + break; + default: + break; + } + + send_rsp(iue, NULL, status, asc); + srp_iu_put(iue); + + return 0; +} + +static ssize_t system_id_show(struct class_device *cdev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", system_id); +} + +static ssize_t partition_number_show(struct class_device *cdev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%x\n", partition_number); +} + +static ssize_t unit_address_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct srp_target *target = host_to_srp_target(shost); + struct vio_port *vport = target_to_port(target); + return snprintf(buf, PAGE_SIZE, "%x\n", vport->dma_dev->unit_address); +} + +static CLASS_DEVICE_ATTR(system_id, S_IRUGO, system_id_show, NULL); +static CLASS_DEVICE_ATTR(partition_number, S_IRUGO, partition_number_show, NULL); +static CLASS_DEVICE_ATTR(unit_address, S_IRUGO, unit_address_show, NULL); + +static struct class_device_attribute *ibmvstgt_attrs[] = { + &class_device_attr_system_id, + &class_device_attr_partition_number, + &class_device_attr_unit_address, + NULL, +}; + +static struct scsi_host_template ibmvstgt_sht = { + .name = TGT_NAME, + .module = THIS_MODULE, + .can_queue = INITIAL_SRP_LIMIT, + .sg_tablesize = SG_ALL, + .use_clustering = DISABLE_CLUSTERING, + .max_sectors = DEFAULT_MAX_SECTORS, + .transfer_response = ibmvstgt_cmd_done, + .transfer_data = ibmvstgt_transfer_data, + .eh_abort_handler = ibmvstgt_eh_abort_handler, + .tsk_mgmt_response = ibmvstgt_tsk_mgmt_response, + .shost_attrs = ibmvstgt_attrs, + .proc_name = TGT_NAME, +}; + +static int ibmvstgt_probe(struct vio_dev *dev, const struct vio_device_id *id) +{ + struct Scsi_Host *shost; + struct srp_target *target; + struct vio_port *vport; + unsigned int *dma, dma_size; + int err = -ENOMEM; + + vport = kzalloc(sizeof(struct vio_port), GFP_KERNEL); + if (!vport) + return err; + shost = scsi_host_alloc(&ibmvstgt_sht, sizeof(struct srp_target)); + if (!shost) + goto free_vport; + err = scsi_tgt_alloc_queue(shost); + if (err) + goto put_host; + + target = host_to_srp_target(shost); + target->shost = shost; + vport->dma_dev = dev; + target->ldata = vport; + err = srp_target_alloc(target, &dev->dev, INITIAL_SRP_LIMIT, + SRP_MAX_IU_LEN); + if (err) + goto put_host; + + dma = (unsigned int *) vio_get_attribute(dev, "ibm,my-dma-window", + &dma_size); + if (!dma || dma_size != 40) { + eprintk("Couldn't get window property %d\n", dma_size); + err = -EIO; + goto free_srp_target; + } + vport->liobn = dma[0]; + vport->riobn = dma[5]; + + INIT_WORK(&vport->crq_work, handle_crq, target); + + err = crq_queue_create(&vport->crq_queue, target); + if (err) + goto free_srp_target; + + err = scsi_add_host(shost, target->dev); + if (err) + goto destroy_queue; + return 0; + +destroy_queue: + crq_queue_destroy(target); +free_srp_target: + srp_target_free(target); +put_host: + scsi_host_put(shost); +free_vport: + kfree(vport); + return err; +} + +static int ibmvstgt_remove(struct vio_dev *dev) +{ + struct srp_target *target = (struct srp_target *) dev->dev.driver_data; + struct Scsi_Host *shost = target->shost; + struct vio_port *vport = target->ldata; + + crq_queue_destroy(target); + scsi_remove_host(shost); + scsi_tgt_free_queue(shost); + srp_target_free(target); + kfree(vport); + scsi_host_put(shost); + return 0; +} + +static struct vio_device_id ibmvstgt_device_table[] __devinitdata = { + {"v-scsi-host", "IBM,v-scsi-host"}, + {"",""} +}; + +MODULE_DEVICE_TABLE(vio, ibmvstgt_device_table); + +static struct vio_driver ibmvstgt_driver = { + .id_table = ibmvstgt_device_table, + .probe = ibmvstgt_probe, + .remove = ibmvstgt_remove, + .driver = { + .name = "ibmvscsis", + .owner = THIS_MODULE, + } +}; + +static int get_system_info(void) +{ + struct device_node *rootdn; + const char *id, *model, *name; + unsigned int *num; + + rootdn = find_path_device("/"); + if (!rootdn) + return -ENOENT; + + model = get_property(rootdn, "model", NULL); + id = get_property(rootdn, "system-id", NULL); + if (model && id) + snprintf(system_id, sizeof(system_id), "%s-%s", model, id); + + name = get_property(rootdn, "ibm,partition-name", NULL); + if (name) + strncpy(partition_name, name, sizeof(partition_name)); + + num = (unsigned int *) get_property(rootdn, "ibm,partition-no", NULL); + if (num) + partition_number = *num; + + return 0; +} + +static int ibmvstgt_init(void) +{ + int err = -ENOMEM; + + printk("IBM eServer i/pSeries Virtual SCSI Target Driver\n"); + + vtgtd = create_workqueue("ibmvtgtd"); + if (!vtgtd) + return err; + + err = get_system_info(); + if (err) + goto destroy_wq; + + err = vio_register_driver(&ibmvstgt_driver); + if (err) + goto destroy_wq; + + return 0; + +destroy_wq: + destroy_workqueue(vtgtd); + return err; +} + +static void ibmvstgt_exit(void) +{ + printk("Unregister IBM virtual SCSI driver\n"); + + destroy_workqueue(vtgtd); + vio_unregister_driver(&ibmvstgt_driver); +} + +MODULE_DESCRIPTION("IBM Virtual SCSI Target"); +MODULE_AUTHOR("Santiago Leon"); +MODULE_LICENSE("GPL"); + +module_init(ibmvstgt_init); +module_exit(ibmvstgt_exit); From 2fb9bd8b9cbe9a8d70bf5f43a9ee6a4fa565ed5a Mon Sep 17 00:00:00 2001 From: James Smart Date: Sat, 2 Dec 2006 13:33:57 -0500 Subject: [PATCH 53/73] [SCSI] lpfc 8.1.11 : Discovery Fixes Discovery Fixes: - Prevent starting discovery of a node if discovery is in progress. - Code improvement (reduction) for lpfc_findnode_did(). - Update discovery to send RFF to Fabric on link up - Bypass unique WWN checks for fabric addresses - Add ndlp to plogi list prior to issuing the plogi els command Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_ct.c | 24 +++- drivers/scsi/lpfc/lpfc_els.c | 7 + drivers/scsi/lpfc/lpfc_hbadisc.c | 197 ++++------------------------- drivers/scsi/lpfc/lpfc_hw.h | 16 +++ drivers/scsi/lpfc/lpfc_nportdisc.c | 4 +- 5 files changed, 73 insertions(+), 175 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c index 3add7c237859..a51a41b7f15d 100644 --- a/drivers/scsi/lpfc/lpfc_ct.c +++ b/drivers/scsi/lpfc/lpfc_ct.c @@ -558,6 +558,14 @@ lpfc_cmpl_ct_cmd_rsnn_nn(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, return; } +static void +lpfc_cmpl_ct_cmd_rff_id(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + lpfc_cmpl_ct_cmd_rft_id(phba, cmdiocb, rspiocb); + return; +} + void lpfc_get_hba_sym_node_name(struct lpfc_hba * phba, uint8_t * symbp) { @@ -629,6 +637,8 @@ lpfc_ns_cmd(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, int cmdcode) bpl->tus.f.bdeSize = RNN_REQUEST_SZ; else if (cmdcode == SLI_CTNS_RSNN_NN) bpl->tus.f.bdeSize = RSNN_REQUEST_SZ; + else if (cmdcode == SLI_CTNS_RFF_ID) + bpl->tus.f.bdeSize = RFF_REQUEST_SZ; else bpl->tus.f.bdeSize = 0; bpl->tus.w = le32_to_cpu(bpl->tus.w); @@ -660,6 +670,17 @@ lpfc_ns_cmd(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, int cmdcode) cmpl = lpfc_cmpl_ct_cmd_rft_id; break; + case SLI_CTNS_RFF_ID: + CtReq->CommandResponse.bits.CmdRsp = + be16_to_cpu(SLI_CTNS_RFF_ID); + CtReq->un.rff.PortId = be32_to_cpu(phba->fc_myDID); + CtReq->un.rff.feature_res = 0; + CtReq->un.rff.feature_tgt = 0; + CtReq->un.rff.type_code = FC_FCP_DATA; + CtReq->un.rff.feature_init = 1; + cmpl = lpfc_cmpl_ct_cmd_rff_id; + break; + case SLI_CTNS_RNN_ID: CtReq->CommandResponse.bits.CmdRsp = be16_to_cpu(SLI_CTNS_RNN_ID); @@ -934,7 +955,8 @@ lpfc_fdmi_cmd(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, int cmdcode) ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); ae->ad.bits.AttrType = be16_to_cpu(OS_NAME_VERSION); sprintf(ae->un.OsNameVersion, "%s %s %s", - init_utsname()->sysname, init_utsname()->release, + init_utsname()->sysname, + init_utsname()->release, init_utsname()->version); len = strlen(ae->un.OsNameVersion); len += (len & 3) ? (4 - (len & 3)) : 4; diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 71864cdc6c71..60af1c60fe9b 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -657,6 +657,12 @@ lpfc_plogi_confirm_nport(struct lpfc_hba * phba, struct lpfc_dmabuf *prsp, uint8_t name[sizeof (struct lpfc_name)]; uint32_t rc; + /* Fabric nodes can have the same WWPN so we don't bother searching + * by WWPN. Just return the ndlp that was given to us. + */ + if (ndlp->nlp_type & NLP_FABRIC) + return ndlp; + lp = (uint32_t *) prsp->virt; sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t)); memset(name, 0, sizeof (struct lpfc_name)); @@ -2644,6 +2650,7 @@ lpfc_els_handle_rscn(struct lpfc_hba * phba) ndlp->nlp_type |= NLP_FABRIC; ndlp->nlp_prev_state = ndlp->nlp_state; ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); lpfc_issue_els_plogi(phba, NameServer_DID, 0); /* Wait for NameServer login cmpl before we can continue */ diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 19c79a0549a7..983faadec5f3 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -1067,6 +1067,7 @@ lpfc_mbx_cmpl_ns_reg_login(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) lpfc_ns_cmd(phba, ndlp, SLI_CTNS_RNN_ID); lpfc_ns_cmd(phba, ndlp, SLI_CTNS_RSNN_NN); lpfc_ns_cmd(phba, ndlp, SLI_CTNS_RFT_ID); + lpfc_ns_cmd(phba, ndlp, SLI_CTNS_RFF_ID); } phba->fc_ns_retry = 0; @@ -1680,21 +1681,38 @@ lpfc_matchdid(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, uint32_t did) struct lpfc_nodelist * lpfc_findnode_did(struct lpfc_hba * phba, uint32_t order, uint32_t did) { - struct lpfc_nodelist *ndlp, *next_ndlp; + struct lpfc_nodelist *ndlp; + struct list_head *lists[]={&phba->fc_nlpunmap_list, + &phba->fc_nlpmap_list, + &phba->fc_plogi_list, + &phba->fc_adisc_list, + &phba->fc_reglogin_list, + &phba->fc_prli_list, + &phba->fc_npr_list, + &phba->fc_unused_list}; + uint32_t search[]={NLP_SEARCH_UNMAPPED, + NLP_SEARCH_MAPPED, + NLP_SEARCH_PLOGI, + NLP_SEARCH_ADISC, + NLP_SEARCH_REGLOGIN, + NLP_SEARCH_PRLI, + NLP_SEARCH_NPR, + NLP_SEARCH_UNUSED}; + int i; uint32_t data1; spin_lock_irq(phba->host->host_lock); - if (order & NLP_SEARCH_UNMAPPED) { - list_for_each_entry_safe(ndlp, next_ndlp, - &phba->fc_nlpunmap_list, nlp_listp) { + for (i = 0; i < ARRAY_SIZE(lists); i++ ) { + if (!(order & search[i])) + continue; + list_for_each_entry(ndlp, lists[i], nlp_listp) { if (lpfc_matchdid(phba, ndlp, did)) { data1 = (((uint32_t) ndlp->nlp_state << 24) | ((uint32_t) ndlp->nlp_xri << 16) | ((uint32_t) ndlp->nlp_type << 8) | ((uint32_t) ndlp->nlp_rpi & 0xff)); - /* FIND node DID unmapped */ lpfc_printf_log(phba, KERN_INFO, LOG_NODE, - "%d:0929 FIND node DID unmapped" + "%d:0929 FIND node DID " " Data: x%p x%x x%x x%x\n", phba->brd_no, ndlp, ndlp->nlp_DID, @@ -1704,177 +1722,12 @@ lpfc_findnode_did(struct lpfc_hba * phba, uint32_t order, uint32_t did) } } } - - if (order & NLP_SEARCH_MAPPED) { - list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpmap_list, - nlp_listp) { - if (lpfc_matchdid(phba, ndlp, did)) { - - data1 = (((uint32_t) ndlp->nlp_state << 24) | - ((uint32_t) ndlp->nlp_xri << 16) | - ((uint32_t) ndlp->nlp_type << 8) | - ((uint32_t) ndlp->nlp_rpi & 0xff)); - /* FIND node DID mapped */ - lpfc_printf_log(phba, KERN_INFO, LOG_NODE, - "%d:0930 FIND node DID mapped " - "Data: x%p x%x x%x x%x\n", - phba->brd_no, - ndlp, ndlp->nlp_DID, - ndlp->nlp_flag, data1); - spin_unlock_irq(phba->host->host_lock); - return ndlp; - } - } - } - - if (order & NLP_SEARCH_PLOGI) { - list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_plogi_list, - nlp_listp) { - if (lpfc_matchdid(phba, ndlp, did)) { - - data1 = (((uint32_t) ndlp->nlp_state << 24) | - ((uint32_t) ndlp->nlp_xri << 16) | - ((uint32_t) ndlp->nlp_type << 8) | - ((uint32_t) ndlp->nlp_rpi & 0xff)); - /* LOG change to PLOGI */ - /* FIND node DID plogi */ - lpfc_printf_log(phba, KERN_INFO, LOG_NODE, - "%d:0908 FIND node DID plogi " - "Data: x%p x%x x%x x%x\n", - phba->brd_no, - ndlp, ndlp->nlp_DID, - ndlp->nlp_flag, data1); - spin_unlock_irq(phba->host->host_lock); - return ndlp; - } - } - } - - if (order & NLP_SEARCH_ADISC) { - list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_adisc_list, - nlp_listp) { - if (lpfc_matchdid(phba, ndlp, did)) { - - data1 = (((uint32_t) ndlp->nlp_state << 24) | - ((uint32_t) ndlp->nlp_xri << 16) | - ((uint32_t) ndlp->nlp_type << 8) | - ((uint32_t) ndlp->nlp_rpi & 0xff)); - /* LOG change to ADISC */ - /* FIND node DID adisc */ - lpfc_printf_log(phba, KERN_INFO, LOG_NODE, - "%d:0931 FIND node DID adisc " - "Data: x%p x%x x%x x%x\n", - phba->brd_no, - ndlp, ndlp->nlp_DID, - ndlp->nlp_flag, data1); - spin_unlock_irq(phba->host->host_lock); - return ndlp; - } - } - } - - if (order & NLP_SEARCH_REGLOGIN) { - list_for_each_entry_safe(ndlp, next_ndlp, - &phba->fc_reglogin_list, nlp_listp) { - if (lpfc_matchdid(phba, ndlp, did)) { - - data1 = (((uint32_t) ndlp->nlp_state << 24) | - ((uint32_t) ndlp->nlp_xri << 16) | - ((uint32_t) ndlp->nlp_type << 8) | - ((uint32_t) ndlp->nlp_rpi & 0xff)); - /* LOG change to REGLOGIN */ - /* FIND node DID reglogin */ - lpfc_printf_log(phba, KERN_INFO, LOG_NODE, - "%d:0901 FIND node DID reglogin" - " Data: x%p x%x x%x x%x\n", - phba->brd_no, - ndlp, ndlp->nlp_DID, - ndlp->nlp_flag, data1); - spin_unlock_irq(phba->host->host_lock); - return ndlp; - } - } - } - - if (order & NLP_SEARCH_PRLI) { - list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_prli_list, - nlp_listp) { - if (lpfc_matchdid(phba, ndlp, did)) { - - data1 = (((uint32_t) ndlp->nlp_state << 24) | - ((uint32_t) ndlp->nlp_xri << 16) | - ((uint32_t) ndlp->nlp_type << 8) | - ((uint32_t) ndlp->nlp_rpi & 0xff)); - /* LOG change to PRLI */ - /* FIND node DID prli */ - lpfc_printf_log(phba, KERN_INFO, LOG_NODE, - "%d:0902 FIND node DID prli " - "Data: x%p x%x x%x x%x\n", - phba->brd_no, - ndlp, ndlp->nlp_DID, - ndlp->nlp_flag, data1); - spin_unlock_irq(phba->host->host_lock); - return ndlp; - } - } - } - - if (order & NLP_SEARCH_NPR) { - list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list, - nlp_listp) { - if (lpfc_matchdid(phba, ndlp, did)) { - - data1 = (((uint32_t) ndlp->nlp_state << 24) | - ((uint32_t) ndlp->nlp_xri << 16) | - ((uint32_t) ndlp->nlp_type << 8) | - ((uint32_t) ndlp->nlp_rpi & 0xff)); - /* LOG change to NPR */ - /* FIND node DID npr */ - lpfc_printf_log(phba, KERN_INFO, LOG_NODE, - "%d:0903 FIND node DID npr " - "Data: x%p x%x x%x x%x\n", - phba->brd_no, - ndlp, ndlp->nlp_DID, - ndlp->nlp_flag, data1); - spin_unlock_irq(phba->host->host_lock); - return ndlp; - } - } - } - - if (order & NLP_SEARCH_UNUSED) { - list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_adisc_list, - nlp_listp) { - if (lpfc_matchdid(phba, ndlp, did)) { - - data1 = (((uint32_t) ndlp->nlp_state << 24) | - ((uint32_t) ndlp->nlp_xri << 16) | - ((uint32_t) ndlp->nlp_type << 8) | - ((uint32_t) ndlp->nlp_rpi & 0xff)); - /* LOG change to UNUSED */ - /* FIND node DID unused */ - lpfc_printf_log(phba, KERN_INFO, LOG_NODE, - "%d:0905 FIND node DID unused " - "Data: x%p x%x x%x x%x\n", - phba->brd_no, - ndlp, ndlp->nlp_DID, - ndlp->nlp_flag, data1); - spin_unlock_irq(phba->host->host_lock); - return ndlp; - } - } - } - spin_unlock_irq(phba->host->host_lock); /* FIND node did NOT FOUND */ - lpfc_printf_log(phba, - KERN_INFO, - LOG_NODE, + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, "%d:0932 FIND node did x%x NOT FOUND Data: x%x\n", phba->brd_no, did, order); - - /* no match found */ return NULL; } diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h index eedf98801366..d7306679f1f9 100644 --- a/drivers/scsi/lpfc/lpfc_hw.h +++ b/drivers/scsi/lpfc/lpfc_hw.h @@ -121,6 +121,20 @@ struct lpfc_sli_ct_request { uint32_t rsvd[7]; } rft; + struct rff { + uint32_t PortId; + uint8_t reserved[2]; +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t feature_res:6; + uint8_t feature_init:1; + uint8_t feature_tgt:1; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t feature_tgt:1; + uint8_t feature_init:1; + uint8_t feature_res:6; +#endif + uint8_t type_code; /* type=8 for FCP */ + } rff; struct rnn { uint32_t PortId; /* For RNN_ID requests */ uint8_t wwnn[8]; @@ -136,6 +150,7 @@ struct lpfc_sli_ct_request { #define SLI_CT_REVISION 1 #define GID_REQUEST_SZ (sizeof(struct lpfc_sli_ct_request) - 260) #define RFT_REQUEST_SZ (sizeof(struct lpfc_sli_ct_request) - 228) +#define RFF_REQUEST_SZ (sizeof(struct lpfc_sli_ct_request) - 235) #define RNN_REQUEST_SZ (sizeof(struct lpfc_sli_ct_request) - 252) #define RSNN_REQUEST_SZ (sizeof(struct lpfc_sli_ct_request)) @@ -225,6 +240,7 @@ struct lpfc_sli_ct_request { #define SLI_CTNS_RNN_ID 0x0213 #define SLI_CTNS_RCS_ID 0x0214 #define SLI_CTNS_RFT_ID 0x0217 +#define SLI_CTNS_RFF_ID 0x021F #define SLI_CTNS_RSPN_ID 0x0218 #define SLI_CTNS_RPT_ID 0x021A #define SLI_CTNS_RIP_NN 0x0235 diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index d5f415007db2..958c330ecaad 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -1620,8 +1620,8 @@ lpfc_rcv_padisc_npr_node(struct lpfc_hba * phba, * or discovery in progress for this node. Starting discovery * here will affect the counting of discovery threads. */ - if ((!(ndlp->nlp_flag & NLP_DELAY_TMO)) && - (ndlp->nlp_flag & NLP_NPR_2B_DISC)){ + if (!(ndlp->nlp_flag & NLP_DELAY_TMO) && + !(ndlp->nlp_flag & NLP_NPR_2B_DISC)){ if (ndlp->nlp_flag & NLP_NPR_ADISC) { ndlp->nlp_prev_state = NLP_STE_NPR_NODE; ndlp->nlp_state = NLP_STE_ADISC_ISSUE; From a4bc3379fbc368597024104727fdf14ced483c14 Mon Sep 17 00:00:00 2001 From: James Smart Date: Sat, 2 Dec 2006 13:34:16 -0500 Subject: [PATCH 54/73] [SCSI] lpfc 8.1.11 : Fix lpfc_multi_ring_support It was not accounted for in the fast/slow rings. Genericize the implementation and control it via sysfs Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc.h | 2 ++ drivers/scsi/lpfc/lpfc_attr.c | 21 ++++++++++++++++++- drivers/scsi/lpfc/lpfc_els.c | 6 +++--- drivers/scsi/lpfc/lpfc_hbadisc.c | 12 +++++------ drivers/scsi/lpfc/lpfc_hw.h | 6 +++--- drivers/scsi/lpfc/lpfc_init.c | 4 ++-- drivers/scsi/lpfc/lpfc_sli.c | 35 +++++++++++++++++++++++++++----- drivers/scsi/lpfc/lpfc_sli.h | 2 +- 8 files changed, 67 insertions(+), 21 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 3f7f5f8abd75..e94190e83fc3 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -296,6 +296,8 @@ struct lpfc_hba { uint32_t cfg_cr_delay; uint32_t cfg_cr_count; uint32_t cfg_multi_ring_support; + uint32_t cfg_multi_ring_rctl; + uint32_t cfg_multi_ring_type; uint32_t cfg_fdmi_on; uint32_t cfg_discovery_threads; uint32_t cfg_max_luns; diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 2a4e02e7a392..f05ef3011307 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -802,7 +802,6 @@ static CLASS_DEVICE_ATTR(lpfc_devloss_tmo, S_IRUGO | S_IWUSR, # LOG_MBOX 0x4 Mailbox events # LOG_INIT 0x8 Initialization events # LOG_LINK_EVENT 0x10 Link events -# LOG_IP 0x20 IP traffic history # LOG_FCP 0x40 FCP traffic history # LOG_NODE 0x80 Node table events # LOG_MISC 0x400 Miscellaneous events @@ -915,6 +914,22 @@ LPFC_ATTR_RW(cr_count, 1, 1, 255, "A count of I/O completions after which an " LPFC_ATTR_R(multi_ring_support, 1, 1, 2, "Determines number of primary " "SLI rings to spread IOCB entries across"); +/* +# lpfc_multi_ring_rctl: If lpfc_multi_ring_support is enabled, this +# identifies what rctl value to configure the additional ring for. +# Value range is [1,0xff]. Default value is 4 (Unsolicated Data). +*/ +LPFC_ATTR_R(multi_ring_rctl, FC_UNSOL_DATA, 1, + 255, "Identifies RCTL for additional ring configuration"); + +/* +# lpfc_multi_ring_type: If lpfc_multi_ring_support is enabled, this +# identifies what type value to configure the additional ring for. +# Value range is [1,0xff]. Default value is 5 (LLC/SNAP). +*/ +LPFC_ATTR_R(multi_ring_type, FC_LLC_SNAP, 1, + 255, "Identifies TYPE for additional ring configuration"); + /* # lpfc_fdmi_on: controls FDMI support. # 0 = no FDMI support @@ -974,6 +989,8 @@ struct class_device_attribute *lpfc_host_attrs[] = { &class_device_attr_lpfc_cr_delay, &class_device_attr_lpfc_cr_count, &class_device_attr_lpfc_multi_ring_support, + &class_device_attr_lpfc_multi_ring_rctl, + &class_device_attr_lpfc_multi_ring_type, &class_device_attr_lpfc_fdmi_on, &class_device_attr_lpfc_max_luns, &class_device_attr_nport_evt_cnt, @@ -1771,6 +1788,8 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) lpfc_cr_delay_init(phba, lpfc_cr_delay); lpfc_cr_count_init(phba, lpfc_cr_count); lpfc_multi_ring_support_init(phba, lpfc_multi_ring_support); + lpfc_multi_ring_rctl_init(phba, lpfc_multi_ring_rctl); + lpfc_multi_ring_type_init(phba, lpfc_multi_ring_type); lpfc_lun_queue_depth_init(phba, lpfc_lun_queue_depth); lpfc_fcp_class_init(phba, lpfc_fcp_class); lpfc_use_adisc_init(phba, lpfc_use_adisc); diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 60af1c60fe9b..a47e5ab1ec4d 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -1128,7 +1128,7 @@ lpfc_cmpl_els_adisc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, mempool_free(mbox, phba->mbox_mem_pool); lpfc_disc_flush_list(phba); - psli->ring[(psli->ip_ring)]. + psli->ring[(psli->extra_ring)]. flag &= ~LPFC_STOP_IOCB_EVENT; psli->ring[(psli->fcp_ring)]. @@ -3046,7 +3046,7 @@ lpfc_els_rcv_farp(struct lpfc_hba * phba, /* FARP-REQ received from DID */ lpfc_printf_log(phba, KERN_INFO, - LOG_IP, + LOG_ELS, "%d:0601 FARP-REQ received from DID x%x\n", phba->brd_no, did); @@ -3108,7 +3108,7 @@ lpfc_els_rcv_farpr(struct lpfc_hba * phba, /* FARP-RSP received from DID */ lpfc_printf_log(phba, KERN_INFO, - LOG_IP, + LOG_ELS, "%d:0600 FARP-RSP received from DID x%x\n", phba->brd_no, did); diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 983faadec5f3..7c28184ad56d 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -525,7 +525,7 @@ lpfc_mbx_cmpl_clear_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) psli = &phba->sli; mb = &pmb->mb; /* Since we don't do discovery right now, turn these off here */ - psli->ring[psli->ip_ring].flag &= ~LPFC_STOP_IOCB_EVENT; + psli->ring[psli->extra_ring].flag &= ~LPFC_STOP_IOCB_EVENT; psli->ring[psli->fcp_ring].flag &= ~LPFC_STOP_IOCB_EVENT; psli->ring[psli->next_ring].flag &= ~LPFC_STOP_IOCB_EVENT; @@ -641,7 +641,7 @@ out: if (rc == MBX_NOT_FINISHED) { mempool_free(pmb, phba->mbox_mem_pool); lpfc_disc_flush_list(phba); - psli->ring[(psli->ip_ring)].flag &= ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->extra_ring)].flag &= ~LPFC_STOP_IOCB_EVENT; psli->ring[(psli->fcp_ring)].flag &= ~LPFC_STOP_IOCB_EVENT; psli->ring[(psli->next_ring)].flag &= ~LPFC_STOP_IOCB_EVENT; phba->hba_state = LPFC_HBA_READY; @@ -696,7 +696,7 @@ out: == MBX_NOT_FINISHED) { mempool_free( pmb, phba->mbox_mem_pool); lpfc_disc_flush_list(phba); - psli->ring[(psli->ip_ring)].flag &= + psli->ring[(psli->extra_ring)].flag &= ~LPFC_STOP_IOCB_EVENT; psli->ring[(psli->fcp_ring)].flag &= ~LPFC_STOP_IOCB_EVENT; @@ -1424,7 +1424,7 @@ lpfc_check_sli_ndlp(struct lpfc_hba * phba, if (iocb->context1 == (uint8_t *) ndlp) return 1; } - } else if (pring->ringno == psli->ip_ring) { + } else if (pring->ringno == psli->extra_ring) { } else if (pring->ringno == psli->fcp_ring) { /* Skip match check if waiting to relogin to FCP target */ @@ -1889,7 +1889,7 @@ lpfc_disc_start(struct lpfc_hba * phba) if (rc == MBX_NOT_FINISHED) { mempool_free( mbox, phba->mbox_mem_pool); lpfc_disc_flush_list(phba); - psli->ring[(psli->ip_ring)].flag &= + psli->ring[(psli->extra_ring)].flag &= ~LPFC_STOP_IOCB_EVENT; psli->ring[(psli->fcp_ring)].flag &= ~LPFC_STOP_IOCB_EVENT; @@ -2268,7 +2268,7 @@ lpfc_disc_timeout_handler(struct lpfc_hba *phba) if (clrlaerr) { lpfc_disc_flush_list(phba); - psli->ring[(psli->ip_ring)].flag &= ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->extra_ring)].flag &= ~LPFC_STOP_IOCB_EVENT; psli->ring[(psli->fcp_ring)].flag &= ~LPFC_STOP_IOCB_EVENT; psli->ring[(psli->next_ring)].flag &= ~LPFC_STOP_IOCB_EVENT; phba->hba_state = LPFC_HBA_READY; diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h index d7306679f1f9..0d08a50ca41e 100644 --- a/drivers/scsi/lpfc/lpfc_hw.h +++ b/drivers/scsi/lpfc/lpfc_hw.h @@ -42,14 +42,14 @@ #define FCELSSIZE 1024 /* maximum ELS transfer size */ #define LPFC_FCP_RING 0 /* ring 0 for FCP initiator commands */ -#define LPFC_IP_RING 1 /* ring 1 for IP commands */ +#define LPFC_EXTRA_RING 1 /* ring 1 for other protocols */ #define LPFC_ELS_RING 2 /* ring 2 for ELS commands */ #define LPFC_FCP_NEXT_RING 3 #define SLI2_IOCB_CMD_R0_ENTRIES 172 /* SLI-2 FCP command ring entries */ #define SLI2_IOCB_RSP_R0_ENTRIES 134 /* SLI-2 FCP response ring entries */ -#define SLI2_IOCB_CMD_R1_ENTRIES 4 /* SLI-2 IP command ring entries */ -#define SLI2_IOCB_RSP_R1_ENTRIES 4 /* SLI-2 IP response ring entries */ +#define SLI2_IOCB_CMD_R1_ENTRIES 4 /* SLI-2 extra command ring entries */ +#define SLI2_IOCB_RSP_R1_ENTRIES 4 /* SLI-2 extra response ring entries */ #define SLI2_IOCB_CMD_R1XTRA_ENTRIES 36 /* SLI-2 extra FCP cmd ring entries */ #define SLI2_IOCB_RSP_R1XTRA_ENTRIES 52 /* SLI-2 extra FCP rsp ring entries */ #define SLI2_IOCB_CMD_R2_ENTRIES 20 /* SLI-2 ELS command ring entries */ diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index a5723ad0a099..fd734b0fe95f 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -349,8 +349,8 @@ lpfc_config_port_post(struct lpfc_hba * phba) phba->hba_state = LPFC_LINK_DOWN; /* Only process IOCBs on ring 0 till hba_state is READY */ - if (psli->ring[psli->ip_ring].cmdringaddr) - psli->ring[psli->ip_ring].flag |= LPFC_STOP_IOCB_EVENT; + if (psli->ring[psli->extra_ring].cmdringaddr) + psli->ring[psli->extra_ring].flag |= LPFC_STOP_IOCB_EVENT; if (psli->ring[psli->fcp_ring].cmdringaddr) psli->ring[psli->fcp_ring].flag |= LPFC_STOP_IOCB_EVENT; if (psli->ring[psli->next_ring].cmdringaddr) diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 582f5ea4e84e..120af3db635a 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -1098,6 +1098,7 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba * phba, lpfc_sli_pcimem_bcopy((uint32_t *) entry, (uint32_t *) &rspiocbq.iocb, sizeof (IOCB_t)); + INIT_LIST_HEAD(&(rspiocbq.list)); irsp = &rspiocbq.iocb; type = lpfc_sli_iocb_cmd_type(irsp->ulpCommand & CMD_IOCB_MASK); @@ -1149,6 +1150,11 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba * phba, } } break; + case LPFC_UNSOL_IOCB: + spin_unlock_irqrestore(phba->host->host_lock, iflag); + lpfc_sli_process_unsol_iocb(phba, pring, &rspiocbq); + spin_lock_irqsave(phba->host->host_lock, iflag); + break; default: if (irsp->ulpCommand == CMD_ADAPTER_MSG) { char adaptermsg[LPFC_MAX_ADPTMSG]; @@ -2472,13 +2478,17 @@ lpfc_extra_ring_setup( struct lpfc_hba *phba) psli = &phba->sli; /* Adjust cmd/rsp ring iocb entries more evenly */ + + /* Take some away from the FCP ring */ pring = &psli->ring[psli->fcp_ring]; pring->numCiocb -= SLI2_IOCB_CMD_R1XTRA_ENTRIES; pring->numRiocb -= SLI2_IOCB_RSP_R1XTRA_ENTRIES; pring->numCiocb -= SLI2_IOCB_CMD_R3XTRA_ENTRIES; pring->numRiocb -= SLI2_IOCB_RSP_R3XTRA_ENTRIES; - pring = &psli->ring[1]; + /* and give them to the extra ring */ + pring = &psli->ring[psli->extra_ring]; + pring->numCiocb += SLI2_IOCB_CMD_R1XTRA_ENTRIES; pring->numRiocb += SLI2_IOCB_RSP_R1XTRA_ENTRIES; pring->numCiocb += SLI2_IOCB_CMD_R3XTRA_ENTRIES; @@ -2488,8 +2498,8 @@ lpfc_extra_ring_setup( struct lpfc_hba *phba) pring->iotag_max = 4096; pring->num_mask = 1; pring->prt[0].profile = 0; /* Mask 0 */ - pring->prt[0].rctl = FC_UNSOL_DATA; - pring->prt[0].type = 5; + pring->prt[0].rctl = phba->cfg_multi_ring_rctl; + pring->prt[0].type = phba->cfg_multi_ring_type; pring->prt[0].lpfc_sli_rcv_unsol_event = NULL; return 0; } @@ -2505,7 +2515,7 @@ lpfc_sli_setup(struct lpfc_hba *phba) psli->sli_flag = 0; psli->fcp_ring = LPFC_FCP_RING; psli->next_ring = LPFC_FCP_NEXT_RING; - psli->ip_ring = LPFC_IP_RING; + psli->extra_ring = LPFC_EXTRA_RING; psli->iocbq_lookup = NULL; psli->iocbq_lookup_len = 0; @@ -2528,7 +2538,7 @@ lpfc_sli_setup(struct lpfc_hba *phba) pring->fast_iotag = pring->iotag_max; pring->num_mask = 0; break; - case LPFC_IP_RING: /* ring 1 - IP */ + case LPFC_EXTRA_RING: /* ring 1 - EXTRA */ /* numCiocb and numRiocb are used in config_port */ pring->numCiocb = SLI2_IOCB_CMD_R1_ENTRIES; pring->numRiocb = SLI2_IOCB_RSP_R1_ENTRIES; @@ -3238,6 +3248,21 @@ lpfc_intr_handler(int irq, void *dev_id) lpfc_sli_handle_fast_ring_event(phba, &phba->sli.ring[LPFC_FCP_RING], status); + + if (phba->cfg_multi_ring_support == 2) { + /* + * Process all events on extra ring. Take the optimized path + * for extra ring IO. Any other IO is slow path and is handled + * by the worker thread. + */ + status = (ha_copy & (HA_RXMASK << (4*LPFC_EXTRA_RING))); + status >>= (4*LPFC_EXTRA_RING); + if (status & HA_RXATT) { + lpfc_sli_handle_fast_ring_event(phba, + &phba->sli.ring[LPFC_EXTRA_RING], + status); + } + } return IRQ_HANDLED; } /* lpfc_intr_handler */ diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h index e26de6809358..a43549959dc7 100644 --- a/drivers/scsi/lpfc/lpfc_sli.h +++ b/drivers/scsi/lpfc/lpfc_sli.h @@ -198,7 +198,7 @@ struct lpfc_sli { int fcp_ring; /* ring used for FCP initiator commands */ int next_ring; - int ip_ring; /* ring used for IP network drv cmds */ + int extra_ring; /* extra ring used for other protocols */ struct lpfc_sli_stat slistat; /* SLI statistical info */ struct list_head mboxq; From 146911500f2572fba31895aebacdc4f283208c37 Mon Sep 17 00:00:00 2001 From: James Smart Date: Sat, 2 Dec 2006 13:34:28 -0500 Subject: [PATCH 55/73] [SCSI] lpfc 8.1.11 : Fix Memory leaks Fix Memory leaks associated with mbox cmds READ_LA, READ_SPARAM, REG_LOGIN Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_els.c | 21 ++++++++++++++++++--- drivers/scsi/lpfc/lpfc_hbadisc.c | 18 ++++++++++++++++-- drivers/scsi/lpfc/lpfc_init.c | 4 +++- drivers/scsi/lpfc/lpfc_nportdisc.c | 5 ++++- 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index a47e5ab1ec4d..a5f33a0dd4e7 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -243,6 +243,7 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, struct serv_parm *sp, IOCB_t *irsp) { LPFC_MBOXQ_t *mbox; + struct lpfc_dmabuf *mp; int rc; spin_lock_irq(phba->host->host_lock); @@ -307,10 +308,14 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT | MBX_STOP_IOCB); if (rc == MBX_NOT_FINISHED) - goto fail_free_mbox; + goto fail_issue_reg_login; return 0; + fail_issue_reg_login: + mp = (struct lpfc_dmabuf *) mbox->context1; + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); fail_free_mbox: mempool_free(mbox, phba->mbox_mem_pool); fail: @@ -1857,6 +1862,7 @@ lpfc_cmpl_els_acc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, IOCB_t *irsp; struct lpfc_nodelist *ndlp; LPFC_MBOXQ_t *mbox = NULL; + struct lpfc_dmabuf *mp; irsp = &rspiocb->iocb; @@ -1868,6 +1874,11 @@ lpfc_cmpl_els_acc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, /* Check to see if link went down during discovery */ if ((lpfc_els_chk_latt(phba)) || !ndlp) { if (mbox) { + mp = (struct lpfc_dmabuf *) mbox->context1; + if (mp) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } mempool_free( mbox, phba->mbox_mem_pool); } goto out; @@ -1899,9 +1910,7 @@ lpfc_cmpl_els_acc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, } /* NOTE: we should have messages for unsuccessful reglogin */ - mempool_free( mbox, phba->mbox_mem_pool); } else { - mempool_free( mbox, phba->mbox_mem_pool); /* Do not call NO_LIST for lpfc_els_abort'ed ELS cmds */ if (!((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) && ((irsp->un.ulpWord[4] == IOERR_SLI_ABORTED) || @@ -1913,6 +1922,12 @@ lpfc_cmpl_els_acc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, } } } + mp = (struct lpfc_dmabuf *) mbox->context1; + if (mp) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } + mempool_free(mbox, phba->mbox_mem_pool); } out: if (ndlp) { diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 7c28184ad56d..1118b6d49fcc 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -715,6 +715,9 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, READ_LA_VAR *la) { int i; LPFC_MBOXQ_t *sparam_mbox, *cfglink_mbox; + struct lpfc_dmabuf *mp; + int rc; + sparam_mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); cfglink_mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); @@ -793,16 +796,27 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, READ_LA_VAR *la) if (sparam_mbox) { lpfc_read_sparam(phba, sparam_mbox); sparam_mbox->mbox_cmpl = lpfc_mbx_cmpl_read_sparam; - lpfc_sli_issue_mbox(phba, sparam_mbox, + rc = lpfc_sli_issue_mbox(phba, sparam_mbox, (MBX_NOWAIT | MBX_STOP_IOCB)); + if (rc == MBX_NOT_FINISHED) { + mp = (struct lpfc_dmabuf *) sparam_mbox->context1; + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free(sparam_mbox, phba->mbox_mem_pool); + if (cfglink_mbox) + mempool_free(cfglink_mbox, phba->mbox_mem_pool); + return; + } } if (cfglink_mbox) { phba->hba_state = LPFC_LOCAL_CFG_LINK; lpfc_config_link(phba, cfglink_mbox); cfglink_mbox->mbox_cmpl = lpfc_mbx_cmpl_local_config_link; - lpfc_sli_issue_mbox(phba, cfglink_mbox, + rc = lpfc_sli_issue_mbox(phba, cfglink_mbox, (MBX_NOWAIT | MBX_STOP_IOCB)); + if (rc == MBX_NOT_FINISHED) + mempool_free(cfglink_mbox, phba->mbox_mem_pool); } } diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index fd734b0fe95f..6b0718573ac5 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -611,7 +611,7 @@ lpfc_handle_latt(struct lpfc_hba * phba) pmb->mbox_cmpl = lpfc_mbx_cmpl_read_la; rc = lpfc_sli_issue_mbox (phba, pmb, (MBX_NOWAIT | MBX_STOP_IOCB)); if (rc == MBX_NOT_FINISHED) - goto lpfc_handle_latt_free_mp; + goto lpfc_handle_latt_free_mbuf; /* Clear Link Attention in HA REG */ spin_lock_irq(phba->host->host_lock); @@ -621,6 +621,8 @@ lpfc_handle_latt(struct lpfc_hba * phba) return; +lpfc_handle_latt_free_mbuf: + lpfc_mbuf_free(phba, mp->virt, mp->phys); lpfc_handle_latt_free_mp: kfree(mp); lpfc_handle_latt_free_pmb: diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index 958c330ecaad..0c7e731dc45a 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -739,7 +739,7 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_hba * phba, uint32_t evt) { struct lpfc_iocbq *cmdiocb, *rspiocb; - struct lpfc_dmabuf *pcmd, *prsp; + struct lpfc_dmabuf *pcmd, *prsp, *mp; uint32_t *lp; IOCB_t *irsp; struct serv_parm *sp; @@ -829,6 +829,9 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_hba * phba, NLP_REGLOGIN_LIST); return ndlp->nlp_state; } + mp = (struct lpfc_dmabuf *)mbox->context1; + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); mempool_free(mbox, phba->mbox_mem_pool); } else { mempool_free(mbox, phba->mbox_mem_pool); From c7743956f8cb7b496c05d8444050eba908662a14 Mon Sep 17 00:00:00 2001 From: James Smart Date: Sat, 2 Dec 2006 13:34:42 -0500 Subject: [PATCH 56/73] [SCSI] lpfc 8.1.11 : Adjust LOG_FCP logging Adjust LOG_FCP logging to be more meaningful. Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_attr.c | 2 +- drivers/scsi/lpfc/lpfc_logmsg.h | 2 +- drivers/scsi/lpfc/lpfc_scsi.c | 35 +++++++++++++++++++++------------ 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index f05ef3011307..73d3afc72de2 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -806,7 +806,7 @@ static CLASS_DEVICE_ATTR(lpfc_devloss_tmo, S_IRUGO | S_IWUSR, # LOG_NODE 0x80 Node table events # LOG_MISC 0x400 Miscellaneous events # LOG_SLI 0x800 SLI events -# LOG_CHK_COND 0x1000 FCP Check condition flag +# LOG_FCP_ERROR 0x1000 Only log FCP errors # LOG_LIBDFC 0x2000 LIBDFC events # LOG_ALL_MSG 0xffff LOG all messages */ diff --git a/drivers/scsi/lpfc/lpfc_logmsg.h b/drivers/scsi/lpfc/lpfc_logmsg.h index 62c8ca862e9e..438cbcd9eb13 100644 --- a/drivers/scsi/lpfc/lpfc_logmsg.h +++ b/drivers/scsi/lpfc/lpfc_logmsg.h @@ -28,7 +28,7 @@ #define LOG_NODE 0x80 /* Node table events */ #define LOG_MISC 0x400 /* Miscellaneous events */ #define LOG_SLI 0x800 /* SLI events */ -#define LOG_CHK_COND 0x1000 /* FCP Check condition flag */ +#define LOG_FCP_ERROR 0x1000 /* log errors, not underruns */ #define LOG_LIBDFC 0x2000 /* Libdfc events */ #define LOG_ALL_MSG 0xffff /* LOG all messages */ diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 97ae98dc95d0..125179af470c 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -297,8 +297,10 @@ lpfc_handle_fcp_err(struct lpfc_scsi_buf *lpfc_cmd) uint32_t fcpi_parm = lpfc_cmd->cur_iocbq.iocb.un.fcpi.fcpi_parm; uint32_t resp_info = fcprsp->rspStatus2; uint32_t scsi_status = fcprsp->rspStatus3; + uint32_t *lp; uint32_t host_status = DID_OK; uint32_t rsplen = 0; + uint32_t logit = LOG_FCP | LOG_FCP_ERROR; /* * If this is a task management command, there is no @@ -310,10 +312,25 @@ lpfc_handle_fcp_err(struct lpfc_scsi_buf *lpfc_cmd) goto out; } - lpfc_printf_log(phba, KERN_WARNING, LOG_FCP, - "%d:0730 FCP command failed: RSP " - "Data: x%x x%x x%x x%x x%x x%x\n", - phba->brd_no, resp_info, scsi_status, + if ((resp_info & SNS_LEN_VALID) && fcprsp->rspSnsLen) { + uint32_t snslen = be32_to_cpu(fcprsp->rspSnsLen); + if (snslen > SCSI_SENSE_BUFFERSIZE) + snslen = SCSI_SENSE_BUFFERSIZE; + + if (resp_info & RSP_LEN_VALID) + rsplen = be32_to_cpu(fcprsp->rspRspLen); + memcpy(cmnd->sense_buffer, &fcprsp->rspInfo0 + rsplen, snslen); + } + lp = (uint32_t *)cmnd->sense_buffer; + + if (!scsi_status && (resp_info & RESID_UNDER)) + logit = LOG_FCP; + + lpfc_printf_log(phba, KERN_WARNING, logit, + "%d:0730 FCP command x%x failed: x%x SNS x%x x%x " + "Data: x%x x%x x%x x%x x%x\n", + phba->brd_no, cmnd->cmnd[0], scsi_status, + be32_to_cpu(*lp), be32_to_cpu(*(lp + 3)), resp_info, be32_to_cpu(fcprsp->rspResId), be32_to_cpu(fcprsp->rspSnsLen), be32_to_cpu(fcprsp->rspRspLen), @@ -328,14 +345,6 @@ lpfc_handle_fcp_err(struct lpfc_scsi_buf *lpfc_cmd) } } - if ((resp_info & SNS_LEN_VALID) && fcprsp->rspSnsLen) { - uint32_t snslen = be32_to_cpu(fcprsp->rspSnsLen); - if (snslen > SCSI_SENSE_BUFFERSIZE) - snslen = SCSI_SENSE_BUFFERSIZE; - - memcpy(cmnd->sense_buffer, &fcprsp->rspInfo0 + rsplen, snslen); - } - cmnd->resid = 0; if (resp_info & RESID_UNDER) { cmnd->resid = be32_to_cpu(fcprsp->rspResId); @@ -378,7 +387,7 @@ lpfc_handle_fcp_err(struct lpfc_scsi_buf *lpfc_cmd) */ } else if ((scsi_status == SAM_STAT_GOOD) && fcpi_parm && (cmnd->sc_data_direction == DMA_FROM_DEVICE)) { - lpfc_printf_log(phba, KERN_WARNING, LOG_FCP, + lpfc_printf_log(phba, KERN_WARNING, LOG_FCP | LOG_FCP_ERROR, "%d:0734 FCP Read Check Error Data: " "x%x x%x x%x x%x\n", phba->brd_no, be32_to_cpu(fcpcmd->fcpDl), From 4ff43246e45943200ae3233732adf8708c2faef6 Mon Sep 17 00:00:00 2001 From: James Smart Date: Sat, 2 Dec 2006 13:34:56 -0500 Subject: [PATCH 57/73] [SCSI] lpfc 8.1.11 : Add MSI (Message Signalled Interrupts) support Add MSI (Message Signalled Interrupts) support Actual use must be enabled via the new module parameter "lpfc_use_msi" Defaults to no use Many thanks to Frederic Temporelli who implemented the initial patch. Signed-off-by: Frederic Temporelli Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc.h | 1 + drivers/scsi/lpfc/lpfc_attr.c | 11 +++++++++++ drivers/scsi/lpfc/lpfc_init.c | 10 ++++++++++ 3 files changed, 22 insertions(+) diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index e94190e83fc3..1ec9cf0e374a 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -303,6 +303,7 @@ struct lpfc_hba { uint32_t cfg_max_luns; uint32_t cfg_poll; uint32_t cfg_poll_tmo; + uint32_t cfg_use_msi; uint32_t cfg_sg_seg_cnt; uint32_t cfg_sg_dma_buf_size; uint64_t cfg_soft_wwpn; diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 73d3afc72de2..e2ac7ba3d8ad 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -961,6 +961,15 @@ LPFC_ATTR_R(max_luns, 255, 0, 65535, LPFC_ATTR_RW(poll_tmo, 10, 1, 255, "Milliseconds driver will wait between polling FCP ring"); +/* +# lpfc_use_msi: Use MSI (Message Signaled Interrupts) in systems that +# support this feature +# 0 = MSI disabled (default) +# 1 = MSI enabled +# Value range is [0,1]. Default value is 0. +*/ +LPFC_ATTR_R(use_msi, 0, 0, 1, "Use Message Signaled Interrupts, if possible"); + struct class_device_attribute *lpfc_host_attrs[] = { &class_device_attr_info, @@ -999,6 +1008,7 @@ struct class_device_attribute *lpfc_host_attrs[] = { &class_device_attr_issue_reset, &class_device_attr_lpfc_poll, &class_device_attr_lpfc_poll_tmo, + &class_device_attr_lpfc_use_msi, &class_device_attr_lpfc_soft_wwpn, &class_device_attr_lpfc_soft_wwpn_enable, NULL, @@ -1801,6 +1811,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) lpfc_discovery_threads_init(phba, lpfc_discovery_threads); lpfc_max_luns_init(phba, lpfc_max_luns); lpfc_poll_tmo_init(phba, lpfc_poll_tmo); + lpfc_use_msi_init(phba, lpfc_use_msi); lpfc_devloss_tmo_init(phba, lpfc_devloss_tmo); lpfc_nodev_tmo_init(phba, lpfc_nodev_tmo); phba->cfg_poll = lpfc_poll; diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 6b0718573ac5..71ee0cd32216 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -1653,6 +1653,14 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) if (error) goto out_remove_host; + if (phba->cfg_use_msi) { + error = pci_enable_msi(phba->pcidev); + if (error) + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, "%d:0452 " + "Enable MSI failed, continuing with " + "IRQ\n", phba->brd_no); + } + error = request_irq(phba->pcidev->irq, lpfc_intr_handler, IRQF_SHARED, LPFC_DRIVER_NAME, phba); if (error) { @@ -1732,6 +1740,7 @@ out_free_irq: lpfc_stop_timer(phba); phba->work_hba_events = 0; free_irq(phba->pcidev->irq, phba); + pci_disable_msi(phba->pcidev); out_free_sysfs_attr: lpfc_free_sysfs_attr(phba); out_remove_host: @@ -1798,6 +1807,7 @@ lpfc_pci_remove_one(struct pci_dev *pdev) /* Release the irq reservation */ free_irq(phba->pcidev->irq, phba); + pci_disable_msi(phba->pcidev); lpfc_cleanup(phba, 0); lpfc_stop_timer(phba); From 18a3b5966611b1d6f539e3e0bf863ad13a44dd45 Mon Sep 17 00:00:00 2001 From: James Smart Date: Sat, 2 Dec 2006 13:35:08 -0500 Subject: [PATCH 58/73] [SCSI] lpfc 8.1.11 : Removed decoding of PCI Subsystem Id To avoid continually updating the driver for new subsystem ids (as adapter modules are proliferating), remove this 2nd level decode. Genericize the reported Adapter names to be consistent across Emulex product line. Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_hw.h | 6 -- drivers/scsi/lpfc/lpfc_init.c | 113 +++++++++++----------------------- 2 files changed, 35 insertions(+), 84 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h index 0d08a50ca41e..3ba1ee4ace9a 100644 --- a/drivers/scsi/lpfc/lpfc_hw.h +++ b/drivers/scsi/lpfc/lpfc_hw.h @@ -1105,12 +1105,6 @@ typedef struct { #define PCI_DEVICE_ID_ZEPHYR_SCSP 0xfe11 #define PCI_DEVICE_ID_ZEPHYR_DCSP 0xfe12 -#define PCI_SUBSYSTEM_ID_LP11000S 0xfc11 -#define PCI_SUBSYSTEM_ID_LP11002S 0xfc12 -#define PCI_SUBSYSTEM_ID_LPE11000S 0xfc21 -#define PCI_SUBSYSTEM_ID_LPE11002S 0xfc22 -#define PCI_SUBSYSTEM_ID_LPE11010S 0xfc2A - #define JEDEC_ID_ADDRESS 0x0080001c #define FIREFLY_JEDEC_ID 0x1ACC #define SUPERFLY_JEDEC_ID 0x0020 diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 71ee0cd32216..b872519e2ced 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -804,19 +804,13 @@ lpfc_get_hba_model_desc(struct lpfc_hba * phba, uint8_t * mdp, uint8_t * descp) { lpfc_vpd_t *vp; uint16_t dev_id = phba->pcidev->device; - uint16_t dev_subid = phba->pcidev->subsystem_device; - uint8_t hdrtype; int max_speed; - char * ports; struct { char * name; int max_speed; - char * ports; char * bus; - } m = {"", 0, "", ""}; + } m = {"", 0, ""}; - pci_read_config_byte(phba->pcidev, PCI_HEADER_TYPE, &hdrtype); - ports = (hdrtype == 0x80) ? "2-port " : ""; if (mdp && mdp[0] != '\0' && descp && descp[0] != '\0') return; @@ -836,130 +830,93 @@ lpfc_get_hba_model_desc(struct lpfc_hba * phba, uint8_t * mdp, uint8_t * descp) switch (dev_id) { case PCI_DEVICE_ID_FIREFLY: - m = (typeof(m)){"LP6000", max_speed, "", "PCI"}; + m = (typeof(m)){"LP6000", max_speed, "PCI"}; break; case PCI_DEVICE_ID_SUPERFLY: if (vp->rev.biuRev >= 1 && vp->rev.biuRev <= 3) - m = (typeof(m)){"LP7000", max_speed, "", "PCI"}; + m = (typeof(m)){"LP7000", max_speed, "PCI"}; else - m = (typeof(m)){"LP7000E", max_speed, "", "PCI"}; + m = (typeof(m)){"LP7000E", max_speed, "PCI"}; break; case PCI_DEVICE_ID_DRAGONFLY: - m = (typeof(m)){"LP8000", max_speed, "", "PCI"}; + m = (typeof(m)){"LP8000", max_speed, "PCI"}; break; case PCI_DEVICE_ID_CENTAUR: if (FC_JEDEC_ID(vp->rev.biuRev) == CENTAUR_2G_JEDEC_ID) - m = (typeof(m)){"LP9002", max_speed, "", "PCI"}; + m = (typeof(m)){"LP9002", max_speed, "PCI"}; else - m = (typeof(m)){"LP9000", max_speed, "", "PCI"}; + m = (typeof(m)){"LP9000", max_speed, "PCI"}; break; case PCI_DEVICE_ID_RFLY: - m = (typeof(m)){"LP952", max_speed, "", "PCI"}; + m = (typeof(m)){"LP952", max_speed, "PCI"}; break; case PCI_DEVICE_ID_PEGASUS: - m = (typeof(m)){"LP9802", max_speed, "", "PCI-X"}; + m = (typeof(m)){"LP9802", max_speed, "PCI-X"}; break; case PCI_DEVICE_ID_THOR: - if (hdrtype == 0x80) - m = (typeof(m)){"LP10000DC", - max_speed, ports, "PCI-X"}; - else - m = (typeof(m)){"LP10000", - max_speed, ports, "PCI-X"}; + m = (typeof(m)){"LP10000", max_speed, "PCI-X"}; break; case PCI_DEVICE_ID_VIPER: - m = (typeof(m)){"LPX1000", max_speed, "", "PCI-X"}; + m = (typeof(m)){"LPX1000", max_speed, "PCI-X"}; break; case PCI_DEVICE_ID_PFLY: - m = (typeof(m)){"LP982", max_speed, "", "PCI-X"}; + m = (typeof(m)){"LP982", max_speed, "PCI-X"}; break; case PCI_DEVICE_ID_TFLY: - if (hdrtype == 0x80) - m = (typeof(m)){"LP1050DC", max_speed, ports, "PCI-X"}; - else - m = (typeof(m)){"LP1050", max_speed, ports, "PCI-X"}; + m = (typeof(m)){"LP1050", max_speed, "PCI-X"}; break; case PCI_DEVICE_ID_HELIOS: - if (hdrtype == 0x80) - m = (typeof(m)){"LP11002", max_speed, ports, "PCI-X2"}; - else - m = (typeof(m)){"LP11000", max_speed, ports, "PCI-X2"}; + m = (typeof(m)){"LP11000", max_speed, "PCI-X2"}; break; case PCI_DEVICE_ID_HELIOS_SCSP: - m = (typeof(m)){"LP11000-SP", max_speed, ports, "PCI-X2"}; + m = (typeof(m)){"LP11000-SP", max_speed, "PCI-X2"}; break; case PCI_DEVICE_ID_HELIOS_DCSP: - m = (typeof(m)){"LP11002-SP", max_speed, ports, "PCI-X2"}; + m = (typeof(m)){"LP11002-SP", max_speed, "PCI-X2"}; break; case PCI_DEVICE_ID_NEPTUNE: - if (hdrtype == 0x80) - m = (typeof(m)){"LPe1002", max_speed, ports, "PCIe"}; - else - m = (typeof(m)){"LPe1000", max_speed, ports, "PCIe"}; + m = (typeof(m)){"LPe1000", max_speed, "PCIe"}; break; case PCI_DEVICE_ID_NEPTUNE_SCSP: - m = (typeof(m)){"LPe1000-SP", max_speed, ports, "PCIe"}; + m = (typeof(m)){"LPe1000-SP", max_speed, "PCIe"}; break; case PCI_DEVICE_ID_NEPTUNE_DCSP: - m = (typeof(m)){"LPe1002-SP", max_speed, ports, "PCIe"}; + m = (typeof(m)){"LPe1002-SP", max_speed, "PCIe"}; break; case PCI_DEVICE_ID_BMID: - m = (typeof(m)){"LP1150", max_speed, ports, "PCI-X2"}; + m = (typeof(m)){"LP1150", max_speed, "PCI-X2"}; break; case PCI_DEVICE_ID_BSMB: - m = (typeof(m)){"LP111", max_speed, ports, "PCI-X2"}; + m = (typeof(m)){"LP111", max_speed, "PCI-X2"}; break; case PCI_DEVICE_ID_ZEPHYR: - if (hdrtype == 0x80) - m = (typeof(m)){"LPe11002", max_speed, ports, "PCIe"}; - else - m = (typeof(m)){"LPe11000", max_speed, ports, "PCIe"}; + m = (typeof(m)){"LPe11000", max_speed, "PCIe"}; break; case PCI_DEVICE_ID_ZEPHYR_SCSP: - m = (typeof(m)){"LPe11000", max_speed, ports, "PCIe"}; + m = (typeof(m)){"LPe11000", max_speed, "PCIe"}; break; case PCI_DEVICE_ID_ZEPHYR_DCSP: - m = (typeof(m)){"LPe11002-SP", max_speed, ports, "PCIe"}; + m = (typeof(m)){"LPe11002-SP", max_speed, "PCIe"}; break; case PCI_DEVICE_ID_ZMID: - m = (typeof(m)){"LPe1150", max_speed, ports, "PCIe"}; + m = (typeof(m)){"LPe1150", max_speed, "PCIe"}; break; case PCI_DEVICE_ID_ZSMB: - m = (typeof(m)){"LPe111", max_speed, ports, "PCIe"}; + m = (typeof(m)){"LPe111", max_speed, "PCIe"}; break; case PCI_DEVICE_ID_LP101: - m = (typeof(m)){"LP101", max_speed, ports, "PCI-X"}; + m = (typeof(m)){"LP101", max_speed, "PCI-X"}; break; case PCI_DEVICE_ID_LP10000S: - m = (typeof(m)){"LP10000-S", max_speed, ports, "PCI"}; + m = (typeof(m)){"LP10000-S", max_speed, "PCI"}; break; case PCI_DEVICE_ID_LP11000S: + m = (typeof(m)){"LP11000-S", max_speed, + "PCI-X2"}; + break; case PCI_DEVICE_ID_LPE11000S: - switch (dev_subid) { - case PCI_SUBSYSTEM_ID_LP11000S: - m = (typeof(m)){"LP11000-S", max_speed, - ports, "PCI-X2"}; - break; - case PCI_SUBSYSTEM_ID_LP11002S: - m = (typeof(m)){"LP11002-S", max_speed, - ports, "PCI-X2"}; - break; - case PCI_SUBSYSTEM_ID_LPE11000S: - m = (typeof(m)){"LPe11000-S", max_speed, - ports, "PCIe"}; - break; - case PCI_SUBSYSTEM_ID_LPE11002S: - m = (typeof(m)){"LPe11002-S", max_speed, - ports, "PCIe"}; - break; - case PCI_SUBSYSTEM_ID_LPE11010S: - m = (typeof(m)){"LPe11010-S", max_speed, - "10-port ", "PCIe"}; - break; - default: - m = (typeof(m)){ NULL }; - break; - } + m = (typeof(m)){"LPe11000-S", max_speed, + "PCIe"}; break; default: m = (typeof(m)){ NULL }; @@ -970,8 +927,8 @@ lpfc_get_hba_model_desc(struct lpfc_hba * phba, uint8_t * mdp, uint8_t * descp) snprintf(mdp, 79,"%s", m.name); if (descp && descp[0] == '\0') snprintf(descp, 255, - "Emulex %s %dGb %s%s Fibre Channel Adapter", - m.name, m.max_speed, m.ports, m.bus); + "Emulex %s %dGb %s Fibre Channel Adapter", + m.name, m.max_speed, m.bus); } /**************************************************/ From a12e07bc6297b6cb97889ca7dfbed6c39048b1c1 Mon Sep 17 00:00:00 2001 From: James Smart Date: Sat, 2 Dec 2006 13:35:30 -0500 Subject: [PATCH 59/73] [SCSI] lpfc 8.1.11 : Add soft_wwnn sysfs attribute, rename soft_wwn_enable The driver now allows both wwpn and wwnn to be set. Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc.h | 3 +- drivers/scsi/lpfc/lpfc_attr.c | 84 ++++++++++++++++++++++++++++---- drivers/scsi/lpfc/lpfc_hbadisc.c | 2 + drivers/scsi/lpfc/lpfc_init.c | 2 + 4 files changed, 80 insertions(+), 11 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 1ec9cf0e374a..a7de0bca5bdd 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -306,6 +306,7 @@ struct lpfc_hba { uint32_t cfg_use_msi; uint32_t cfg_sg_seg_cnt; uint32_t cfg_sg_dma_buf_size; + uint64_t cfg_soft_wwnn; uint64_t cfg_soft_wwpn; uint32_t dev_loss_tmo_changed; @@ -358,7 +359,7 @@ struct lpfc_hba { #define VPD_PORT 0x8 /* valid vpd port data */ #define VPD_MASK 0xf /* mask for any vpd data */ - uint8_t soft_wwpn_enable; + uint8_t soft_wwn_enable; struct timer_list fcp_poll_timer; struct timer_list els_tmofunc; diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index e2ac7ba3d8ad..f247e786af99 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -552,10 +552,10 @@ static CLASS_DEVICE_ATTR(board_mode, S_IRUGO | S_IWUSR, static CLASS_DEVICE_ATTR(issue_reset, S_IWUSR, NULL, lpfc_issue_reset); -static char *lpfc_soft_wwpn_key = "C99G71SL8032A"; +static char *lpfc_soft_wwn_key = "C99G71SL8032A"; static ssize_t -lpfc_soft_wwpn_enable_store(struct class_device *cdev, const char *buf, +lpfc_soft_wwn_enable_store(struct class_device *cdev, const char *buf, size_t count) { struct Scsi_Host *host = class_to_shost(cdev); @@ -579,15 +579,15 @@ lpfc_soft_wwpn_enable_store(struct class_device *cdev, const char *buf, if (buf[cnt-1] == '\n') cnt--; - if ((cnt != strlen(lpfc_soft_wwpn_key)) || - (strncmp(buf, lpfc_soft_wwpn_key, strlen(lpfc_soft_wwpn_key)) != 0)) + if ((cnt != strlen(lpfc_soft_wwn_key)) || + (strncmp(buf, lpfc_soft_wwn_key, strlen(lpfc_soft_wwn_key)) != 0)) return -EINVAL; - phba->soft_wwpn_enable = 1; + phba->soft_wwn_enable = 1; return count; } -static CLASS_DEVICE_ATTR(lpfc_soft_wwpn_enable, S_IWUSR, NULL, - lpfc_soft_wwpn_enable_store); +static CLASS_DEVICE_ATTR(lpfc_soft_wwn_enable, S_IWUSR, NULL, + lpfc_soft_wwn_enable_store); static ssize_t lpfc_soft_wwpn_show(struct class_device *cdev, char *buf) @@ -613,12 +613,12 @@ lpfc_soft_wwpn_store(struct class_device *cdev, const char *buf, size_t count) if (buf[cnt-1] == '\n') cnt--; - if (!phba->soft_wwpn_enable || (cnt < 16) || (cnt > 18) || + if (!phba->soft_wwn_enable || (cnt < 16) || (cnt > 18) || ((cnt == 17) && (*buf++ != 'x')) || ((cnt == 18) && ((*buf++ != '0') || (*buf++ != 'x')))) return -EINVAL; - phba->soft_wwpn_enable = 0; + phba->soft_wwn_enable = 0; memset(wwpn, 0, sizeof(wwpn)); @@ -639,6 +639,8 @@ lpfc_soft_wwpn_store(struct class_device *cdev, const char *buf, size_t count) } phba->cfg_soft_wwpn = wwn_to_u64(wwpn); fc_host_port_name(host) = phba->cfg_soft_wwpn; + if (phba->cfg_soft_wwnn) + fc_host_node_name(host) = phba->cfg_soft_wwnn; dev_printk(KERN_NOTICE, &phba->pcidev->dev, "lpfc%d: Reinitializing to use soft_wwpn\n", phba->brd_no); @@ -664,6 +666,66 @@ lpfc_soft_wwpn_store(struct class_device *cdev, const char *buf, size_t count) static CLASS_DEVICE_ATTR(lpfc_soft_wwpn, S_IRUGO | S_IWUSR,\ lpfc_soft_wwpn_show, lpfc_soft_wwpn_store); +static ssize_t +lpfc_soft_wwnn_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata; + return snprintf(buf, PAGE_SIZE, "0x%llx\n", + (unsigned long long)phba->cfg_soft_wwnn); +} + + +static ssize_t +lpfc_soft_wwnn_store(struct class_device *cdev, const char *buf, size_t count) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata; + unsigned int i, j, cnt=count; + u8 wwnn[8]; + + /* count may include a LF at end of string */ + if (buf[cnt-1] == '\n') + cnt--; + + if (!phba->soft_wwn_enable || (cnt < 16) || (cnt > 18) || + ((cnt == 17) && (*buf++ != 'x')) || + ((cnt == 18) && ((*buf++ != '0') || (*buf++ != 'x')))) + return -EINVAL; + + /* + * Allow wwnn to be set many times, as long as the enable is set. + * However, once the wwpn is set, everything locks. + */ + + memset(wwnn, 0, sizeof(wwnn)); + + /* Validate and store the new name */ + for (i=0, j=0; i < 16; i++) { + if ((*buf >= 'a') && (*buf <= 'f')) + j = ((j << 4) | ((*buf++ -'a') + 10)); + else if ((*buf >= 'A') && (*buf <= 'F')) + j = ((j << 4) | ((*buf++ -'A') + 10)); + else if ((*buf >= '0') && (*buf <= '9')) + j = ((j << 4) | (*buf++ -'0')); + else + return -EINVAL; + if (i % 2) { + wwnn[i/2] = j & 0xff; + j = 0; + } + } + phba->cfg_soft_wwnn = wwn_to_u64(wwnn); + + dev_printk(KERN_NOTICE, &phba->pcidev->dev, + "lpfc%d: soft_wwnn set. Value will take effect upon " + "setting of the soft_wwpn\n", phba->brd_no); + + return count; +} +static CLASS_DEVICE_ATTR(lpfc_soft_wwnn, S_IRUGO | S_IWUSR,\ + lpfc_soft_wwnn_show, lpfc_soft_wwnn_store); + static int lpfc_poll = 0; module_param(lpfc_poll, int, 0); @@ -1009,8 +1071,9 @@ struct class_device_attribute *lpfc_host_attrs[] = { &class_device_attr_lpfc_poll, &class_device_attr_lpfc_poll_tmo, &class_device_attr_lpfc_use_msi, + &class_device_attr_lpfc_soft_wwnn, &class_device_attr_lpfc_soft_wwpn, - &class_device_attr_lpfc_soft_wwpn_enable, + &class_device_attr_lpfc_soft_wwn_enable, NULL, }; @@ -1815,6 +1878,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) lpfc_devloss_tmo_init(phba, lpfc_devloss_tmo); lpfc_nodev_tmo_init(phba, lpfc_nodev_tmo); phba->cfg_poll = lpfc_poll; + phba->cfg_soft_wwnn = 0L; phba->cfg_soft_wwpn = 0L; /* diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 1118b6d49fcc..c39564e85e94 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -672,6 +672,8 @@ lpfc_mbx_cmpl_read_sparam(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) memcpy((uint8_t *) & phba->fc_sparam, (uint8_t *) mp->virt, sizeof (struct serv_parm)); + if (phba->cfg_soft_wwnn) + u64_to_wwn(phba->cfg_soft_wwnn, phba->fc_sparam.nodeName.u.wwn); if (phba->cfg_soft_wwpn) u64_to_wwn(phba->cfg_soft_wwpn, phba->fc_sparam.portName.u.wwn); memcpy((uint8_t *) & phba->fc_nodename, diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index b872519e2ced..cf9081c262d7 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -268,6 +268,8 @@ lpfc_config_port_post(struct lpfc_hba * phba) kfree(mp); pmb->context1 = NULL; + if (phba->cfg_soft_wwnn) + u64_to_wwn(phba->cfg_soft_wwnn, phba->fc_sparam.nodeName.u.wwn); if (phba->cfg_soft_wwpn) u64_to_wwn(phba->cfg_soft_wwpn, phba->fc_sparam.portName.u.wwn); memcpy(&phba->fc_nodename, &phba->fc_sparam.nodeName, From f56035110661417e15814fa08e1f4bf19cb26f93 Mon Sep 17 00:00:00 2001 From: James Smart Date: Sat, 2 Dec 2006 13:35:43 -0500 Subject: [PATCH 60/73] [SCSI] lpfc 8.1.11 : Misc Fixes Misc Fixes: - Prevent references to NULL node list element in reset routines. - Add missing IOCB types to switch tables - Reset the card on Port Error 5 - Fix infinite loop in LUN reset Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_hw.h | 7 +++++++ drivers/scsi/lpfc/lpfc_init.c | 3 ++- drivers/scsi/lpfc/lpfc_scsi.c | 21 +++++++++++++++++++-- drivers/scsi/lpfc/lpfc_sli.c | 7 +++++++ 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h index 3ba1ee4ace9a..f79cb6136906 100644 --- a/drivers/scsi/lpfc/lpfc_hw.h +++ b/drivers/scsi/lpfc/lpfc_hw.h @@ -1294,6 +1294,10 @@ typedef struct { /* FireFly BIU registers */ #define CMD_FCP_IREAD_CX 0x1B #define CMD_FCP_ICMND_CR 0x1C #define CMD_FCP_ICMND_CX 0x1D +#define CMD_FCP_TSEND_CX 0x1F +#define CMD_FCP_TRECEIVE_CX 0x21 +#define CMD_FCP_TRSP_CX 0x23 +#define CMD_FCP_AUTO_TRSP_CX 0x29 #define CMD_ADAPTER_MSG 0x20 #define CMD_ADAPTER_DUMP 0x22 @@ -1320,6 +1324,9 @@ typedef struct { /* FireFly BIU registers */ #define CMD_FCP_IREAD64_CX 0x9B #define CMD_FCP_ICMND64_CR 0x9C #define CMD_FCP_ICMND64_CX 0x9D +#define CMD_FCP_TSEND64_CX 0x9F +#define CMD_FCP_TRECEIVE64_CX 0xA1 +#define CMD_FCP_TRSP64_CX 0xA3 #define CMD_GEN_REQUEST64_CR 0xC2 #define CMD_GEN_REQUEST64_CX 0xC3 diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index cf9081c262d7..afca45cdbcef 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -519,7 +519,8 @@ lpfc_handle_eratt(struct lpfc_hba * phba) struct lpfc_sli_ring *pring; uint32_t event_data; - if (phba->work_hs & HS_FFER6) { + if (phba->work_hs & HS_FFER6 || + phba->work_hs & HS_FFER5) { /* Re-establishing Link */ lpfc_printf_log(phba, KERN_INFO, LOG_LINK_EVENT, "%d:1301 Re-establishing Link " diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 125179af470c..c3e68e0d8f74 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -679,6 +679,9 @@ lpfc_scsi_tgt_reset(struct lpfc_scsi_buf * lpfc_cmd, struct lpfc_hba * phba, struct lpfc_iocbq *iocbqrsp; int ret; + if (!rdata->pnode) + return FAILED; + lpfc_cmd->rdata = rdata; ret = lpfc_scsi_prep_task_mgmt_cmd(phba, lpfc_cmd, lun, FCP_TARGET_RESET); @@ -985,20 +988,34 @@ lpfc_reset_lun_handler(struct scsi_cmnd *cmnd) lpfc_block_error_handler(cmnd); spin_lock_irq(shost->host_lock); + loopcnt = 0; /* * If target is not in a MAPPED state, delay the reset until * target is rediscovered or devloss timeout expires. */ while ( 1 ) { if (!pnode) - break; + return FAILED; if (pnode->nlp_state != NLP_STE_MAPPED_NODE) { spin_unlock_irq(phba->host->host_lock); schedule_timeout_uninterruptible(msecs_to_jiffies(500)); spin_lock_irq(phba->host->host_lock); + loopcnt++; + rdata = cmnd->device->hostdata; + if (!rdata || + (loopcnt > ((phba->cfg_devloss_tmo * 2) + 1))) { + lpfc_printf_log(phba, KERN_ERR, LOG_FCP, + "%d:0721 LUN Reset rport failure:" + " cnt x%x rdata x%p\n", + phba->brd_no, loopcnt, rdata); + goto out; + } + pnode = rdata->pnode; + if (!pnode) + return FAILED; } - if ((pnode) && (pnode->nlp_state == NLP_STE_MAPPED_NODE)) + if (pnode->nlp_state == NLP_STE_MAPPED_NODE) break; } diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 120af3db635a..a4128e19338a 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -117,6 +117,10 @@ lpfc_sli_iocb_cmd_type(uint8_t iocb_cmnd) case CMD_FCP_IREAD_CX: case CMD_FCP_ICMND_CR: case CMD_FCP_ICMND_CX: + case CMD_FCP_TSEND_CX: + case CMD_FCP_TRSP_CX: + case CMD_FCP_TRECEIVE_CX: + case CMD_FCP_AUTO_TRSP_CX: case CMD_ADAPTER_MSG: case CMD_ADAPTER_DUMP: case CMD_XMIT_SEQUENCE64_CR: @@ -131,6 +135,9 @@ lpfc_sli_iocb_cmd_type(uint8_t iocb_cmnd) case CMD_FCP_IREAD64_CX: case CMD_FCP_ICMND64_CR: case CMD_FCP_ICMND64_CX: + case CMD_FCP_TSEND64_CX: + case CMD_FCP_TRSP64_CX: + case CMD_FCP_TRECEIVE64_CX: case CMD_GEN_REQUEST64_CR: case CMD_GEN_REQUEST64_CX: case CMD_XMIT_ELS_RSP64_CX: From ba8d55048ae08c442a27944e8beb76e73c3cce03 Mon Sep 17 00:00:00 2001 From: James Smart Date: Sat, 2 Dec 2006 13:36:04 -0500 Subject: [PATCH 61/73] [SCSI] lpfc 8.1.11 : Change version number to 8.1.11 Change version number to 8.1.11 Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index ac417908b407..a61ef3d1e7f1 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -18,7 +18,7 @@ * included with this package. * *******************************************************************/ -#define LPFC_DRIVER_VERSION "8.1.10" +#define LPFC_DRIVER_VERSION "8.1.11" #define LPFC_DRIVER_NAME "lpfc" From 024879ead9594acab30ce9e23c955086e2d967a0 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Wed, 15 Nov 2006 18:03:07 -0600 Subject: [PATCH 62/73] [SCSI] libsas: better error handling in sas_expander.c With async scanning, we're now tripping the BUG_ON in sas_ex_discover_end_dev(), so make the error handling here correct. Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_expander.c | 36 ++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index e34a93435497..d31e6fa466f7 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -597,10 +597,15 @@ static struct domain_device *sas_ex_discover_end_dev( child->iproto = phy->attached_iproto; memcpy(child->sas_addr, phy->attached_sas_addr, SAS_ADDR_SIZE); sas_hash_addr(child->hashed_sas_addr, child->sas_addr); - phy->port = sas_port_alloc(&parent->rphy->dev, phy_id); - BUG_ON(!phy->port); - /* FIXME: better error handling*/ - BUG_ON(sas_port_add(phy->port) != 0); + if (!phy->port) { + phy->port = sas_port_alloc(&parent->rphy->dev, phy_id); + if (unlikely(!phy->port)) + goto out_err; + if (unlikely(sas_port_add(phy->port) != 0)) { + sas_port_free(phy->port); + goto out_err; + } + } sas_ex_get_linkrate(parent, child, phy); if ((phy->attached_tproto & SAS_PROTO_STP) || phy->attached_sata_dev) { @@ -615,8 +620,7 @@ static struct domain_device *sas_ex_discover_end_dev( SAS_DPRINTK("report phy sata to %016llx:0x%x returned " "0x%x\n", SAS_ADDR(parent->sas_addr), phy_id, res); - kfree(child); - return NULL; + goto out_free; } memcpy(child->frame_rcvd, &child->sata_dev.rps_resp.rps.fis, sizeof(struct dev_to_host_fis)); @@ -627,14 +631,14 @@ static struct domain_device *sas_ex_discover_end_dev( "%016llx:0x%x returned 0x%x\n", SAS_ADDR(child->sas_addr), SAS_ADDR(parent->sas_addr), phy_id, res); - kfree(child); - return NULL; + goto out_free; } } else if (phy->attached_tproto & SAS_PROTO_SSP) { child->dev_type = SAS_END_DEV; rphy = sas_end_device_alloc(phy->port); /* FIXME: error handling */ - BUG_ON(!rphy); + if (unlikely(!rphy)) + goto out_free; child->tproto = phy->attached_tproto; sas_init_dev(child); @@ -651,9 +655,7 @@ static struct domain_device *sas_ex_discover_end_dev( "at %016llx:0x%x returned 0x%x\n", SAS_ADDR(child->sas_addr), SAS_ADDR(parent->sas_addr), phy_id, res); - /* FIXME: this kfrees list elements without removing them */ - //kfree(child); - return NULL; + goto out_list_del; } } else { SAS_DPRINTK("target proto 0x%x at %016llx:0x%x not handled\n", @@ -663,6 +665,16 @@ static struct domain_device *sas_ex_discover_end_dev( list_add_tail(&child->siblings, &parent_ex->children); return child; + + out_list_del: + list_del(&child->dev_list_node); + sas_rphy_free(rphy); + out_free: + sas_port_delete(phy->port); + out_err: + phy->port = NULL; + kfree(child); + return NULL; } static struct domain_device *sas_ex_discover_expander( From e42ebefee15894522f3a84045887573ebc9b764e Mon Sep 17 00:00:00 2001 From: "akpm@osdl.org" Date: Mon, 4 Dec 2006 23:06:48 -0800 Subject: [PATCH 63/73] [SCSI] tgt: fix undefined flush_dcache_page() problem drivers/scsi/scsi_tgt_if.c: In function 'tgt_uspace_send_event': drivers/scsi/scsi_tgt_if.c:88: warning: implicit declaration of function 'flush_dcache_page' Signed-off-by: Andrew Morton Signed-off-by: James Bottomley --- drivers/scsi/scsi_tgt_if.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/scsi_tgt_if.c b/drivers/scsi/scsi_tgt_if.c index 28fd2223765b..37bbfbdb870f 100644 --- a/drivers/scsi/scsi_tgt_if.c +++ b/drivers/scsi/scsi_tgt_if.c @@ -29,6 +29,8 @@ #include #include +#include + #include "scsi_tgt_priv.h" struct tgt_ring { From 00769ec40074b753c9b218c2ccaba2bfbfffe056 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Sun, 3 Dec 2006 20:49:23 -0500 Subject: [PATCH 64/73] [SCSI] megaraid: fix MMIO casts megaraid's MMIO RD*/WR* macros directly call readl() and writel() with an 'unsigned long' argument. This throws a warning, but is otherwise OK because the 'unsigned long' is really the result of ioremap(). This setup is also OK because the variable can hold an ioremap cookie /or/ a PCI I/O port (PIO). However, to fix the warning thrown when readl() and writel() are passed an unsigned long cookie, I introduce 'void __iomem *mmio_base', holding the same value as 'base'. This will silence the warnings, and also cause an oops whenever these MMIO-only functions are ever accidentally passed an I/O address. Signed-off-by: Jeff Garzik Signed-off-by: James Bottomley --- drivers/scsi/megaraid.c | 13 ++++++++----- drivers/scsi/megaraid.h | 3 ++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c index 86099fde1b2a..77d9d3804ccf 100644 --- a/drivers/scsi/megaraid.c +++ b/drivers/scsi/megaraid.c @@ -73,10 +73,10 @@ static unsigned short int max_mbox_busy_wait = MBOX_BUSY_WAIT; module_param(max_mbox_busy_wait, ushort, 0); MODULE_PARM_DESC(max_mbox_busy_wait, "Maximum wait for mailbox in microseconds if busy (default=MBOX_BUSY_WAIT=10)"); -#define RDINDOOR(adapter) readl((adapter)->base + 0x20) -#define RDOUTDOOR(adapter) readl((adapter)->base + 0x2C) -#define WRINDOOR(adapter,value) writel(value, (adapter)->base + 0x20) -#define WROUTDOOR(adapter,value) writel(value, (adapter)->base + 0x2C) +#define RDINDOOR(adapter) readl((adapter)->mmio_base + 0x20) +#define RDOUTDOOR(adapter) readl((adapter)->mmio_base + 0x2C) +#define WRINDOOR(adapter,value) writel(value, (adapter)->mmio_base + 0x20) +#define WROUTDOOR(adapter,value) writel(value, (adapter)->mmio_base + 0x2C) /* * Global variables @@ -1386,7 +1386,8 @@ megaraid_isr_memmapped(int irq, void *devp) handled = 1; - while( RDINDOOR(adapter) & 0x02 ) cpu_relax(); + while( RDINDOOR(adapter) & 0x02 ) + cpu_relax(); mega_cmd_done(adapter, completed, nstatus, status); @@ -4668,6 +4669,8 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) host->host_no, mega_baseport, irq); adapter->base = mega_baseport; + if (flag & BOARD_MEMMAP) + adapter->mmio_base = (void __iomem *) mega_baseport; INIT_LIST_HEAD(&adapter->free_list); INIT_LIST_HEAD(&adapter->pending_list); diff --git a/drivers/scsi/megaraid.h b/drivers/scsi/megaraid.h index 66529f11d23c..c6e74643abe2 100644 --- a/drivers/scsi/megaraid.h +++ b/drivers/scsi/megaraid.h @@ -801,7 +801,8 @@ typedef struct { clustering is available */ u32 flag; - unsigned long base; + unsigned long base; + void __iomem *mmio_base; /* mbox64 with mbox not aligned on 16-byte boundry */ mbox64_t *una_mbox64; From b4b8bed12345bc03dfcef80cbbe78582429b063c Mon Sep 17 00:00:00 2001 From: Ed Lin Date: Mon, 4 Dec 2006 17:49:24 -0800 Subject: [PATCH 65/73] [SCSI] stex: fix biosparam calculation Fix biosparam calculation. Signed-off-by: Ed Lin Signed-off-by: James Bottomley --- drivers/scsi/stex.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c index 185c270bb043..91d484c0dd93 100644 --- a/drivers/scsi/stex.c +++ b/drivers/scsi/stex.c @@ -1100,18 +1100,18 @@ static int stex_reset(struct scsi_cmnd *cmd) static int stex_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[]) { - int heads = 255, sectors = 63, cylinders; + int heads = 255, sectors = 63; if (capacity < 0x200000) { heads = 64; sectors = 32; } - cylinders = sector_div(capacity, heads * sectors); + sector_div(capacity, heads * sectors); geom[0] = heads; geom[1] = sectors; - geom[2] = cylinders; + geom[2] = capacity; return 0; } From 4eea9dc45fa7feb42e6dce32d94ea9ea4d64e40b Mon Sep 17 00:00:00 2001 From: Ed Lin Date: Mon, 4 Dec 2006 17:49:28 -0800 Subject: [PATCH 66/73] [SCSI] stex: fix controller_info command handling This command needs information from both firmware and driver. First copy information from firmware to buffer, then fill in driver information. Signed-off-by: Ed Lin Signed-off-by: James Bottomley --- drivers/scsi/stex.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c index 91d484c0dd93..ea0d82461317 100644 --- a/drivers/scsi/stex.c +++ b/drivers/scsi/stex.c @@ -507,6 +507,7 @@ static void stex_controller_info(struct st_hba *hba, struct st_ccb *ccb) size_t count = sizeof(struct st_frame); p = hba->copy_buffer; + stex_internal_copy(ccb->cmd, p, &count, ccb->sg_count, ST_FROM_CMD); memset(p->base, 0, sizeof(u32)*6); *(unsigned long *)(p->base) = pci_resource_start(hba->pdev, 0); p->rom_addr = 0; From 47c4f997c7bd9ab142c65950317232728b921aa0 Mon Sep 17 00:00:00 2001 From: Ed Lin Date: Mon, 4 Dec 2006 17:49:31 -0800 Subject: [PATCH 67/73] [SCSI] stex: add value check in hard reset routine During hard reset, an all-1 value from PCI_COMMAND should be invalid. Signed-off-by: Ed Lin Signed-off-by: James Bottomley --- drivers/scsi/stex.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c index ea0d82461317..92dbac03c2e5 100644 --- a/drivers/scsi/stex.c +++ b/drivers/scsi/stex.c @@ -1041,7 +1041,7 @@ static void stex_hard_reset(struct st_hba *hba) for (i = 0; i < MU_MAX_DELAY_TIME; i++) { pci_read_config_word(hba->pdev, PCI_COMMAND, &pci_cmd); - if (pci_cmd & PCI_COMMAND_MASTER) + if (pci_cmd != 0xffff && (pci_cmd & PCI_COMMAND_MASTER)) break; msleep(1); } From 529e7a62c8016ab7d3ef4ef6d39de23c0569781f Mon Sep 17 00:00:00 2001 From: Ed Lin Date: Mon, 4 Dec 2006 17:49:34 -0800 Subject: [PATCH 68/73] [SCSI] stex: adjust default queue length Firmware of new version may adjust default queue length. It is backward compatible. Signed-off-by: Ed Lin Signed-off-by: James Bottomley --- drivers/scsi/stex.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c index 92dbac03c2e5..a20cbd9d325a 100644 --- a/drivers/scsi/stex.c +++ b/drivers/scsi/stex.c @@ -78,6 +78,7 @@ enum { MU_MAX_DELAY_TIME = 240000, MU_HANDSHAKE_SIGNATURE = 0x55aaaa55, + MU_HANDSHAKE_SIGNATURE_HALF = 0x5a5a0000, HMU_PARTNER_TYPE = 2, /* firmware returned values */ @@ -902,6 +903,7 @@ static int stex_handshake(struct st_hba *hba) void __iomem *base = hba->mmio_base; struct handshake_frame *h; dma_addr_t status_phys; + u32 data; int i; if (readl(base + OMR0) != MU_HANDSHAKE_SIGNATURE) { @@ -923,6 +925,13 @@ static int stex_handshake(struct st_hba *hba) udelay(10); + data = readl(base + OMR1); + if ((data & 0xffff0000) == MU_HANDSHAKE_SIGNATURE_HALF) { + data &= 0x0000ffff; + if (hba->host->can_queue > data) + hba->host->can_queue = data; + } + h = (struct handshake_frame *)(hba->dma_mem + MU_REQ_BUFFER_SIZE); h->rb_phy = cpu_to_le32(hba->dma_handle); h->rb_phy_hi = cpu_to_le32((hba->dma_handle >> 16) >> 16); @@ -1234,7 +1243,7 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (err) goto out_free_irq; - err = scsi_init_shared_tag_map(host, ST_CAN_QUEUE); + err = scsi_init_shared_tag_map(host, host->can_queue); if (err) { printk(KERN_ERR DRV_NAME "(%s): init shared queue failed\n", pci_name(pdev)); From ee926b27a01da1c1f0f0017c0e6af6e4fd6c05b5 Mon Sep 17 00:00:00 2001 From: Ed Lin Date: Mon, 4 Dec 2006 17:49:36 -0800 Subject: [PATCH 69/73] [SCSI] stex: update device id info - add comments for various devices - remove unused device ids(0xf350, 0x4301, 0x8301, 0x8302) - add new device id(0xe350) - fix vendor id of st_vsc - modify Kconfig help info Signed-off-by: Ed Lin Signed-off-by: James Bottomley --- drivers/scsi/Kconfig | 9 +++++++-- drivers/scsi/stex.c | 35 ++++++++++++++++++++++++++--------- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 1b4bfaabcfbc..69569096dae5 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -982,8 +982,13 @@ config SCSI_STEX tristate "Promise SuperTrak EX Series support" depends on PCI && SCSI ---help--- - This driver supports Promise SuperTrak EX8350/8300/16350/16300 - Storage controllers. + This driver supports Promise SuperTrak EX series storage controllers. + + Promise provides Linux RAID configuration utility for these + controllers. Please visit to download. + + To compile this driver as a module, choose M here: the + module will be called stex. config SCSI_SYM53C8XX_2 tristate "SYM53C8XX Version 2 SCSI support" diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c index a20cbd9d325a..0fd115c04e59 100644 --- a/drivers/scsi/stex.c +++ b/drivers/scsi/stex.c @@ -1356,15 +1356,32 @@ static void stex_shutdown(struct pci_dev *pdev) } static struct pci_device_id stex_pci_tbl[] = { - { 0x105a, 0x8350, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_shasta }, - { 0x105a, 0xc350, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_shasta }, - { 0x105a, 0xf350, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_shasta }, - { 0x105a, 0x4301, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_shasta }, - { 0x105a, 0x4302, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_shasta }, - { 0x105a, 0x8301, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_shasta }, - { 0x105a, 0x8302, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_shasta }, - { 0x1725, 0x7250, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_vsc }, - { 0x105a, 0x8650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_yosemite }, + /* st_shasta */ + { 0x105a, 0x8350, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + st_shasta }, /* SuperTrak EX8350/8300/16350/16300 */ + { 0x105a, 0xc350, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + st_shasta }, /* SuperTrak EX12350 */ + { 0x105a, 0x4302, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + st_shasta }, /* SuperTrak EX4350 */ + { 0x105a, 0xe350, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + st_shasta }, /* SuperTrak EX24350 */ + + /* st_vsc */ + { 0x105a, 0x7250, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_vsc }, + + /* st_yosemite */ + { 0x105a, 0x8650, PCI_ANY_ID, 0x4600, 0, 0, + st_yosemite }, /* SuperTrak EX4650 */ + { 0x105a, 0x8650, PCI_ANY_ID, 0x4610, 0, 0, + st_yosemite }, /* SuperTrak EX4650o */ + { 0x105a, 0x8650, PCI_ANY_ID, 0x8600, 0, 0, + st_yosemite }, /* SuperTrak EX8650EL */ + { 0x105a, 0x8650, PCI_ANY_ID, 0x8601, 0, 0, + st_yosemite }, /* SuperTrak EX8650 */ + { 0x105a, 0x8650, PCI_ANY_ID, 0x8602, 0, 0, + st_yosemite }, /* SuperTrak EX8654 */ + { 0x105a, 0x8650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + st_yosemite }, /* generic st_yosemite */ { } /* terminate list */ }; MODULE_DEVICE_TABLE(pci, stex_pci_tbl); From 94e9108b5ad8da9c02ebf5183b861f1717409555 Mon Sep 17 00:00:00 2001 From: Ed Lin Date: Mon, 4 Dec 2006 17:49:39 -0800 Subject: [PATCH 70/73] [SCSI] stex: add new device type support Add support for st_vsc1 type device (st_vsc is ok because it does not require extra buffer). Signed-off-by: Ed Lin Signed-off-by: James Bottomley --- drivers/scsi/stex.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c index 0fd115c04e59..bfd7983045b7 100644 --- a/drivers/scsi/stex.c +++ b/drivers/scsi/stex.c @@ -121,7 +121,8 @@ enum { st_shasta = 0, st_vsc = 1, - st_yosemite = 2, + st_vsc1 = 2, + st_yosemite = 3, PASSTHRU_REQ_TYPE = 0x00000001, PASSTHRU_REQ_NO_WAKEUP = 0x00000100, @@ -151,6 +152,8 @@ enum { MGT_CMD_SIGNATURE = 0xba, INQUIRY_EVPD = 0x01, + + ST_ADDITIONAL_MEM = 0x200000, }; /* SCSI inquiry data */ @@ -212,7 +215,9 @@ struct handshake_frame { __le32 partner_ver_minor; __le32 partner_ver_oem; __le32 partner_ver_build; - u32 reserved1[4]; + __le32 extra_offset; /* NEW */ + __le32 extra_size; /* NEW */ + u32 reserved1[2]; }; struct req_msg { @@ -303,6 +308,7 @@ struct st_hba { void __iomem *mmio_base; /* iomapped PCI memory space */ void *dma_mem; dma_addr_t dma_handle; + size_t dma_size; struct Scsi_Host *host; struct pci_dev *pdev; @@ -941,6 +947,11 @@ static int stex_handshake(struct st_hba *hba) h->status_cnt = cpu_to_le16(MU_STATUS_COUNT); stex_gettime(&h->hosttime); h->partner_type = HMU_PARTNER_TYPE; + if (hba->dma_size > STEX_BUFFER_SIZE) { + h->extra_offset = cpu_to_le32(STEX_BUFFER_SIZE); + h->extra_size = cpu_to_le32(ST_ADDITIONAL_MEM); + } else + h->extra_offset = h->extra_size = 0; status_phys = hba->dma_handle + MU_REQ_BUFFER_SIZE; writel(status_phys, base + IMR0); @@ -1203,8 +1214,13 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto out_iounmap; } + hba->cardtype = (unsigned int) id->driver_data; + if (hba->cardtype == st_vsc && (pdev->subsystem_device & 0xf) == 0x1) + hba->cardtype = st_vsc1; + hba->dma_size = (hba->cardtype == st_vsc1) ? + (STEX_BUFFER_SIZE + ST_ADDITIONAL_MEM) : (STEX_BUFFER_SIZE); hba->dma_mem = dma_alloc_coherent(&pdev->dev, - STEX_BUFFER_SIZE, &hba->dma_handle, GFP_KERNEL); + hba->dma_size, &hba->dma_handle, GFP_KERNEL); if (!hba->dma_mem) { err = -ENOMEM; printk(KERN_ERR DRV_NAME "(%s): dma mem alloc failed\n", @@ -1217,8 +1233,6 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id) hba->copy_buffer = hba->dma_mem + MU_BUFFER_SIZE; hba->mu_status = MU_STATE_STARTING; - hba->cardtype = (unsigned int) id->driver_data; - /* firmware uses id/lun pair for a logical drive, but lun would be always 0 if CONFIG_SCSI_MULTI_LUN not configured, so we use channel to map lun here */ @@ -1266,7 +1280,7 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id) out_free_irq: free_irq(pdev->irq, hba); out_pci_free: - dma_free_coherent(&pdev->dev, STEX_BUFFER_SIZE, + dma_free_coherent(&pdev->dev, hba->dma_size, hba->dma_mem, hba->dma_handle); out_iounmap: iounmap(hba->mmio_base); @@ -1327,7 +1341,7 @@ static void stex_hba_free(struct st_hba *hba) pci_release_regions(hba->pdev); - dma_free_coherent(&hba->pdev->dev, STEX_BUFFER_SIZE, + dma_free_coherent(&hba->pdev->dev, hba->dma_size, hba->dma_mem, hba->dma_handle); } From 76fbf96f82636c8cdc40485506c50c880fd45a56 Mon Sep 17 00:00:00 2001 From: Ed Lin Date: Mon, 4 Dec 2006 17:49:42 -0800 Subject: [PATCH 71/73] [SCSI] stex: change wait loop code The original wait loop may be much longer than intended time. Use more accurate timer_after for it. Also adjust wait value to avoid unnecessary long waiting. Signed-off-by: Ed Lin Signed-off-by: James Bottomley --- drivers/scsi/stex.c | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c index bfd7983045b7..7edbcc9bc9d6 100644 --- a/drivers/scsi/stex.c +++ b/drivers/scsi/stex.c @@ -76,9 +76,10 @@ enum { MU_STATE_STARTED = 4, MU_STATE_RESETTING = 5, - MU_MAX_DELAY_TIME = 240000, + MU_MAX_DELAY = 120, MU_HANDSHAKE_SIGNATURE = 0x55aaaa55, MU_HANDSHAKE_SIGNATURE_HALF = 0x5a5a0000, + MU_HARD_RESET_WAIT = 30000, HMU_PARTNER_TYPE = 2, /* firmware returned values */ @@ -910,23 +911,22 @@ static int stex_handshake(struct st_hba *hba) struct handshake_frame *h; dma_addr_t status_phys; u32 data; - int i; + unsigned long before; if (readl(base + OMR0) != MU_HANDSHAKE_SIGNATURE) { writel(MU_INBOUND_DOORBELL_HANDSHAKE, base + IDBL); readl(base + IDBL); - for (i = 0; readl(base + OMR0) != MU_HANDSHAKE_SIGNATURE - && i < MU_MAX_DELAY_TIME; i++) { + before = jiffies; + while (readl(base + OMR0) != MU_HANDSHAKE_SIGNATURE) { + if (time_after(jiffies, before + MU_MAX_DELAY * HZ)) { + printk(KERN_ERR DRV_NAME + "(%s): no handshake signature\n", + pci_name(hba->pdev)); + return -1; + } rmb(); msleep(1); } - - if (i == MU_MAX_DELAY_TIME) { - printk(KERN_ERR DRV_NAME - "(%s): no handshake signature\n", - pci_name(hba->pdev)); - return -1; - } } udelay(10); @@ -965,19 +965,18 @@ static int stex_handshake(struct st_hba *hba) readl(base + IDBL); /* flush */ udelay(10); - for (i = 0; readl(base + OMR0) != MU_HANDSHAKE_SIGNATURE - && i < MU_MAX_DELAY_TIME; i++) { + before = jiffies; + while (readl(base + OMR0) != MU_HANDSHAKE_SIGNATURE) { + if (time_after(jiffies, before + MU_MAX_DELAY * HZ)) { + printk(KERN_ERR DRV_NAME + "(%s): no signature after handshake frame\n", + pci_name(hba->pdev)); + return -1; + } rmb(); msleep(1); } - if (i == MU_MAX_DELAY_TIME) { - printk(KERN_ERR DRV_NAME - "(%s): no signature after handshake frame\n", - pci_name(hba->pdev)); - return -1; - } - writel(0, base + IMR0); readl(base + IMR0); writel(0, base + OMR0); @@ -1059,7 +1058,7 @@ static void stex_hard_reset(struct st_hba *hba) pci_bctl &= ~PCI_BRIDGE_CTL_BUS_RESET; pci_write_config_byte(bus->self, PCI_BRIDGE_CONTROL, pci_bctl); - for (i = 0; i < MU_MAX_DELAY_TIME; i++) { + for (i = 0; i < MU_HARD_RESET_WAIT; i++) { pci_read_config_word(hba->pdev, PCI_COMMAND, &pci_cmd); if (pci_cmd != 0xffff && (pci_cmd & PCI_COMMAND_MASTER)) break; From febb631c11d450473a1648a4b808ab3bb9f139cb Mon Sep 17 00:00:00 2001 From: Ed Lin Date: Mon, 4 Dec 2006 17:49:46 -0800 Subject: [PATCH 72/73] [SCSI] stex: version update Update version to 3.1.0.1 Signed-off-by: Ed Lin Signed-off-by: James Bottomley --- drivers/scsi/stex.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c index 7edbcc9bc9d6..ba6bcdaf2a6a 100644 --- a/drivers/scsi/stex.c +++ b/drivers/scsi/stex.c @@ -11,8 +11,6 @@ * Written By: * Ed Lin * - * Version: 3.0.0.1 - * */ #include @@ -37,9 +35,9 @@ #include #define DRV_NAME "stex" -#define ST_DRIVER_VERSION "3.0.0.1" +#define ST_DRIVER_VERSION "3.1.0.1" #define ST_VER_MAJOR 3 -#define ST_VER_MINOR 0 +#define ST_VER_MINOR 1 #define ST_OEM 0 #define ST_BUILD_VER 1 From d32adcb85c74fd81963714689842993e7014515f Mon Sep 17 00:00:00 2001 From: "Salyzyn, Mark" Date: Tue, 5 Dec 2006 14:37:30 -0500 Subject: [PATCH 73/73] [SCSI] aic79xx: Add ASC-29320LPE ids to driver Simple patch to add the new PCIe version of the 29320 card. Signed-off: Mark Salyzyn Signed-off-by: James Bottomley --- drivers/scsi/aic7xxx/aic79xx_osm_pci.c | 1 + drivers/scsi/aic7xxx/aic79xx_pci.c | 8 +++++++- drivers/scsi/aic7xxx/aic79xx_pci.h | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/aic7xxx/aic79xx_osm_pci.c b/drivers/scsi/aic7xxx/aic79xx_osm_pci.c index 2001fe890e71..1a3ab6aa856b 100644 --- a/drivers/scsi/aic7xxx/aic79xx_osm_pci.c +++ b/drivers/scsi/aic7xxx/aic79xx_osm_pci.c @@ -62,6 +62,7 @@ static struct pci_device_id ahd_linux_pci_id_table[] = { /* aic7901 based controllers */ ID(ID_AHA_29320A), ID(ID_AHA_29320ALP), + ID(ID_AHA_29320LPE), /* aic7902 based controllers */ ID(ID_AHA_29320), ID(ID_AHA_29320B), diff --git a/drivers/scsi/aic7xxx/aic79xx_pci.c b/drivers/scsi/aic7xxx/aic79xx_pci.c index c07735819cd1..2cf7bb3123f0 100644 --- a/drivers/scsi/aic7xxx/aic79xx_pci.c +++ b/drivers/scsi/aic7xxx/aic79xx_pci.c @@ -109,7 +109,13 @@ static struct ahd_pci_identity ahd_pci_ident_table [] = { ID_AHA_29320ALP, ID_ALL_MASK, - "Adaptec 29320ALP Ultra320 SCSI adapter", + "Adaptec 29320ALP PCIx Ultra320 SCSI adapter", + ahd_aic7901_setup + }, + { + ID_AHA_29320LPE, + ID_ALL_MASK, + "Adaptec 29320LPE PCIe Ultra320 SCSI adapter", ahd_aic7901_setup }, /* aic7901A based controllers */ diff --git a/drivers/scsi/aic7xxx/aic79xx_pci.h b/drivers/scsi/aic7xxx/aic79xx_pci.h index da45153668c7..16b7c70a673c 100644 --- a/drivers/scsi/aic7xxx/aic79xx_pci.h +++ b/drivers/scsi/aic7xxx/aic79xx_pci.h @@ -51,6 +51,7 @@ #define ID_AIC7901 0x800F9005FFFF9005ull #define ID_AHA_29320A 0x8000900500609005ull #define ID_AHA_29320ALP 0x8017900500449005ull +#define ID_AHA_29320LPE 0x8017900500459005ull #define ID_AIC7901A 0x801E9005FFFF9005ull #define ID_AHA_29320LP 0x8014900500449005ull