From eea5ff7bde45c7724594e6a3c9a6290691ddabe9 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sat, 23 Feb 2008 23:51:00 -0600 Subject: [PATCH 01/27] [SCSI] mvsas: remove execute permission from file mvsas.c picked up execute permissions. Move it back to being a plane old file. James Bottomley Signed-off-by: James Bottomley --- drivers/scsi/mvsas.c | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 drivers/scsi/mvsas.c diff --git a/drivers/scsi/mvsas.c b/drivers/scsi/mvsas.c old mode 100755 new mode 100644 From 5319578ca38a8b90b6d0270c194c65d1dd8f7725 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sat, 23 Feb 2008 23:35:44 -0600 Subject: [PATCH 02/27] [SCSI] libsas: export sas_find_local_phy function This is needed by the to be added I_T reset function in aic94xx. It needs to know the local phy so it can send a link or hard reset along the path. Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_scsi_host.c | 5 +++-- include/scsi/libsas.h | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 704ea06a6e50..f3706f4b79ce 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -434,7 +434,7 @@ static int sas_recover_I_T(struct domain_device *dev) } /* Find the sas_phy that's attached to this device */ -static struct sas_phy *find_local_sas_phy(struct domain_device *dev) +struct sas_phy *sas_find_local_phy(struct domain_device *dev) { struct domain_device *pdev = dev->parent; struct ex_phy *exphy = NULL; @@ -456,6 +456,7 @@ static struct sas_phy *find_local_sas_phy(struct domain_device *dev) BUG_ON(!exphy); return exphy->phy; } +EXPORT_SYMBOL_GPL(sas_find_local_phy); /* Attempt to send a LUN reset message to a device */ int sas_eh_device_reset_handler(struct scsi_cmnd *cmd) @@ -482,7 +483,7 @@ int sas_eh_device_reset_handler(struct scsi_cmnd *cmd) int sas_eh_bus_reset_handler(struct scsi_cmnd *cmd) { struct domain_device *dev = cmd_to_domain_dev(cmd); - struct sas_phy *phy = find_local_sas_phy(dev); + struct sas_phy *phy = sas_find_local_phy(dev); int res; res = sas_phy_reset(phy, 1); diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 3ffd6b582a97..39e1cac24bb7 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -675,5 +675,6 @@ extern int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, extern void sas_ssp_task_response(struct device *dev, struct sas_task *task, struct ssp_response_iu *iu); +struct sas_phy *sas_find_local_phy(struct domain_device *dev); #endif /* _SASLIB_H_ */ From 63edf49e67cac710826108697c4e8636c89abd17 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sat, 23 Feb 2008 23:37:26 -0600 Subject: [PATCH 03/27] [SCSI] aic94xx: plumb in I_T_nexus_reset task management function Currently aic94xx has no exported I_T_nexus_reset function. This is a bit of a huge problem, since sas_ata relies on this function to perform an ATA phy reset and also it means that if abort fails, we really have no bigger hammer to hit everything with. Plumb in the I_T_nexus_reset by quiescing the sequencer, sending the correct phy reset (link for ATA and hard for SAS) and then carefully resuming the sequencer again. Signed-off-by: James Bottomley --- drivers/scsi/aic94xx/aic94xx.h | 1 + drivers/scsi/aic94xx/aic94xx_init.c | 2 +- drivers/scsi/aic94xx/aic94xx_tmf.c | 58 +++++++++++++++++++++++++++-- 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/aic94xx/aic94xx.h b/drivers/scsi/aic94xx/aic94xx.h index 32f513b1b78a..eb8efdcefe48 100644 --- a/drivers/scsi/aic94xx/aic94xx.h +++ b/drivers/scsi/aic94xx/aic94xx.h @@ -102,6 +102,7 @@ int asd_abort_task_set(struct domain_device *, u8 *lun); int asd_clear_aca(struct domain_device *, u8 *lun); int asd_clear_task_set(struct domain_device *, u8 *lun); int asd_lu_reset(struct domain_device *, u8 *lun); +int asd_I_T_nexus_reset(struct domain_device *dev); int asd_query_task(struct sas_task *); /* ---------- Adapter and Port management ---------- */ diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c index 5d761eb67442..88d1e731b65e 100644 --- a/drivers/scsi/aic94xx/aic94xx_init.c +++ b/drivers/scsi/aic94xx/aic94xx_init.c @@ -1003,7 +1003,7 @@ static struct sas_domain_function_template aic94xx_transport_functions = { .lldd_abort_task_set = asd_abort_task_set, .lldd_clear_aca = asd_clear_aca, .lldd_clear_task_set = asd_clear_task_set, - .lldd_I_T_nexus_reset = NULL, + .lldd_I_T_nexus_reset = asd_I_T_nexus_reset, .lldd_lu_reset = asd_lu_reset, .lldd_query_task = asd_query_task, diff --git a/drivers/scsi/aic94xx/aic94xx_tmf.c b/drivers/scsi/aic94xx/aic94xx_tmf.c index 144f5ad20453..d684c7432e63 100644 --- a/drivers/scsi/aic94xx/aic94xx_tmf.c +++ b/drivers/scsi/aic94xx/aic94xx_tmf.c @@ -140,8 +140,14 @@ int asd_clear_nexus_port(struct asd_sas_port *port) CLEAR_NEXUS_POST; } -#if 0 -static int asd_clear_nexus_I_T(struct domain_device *dev) +enum clear_nexus_phase { + NEXUS_PHASE_PRE, + NEXUS_PHASE_POST, + NEXUS_PHASE_RESUME, +}; + +static int asd_clear_nexus_I_T(struct domain_device *dev, + enum clear_nexus_phase phase) { struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; struct asd_ascb *ascb; @@ -150,12 +156,56 @@ static int asd_clear_nexus_I_T(struct domain_device *dev) CLEAR_NEXUS_PRE; scb->clear_nexus.nexus = NEXUS_I_T; - scb->clear_nexus.flags = SEND_Q | EXEC_Q | NOTINQ; + switch (phase) { + case NEXUS_PHASE_PRE: + scb->clear_nexus.flags = EXEC_Q | SUSPEND_TX; + break; + case NEXUS_PHASE_POST: + scb->clear_nexus.flags = SEND_Q | NOTINQ; + break; + case NEXUS_PHASE_RESUME: + scb->clear_nexus.flags = RESUME_TX; + } scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long) dev->lldd_dev); CLEAR_NEXUS_POST; } -#endif + +int asd_I_T_nexus_reset(struct domain_device *dev) +{ + int res, tmp_res, i; + struct sas_phy *phy = sas_find_local_phy(dev); + /* Standard mandates link reset for ATA (type 0) and + * hard reset for SSP (type 1) */ + int reset_type = (dev->dev_type == SATA_DEV || + (dev->tproto & SAS_PROTOCOL_STP)) ? 0 : 1; + + asd_clear_nexus_I_T(dev, NEXUS_PHASE_PRE); + /* send a hard reset */ + ASD_DPRINTK("sending %s reset to %s\n", + reset_type ? "hard" : "soft", phy->dev.bus_id); + res = sas_phy_reset(phy, reset_type); + if (res == TMF_RESP_FUNC_COMPLETE) { + /* wait for the maximum settle time */ + msleep(500); + /* clear all outstanding commands (keep nexus suspended) */ + asd_clear_nexus_I_T(dev, NEXUS_PHASE_POST); + } + for (i = 0 ; i < 3; i++) { + tmp_res = asd_clear_nexus_I_T(dev, NEXUS_PHASE_RESUME); + if (tmp_res == TC_RESUME) + return res; + msleep(500); + } + + /* This is a bit of a problem: the sequencer is still suspended + * and is refusing to resume. Hope it will resume on a bigger hammer + * or the disk is lost */ + dev_printk(KERN_ERR, &phy->dev, + "Failed to resume nexus after reset 0x%x\n", tmp_res); + + return TMF_RESP_FUNC_FAILED; +} static int asd_clear_nexus_I_T_L(struct domain_device *dev, u8 *lun) { From a29c05153630b2cd5ea078c97c0abe084cd830d8 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sat, 23 Feb 2008 23:38:44 -0600 Subject: [PATCH 04/27] [SCSI] libsas: use the supplied address for SATA devices rather than changing it Once the phy reset is plumbed in properly, SATA error handling fails nastily because we change the port attached_sas_address using the WWN field of the IDENTIFY message. This is a nice thing to do in theory, but it really destroys hotplug because any event on the port causes an automatic mismatch between the sas_address the phy just picked up and the one we propagate into the port. However ugly they are, we have to stick with the sas addresses made up by the phys and expanders. Also does a few cosmetic changes to the way port printing is done to make it clearer how a port is formed. Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 39 ++-------------------------------- drivers/scsi/libsas/sas_port.c | 11 +++++----- 2 files changed, 8 insertions(+), 42 deletions(-) diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 7cd05b599a12..b0e5ac372a32 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -236,12 +236,12 @@ static void sas_ata_phy_reset(struct ata_port *ap) struct domain_device *dev = ap->private_data; struct sas_internal *i = to_sas_internal(dev->port->ha->core.shost->transportt); - int res = 0; + int res = TMF_RESP_FUNC_FAILED; if (i->dft->lldd_I_T_nexus_reset) res = i->dft->lldd_I_T_nexus_reset(dev); - if (res) + if (res != TMF_RESP_FUNC_COMPLETE) SAS_DPRINTK("%s: Unable to reset I T nexus?\n", __FUNCTION__); switch (dev->sata_dev.command_set) { @@ -656,21 +656,6 @@ out: return res; } -static void sas_sata_propagate_sas_addr(struct domain_device *dev) -{ - unsigned long flags; - struct asd_sas_port *port = dev->port; - struct asd_sas_phy *phy; - - BUG_ON(dev->parent); - - memcpy(port->attached_sas_addr, dev->sas_addr, SAS_ADDR_SIZE); - spin_lock_irqsave(&port->phy_list_lock, flags); - list_for_each_entry(phy, &port->phy_list, port_phy_el) - memcpy(phy->attached_sas_addr, dev->sas_addr, SAS_ADDR_SIZE); - spin_unlock_irqrestore(&port->phy_list_lock, flags); -} - #define ATA_IDENTIFY_DEV 0xEC #define ATA_IDENTIFY_PACKET_DEV 0xA1 #define ATA_SET_FEATURES 0xEF @@ -728,26 +713,6 @@ static int sas_discover_sata_dev(struct domain_device *dev) goto out_err; } cont1: - /* Get WWN */ - if (dev->port->oob_mode != SATA_OOB_MODE) { - memcpy(dev->sas_addr, dev->sata_dev.rps_resp.rps.stp_sas_addr, - SAS_ADDR_SIZE); - } else if (dev->sata_dev.command_set == ATA_COMMAND_SET && - (le16_to_cpu(dev->sata_dev.identify_device[108]) & 0xF000) - == 0x5000) { - int i; - - for (i = 0; i < 4; i++) { - dev->sas_addr[2*i] = - (le16_to_cpu(dev->sata_dev.identify_device[108+i]) & 0xFF00) >> 8; - dev->sas_addr[2*i+1] = - le16_to_cpu(dev->sata_dev.identify_device[108+i]) & 0x00FF; - } - } - sas_hash_addr(dev->hashed_sas_addr, dev->sas_addr); - if (!dev->parent) - sas_sata_propagate_sas_addr(dev); - /* XXX Hint: register this SATA device with SATL. When this returns, dev->sata_dev->lu is alive and present. diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index e1e2d085c920..39ae68a3b0ef 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -92,9 +92,6 @@ static void sas_form_port(struct asd_sas_phy *phy) if (!port->phy) port->phy = phy->phy; - SAS_DPRINTK("phy%d added to port%d, phy_mask:0x%x\n", phy->id, - port->id, port->phy_mask); - if (*(u64 *)port->attached_sas_addr == 0) { port->class = phy->class; memcpy(port->attached_sas_addr, phy->attached_sas_addr, @@ -115,6 +112,11 @@ static void sas_form_port(struct asd_sas_phy *phy) } sas_port_add_phy(port->port, phy->phy); + SAS_DPRINTK("%s added to %s, phy_mask:0x%x (%16llx)\n", + phy->phy->dev.bus_id,port->port->dev.bus_id, + port->phy_mask, + SAS_ADDR(port->attached_sas_addr)); + if (port->port_dev) port->port_dev->pathways = port->num_phys; @@ -255,12 +257,11 @@ void sas_porte_hard_reset(struct work_struct *work) static void sas_init_port(struct asd_sas_port *port, struct sas_ha_struct *sas_ha, int i) { + memset(port, 0, sizeof(*port)); port->id = i; INIT_LIST_HEAD(&port->dev_list); spin_lock_init(&port->phy_list_lock); INIT_LIST_HEAD(&port->phy_list); - port->num_phys = 0; - port->phy_mask = 0; port->ha = sas_ha; spin_lock_init(&port->dev_list_lock); From 8de3ef25a1fcd28d270b69417a41b424826d4f89 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sat, 23 Feb 2008 23:39:59 -0600 Subject: [PATCH 05/27] [SCSI] libsas: misc fixes to the eh path - Correct one use after free of the sas task - update the reset required path to move straight to LUN reset - make the bigger hammer actually reset something instead of just trying to clear all the tasks. Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_scsi_host.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index f3706f4b79ce..1f8241563c6c 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -498,10 +498,10 @@ int sas_eh_bus_reset_handler(struct scsi_cmnd *cmd) } /* Try to reset a device */ -static int try_to_reset_cmd_device(struct Scsi_Host *shost, - struct scsi_cmnd *cmd) +static int try_to_reset_cmd_device(struct scsi_cmnd *cmd) { int res; + struct Scsi_Host *shost = cmd->device->host; if (!shost->hostt->eh_device_reset_handler) goto try_bus_reset; @@ -541,6 +541,12 @@ Again: need_reset = task->task_state_flags & SAS_TASK_NEED_DEV_RESET; spin_unlock_irqrestore(&task->task_state_lock, flags); + if (need_reset) { + SAS_DPRINTK("%s: task 0x%p requests reset\n", + __FUNCTION__, task); + goto reset; + } + SAS_DPRINTK("trying to find task 0x%p\n", task); res = sas_scsi_find_task(task); @@ -551,18 +557,15 @@ Again: SAS_DPRINTK("%s: task 0x%p is done\n", __FUNCTION__, task); sas_eh_finish_cmd(cmd); - if (need_reset) - try_to_reset_cmd_device(shost, cmd); continue; case TASK_IS_ABORTED: SAS_DPRINTK("%s: task 0x%p is aborted\n", __FUNCTION__, task); sas_eh_finish_cmd(cmd); - if (need_reset) - try_to_reset_cmd_device(shost, cmd); continue; case TASK_IS_AT_LU: SAS_DPRINTK("task 0x%p is at LU: lu recover\n", task); + reset: tmf_resp = sas_recover_lu(task->dev, cmd); if (tmf_resp == TMF_RESP_FUNC_COMPLETE) { SAS_DPRINTK("dev %016llx LU %x is " @@ -570,8 +573,6 @@ Again: SAS_ADDR(task->dev), cmd->device->lun); sas_eh_finish_cmd(cmd); - if (need_reset) - try_to_reset_cmd_device(shost, cmd); sas_scsi_clear_queue_lu(work_q, cmd); goto Again; } @@ -582,15 +583,15 @@ Again: task); tmf_resp = sas_recover_I_T(task->dev); if (tmf_resp == TMF_RESP_FUNC_COMPLETE) { + struct domain_device *dev = task->dev; SAS_DPRINTK("I_T %016llx recovered\n", SAS_ADDR(task->dev->sas_addr)); sas_eh_finish_cmd(cmd); - if (need_reset) - try_to_reset_cmd_device(shost, cmd); - sas_scsi_clear_queue_I_T(work_q, task->dev); + sas_scsi_clear_queue_I_T(work_q, dev); goto Again; } /* Hammer time :-) */ + try_to_reset_cmd_device(cmd); if (i->dft->lldd_clear_nexus_port) { struct asd_sas_port *port = task->dev->port; SAS_DPRINTK("clearing nexus for port:%d\n", @@ -600,8 +601,6 @@ Again: SAS_DPRINTK("clear nexus port:%d " "succeeded\n", port->id); sas_eh_finish_cmd(cmd); - if (need_reset) - try_to_reset_cmd_device(shost, cmd); sas_scsi_clear_queue_port(work_q, port); goto Again; @@ -614,8 +613,6 @@ Again: SAS_DPRINTK("clear nexus ha " "succeeded\n"); sas_eh_finish_cmd(cmd); - if (need_reset) - try_to_reset_cmd_device(shost, cmd); goto clear_q; } } @@ -629,8 +626,6 @@ Again: cmd->device->lun); sas_eh_finish_cmd(cmd); - if (need_reset) - try_to_reset_cmd_device(shost, cmd); goto clear_q; } } From e2396f1e4ecd438a15fa653a028b93e95013caa3 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sat, 23 Feb 2008 23:44:19 -0600 Subject: [PATCH 06/27] [SCSI] aic94xx: fix TMF ascb handling to prevent sequencer panic This is a particularly nasty bug. The problem is that if any internal ascb times out, currently we free it even though it's pending at the sequencer. This results in the sequencer getting terminally confused and the error message: BUG:sequencer:dl:no ascb Being returned when it comes back. The way to fix this is to manage freeing the ascb from the tasklet completion routine, so that we only free it when the sequencer actually returns it. The code is also altered to use on stack completions and transfer variables. Signed-off-by: James Bottomley --- drivers/scsi/aic94xx/aic94xx_hwi.h | 3 +- drivers/scsi/aic94xx/aic94xx_task.c | 4 +- drivers/scsi/aic94xx/aic94xx_tmf.c | 248 +++++++++++++++------------- 3 files changed, 140 insertions(+), 115 deletions(-) diff --git a/drivers/scsi/aic94xx/aic94xx_hwi.h b/drivers/scsi/aic94xx/aic94xx_hwi.h index 150f6706d23f..abc757559c1a 100644 --- a/drivers/scsi/aic94xx/aic94xx_hwi.h +++ b/drivers/scsi/aic94xx/aic94xx_hwi.h @@ -140,7 +140,7 @@ struct asd_ascb { /* internally generated command */ struct timer_list timer; - struct completion completion; + struct completion *completion; u8 tag_valid:1; __be16 tag; /* error recovery only */ @@ -294,7 +294,6 @@ static inline void asd_init_ascb(struct asd_ha_struct *asd_ha, ascb->timer.function = NULL; init_timer(&ascb->timer); ascb->tc_index = -1; - init_completion(&ascb->completion); } /* Must be called with the tc_index_lock held! diff --git a/drivers/scsi/aic94xx/aic94xx_task.c b/drivers/scsi/aic94xx/aic94xx_task.c index 965d4bb999d9..008df9ab92a5 100644 --- a/drivers/scsi/aic94xx/aic94xx_task.c +++ b/drivers/scsi/aic94xx/aic94xx_task.c @@ -343,11 +343,13 @@ Again: task->task_state_flags &= ~SAS_TASK_AT_INITIATOR; task->task_state_flags |= SAS_TASK_STATE_DONE; if (unlikely((task->task_state_flags & SAS_TASK_STATE_ABORTED))) { + struct completion *completion = ascb->completion; spin_unlock_irqrestore(&task->task_state_lock, flags); ASD_DPRINTK("task 0x%p done with opcode 0x%x resp 0x%x " "stat 0x%x but aborted by upper layer!\n", task, opcode, ts->resp, ts->stat); - complete(&ascb->completion); + if (completion) + complete(completion); } else { spin_unlock_irqrestore(&task->task_state_lock, flags); task->lldd_task = NULL; diff --git a/drivers/scsi/aic94xx/aic94xx_tmf.c b/drivers/scsi/aic94xx/aic94xx_tmf.c index d684c7432e63..b9ac8f703a1d 100644 --- a/drivers/scsi/aic94xx/aic94xx_tmf.c +++ b/drivers/scsi/aic94xx/aic94xx_tmf.c @@ -53,50 +53,64 @@ static int asd_enqueue_internal(struct asd_ascb *ascb, return res; } -static inline void asd_timedout_common(unsigned long data) -{ - struct asd_ascb *ascb = (void *) data; - struct asd_seq_data *seq = &ascb->ha->seq; - unsigned long flags; - - spin_lock_irqsave(&seq->pend_q_lock, flags); - seq->pending--; - list_del_init(&ascb->list); - spin_unlock_irqrestore(&seq->pend_q_lock, flags); -} - /* ---------- CLEAR NEXUS ---------- */ +struct tasklet_completion_status { + int dl_opcode; + int tmf_state; + u8 tag_valid:1; + __be16 tag; +}; + +#define DECLARE_TCS(tcs) \ + struct tasklet_completion_status tcs = { \ + .dl_opcode = 0, \ + .tmf_state = 0, \ + .tag_valid = 0, \ + .tag = 0, \ + } + + static void asd_clear_nexus_tasklet_complete(struct asd_ascb *ascb, struct done_list_struct *dl) { + struct tasklet_completion_status *tcs = ascb->uldd_task; ASD_DPRINTK("%s: here\n", __FUNCTION__); if (!del_timer(&ascb->timer)) { ASD_DPRINTK("%s: couldn't delete timer\n", __FUNCTION__); return; } ASD_DPRINTK("%s: opcode: 0x%x\n", __FUNCTION__, dl->opcode); - ascb->uldd_task = (void *) (unsigned long) dl->opcode; - complete(&ascb->completion); + tcs->dl_opcode = dl->opcode; + complete(ascb->completion); + asd_ascb_free(ascb); } static void asd_clear_nexus_timedout(unsigned long data) { - struct asd_ascb *ascb = (void *) data; + struct asd_ascb *ascb = (void *)data; + struct tasklet_completion_status *tcs = ascb->uldd_task; ASD_DPRINTK("%s: here\n", __FUNCTION__); - asd_timedout_common(data); - ascb->uldd_task = (void *) TMF_RESP_FUNC_FAILED; - complete(&ascb->completion); + tcs->dl_opcode = TMF_RESP_FUNC_FAILED; + complete(ascb->completion); } #define CLEAR_NEXUS_PRE \ + struct asd_ascb *ascb; \ + struct scb *scb; \ + int res; \ + DECLARE_COMPLETION_ONSTACK(completion); \ + DECLARE_TCS(tcs); \ + \ ASD_DPRINTK("%s: PRE\n", __FUNCTION__); \ res = 1; \ ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL); \ if (!ascb) \ return -ENOMEM; \ \ + ascb->completion = &completion; \ + ascb->uldd_task = &tcs; \ scb = ascb->scb; \ scb->header.opcode = CLEAR_NEXUS @@ -107,10 +121,11 @@ static void asd_clear_nexus_timedout(unsigned long data) if (res) \ goto out_err; \ ASD_DPRINTK("%s: clear nexus posted, waiting...\n", __FUNCTION__); \ - wait_for_completion(&ascb->completion); \ - res = (int) (unsigned long) ascb->uldd_task; \ + wait_for_completion(&completion); \ + res = tcs.dl_opcode; \ if (res == TC_NO_ERROR) \ res = TMF_RESP_FUNC_COMPLETE; \ + return res; \ out_err: \ asd_ascb_free(ascb); \ return res @@ -118,9 +133,6 @@ out_err: \ int asd_clear_nexus_ha(struct sas_ha_struct *sas_ha) { struct asd_ha_struct *asd_ha = sas_ha->lldd_ha; - struct asd_ascb *ascb; - struct scb *scb; - int res; CLEAR_NEXUS_PRE; scb->clear_nexus.nexus = NEXUS_ADAPTER; @@ -130,9 +142,6 @@ int asd_clear_nexus_ha(struct sas_ha_struct *sas_ha) int asd_clear_nexus_port(struct asd_sas_port *port) { struct asd_ha_struct *asd_ha = port->ha->lldd_ha; - struct asd_ascb *ascb; - struct scb *scb; - int res; CLEAR_NEXUS_PRE; scb->clear_nexus.nexus = NEXUS_PORT; @@ -150,9 +159,6 @@ static int asd_clear_nexus_I_T(struct domain_device *dev, enum clear_nexus_phase phase) { struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; - struct asd_ascb *ascb; - struct scb *scb; - int res; CLEAR_NEXUS_PRE; scb->clear_nexus.nexus = NEXUS_I_T; @@ -210,9 +216,6 @@ int asd_I_T_nexus_reset(struct domain_device *dev) static int asd_clear_nexus_I_T_L(struct domain_device *dev, u8 *lun) { struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; - struct asd_ascb *ascb; - struct scb *scb; - int res; CLEAR_NEXUS_PRE; scb->clear_nexus.nexus = NEXUS_I_T_L; @@ -227,9 +230,6 @@ static int asd_clear_nexus_tag(struct sas_task *task) { struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha; struct asd_ascb *tascb = task->lldd_task; - struct asd_ascb *ascb; - struct scb *scb; - int res; CLEAR_NEXUS_PRE; scb->clear_nexus.nexus = NEXUS_TAG; @@ -245,9 +245,6 @@ static int asd_clear_nexus_index(struct sas_task *task) { struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha; struct asd_ascb *tascb = task->lldd_task; - struct asd_ascb *ascb; - struct scb *scb; - int res; CLEAR_NEXUS_PRE; scb->clear_nexus.nexus = NEXUS_TRANS_CX; @@ -263,11 +260,11 @@ static int asd_clear_nexus_index(struct sas_task *task) static void asd_tmf_timedout(unsigned long data) { struct asd_ascb *ascb = (void *) data; + struct tasklet_completion_status *tcs = ascb->uldd_task; ASD_DPRINTK("tmf timed out\n"); - asd_timedout_common(data); - ascb->uldd_task = (void *) TMF_RESP_FUNC_FAILED; - complete(&ascb->completion); + tcs->tmf_state = TMF_RESP_FUNC_FAILED; + complete(ascb->completion); } static int asd_get_tmf_resp_tasklet(struct asd_ascb *ascb, @@ -319,18 +316,24 @@ static int asd_get_tmf_resp_tasklet(struct asd_ascb *ascb, static void asd_tmf_tasklet_complete(struct asd_ascb *ascb, struct done_list_struct *dl) { + struct tasklet_completion_status *tcs; + if (!del_timer(&ascb->timer)) return; + tcs = ascb->uldd_task; ASD_DPRINTK("tmf tasklet complete\n"); - if (dl->opcode == TC_SSP_RESP) - ascb->uldd_task = (void *) (unsigned long) - asd_get_tmf_resp_tasklet(ascb, dl); - else - ascb->uldd_task = (void *) 0xFF00 + (unsigned long) dl->opcode; + tcs->dl_opcode = dl->opcode; - complete(&ascb->completion); + if (dl->opcode == TC_SSP_RESP) { + tcs->tmf_state = asd_get_tmf_resp_tasklet(ascb, dl); + tcs->tag_valid = ascb->tag_valid; + tcs->tag = ascb->tag; + } + + complete(ascb->completion); + asd_ascb_free(ascb); } static inline int asd_clear_nexus(struct sas_task *task) @@ -338,15 +341,19 @@ static inline int asd_clear_nexus(struct sas_task *task) int res = TMF_RESP_FUNC_FAILED; int leftover; struct asd_ascb *tascb = task->lldd_task; + DECLARE_COMPLETION_ONSTACK(completion); unsigned long flags; + tascb->completion = &completion; + ASD_DPRINTK("task not done, clearing nexus\n"); if (tascb->tag_valid) res = asd_clear_nexus_tag(task); else res = asd_clear_nexus_index(task); - leftover = wait_for_completion_timeout(&tascb->completion, + leftover = wait_for_completion_timeout(&completion, AIC94XX_SCB_TIMEOUT); + tascb->completion = NULL; ASD_DPRINTK("came back from clear nexus\n"); spin_lock_irqsave(&task->task_state_lock, flags); if (leftover < 1) @@ -400,6 +407,11 @@ int asd_abort_task(struct sas_task *task) struct asd_ascb *ascb = NULL; struct scb *scb; int leftover; + DECLARE_TCS(tcs); + DECLARE_COMPLETION_ONSTACK(completion); + DECLARE_COMPLETION_ONSTACK(tascb_completion); + + tascb->completion = &tascb_completion; spin_lock_irqsave(&task->task_state_lock, flags); if (task->task_state_flags & SAS_TASK_STATE_DONE) { @@ -413,8 +425,10 @@ int asd_abort_task(struct sas_task *task) ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL); if (!ascb) return -ENOMEM; - scb = ascb->scb; + ascb->uldd_task = &tcs; + ascb->completion = &completion; + scb = ascb->scb; scb->header.opcode = SCB_ABORT_TASK; switch (task->task_proto) { @@ -456,13 +470,12 @@ int asd_abort_task(struct sas_task *task) res = asd_enqueue_internal(ascb, asd_tmf_tasklet_complete, asd_tmf_timedout); if (res) - goto out; - wait_for_completion(&ascb->completion); + goto out_free; + wait_for_completion(&completion); ASD_DPRINTK("tmf came back\n"); - res = (int) (unsigned long) ascb->uldd_task; - tascb->tag = ascb->tag; - tascb->tag_valid = ascb->tag_valid; + tascb->tag = tcs.tag; + tascb->tag_valid = tcs.tag_valid; spin_lock_irqsave(&task->task_state_lock, flags); if (task->task_state_flags & SAS_TASK_STATE_DONE) { @@ -473,63 +486,68 @@ int asd_abort_task(struct sas_task *task) } spin_unlock_irqrestore(&task->task_state_lock, flags); - switch (res) { - /* The task to be aborted has been sent to the device. - * We got a Response IU for the ABORT TASK TMF. */ - case TC_NO_ERROR + 0xFF00: - case TMF_RESP_FUNC_COMPLETE: - case TMF_RESP_FUNC_FAILED: - res = asd_clear_nexus(task); - break; - case TMF_RESP_INVALID_FRAME: - case TMF_RESP_OVERLAPPED_TAG: - case TMF_RESP_FUNC_ESUPP: - case TMF_RESP_NO_LUN: - goto out_done; break; - } - /* In the following we assume that the managing layer - * will _never_ make a mistake, when issuing ABORT TASK. - */ - switch (res) { - default: - res = asd_clear_nexus(task); - /* fallthrough */ - case TC_NO_ERROR + 0xFF00: - case TMF_RESP_FUNC_COMPLETE: - break; - /* The task hasn't been sent to the device xor we never got - * a (sane) Response IU for the ABORT TASK TMF. - */ - case TF_NAK_RECV + 0xFF00: - res = TMF_RESP_INVALID_FRAME; - break; - case TF_TMF_TASK_DONE + 0xFF00: /* done but not reported yet */ + if (tcs.dl_opcode == TC_SSP_RESP) { + /* The task to be aborted has been sent to the device. + * We got a Response IU for the ABORT TASK TMF. */ + if (tcs.tmf_state == TMF_RESP_FUNC_COMPLETE) + res = asd_clear_nexus(task); + else + res = tcs.tmf_state; + } else if (tcs.dl_opcode == TC_NO_ERROR && + tcs.tmf_state == TMF_RESP_FUNC_FAILED) { + /* timeout */ res = TMF_RESP_FUNC_FAILED; - leftover = wait_for_completion_timeout(&tascb->completion, - AIC94XX_SCB_TIMEOUT); - spin_lock_irqsave(&task->task_state_lock, flags); - if (leftover < 1) + } else { + /* In the following we assume that the managing layer + * will _never_ make a mistake, when issuing ABORT + * TASK. + */ + switch (tcs.dl_opcode) { + default: + res = asd_clear_nexus(task); + /* fallthrough */ + case TC_NO_ERROR: + break; + /* The task hasn't been sent to the device xor + * we never got a (sane) Response IU for the + * ABORT TASK TMF. + */ + case TF_NAK_RECV: + res = TMF_RESP_INVALID_FRAME; + break; + case TF_TMF_TASK_DONE: /* done but not reported yet */ res = TMF_RESP_FUNC_FAILED; - if (task->task_state_flags & SAS_TASK_STATE_DONE) + leftover = + wait_for_completion_timeout(&tascb_completion, + AIC94XX_SCB_TIMEOUT); + spin_lock_irqsave(&task->task_state_lock, flags); + if (leftover < 1) + res = TMF_RESP_FUNC_FAILED; + if (task->task_state_flags & SAS_TASK_STATE_DONE) + res = TMF_RESP_FUNC_COMPLETE; + spin_unlock_irqrestore(&task->task_state_lock, flags); + break; + case TF_TMF_NO_TAG: + case TF_TMF_TAG_FREE: /* the tag is in the free list */ + case TF_TMF_NO_CONN_HANDLE: /* no such device */ res = TMF_RESP_FUNC_COMPLETE; - spin_unlock_irqrestore(&task->task_state_lock, flags); - goto out_done; - case TF_TMF_NO_TAG + 0xFF00: - case TF_TMF_TAG_FREE + 0xFF00: /* the tag is in the free list */ - case TF_TMF_NO_CONN_HANDLE + 0xFF00: /* no such device */ - res = TMF_RESP_FUNC_COMPLETE; - goto out_done; - case TF_TMF_NO_CTX + 0xFF00: /* not in seq, or proto != SSP */ - res = TMF_RESP_FUNC_ESUPP; - goto out; + break; + case TF_TMF_NO_CTX: /* not in seq, or proto != SSP */ + res = TMF_RESP_FUNC_ESUPP; + break; + } } -out_done: + out_done: + tascb->completion = NULL; if (res == TMF_RESP_FUNC_COMPLETE) { task->lldd_task = NULL; mb(); asd_ascb_free(tascb); } -out: + ASD_DPRINTK("task 0x%p aborted, res: 0x%x\n", task, res); + return res; + + out_free: asd_ascb_free(ascb); ASD_DPRINTK("task 0x%p aborted, res: 0x%x\n", task, res); return res; @@ -557,6 +575,8 @@ static int asd_initiate_ssp_tmf(struct domain_device *dev, u8 *lun, struct asd_ascb *ascb; int res = 1; struct scb *scb; + DECLARE_COMPLETION_ONSTACK(completion); + DECLARE_TCS(tcs); if (!(dev->tproto & SAS_PROTOCOL_SSP)) return TMF_RESP_FUNC_ESUPP; @@ -564,6 +584,9 @@ static int asd_initiate_ssp_tmf(struct domain_device *dev, u8 *lun, ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL); if (!ascb) return -ENOMEM; + + ascb->completion = &completion; + ascb->uldd_task = &tcs; scb = ascb->scb; if (tmf == TMF_QUERY_TASK) @@ -596,31 +619,32 @@ static int asd_initiate_ssp_tmf(struct domain_device *dev, u8 *lun, asd_tmf_timedout); if (res) goto out_err; - wait_for_completion(&ascb->completion); - res = (int) (unsigned long) ascb->uldd_task; + wait_for_completion(&completion); - switch (res) { - case TC_NO_ERROR + 0xFF00: + switch (tcs.dl_opcode) { + case TC_NO_ERROR: res = TMF_RESP_FUNC_COMPLETE; break; - case TF_NAK_RECV + 0xFF00: + case TF_NAK_RECV: res = TMF_RESP_INVALID_FRAME; break; - case TF_TMF_TASK_DONE + 0xFF00: + case TF_TMF_TASK_DONE: res = TMF_RESP_FUNC_FAILED; break; - case TF_TMF_NO_TAG + 0xFF00: - case TF_TMF_TAG_FREE + 0xFF00: /* the tag is in the free list */ - case TF_TMF_NO_CONN_HANDLE + 0xFF00: /* no such device */ + case TF_TMF_NO_TAG: + case TF_TMF_TAG_FREE: /* the tag is in the free list */ + case TF_TMF_NO_CONN_HANDLE: /* no such device */ res = TMF_RESP_FUNC_COMPLETE; break; - case TF_TMF_NO_CTX + 0xFF00: /* not in seq, or proto != SSP */ + case TF_TMF_NO_CTX: /* not in seq, or proto != SSP */ res = TMF_RESP_FUNC_ESUPP; break; default: /* Allow TMF response codes to propagate upwards */ + res = tcs.dl_opcode; break; } + return res; out_err: asd_ascb_free(ascb); return res; From 31ed0bf439a15363c28c7a239f52eb127cb6feb3 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Tue, 26 Feb 2008 12:35:23 -0600 Subject: [PATCH 07/27] [SCSI] iscsi regression: check for zero max session cmds The old tools did not set max session cmds. This is a regression. I removed the check when merging the power of 2 patch. Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/libiscsi.c | 4 ++-- drivers/scsi/scsi_transport_iscsi.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 59f8445eab0d..bdd7de7da39a 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -1708,8 +1708,8 @@ iscsi_session_setup(struct iscsi_transport *iscsit, qdepth = ISCSI_DEF_CMD_PER_LUN; } - if (!is_power_of_2(cmds_max) || - cmds_max >= ISCSI_MGMT_ITT_OFFSET) { + if (!is_power_of_2(cmds_max) || cmds_max >= ISCSI_MGMT_ITT_OFFSET || + cmds_max < 2) { if (cmds_max != 0) printk(KERN_ERR "iscsi: invalid can_queue of %d. " "can_queue must be a power of 2 and between " diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 9981682d5302..dfb026b95a6a 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -33,7 +33,7 @@ #define ISCSI_SESSION_ATTRS 19 #define ISCSI_CONN_ATTRS 13 #define ISCSI_HOST_ATTRS 4 -#define ISCSI_TRANSPORT_VERSION "2.0-868" +#define ISCSI_TRANSPORT_VERSION "2.0-869" struct iscsi_internal { int daemon_pid; From b31ddd31c266c2ad1b708cad0d3d8e0aa7fa2737 Mon Sep 17 00:00:00 2001 From: Boaz Harrosh Date: Wed, 27 Feb 2008 15:27:16 -0800 Subject: [PATCH 08/27] [SCSI] gdth: bugfix for the at-exit problems gdth_exit would first remove all cards then stop the timer and would not sync with the timer function. This caused a crash in gdth_timer() when module was unloaded. So del_timer_sync the timer before we delete the cards. also the reboot notifier function would crash. So clean that up and fix the crashes. Signed-off-by: Boaz Harrosh Tested-by: Joerg Dorchain: Tested-by: Stefan Priebe Tested-by: Jon Chelton Cc: Stable Tree Signed-off-by: James Bottomley --- drivers/scsi/gdth.c | 82 ++++++++++++++++----------------------------- 1 file changed, 28 insertions(+), 54 deletions(-) diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c index 6d67f5c0eb8e..23d1a28b929d 100644 --- a/drivers/scsi/gdth.c +++ b/drivers/scsi/gdth.c @@ -182,7 +182,6 @@ static int gdth_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg); static void gdth_flush(gdth_ha_str *ha); -static int gdth_halt(struct notifier_block *nb, ulong event, void *buf); static int gdth_queuecommand(Scsi_Cmnd *scp,void (*done)(Scsi_Cmnd *)); static int __gdth_queuecommand(gdth_ha_str *ha, struct scsi_cmnd *scp, struct gdth_cmndinfo *cmndinfo); @@ -417,12 +416,6 @@ static inline void gdth_set_sglist(struct scsi_cmnd *cmd, #include "gdth_proc.h" #include "gdth_proc.c" -/* notifier block to get a notify on system shutdown/halt/reboot */ -static struct notifier_block gdth_notifier = { - gdth_halt, NULL, 0 -}; -static int notifier_disabled = 0; - static gdth_ha_str *gdth_find_ha(int hanum) { gdth_ha_str *ha; @@ -3794,6 +3787,8 @@ static void gdth_timeout(ulong data) gdth_ha_str *ha; ulong flags; + BUG_ON(list_empty(&gdth_instances)); + ha = list_first_entry(&gdth_instances, gdth_ha_str, list); spin_lock_irqsave(&ha->smp_lock, flags); @@ -4669,45 +4664,6 @@ static void gdth_flush(gdth_ha_str *ha) } } -/* shutdown routine */ -static int gdth_halt(struct notifier_block *nb, ulong event, void *buf) -{ - gdth_ha_str *ha; -#ifndef __alpha__ - gdth_cmd_str gdtcmd; - char cmnd[MAX_COMMAND_SIZE]; -#endif - - if (notifier_disabled) - return NOTIFY_OK; - - TRACE2(("gdth_halt() event %d\n",(int)event)); - if (event != SYS_RESTART && event != SYS_HALT && event != SYS_POWER_OFF) - return NOTIFY_DONE; - - notifier_disabled = 1; - printk("GDT-HA: Flushing all host drives .. "); - list_for_each_entry(ha, &gdth_instances, list) { - gdth_flush(ha); - -#ifndef __alpha__ - /* controller reset */ - memset(cmnd, 0xff, MAX_COMMAND_SIZE); - gdtcmd.BoardNode = LOCALBOARD; - gdtcmd.Service = CACHESERVICE; - gdtcmd.OpCode = GDT_RESET; - TRACE2(("gdth_halt(): reset controller %d\n", ha->hanum)); - gdth_execute(ha->shost, &gdtcmd, cmnd, 10, NULL); -#endif - } - printk("Done.\n"); - -#ifdef GDTH_STATISTICS - del_timer(&gdth_timer); -#endif - return NOTIFY_OK; -} - /* configure lun */ static int gdth_slave_configure(struct scsi_device *sdev) { @@ -5142,13 +5098,13 @@ static void gdth_remove_one(gdth_ha_str *ha) scsi_remove_host(shp); + gdth_flush(ha); + if (ha->sdev) { scsi_free_host_dev(ha->sdev); ha->sdev = NULL; } - gdth_flush(ha); - if (shp->irq) free_irq(shp->irq,ha); @@ -5174,6 +5130,24 @@ static void gdth_remove_one(gdth_ha_str *ha) scsi_host_put(shp); } +static int gdth_halt(struct notifier_block *nb, ulong event, void *buf) +{ + gdth_ha_str *ha; + + TRACE2(("gdth_halt() event %d\n", (int)event)); + if (event != SYS_RESTART && event != SYS_HALT && event != SYS_POWER_OFF) + return NOTIFY_DONE; + + list_for_each_entry(ha, &gdth_instances, list) + gdth_flush(ha); + + return NOTIFY_OK; +} + +static struct notifier_block gdth_notifier = { + gdth_halt, NULL, 0 +}; + static int __init gdth_init(void) { if (disable) { @@ -5236,7 +5210,6 @@ static int __init gdth_init(void) add_timer(&gdth_timer); #endif major = register_chrdev(0,"gdth", &gdth_fops); - notifier_disabled = 0; register_reboot_notifier(&gdth_notifier); gdth_polling = FALSE; return 0; @@ -5246,14 +5219,15 @@ static void __exit gdth_exit(void) { gdth_ha_str *ha; - list_for_each_entry(ha, &gdth_instances, list) - gdth_remove_one(ha); + unregister_chrdev(major, "gdth"); + unregister_reboot_notifier(&gdth_notifier); #ifdef GDTH_STATISTICS - del_timer(&gdth_timer); + del_timer_sync(&gdth_timer); #endif - unregister_chrdev(major,"gdth"); - unregister_reboot_notifier(&gdth_notifier); + + list_for_each_entry(ha, &gdth_instances, list) + gdth_remove_one(ha); } module_init(gdth_init); From ee54cc6af95a7fa09da298493b853a9e64fa8abd Mon Sep 17 00:00:00 2001 From: Boaz Harrosh Date: Wed, 27 Feb 2008 15:29:15 -0800 Subject: [PATCH 09/27] [SCSI] gdth: fix to internal commands execution The recent patch named: [SCSI] gdth: !use_sg cleanup and use of scsi accessors has done a bad job in handling internal commands issued by gdth_execute(). Internal commands are issued with device gdth_cmd_str ready made directly to the card, without any mapping or translations of scsi commands. So here I added a gdth_cmd_str pointer to the gdth_cmndinfo private structure which is then copied directly to host. following this patch is a cleanup that removes the home cooked accessors and reverts them to regular scsi_cmnd accessors. Since they are not used anymore. After review maybe the 2 patches should be squashed together. FIXME: There is still a problem with gdth_get_info(). as reported there is a WARN_ON trigerd in dma_free_coherent() when doing: $ cat /proc/sys/gdth/0 Signed-off-by: Boaz Harrosh Tested-by: Joerg Dorchain: Tested-by: Stefan Priebe Tested-by: Jon Chelton Cc: Stable Tree Signed-off-by: James Bottomley --- drivers/scsi/gdth.c | 30 ++++++++++++------------------ drivers/scsi/gdth.h | 1 + 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c index 23d1a28b929d..27ebd336409b 100644 --- a/drivers/scsi/gdth.c +++ b/drivers/scsi/gdth.c @@ -160,7 +160,7 @@ static void gdth_readapp_event(gdth_ha_str *ha, unchar application, static void gdth_clear_events(void); static void gdth_copy_internal_data(gdth_ha_str *ha, Scsi_Cmnd *scp, - char *buffer, ushort count, int to_buffer); + char *buffer, ushort count); static int gdth_internal_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp); static int gdth_fill_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp, ushort hdrive); @@ -438,8 +438,8 @@ static struct gdth_cmndinfo *gdth_get_cmndinfo(gdth_ha_str *ha) for (i=0; icmndinfo[i].index == 0) { priv = &ha->cmndinfo[i]; - priv->index = i+1; memset(priv, 0, sizeof(*priv)); + priv->index = i+1; break; } } @@ -486,7 +486,6 @@ int __gdth_execute(struct scsi_device *sdev, gdth_cmd_str *gdtcmd, char *cmnd, gdth_ha_str *ha = shost_priv(sdev->host); Scsi_Cmnd *scp; struct gdth_cmndinfo cmndinfo; - struct scatterlist one_sg; DECLARE_COMPLETION_ONSTACK(wait); int rval; @@ -500,13 +499,10 @@ int __gdth_execute(struct scsi_device *sdev, gdth_cmd_str *gdtcmd, char *cmnd, /* use request field to save the ptr. to completion struct. */ scp->request = (struct request *)&wait; scp->timeout_per_command = timeout*HZ; - sg_init_one(&one_sg, gdtcmd, sizeof(*gdtcmd)); - gdth_set_sglist(scp, &one_sg); - gdth_set_sg_count(scp, 1); - gdth_set_bufflen(scp, sizeof(*gdtcmd)); scp->cmd_len = 12; memcpy(scp->cmnd, cmnd, 12); cmndinfo.priority = IOCTL_PRI; + cmndinfo.internal_cmd_str = gdtcmd; cmndinfo.internal_command = 1; TRACE(("__gdth_execute() cmd 0x%x\n", scp->cmnd[0])); @@ -2348,7 +2344,7 @@ static void gdth_next(gdth_ha_str *ha) * buffers, kmap_atomic() as needed. */ static void gdth_copy_internal_data(gdth_ha_str *ha, Scsi_Cmnd *scp, - char *buffer, ushort count, int to_buffer) + char *buffer, ushort count) { ushort cpcount,i, max_sg = gdth_sg_count(scp); ushort cpsum,cpnow; @@ -2374,10 +2370,7 @@ static void gdth_copy_internal_data(gdth_ha_str *ha, Scsi_Cmnd *scp, } local_irq_save(flags); address = kmap_atomic(sg_page(sl), KM_BIO_SRC_IRQ) + sl->offset; - if (to_buffer) - memcpy(buffer, address, cpnow); - else - memcpy(address, buffer, cpnow); + memcpy(address, buffer, cpnow); flush_dcache_page(sg_page(sl)); kunmap_atomic(address, KM_BIO_SRC_IRQ); local_irq_restore(flags); @@ -2431,7 +2424,7 @@ static int gdth_internal_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp) strcpy(inq.vendor,ha->oem_name); sprintf(inq.product,"Host Drive #%02d",t); strcpy(inq.revision," "); - gdth_copy_internal_data(ha, scp, (char*)&inq, sizeof(gdth_inq_data), 0); + gdth_copy_internal_data(ha, scp, (char*)&inq, sizeof(gdth_inq_data)); break; case REQUEST_SENSE: @@ -2441,7 +2434,7 @@ static int gdth_internal_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp) sd.key = NO_SENSE; sd.info = 0; sd.add_length= 0; - gdth_copy_internal_data(ha, scp, (char*)&sd, sizeof(gdth_sense_data), 0); + gdth_copy_internal_data(ha, scp, (char*)&sd, sizeof(gdth_sense_data)); break; case MODE_SENSE: @@ -2453,7 +2446,7 @@ static int gdth_internal_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp) mpd.bd.block_length[0] = (SECTOR_SIZE & 0x00ff0000) >> 16; mpd.bd.block_length[1] = (SECTOR_SIZE & 0x0000ff00) >> 8; mpd.bd.block_length[2] = (SECTOR_SIZE & 0x000000ff); - gdth_copy_internal_data(ha, scp, (char*)&mpd, sizeof(gdth_modep_data), 0); + gdth_copy_internal_data(ha, scp, (char*)&mpd, sizeof(gdth_modep_data)); break; case READ_CAPACITY: @@ -2463,7 +2456,7 @@ static int gdth_internal_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp) else rdc.last_block_no = cpu_to_be32(ha->hdr[t].size-1); rdc.block_length = cpu_to_be32(SECTOR_SIZE); - gdth_copy_internal_data(ha, scp, (char*)&rdc, sizeof(gdth_rdcap_data), 0); + gdth_copy_internal_data(ha, scp, (char*)&rdc, sizeof(gdth_rdcap_data)); break; case SERVICE_ACTION_IN: @@ -2475,7 +2468,7 @@ static int gdth_internal_cache_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp) rdc16.last_block_no = cpu_to_be64(ha->hdr[t].size-1); rdc16.block_length = cpu_to_be32(SECTOR_SIZE); gdth_copy_internal_data(ha, scp, (char*)&rdc16, - sizeof(gdth_rdcap16_data), 0); + sizeof(gdth_rdcap16_data)); } else { scp->result = DID_ABORT << 16; } @@ -2845,6 +2838,7 @@ static int gdth_fill_raw_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp, unchar b) static int gdth_special_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp) { register gdth_cmd_str *cmdp; + struct gdth_cmndinfo *cmndinfo = gdth_cmnd_priv(scp); int cmd_index; cmdp= ha->pccb; @@ -2853,7 +2847,7 @@ static int gdth_special_cmd(gdth_ha_str *ha, Scsi_Cmnd *scp) if (ha->type==GDT_EISA && ha->cmd_cnt>0) return 0; - gdth_copy_internal_data(ha, scp, (char *)cmdp, sizeof(gdth_cmd_str), 1); + *cmdp = *cmndinfo->internal_cmd_str; cmdp->RequestBuffer = scp; /* search free command index */ diff --git a/drivers/scsi/gdth.h b/drivers/scsi/gdth.h index 1434c6b0297c..26e4e92515e0 100644 --- a/drivers/scsi/gdth.h +++ b/drivers/scsi/gdth.h @@ -915,6 +915,7 @@ typedef struct { struct gdth_cmndinfo { /* per-command private info */ int index; int internal_command; /* don't call scsi_done */ + gdth_cmd_str *internal_cmd_str; /* crier for internal messages*/ dma_addr_t sense_paddr; /* sense dma-addr */ unchar priority; int timeout; From 00da714b31b944400ee789e477f58247cff30b1b Mon Sep 17 00:00:00 2001 From: Ke Wei Date: Wed, 27 Feb 2008 20:50:25 +0800 Subject: [PATCH 10/27] [SCSI] mvsas: fix phy sas address The phy sas address is showing wrongly (wrong endianness). Fix up the endian transforms to make this correct. Signed-off-by: Ke Wei Signed-off-by: James Bottomley --- drivers/scsi/mvsas.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) mode change 100644 => 100755 drivers/scsi/mvsas.c diff --git a/drivers/scsi/mvsas.c b/drivers/scsi/mvsas.c old mode 100644 new mode 100755 index d4a6ac3c9c47..5ec0665b3a3d --- a/drivers/scsi/mvsas.c +++ b/drivers/scsi/mvsas.c @@ -40,7 +40,7 @@ #include #define DRV_NAME "mvsas" -#define DRV_VERSION "0.5" +#define DRV_VERSION "0.5.1" #define _MV_DUMP 0 #define MVS_DISABLE_NVRAM #define MVS_DISABLE_MSI @@ -1005,7 +1005,7 @@ err_out: return rc; #else /* FIXME , For SAS target mode */ - memcpy(buf, "\x00\x00\xab\x11\x30\x04\x05\x50", 8); + memcpy(buf, "\x50\x05\x04\x30\x11\xab\x00\x00", 8); return 0; #endif } @@ -1330,7 +1330,7 @@ static int mvs_int_rx(struct mvs_info *mvi, bool self_clear) mvs_hba_cq_dump(mvi); - if (unlikely(rx_desc & RXQ_DONE)) + if (likely(rx_desc & RXQ_DONE)) mvs_slot_complete(mvi, rx_desc); if (rx_desc & RXQ_ATTN) { attn = true; @@ -2720,9 +2720,8 @@ static int __devinit mvs_hw_init(struct mvs_info *mvi) msleep(100); /* init and reset phys */ for (i = 0; i < mvi->chip->n_phy; i++) { - /* FIXME: is this the correct dword order? */ - u32 lo = *((u32 *)&mvi->sas_addr[0]); - u32 hi = *((u32 *)&mvi->sas_addr[4]); + u32 lo = be32_to_cpu(*(u32 *)&mvi->sas_addr[4]); + u32 hi = be32_to_cpu(*(u32 *)&mvi->sas_addr[0]); mvs_detect_porttype(mvi, i); From 67768f675ffa587d6081ed1d259e796823023926 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Mon, 25 Feb 2008 14:23:45 +0100 Subject: [PATCH 11/27] [SCSI] ps3rom: fix wrong resid calculation bug sg driver rounds up the length in struct scatterlist to be a multiple of 512 in some conditions. So LLDs can't use the data length in a sg list to calculate residual. Instead, the length in struct scsi_cmnd should be used. [Geert: the variable buflen already contains scsi_bufflen(cmd)] Signed-off-by: FUJITA Tomonori Signed-off-by: Geert Uytterhoeven Signed-off-by: James Bottomley --- drivers/scsi/ps3rom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/ps3rom.c b/drivers/scsi/ps3rom.c index 0cd614a0fa73..e052c8479d51 100644 --- a/drivers/scsi/ps3rom.c +++ b/drivers/scsi/ps3rom.c @@ -124,7 +124,7 @@ static int fill_from_dev_buffer(struct scsi_cmnd *cmd, const void *buf) } req_len += sgpnt->length; } - scsi_set_resid(cmd, req_len - act_len); + scsi_set_resid(cmd, buflen - act_len); return 0; } From 57fd2b6c893ed28ccf1a674699f1ea9d8c556281 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Sun, 17 Feb 2008 23:46:00 +0900 Subject: [PATCH 12/27] [SCSI] ps3rom: disable clustering ps3rom does: scsi_for_each_sg(cmd, sgpnt, scsi_sg_count(cmd), k) { kaddr = kmap_atomic(sg_page(sgpnt), KM_IRQ0); We cannot do something like that with the clustering enabled (or we can use scsi_kmap_atomic_sg). Signed-off-by: FUJITA Tomonori Cc: Geert Uytterhoeven Signed-off-by: James Bottomley --- drivers/scsi/ps3rom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/ps3rom.c b/drivers/scsi/ps3rom.c index e052c8479d51..fad6cb5cba28 100644 --- a/drivers/scsi/ps3rom.c +++ b/drivers/scsi/ps3rom.c @@ -427,7 +427,7 @@ static struct scsi_host_template ps3rom_host_template = { .cmd_per_lun = 1, .emulated = 1, /* only sg driver uses this */ .max_sectors = PS3ROM_MAX_SECTORS, - .use_clustering = ENABLE_CLUSTERING, + .use_clustering = DISABLE_CLUSTERING, .module = THIS_MODULE, }; From ba1cb4618b2d7becc62c9fd67287e733a23611bc Mon Sep 17 00:00:00 2001 From: Nick Cheng Date: Wed, 27 Feb 2008 16:22:03 +0800 Subject: [PATCH 13/27] [SCSI] arcmsr: update version and changelog The fix up from Daniel Drake for replacing GFP_DMA with something more sensible has gone in here: commit 69e562c234440fb7410877b5b24f4b29ef8521d1 Author: Daniel Drake Date: Wed Feb 20 13:29:05 2008 +0000 [SCSI] arcmsr: fix message allocation add a change log and update the version for this. Signed-off-by: Nick Cheng Signed-off-by: James Bottomley --- Documentation/scsi/ChangeLog.arcmsr | 6 ++++++ drivers/scsi/arcmsr/arcmsr.h | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Documentation/scsi/ChangeLog.arcmsr b/Documentation/scsi/ChangeLog.arcmsr index de2bcacfa870..038a3e6ecaa4 100644 --- a/Documentation/scsi/ChangeLog.arcmsr +++ b/Documentation/scsi/ChangeLog.arcmsr @@ -109,4 +109,10 @@ ** 8.replace pci_alloc_consistent()/pci_free_consistent() with kmalloc()/kfree() in arcmsr_iop_message_xfer() ** 9. fix the release of dma memory for type B in arcmsr_free_ccb_pool() ** 10.fix the arcmsr_polling_hbb_ccbdone() +** 1.20.00.15 02/27/2008 Erich Chen & Nick Cheng +** 1.arcmsr_iop_message_xfer() is called from atomic context under the +** queuecommand scsi_host_template handler. James Bottomley pointed out +** that the current GFP_KERNEL|GFP_DMA flags are wrong: firstly we are in +** atomic context, secondly this memory is not used for DMA. +** Also removed some unneeded casts. Thanks to Daniel Drake ************************************************************************** diff --git a/drivers/scsi/arcmsr/arcmsr.h b/drivers/scsi/arcmsr/arcmsr.h index 57786502e3ec..0393707bdfce 100644 --- a/drivers/scsi/arcmsr/arcmsr.h +++ b/drivers/scsi/arcmsr/arcmsr.h @@ -48,7 +48,7 @@ struct class_device_attribute; /*The limit of outstanding scsi command that firmware can handle*/ #define ARCMSR_MAX_OUTSTANDING_CMD 256 #define ARCMSR_MAX_FREECCB_NUM 320 -#define ARCMSR_DRIVER_VERSION "Driver Version 1.20.00.15 2007/12/24" +#define ARCMSR_DRIVER_VERSION "Driver Version 1.20.00.15 2008/02/27" #define ARCMSR_SCSI_INITIATOR_ID 255 #define ARCMSR_MAX_XFER_SECTORS 512 #define ARCMSR_MAX_XFER_SECTORS_B 4096 From 7c7f1f299b7f21f1f5fd4fa3da6b626406109f30 Mon Sep 17 00:00:00 2001 From: Andrew Vasquez Date: Thu, 28 Feb 2008 14:06:09 -0800 Subject: [PATCH 14/27] [SCSI] qla2xxx: Correct needless clean-up resets during shutdown. There's no point in hitting the RISC with what will most assuredly be an unsucessful reset of the RISC hardware if the initial stop-firmware mailbox command fails with a time-out status. Instead, to avoid what could amount to a lengthy stop-firmware/detect-failure/reset-risc loop, continue with driver unloading and discard the stop-firmware requirement. Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_init.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index d5c7853e7eba..4897663f3c2b 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -4022,7 +4022,8 @@ qla2x00_try_to_stop_firmware(scsi_qla_host_t *ha) return; ret = qla2x00_stop_firmware(ha); - for (retries = 5; ret != QLA_SUCCESS && retries ; retries--) { + for (retries = 5; ret != QLA_SUCCESS && ret != QLA_FUNCTION_TIMEOUT && + retries ; retries--) { qla2x00_reset_chip(ha); if (qla2x00_chip_diag(ha) != QLA_SUCCESS) continue; From 3e8ce320cfc6cc10a7b99d8d6508d00bde20fdb7 Mon Sep 17 00:00:00 2001 From: Andrew Vasquez Date: Thu, 28 Feb 2008 14:06:10 -0800 Subject: [PATCH 15/27] [SCSI] qla2xxx: Correct discrepancies during OVERRUN handling on FWI2-capable cards. For recent ISPs, software must detect OVERRUN conditions by checking the SS_RESIDUAL_OVER bit during CS_COMPLETE handling. Update the driver to perform this check, which is consistent with what earlier firmwares did by explicitly cracking open the FCP_RSP statuses and returning an CS_DATA_OVERRUN. Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_isr.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 14e6f22944b7..f0337036c7bb 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -958,6 +958,11 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt) } } + /* Check for overrun. */ + if (IS_FWI2_CAPABLE(ha) && comp_status == CS_COMPLETE && + scsi_status & SS_RESIDUAL_OVER) + comp_status = CS_DATA_OVERRUN; + /* * Based on Host and scsi status generate status code for Linux */ From 00a537b8204c7360852379b4d56adbeedecc9bb9 Mon Sep 17 00:00:00 2001 From: Andrew Vasquez Date: Thu, 28 Feb 2008 14:06:11 -0800 Subject: [PATCH 16/27] [SCSI] qla2xxx: Correct usage of inconsistent timeout values while issuing ELS commands. The original code would incorrectly hardcode ELS timeout values rather than using the traditional '2 * r_a_tov' value. In some cases, the hardcoded values would be larger than the mailbox-command-timeout and result in a needless BIG_HAMMER (ISP reset), the typical recovery mechanism employed in such cases. The second defect in the original code was in the assignment of the default 'ha->r_a_tov' to twice the traditional value. Correct this by setting the value to 10 seconds. Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_gs.c | 10 +++++----- drivers/scsi/qla2xxx/qla_init.c | 8 ++++---- drivers/scsi/qla2xxx/qla_mbx.c | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c index 6226d88479f5..c1808763d40e 100644 --- a/drivers/scsi/qla2xxx/qla_gs.c +++ b/drivers/scsi/qla2xxx/qla_gs.c @@ -39,7 +39,7 @@ qla2x00_prep_ms_iocb(scsi_qla_host_t *ha, uint32_t req_size, uint32_t rsp_size) ms_pkt->entry_count = 1; SET_TARGET_ID(ha, ms_pkt->loop_id, SIMPLE_NAME_SERVER); ms_pkt->control_flags = __constant_cpu_to_le16(CF_READ | CF_HEAD_TAG); - ms_pkt->timeout = __constant_cpu_to_le16(25); + ms_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); ms_pkt->cmd_dsd_count = __constant_cpu_to_le16(1); ms_pkt->total_dsd_count = __constant_cpu_to_le16(2); ms_pkt->rsp_bytecount = cpu_to_le32(rsp_size); @@ -75,7 +75,7 @@ qla24xx_prep_ms_iocb(scsi_qla_host_t *ha, uint32_t req_size, uint32_t rsp_size) ct_pkt->entry_type = CT_IOCB_TYPE; ct_pkt->entry_count = 1; ct_pkt->nport_handle = __constant_cpu_to_le16(NPH_SNS); - ct_pkt->timeout = __constant_cpu_to_le16(25); + ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); ct_pkt->cmd_dsd_count = __constant_cpu_to_le16(1); ct_pkt->rsp_dsd_count = __constant_cpu_to_le16(1); ct_pkt->rsp_byte_count = cpu_to_le32(rsp_size); @@ -1144,7 +1144,7 @@ qla2x00_prep_ms_fdmi_iocb(scsi_qla_host_t *ha, uint32_t req_size, ms_pkt->entry_count = 1; SET_TARGET_ID(ha, ms_pkt->loop_id, ha->mgmt_svr_loop_id); ms_pkt->control_flags = __constant_cpu_to_le16(CF_READ | CF_HEAD_TAG); - ms_pkt->timeout = __constant_cpu_to_le16(59); + ms_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); ms_pkt->cmd_dsd_count = __constant_cpu_to_le16(1); ms_pkt->total_dsd_count = __constant_cpu_to_le16(2); ms_pkt->rsp_bytecount = cpu_to_le32(rsp_size); @@ -1181,7 +1181,7 @@ qla24xx_prep_ms_fdmi_iocb(scsi_qla_host_t *ha, uint32_t req_size, ct_pkt->entry_type = CT_IOCB_TYPE; ct_pkt->entry_count = 1; ct_pkt->nport_handle = cpu_to_le16(ha->mgmt_svr_loop_id); - ct_pkt->timeout = __constant_cpu_to_le16(59); + ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); ct_pkt->cmd_dsd_count = __constant_cpu_to_le16(1); ct_pkt->rsp_dsd_count = __constant_cpu_to_le16(1); ct_pkt->rsp_byte_count = cpu_to_le32(rsp_size); @@ -1761,7 +1761,7 @@ qla24xx_prep_ms_fm_iocb(scsi_qla_host_t *ha, uint32_t req_size, ct_pkt->entry_type = CT_IOCB_TYPE; ct_pkt->entry_count = 1; ct_pkt->nport_handle = cpu_to_le16(ha->mgmt_svr_loop_id); - ct_pkt->timeout = __constant_cpu_to_le16(59); + ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); ct_pkt->cmd_dsd_count = __constant_cpu_to_le16(1); ct_pkt->rsp_dsd_count = __constant_cpu_to_le16(1); ct_pkt->rsp_byte_count = cpu_to_le32(rsp_size); diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 4897663f3c2b..364be7d06875 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -1733,8 +1733,8 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) ha->login_timeout = nv->login_timeout; icb->login_timeout = nv->login_timeout; - /* Set minimum RATOV to 200 tenths of a second. */ - ha->r_a_tov = 200; + /* Set minimum RATOV to 100 tenths of a second. */ + ha->r_a_tov = 100; ha->loop_reset_delay = nv->reset_delay; @@ -3645,8 +3645,8 @@ qla24xx_nvram_config(scsi_qla_host_t *ha) ha->login_timeout = le16_to_cpu(nv->login_timeout); icb->login_timeout = cpu_to_le16(nv->login_timeout); - /* Set minimum RATOV to 200 tenths of a second. */ - ha->r_a_tov = 200; + /* Set minimum RATOV to 100 tenths of a second. */ + ha->r_a_tov = 100; ha->loop_reset_delay = nv->reset_delay; diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 99d29fff836d..bb103580e1ba 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -2206,7 +2206,7 @@ qla24xx_abort_target(fc_port_t *fcport) tsk->p.tsk.entry_type = TSK_MGMT_IOCB_TYPE; tsk->p.tsk.entry_count = 1; tsk->p.tsk.nport_handle = cpu_to_le16(fcport->loop_id); - tsk->p.tsk.timeout = __constant_cpu_to_le16(25); + tsk->p.tsk.timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); tsk->p.tsk.control_flags = __constant_cpu_to_le32(TCF_TARGET_RESET); tsk->p.tsk.port_id[0] = fcport->d_id.b.al_pa; tsk->p.tsk.port_id[1] = fcport->d_id.b.area; From ca3aefb8227aee7ede49a71c26adf8538b889724 Mon Sep 17 00:00:00 2001 From: Andrew Vasquez Date: Thu, 28 Feb 2008 14:06:12 -0800 Subject: [PATCH 17/27] [SCSI] qla2xxx: Update version number to 8.02.00-k9. Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h index c5742cc15abb..ea08a129fee9 100644 --- a/drivers/scsi/qla2xxx/qla_version.h +++ b/drivers/scsi/qla2xxx/qla_version.h @@ -7,7 +7,7 @@ /* * Driver version */ -#define QLA2XXX_VERSION "8.02.00-k8" +#define QLA2XXX_VERSION "8.02.00-k9" #define QLA_DRIVER_MAJOR_VER 8 #define QLA_DRIVER_MINOR_VER 2 From 79f5bb2839c41a007d7ce1a35f58ea14cef6fdb4 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 29 Feb 2008 22:02:50 -0800 Subject: [PATCH 18/27] [SCSI] docbook: fix scsi source file Fix docbook problem in SCSI source files. These cause the generated docbook to be incorrect. Signed-off-by: Randy Dunlap Signed-off-by: James Bottomley --- drivers/scsi/scsi_scan.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 1dc165ad17fb..e67c14e31bab 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -1577,8 +1577,7 @@ static void __scsi_scan_target(struct device *parent, unsigned int channel, } /** - * scsi_scan_target - scan a target id, possibly including all LUNs on the - * target. + * scsi_scan_target - scan a target id, possibly including all LUNs on the target. * @parent: host to scan * @channel: channel to scan * @id: target id to scan From ab3b0be84c3877dd0cccef38693254b83782bb70 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 29 Feb 2008 22:03:27 -0800 Subject: [PATCH 19/27] [SCSI] docbook: fix fusion source files Fix docbook problems in fusion source files. These cause the generated docbook to be incorrect. Signed-off-by: Randy Dunlap Acked-by: Eric Moore Signed-off-by: James Bottomley --- drivers/message/fusion/mptbase.c | 25 +++++++++++++------------ drivers/message/fusion/mptscsih.c | 14 +++++--------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c index 0c303c84b37b..6b6df8679585 100644 --- a/drivers/message/fusion/mptbase.c +++ b/drivers/message/fusion/mptbase.c @@ -632,8 +632,7 @@ mpt_deregister(u8 cb_idx) /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /** - * mpt_event_register - Register protocol-specific event callback - * handler. + * mpt_event_register - Register protocol-specific event callback handler. * @cb_idx: previously registered (via mpt_register) callback handle * @ev_cbfunc: callback function * @@ -654,8 +653,7 @@ mpt_event_register(u8 cb_idx, MPT_EVHANDLER ev_cbfunc) /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /** - * mpt_event_deregister - Deregister protocol-specific event callback - * handler. + * mpt_event_deregister - Deregister protocol-specific event callback handler * @cb_idx: previously registered callback handle * * Each protocol-specific driver should call this routine @@ -765,11 +763,13 @@ mpt_device_driver_deregister(u8 cb_idx) /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /** - * mpt_get_msg_frame - Obtain a MPT request frame from the pool (of 1024) - * allocated per MPT adapter. + * mpt_get_msg_frame - Obtain an MPT request frame from the pool * @cb_idx: Handle of registered MPT protocol driver * @ioc: Pointer to MPT adapter structure * + * Obtain an MPT request frame from the pool (of 1024) that are + * allocated per MPT adapter. + * * Returns pointer to a MPT request frame or %NULL if none are available * or IOC is not active. */ @@ -834,13 +834,12 @@ mpt_get_msg_frame(u8 cb_idx, MPT_ADAPTER *ioc) /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /** - * mpt_put_msg_frame - Send a protocol specific MPT request frame - * to a IOC. + * mpt_put_msg_frame - Send a protocol-specific MPT request frame to an IOC * @cb_idx: Handle of registered MPT protocol driver * @ioc: Pointer to MPT adapter structure * @mf: Pointer to MPT request frame * - * This routine posts a MPT request frame to the request post FIFO of a + * This routine posts an MPT request frame to the request post FIFO of a * specific MPT adapter. */ void @@ -868,13 +867,15 @@ mpt_put_msg_frame(u8 cb_idx, MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf) } /** - * mpt_put_msg_frame_hi_pri - Send a protocol specific MPT request frame - * to a IOC using hi priority request queue. + * mpt_put_msg_frame_hi_pri - Send a hi-pri protocol-specific MPT request frame * @cb_idx: Handle of registered MPT protocol driver * @ioc: Pointer to MPT adapter structure * @mf: Pointer to MPT request frame * - * This routine posts a MPT request frame to the request post FIFO of a + * Send a protocol-specific MPT request frame to an IOC using + * hi-priority request queue. + * + * This routine posts an MPT request frame to the request post FIFO of a * specific MPT adapter. **/ void diff --git a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c index af1de0ccee2f..0c252f60c4c1 100644 --- a/drivers/message/fusion/mptscsih.c +++ b/drivers/message/fusion/mptscsih.c @@ -1533,7 +1533,7 @@ mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx) * * Remark: Currently invoked from a non-interrupt thread (_bh). * - * Remark: With old EH code, at most 1 SCSI TaskMgmt function per IOC + * Note: With old EH code, at most 1 SCSI TaskMgmt function per IOC * will be active. * * Returns 0 for SUCCESS, or %FAILED. @@ -2537,14 +2537,12 @@ mptscsih_copy_sense_data(struct scsi_cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR /** * mptscsih_get_scsi_lookup - * - * retrieves scmd entry from ScsiLookup[] array list - * * @ioc: Pointer to MPT_ADAPTER structure * @i: index into the array * - * Returns the scsi_cmd pointer + * retrieves scmd entry from ScsiLookup[] array list * + * Returns the scsi_cmd pointer **/ static struct scsi_cmnd * mptscsih_get_scsi_lookup(MPT_ADAPTER *ioc, int i) @@ -2561,14 +2559,12 @@ mptscsih_get_scsi_lookup(MPT_ADAPTER *ioc, int i) /** * mptscsih_getclear_scsi_lookup - * - * retrieves and clears scmd entry from ScsiLookup[] array list - * * @ioc: Pointer to MPT_ADAPTER structure * @i: index into the array * - * Returns the scsi_cmd pointer + * retrieves and clears scmd entry from ScsiLookup[] array list * + * Returns the scsi_cmd pointer **/ static struct scsi_cmnd * mptscsih_getclear_scsi_lookup(MPT_ADAPTER *ioc, int i) From b560665ce5a617aff9c62b94a82340fe11fc0d91 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Sat, 1 Mar 2008 15:36:34 +0900 Subject: [PATCH 20/27] [SCSI] ibmvstgt: set up scsi_host properly before __scsi_alloc_queue Before calling __scsi_alloc_queue, scsi_host->shost_gendev.parent must be initialized properly. This patch moves __scsi_alloc_queue after scsi_add_host (like initiator drivers do). Signed-off-by: FUJITA Tomonori Signed-off-by: James Bottomley --- drivers/scsi/ibmvscsi/ibmvstgt.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/ibmvscsi/ibmvstgt.c b/drivers/scsi/ibmvscsi/ibmvstgt.c index bd62131b97a1..429bf53f4118 100644 --- a/drivers/scsi/ibmvscsi/ibmvstgt.c +++ b/drivers/scsi/ibmvscsi/ibmvstgt.c @@ -838,9 +838,6 @@ static int ibmvstgt_probe(struct vio_dev *dev, const struct vio_device_id *id) if (!shost) goto free_vport; shost->transportt = ibmvstgt_transport_template; - err = scsi_tgt_alloc_queue(shost); - if (err) - goto put_host; target = host_to_srp_target(shost); target->shost = shost; @@ -872,6 +869,10 @@ static int ibmvstgt_probe(struct vio_dev *dev, const struct vio_device_id *id) if (err) goto destroy_queue; + err = scsi_tgt_alloc_queue(shost); + if (err) + goto destroy_queue; + return 0; destroy_queue: crq_queue_destroy(target); From 36802e99894e9757de2441b4f719c05a29495dc5 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Sat, 1 Mar 2008 15:36:35 +0900 Subject: [PATCH 21/27] [SCSI] tgt: stop zero'ing scsi_cmnd The scsi midlayer allocates scsi_cmnd->sense_buffer dynamically so we can't initialize scsi_cmnd (the midlyaer does for us). Signed-off-by: FUJITA Tomonori Signed-off-by: James Bottomley --- drivers/scsi/scsi_tgt_lib.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/scsi/scsi_tgt_lib.c b/drivers/scsi/scsi_tgt_lib.c index 3677fbb30b72..b14251483c3b 100644 --- a/drivers/scsi/scsi_tgt_lib.c +++ b/drivers/scsi/scsi_tgt_lib.c @@ -103,7 +103,6 @@ struct scsi_cmnd *scsi_host_get_command(struct Scsi_Host *shost, if (!cmd) goto release_rq; - memset(cmd, 0, sizeof(*cmd)); cmd->sc_data_direction = data_dir; cmd->jiffies_at_alloc = jiffies; cmd->request = rq; From cccddc2d15c32bdb6972aaf98f4c9e3e807833df Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Sat, 1 Mar 2008 15:36:36 +0900 Subject: [PATCH 22/27] [SCSI] tgt: set the data length properly scsi_tgt uses REQ_TYPE_BLOCK_PC so scsi_init_io doesn't set the length for us. scsi_tgt needs to do it by itself. Signed-off-by: FUJITA Tomonori Signed-off-by: James Bottomley --- drivers/scsi/scsi_tgt_lib.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/scsi/scsi_tgt_lib.c b/drivers/scsi/scsi_tgt_lib.c index b14251483c3b..a0f308bd145b 100644 --- a/drivers/scsi/scsi_tgt_lib.c +++ b/drivers/scsi/scsi_tgt_lib.c @@ -381,6 +381,11 @@ static int scsi_map_user_pages(struct scsi_tgt_cmd *tcmd, struct scsi_cmnd *cmd, scsi_release_buffers(cmd); goto unmap_rq; } + /* + * we use REQ_TYPE_BLOCK_PC so scsi_init_io doesn't set the + * length for us. + */ + cmd->sdb.length = rq->data_len; return 0; From 21f1e91d4bb8fa7cd3a59938471fc7c7d27f82da Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Sat, 1 Mar 2008 15:36:37 +0900 Subject: [PATCH 23/27] [SCSI] tgt: fix build errors when dprintk is defined drivers/scsi/ibmvscsi/ibmvstgt.c: In function 'ibmvstgt_cmd_done': drivers/scsi/ibmvscsi/ibmvstgt.c:292: error: 'cmd' undeclared (first use in this function) drivers/scsi/ibmvscsi/ibmvstgt.c:292: error: (Each undeclared identifier is reported only once drivers/scsi/ibmvscsi/ibmvstgt.c:292: error: for each function it appears in.) Signed-off-by: FUJITA Tomonori Signed-off-by: James Bottomley --- drivers/scsi/ibmvscsi/ibmvstgt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/ibmvscsi/ibmvstgt.c b/drivers/scsi/ibmvscsi/ibmvstgt.c index 429bf53f4118..e5881e92d0fb 100644 --- a/drivers/scsi/ibmvscsi/ibmvstgt.c +++ b/drivers/scsi/ibmvscsi/ibmvstgt.c @@ -290,7 +290,7 @@ static int ibmvstgt_cmd_done(struct scsi_cmnd *sc, int err = 0; dprintk("%p %p %x %u\n", iue, target, vio_iu(iue)->srp.cmd.cdb[0], - cmd->usg_sg); + scsi_sg_count(sc)); if (scsi_sg_count(sc)) err = srp_transfer_data(sc, &vio_iu(iue)->srp.cmd, ibmvstgt_rdma, 1, 1); From 50a29aec9c47d26e869df83ef1d69e3b63c83bf4 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Tue, 4 Mar 2008 13:26:53 -0600 Subject: [PATCH 24/27] [SCSI] qla4xxx: fix host reset dpc race The host reset callout could be starting to reset the hba at the same time the dpc thread is. This creates lots of problems because they both want to do wierd things with the firmware and interrupts, etc. This patch just has the host reset function fully shutdown the dpc thread before resetting the hba. This patch also moves the setting of the session online bit to fix a potential race with the dpc thread and iscsi recovery thread. Signed-off-by: Mike Christie Acked-by: David C Somayajulu Signed-off-by: James Bottomley --- drivers/scsi/qla4xxx/ql4_init.c | 2 +- drivers/scsi/qla4xxx/ql4_os.c | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/qla4xxx/ql4_init.c b/drivers/scsi/qla4xxx/ql4_init.c index 10b3b9a620f3..109c5f5985ec 100644 --- a/drivers/scsi/qla4xxx/ql4_init.c +++ b/drivers/scsi/qla4xxx/ql4_init.c @@ -1299,9 +1299,9 @@ int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, ddb_entry->fw_ddb_device_state = state; /* Device is back online. */ if (ddb_entry->fw_ddb_device_state == DDB_DS_SESSION_ACTIVE) { + atomic_set(&ddb_entry->state, DDB_STATE_ONLINE); atomic_set(&ddb_entry->port_down_timer, ha->port_down_retry_count); - atomic_set(&ddb_entry->state, DDB_STATE_ONLINE); atomic_set(&ddb_entry->relogin_retry_count, 0); atomic_set(&ddb_entry->relogin_timer, 0); clear_bit(DF_RELOGIN, &ddb_entry->flags); diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index c3c59d763037..10a233e65bc9 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -864,8 +864,9 @@ static void qla4xxx_flush_active_srbs(struct scsi_qla_host *ha) * qla4xxx_recover_adapter - recovers adapter after a fatal error * @ha: Pointer to host adapter structure. * @renew_ddb_list: Indicates what to do with the adapter's ddb list - * after adapter recovery has completed. - * 0=preserve ddb list, 1=destroy and rebuild ddb list + * + * renew_ddb_list value can be 0=preserve ddb list, 1=destroy and rebuild + * ddb list. **/ static int qla4xxx_recover_adapter(struct scsi_qla_host *ha, uint8_t renew_ddb_list) @@ -874,6 +875,7 @@ static int qla4xxx_recover_adapter(struct scsi_qla_host *ha, /* Stall incoming I/O until we are done */ clear_bit(AF_ONLINE, &ha->flags); + DEBUG2(printk("scsi%ld: %s calling qla4xxx_cmd_wait\n", ha->host_no, __func__)); @@ -1600,9 +1602,12 @@ static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd) return FAILED; } - if (qla4xxx_recover_adapter(ha, PRESERVE_DDB_LIST) == QLA_SUCCESS) { + /* make sure the dpc thread is stopped while we reset the hba */ + clear_bit(AF_ONLINE, &ha->flags); + flush_workqueue(ha->dpc_thread); + + if (qla4xxx_recover_adapter(ha, PRESERVE_DDB_LIST) == QLA_SUCCESS) return_status = SUCCESS; - } dev_info(&ha->pdev->dev, "HOST RESET %s.\n", return_status == FAILED ? "FAILED" : "SUCCEDED"); From 024f801f528220edc89275a724ea00cd18c5ebb7 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Tue, 4 Mar 2008 13:26:54 -0600 Subject: [PATCH 25/27] [SCSI] qla4xxx: regression - add start scan callout We are seeing EXIST errors from sysfs during device addition. We need a start scan callout so we do not start scanning sessions found during hba setup, before the async scsi scan code is ready. Signed-off-by: Mike Christie Acked-by: David C Somayajulu Signed-off-by: James Bottomley --- drivers/scsi/qla4xxx/ql4_os.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 10a233e65bc9..8b92f348f02c 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -75,6 +75,7 @@ static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd); static int qla4xxx_slave_alloc(struct scsi_device *device); static int qla4xxx_slave_configure(struct scsi_device *device); static void qla4xxx_slave_destroy(struct scsi_device *sdev); +static void qla4xxx_scan_start(struct Scsi_Host *shost); static struct scsi_host_template qla4xxx_driver_template = { .module = THIS_MODULE, @@ -90,6 +91,7 @@ static struct scsi_host_template qla4xxx_driver_template = { .slave_destroy = qla4xxx_slave_destroy, .scan_finished = iscsi_scan_finished, + .scan_start = qla4xxx_scan_start, .this_id = -1, .cmd_per_lun = 3, @@ -299,6 +301,18 @@ struct ddb_entry *qla4xxx_alloc_sess(struct scsi_qla_host *ha) return ddb_entry; } +static void qla4xxx_scan_start(struct Scsi_Host *shost) +{ + struct scsi_qla_host *ha = shost_priv(shost); + struct ddb_entry *ddb_entry, *ddbtemp; + + /* finish setup of sessions that were already setup in firmware */ + list_for_each_entry_safe(ddb_entry, ddbtemp, &ha->ddb_list, list) { + if (ddb_entry->fw_ddb_device_state == DDB_DS_SESSION_ACTIVE) + qla4xxx_add_sess(ddb_entry); + } +} + /* * Timer routines */ @@ -1178,7 +1192,6 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev, int ret = -ENODEV, status; struct Scsi_Host *host; struct scsi_qla_host *ha; - struct ddb_entry *ddb_entry, *ddbtemp; uint8_t init_retry_count = 0; char buf[34]; @@ -1297,13 +1310,6 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev, if (ret) goto probe_failed; - /* Update transport device information for all devices. */ - list_for_each_entry_safe(ddb_entry, ddbtemp, &ha->ddb_list, list) { - if (ddb_entry->fw_ddb_device_state == DDB_DS_SESSION_ACTIVE) - if (qla4xxx_add_sess(ddb_entry)) - goto remove_host; - } - printk(KERN_INFO " QLogic iSCSI HBA Driver version: %s\n" " QLogic ISP%04x @ %s, host#=%ld, fw=%02d.%02d.%02d.%02d\n", @@ -1313,10 +1319,6 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev, scsi_scan_host(host); return 0; -remove_host: - qla4xxx_free_ddb_list(ha); - scsi_remove_host(host); - probe_failed: qla4xxx_free_adapter(ha); scsi_host_put(ha->host); From 45ab33b6c190c4a8c58f1d13be2ff89ee62024ba Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Tue, 4 Mar 2008 13:26:55 -0600 Subject: [PATCH 26/27] [SCSI] iscsi class: regression - fix races with state manipulation and blocking/unblocking For qla4xxx, we could be starting a session, but some error (network, target, IO from a device that got started, etc) could cause the session to fail and curring the block/unblock and state manipulation could race with each other. This patch just has those operations done in the single threaded iscsi eh work queue, so that way they are serialized. Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/scsi_transport_iscsi.c | 76 ++++++++++++++++++----------- include/scsi/scsi_transport_iscsi.h | 2 + 2 files changed, 50 insertions(+), 28 deletions(-) diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index dfb026b95a6a..ca7bb6f63bde 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -373,24 +373,25 @@ static void session_recovery_timedout(struct work_struct *work) scsi_target_unblock(&session->dev); } -static void __iscsi_unblock_session(struct iscsi_cls_session *session) -{ - if (!cancel_delayed_work(&session->recovery_work)) - flush_workqueue(iscsi_eh_timer_workq); - scsi_target_unblock(&session->dev); -} - -void iscsi_unblock_session(struct iscsi_cls_session *session) +static void __iscsi_unblock_session(struct work_struct *work) { + struct iscsi_cls_session *session = + container_of(work, struct iscsi_cls_session, + unblock_work); struct Scsi_Host *shost = iscsi_session_to_shost(session); struct iscsi_host *ihost = shost->shost_data; unsigned long flags; + /* + * The recovery and unblock work get run from the same workqueue, + * so try to cancel it if it was going to run after this unblock. + */ + cancel_delayed_work(&session->recovery_work); spin_lock_irqsave(&session->lock, flags); session->state = ISCSI_SESSION_LOGGED_IN; spin_unlock_irqrestore(&session->lock, flags); - - __iscsi_unblock_session(session); + /* start IO */ + scsi_target_unblock(&session->dev); /* * Only do kernel scanning if the driver is properly hooked into * the async scanning code (drivers like iscsi_tcp do login and @@ -401,20 +402,43 @@ void iscsi_unblock_session(struct iscsi_cls_session *session) atomic_inc(&ihost->nr_scans); } } + +/** + * iscsi_unblock_session - set a session as logged in and start IO. + * @session: iscsi session + * + * Mark a session as ready to accept IO. + */ +void iscsi_unblock_session(struct iscsi_cls_session *session) +{ + queue_work(iscsi_eh_timer_workq, &session->unblock_work); + /* + * make sure all the events have completed before tell the driver + * it is safe + */ + flush_workqueue(iscsi_eh_timer_workq); +} EXPORT_SYMBOL_GPL(iscsi_unblock_session); -void iscsi_block_session(struct iscsi_cls_session *session) +static void __iscsi_block_session(struct work_struct *work) { + struct iscsi_cls_session *session = + container_of(work, struct iscsi_cls_session, + block_work); unsigned long flags; spin_lock_irqsave(&session->lock, flags); session->state = ISCSI_SESSION_FAILED; spin_unlock_irqrestore(&session->lock, flags); - scsi_target_block(&session->dev); queue_delayed_work(iscsi_eh_timer_workq, &session->recovery_work, session->recovery_tmo * HZ); } + +void iscsi_block_session(struct iscsi_cls_session *session) +{ + queue_work(iscsi_eh_timer_workq, &session->block_work); +} EXPORT_SYMBOL_GPL(iscsi_block_session); static void __iscsi_unbind_session(struct work_struct *work) @@ -463,6 +487,8 @@ iscsi_alloc_session(struct Scsi_Host *shost, INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout); INIT_LIST_HEAD(&session->host_list); INIT_LIST_HEAD(&session->sess_list); + INIT_WORK(&session->unblock_work, __iscsi_unblock_session); + INIT_WORK(&session->block_work, __iscsi_block_session); INIT_WORK(&session->unbind_work, __iscsi_unbind_session); INIT_WORK(&session->scan_work, iscsi_scan_session); spin_lock_init(&session->lock); @@ -575,24 +601,25 @@ void iscsi_remove_session(struct iscsi_cls_session *session) list_del(&session->sess_list); spin_unlock_irqrestore(&sesslock, flags); + /* make sure there are no blocks/unblocks queued */ + flush_workqueue(iscsi_eh_timer_workq); + /* make sure the timedout callout is not running */ + if (!cancel_delayed_work(&session->recovery_work)) + flush_workqueue(iscsi_eh_timer_workq); /* * If we are blocked let commands flow again. The lld or iscsi * layer should set up the queuecommand to fail commands. + * We assume that LLD will not be calling block/unblock while + * removing the session. */ spin_lock_irqsave(&session->lock, flags); session->state = ISCSI_SESSION_FREE; spin_unlock_irqrestore(&session->lock, flags); - __iscsi_unblock_session(session); - __iscsi_unbind_session(&session->unbind_work); - /* flush running scans */ + scsi_target_unblock(&session->dev); + /* flush running scans then delete devices */ flush_workqueue(ihost->scan_workq); - /* - * If the session dropped while removing devices then we need to make - * sure it is not blocked - */ - if (!cancel_delayed_work(&session->recovery_work)) - flush_workqueue(iscsi_eh_timer_workq); + __iscsi_unbind_session(&session->unbind_work); /* hw iscsi may not have removed all connections from session */ err = device_for_each_child(&session->dev, NULL, @@ -802,23 +829,16 @@ EXPORT_SYMBOL_GPL(iscsi_recv_pdu); void iscsi_conn_error(struct iscsi_cls_conn *conn, enum iscsi_err error) { - struct iscsi_cls_session *session = iscsi_conn_to_session(conn); struct nlmsghdr *nlh; struct sk_buff *skb; struct iscsi_uevent *ev; struct iscsi_internal *priv; int len = NLMSG_SPACE(sizeof(*ev)); - unsigned long flags; priv = iscsi_if_transport_lookup(conn->transport); if (!priv) return; - spin_lock_irqsave(&session->lock, flags); - if (session->state == ISCSI_SESSION_LOGGED_IN) - session->state = ISCSI_SESSION_FAILED; - spin_unlock_irqrestore(&session->lock, flags); - skb = alloc_skb(len, GFP_ATOMIC); if (!skb) { iscsi_cls_conn_printk(KERN_ERR, conn, "gracefully ignored " diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h index dbc96ef4cc72..aab1eae2ec4c 100644 --- a/include/scsi/scsi_transport_iscsi.h +++ b/include/scsi/scsi_transport_iscsi.h @@ -177,6 +177,8 @@ struct iscsi_cls_session { struct list_head host_list; struct iscsi_transport *transport; spinlock_t lock; + struct work_struct block_work; + struct work_struct unblock_work; struct work_struct scan_work; struct work_struct unbind_work; From 51f39eae14b4874618e73281c236e3a1c1572d4d Mon Sep 17 00:00:00 2001 From: Krzysztof Oledzki Date: Tue, 4 Mar 2008 14:56:23 -0800 Subject: [PATCH 27/27] [SCSI] mpt fusion: don't oops if NumPhys==0 Don't oops if NumPhys==0, instead return -ENODEV. This patch fixes http://bugzilla.kernel.org/show_bug.cgi?id=9909 Signed-off-by: Krzysztof Piotr Oledzki Acked-by: Eric Moore Signed-off-by: Andrew Morton Signed-off-by: James Bottomley --- drivers/message/fusion/mptsas.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c index f77b329f6923..78734e25edd5 100644 --- a/drivers/message/fusion/mptsas.c +++ b/drivers/message/fusion/mptsas.c @@ -1701,6 +1701,11 @@ mptsas_sas_expander_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info, if (error) goto out_free_consistent; + if (!buffer->NumPhys) { + error = -ENODEV; + goto out_free_consistent; + } + /* save config data */ port_info->num_phys = buffer->NumPhys; port_info->phy_info = kcalloc(port_info->num_phys,