diff options
Diffstat (limited to 'drivers/scsi/libsas')
-rw-r--r-- | drivers/scsi/libsas/Kconfig | 8 | ||||
-rw-r--r-- | drivers/scsi/libsas/Makefile | 4 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_ata.c | 166 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_dump.c | 4 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_dump.h | 12 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_expander.c | 5 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_internal.h | 6 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_scsi_host.c | 13 |
8 files changed, 119 insertions, 99 deletions
diff --git a/drivers/scsi/libsas/Kconfig b/drivers/scsi/libsas/Kconfig index 18f33cd54411..9dafe64e7c7a 100644 --- a/drivers/scsi/libsas/Kconfig +++ b/drivers/scsi/libsas/Kconfig @@ -46,11 +46,3 @@ config SCSI_SAS_HOST_SMP Allows sas hosts to receive SMP frames. Selecting this option builds an SMP interpreter into libsas. Say N here if you want to save the few kb this consumes. - -config SCSI_SAS_LIBSAS_DEBUG - bool "Compile the SAS Domain Transport Attributes in debug mode" - default y - depends on SCSI_SAS_LIBSAS - help - Compiles the SAS Layer in debug mode. In debug mode, the - SAS Layer prints diagnostic and debug messages. diff --git a/drivers/scsi/libsas/Makefile b/drivers/scsi/libsas/Makefile index 1ad1323c60fa..566a10024598 100644 --- a/drivers/scsi/libsas/Makefile +++ b/drivers/scsi/libsas/Makefile @@ -21,10 +21,6 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA -ifeq ($(CONFIG_SCSI_SAS_LIBSAS_DEBUG),y) - EXTRA_CFLAGS += -DSAS_DEBUG -endif - obj-$(CONFIG_SCSI_SAS_LIBSAS) += libsas.o libsas-y += sas_init.o \ sas_phy.o \ diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index e1a395b438ee..31fc21f4d831 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -71,13 +71,13 @@ static enum ata_completion_errors sas_to_ata_err(struct task_status_struct *ts) case SAS_SG_ERR: return AC_ERR_INVALID; - case SAM_STAT_CHECK_CONDITION: case SAS_OPEN_TO: case SAS_OPEN_REJECT: SAS_DPRINTK("%s: Saw error %d. What to do?\n", __func__, ts->stat); return AC_ERR_OTHER; + case SAM_STAT_CHECK_CONDITION: case SAS_ABORTED_TASK: return AC_ERR_DEV; @@ -107,13 +107,15 @@ static void sas_ata_task_done(struct sas_task *task) sas_ha = dev->port->ha; spin_lock_irqsave(dev->sata_dev.ap->lock, flags); - if (stat->stat == SAS_PROTO_RESPONSE || stat->stat == SAM_STAT_GOOD) { + if (stat->stat == SAS_PROTO_RESPONSE || stat->stat == SAM_STAT_GOOD || + ((stat->stat == SAM_STAT_CHECK_CONDITION && + dev->sata_dev.command_set == ATAPI_COMMAND_SET))) { ata_tf_from_fis(resp->ending_fis, &dev->sata_dev.tf); qc->err_mask |= ac_err_mask(dev->sata_dev.tf.command); dev->sata_dev.sstatus = resp->sstatus; dev->sata_dev.serror = resp->serror; dev->sata_dev.scontrol = resp->scontrol; - } else if (stat->stat != SAM_STAT_GOOD) { + } else { ac = sas_to_ata_err(stat); if (ac) { SAS_DPRINTK("%s: SAS error %x\n", __func__, @@ -238,37 +240,43 @@ static bool sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc) return true; } -static void sas_ata_phy_reset(struct ata_port *ap) +static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, + unsigned long deadline) { + struct ata_port *ap = link->ap; struct domain_device *dev = ap->private_data; struct sas_internal *i = to_sas_internal(dev->port->ha->core.shost->transportt); int res = TMF_RESP_FUNC_FAILED; + int ret = 0; if (i->dft->lldd_I_T_nexus_reset) res = i->dft->lldd_I_T_nexus_reset(dev); - if (res != TMF_RESP_FUNC_COMPLETE) + if (res != TMF_RESP_FUNC_COMPLETE) { SAS_DPRINTK("%s: Unable to reset I T nexus?\n", __func__); + ret = -EAGAIN; + } switch (dev->sata_dev.command_set) { case ATA_COMMAND_SET: SAS_DPRINTK("%s: Found ATA device.\n", __func__); - ap->link.device[0].class = ATA_DEV_ATA; + *class = ATA_DEV_ATA; break; case ATAPI_COMMAND_SET: SAS_DPRINTK("%s: Found ATAPI device.\n", __func__); - ap->link.device[0].class = ATA_DEV_ATAPI; + *class = ATA_DEV_ATAPI; break; default: SAS_DPRINTK("%s: Unknown SATA command set: %d.\n", __func__, dev->sata_dev.command_set); - ap->link.device[0].class = ATA_DEV_UNKNOWN; + *class = ATA_DEV_UNKNOWN; break; } ap->cbl = ATA_CBL_SATA; + return ret; } static void sas_ata_post_internal(struct ata_queued_cmd *qc) @@ -299,57 +307,12 @@ static void sas_ata_post_internal(struct ata_queued_cmd *qc) } } -static int sas_ata_scr_write(struct ata_link *link, unsigned int sc_reg_in, - u32 val) -{ - struct domain_device *dev = link->ap->private_data; - - SAS_DPRINTK("STUB %s\n", __func__); - switch (sc_reg_in) { - case SCR_STATUS: - dev->sata_dev.sstatus = val; - break; - case SCR_CONTROL: - dev->sata_dev.scontrol = val; - break; - case SCR_ERROR: - dev->sata_dev.serror = val; - break; - case SCR_ACTIVE: - dev->sata_dev.ap->link.sactive = val; - break; - default: - return -EINVAL; - } - return 0; -} - -static int sas_ata_scr_read(struct ata_link *link, unsigned int sc_reg_in, - u32 *val) -{ - struct domain_device *dev = link->ap->private_data; - - SAS_DPRINTK("STUB %s\n", __func__); - switch (sc_reg_in) { - case SCR_STATUS: - *val = dev->sata_dev.sstatus; - return 0; - case SCR_CONTROL: - *val = dev->sata_dev.scontrol; - return 0; - case SCR_ERROR: - *val = dev->sata_dev.serror; - return 0; - case SCR_ACTIVE: - *val = dev->sata_dev.ap->link.sactive; - return 0; - default: - return -EINVAL; - } -} - static struct ata_port_operations sas_sata_ops = { - .phy_reset = sas_ata_phy_reset, + .prereset = ata_std_prereset, + .softreset = NULL, + .hardreset = sas_ata_hard_reset, + .postreset = ata_std_postreset, + .error_handler = ata_std_error_handler, .post_internal_cmd = sas_ata_post_internal, .qc_defer = ata_std_qc_defer, .qc_prep = ata_noop_qc_prep, @@ -357,15 +320,12 @@ static struct ata_port_operations sas_sata_ops = { .qc_fill_rtf = sas_ata_qc_fill_rtf, .port_start = ata_sas_port_start, .port_stop = ata_sas_port_stop, - .scr_read = sas_ata_scr_read, - .scr_write = sas_ata_scr_write }; static struct ata_port_info sata_port_info = { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_SATA_RESET | - ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ, - .pio_mask = 0x1f, /* PIO0-4 */ - .mwdma_mask = 0x07, /* MWDMA0-2 */ + .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, .port_ops = &sas_sata_ops }; @@ -781,3 +741,81 @@ int sas_discover_sata(struct domain_device *dev) return res; } + +void sas_ata_strategy_handler(struct Scsi_Host *shost) +{ + struct scsi_device *sdev; + + shost_for_each_device(sdev, shost) { + struct domain_device *ddev = sdev_to_domain_dev(sdev); + struct ata_port *ap = ddev->sata_dev.ap; + + if (!dev_is_sata(ddev)) + continue; + + ata_port_printk(ap, KERN_DEBUG, "sas eh calling libata port error handler"); + ata_scsi_port_error_handler(shost, ap); + } +} + +int sas_ata_timed_out(struct scsi_cmnd *cmd, struct sas_task *task, + enum blk_eh_timer_return *rtn) +{ + struct domain_device *ddev = cmd_to_domain_dev(cmd); + + if (!dev_is_sata(ddev) || task) + return 0; + + /* we're a sata device with no task, so this must be a libata + * eh timeout. Ideally should hook into libata timeout + * handling, but there's no point, it just wants to activate + * the eh thread */ + *rtn = BLK_EH_NOT_HANDLED; + return 1; +} + +int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, + struct list_head *done_q) +{ + int rtn = 0; + struct scsi_cmnd *cmd, *n; + struct ata_port *ap; + + do { + LIST_HEAD(sata_q); + + ap = NULL; + + list_for_each_entry_safe(cmd, n, work_q, eh_entry) { + struct domain_device *ddev = cmd_to_domain_dev(cmd); + + if (!dev_is_sata(ddev) || TO_SAS_TASK(cmd)) + continue; + if (ap && ap != ddev->sata_dev.ap) + continue; + ap = ddev->sata_dev.ap; + rtn = 1; + list_move(&cmd->eh_entry, &sata_q); + } + + if (!list_empty(&sata_q)) { + ata_port_printk(ap, KERN_DEBUG, "sas eh calling libata cmd error handler\n"); + ata_scsi_cmd_error_handler(shost, ap, &sata_q); + /* + * ata's error handler may leave the cmd on the list + * so make sure they don't remain on a stack list + * about to go out of scope. + * + * This looks strange, since the commands are + * now part of no list, but the next error + * action will be ata_port_error_handler() + * which takes no list and sweeps them up + * anyway from the ata tag array. + */ + while (!list_empty(&sata_q)) + list_del_init(sata_q.next); + } + } while (ap); + + return rtn; +} diff --git a/drivers/scsi/libsas/sas_dump.c b/drivers/scsi/libsas/sas_dump.c index c17c25030f1c..fc460933575c 100644 --- a/drivers/scsi/libsas/sas_dump.c +++ b/drivers/scsi/libsas/sas_dump.c @@ -24,8 +24,6 @@ #include "sas_dump.h" -#ifdef SAS_DEBUG - static const char *sas_hae_str[] = { [0] = "HAE_RESET", }; @@ -72,5 +70,3 @@ void sas_dump_port(struct asd_sas_port *port) SAS_DPRINTK("port%d: oob_mode:0x%x\n", port->id, port->oob_mode); SAS_DPRINTK("port%d: num_phys:%d\n", port->id, port->num_phys); } - -#endif /* SAS_DEBUG */ diff --git a/drivers/scsi/libsas/sas_dump.h b/drivers/scsi/libsas/sas_dump.h index 47b45d4f5258..800e4c69093f 100644 --- a/drivers/scsi/libsas/sas_dump.h +++ b/drivers/scsi/libsas/sas_dump.h @@ -24,19 +24,7 @@ #include "sas_internal.h" -#ifdef SAS_DEBUG - void sas_dprint_porte(int phyid, enum port_event pe); void sas_dprint_phye(int phyid, enum phy_event pe); void sas_dprint_hae(struct sas_ha_struct *sas_ha, enum ha_event he); void sas_dump_port(struct asd_sas_port *port); - -#else /* SAS_DEBUG */ - -static inline void sas_dprint_porte(int phyid, enum port_event pe) { } -static inline void sas_dprint_phye(int phyid, enum phy_event pe) { } -static inline void sas_dprint_hae(struct sas_ha_struct *sas_ha, - enum ha_event he) { } -static inline void sas_dump_port(struct asd_sas_port *port) { } - -#endif /* SAS_DEBUG */ diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 505ffe358293..f3f693b772ac 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -244,6 +244,11 @@ static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req, * dev to host FIS as described in section G.5 of * sas-2 r 04b */ dr = &((struct smp_resp *)disc_resp)->disc; + if (memcmp(dev->sas_addr, dr->attached_sas_addr, + SAS_ADDR_SIZE) == 0) { + sas_printk("Found loopback topology, just ignore it!\n"); + return 0; + } if (!(dr->attached_dev_type == 0 && dr->attached_sata_dev)) break; diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 0001374bd6b2..8b538bd1ff2b 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -33,11 +33,7 @@ #define sas_printk(fmt, ...) printk(KERN_NOTICE "sas: " fmt, ## __VA_ARGS__) -#ifdef SAS_DEBUG -#define SAS_DPRINTK(fmt, ...) printk(KERN_NOTICE "sas: " fmt, ## __VA_ARGS__) -#else -#define SAS_DPRINTK(fmt, ...) -#endif +#define SAS_DPRINTK(fmt, ...) printk(KERN_DEBUG "sas: " fmt, ## __VA_ARGS__) #define TO_SAS_TASK(_scsi_cmd) ((void *)(_scsi_cmd)->host_scribble) #define ASSIGN_SAS_TASK(_sc, _t) do { (_sc)->host_scribble = (void *) _t; } while (0) diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 9a7aaf5f1311..f6e189f40917 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -663,11 +663,16 @@ void sas_scsi_recover_host(struct Scsi_Host *shost) * scsi_unjam_host does, but we skip scsi_eh_abort_cmds because any * command we see here has no sas_task and is thus unknown to the HA. */ - if (!scsi_eh_get_sense(&eh_work_q, &ha->eh_done_q)) - scsi_eh_ready_devs(shost, &eh_work_q, &ha->eh_done_q); + if (!sas_ata_eh(shost, &eh_work_q, &ha->eh_done_q)) + if (!scsi_eh_get_sense(&eh_work_q, &ha->eh_done_q)) + scsi_eh_ready_devs(shost, &eh_work_q, &ha->eh_done_q); out: + /* now link into libata eh --- if we have any ata devices */ + sas_ata_strategy_handler(shost); + scsi_eh_flush_done_q(&ha->eh_done_q); + SAS_DPRINTK("--- Exit %s\n", __func__); return; } @@ -676,6 +681,10 @@ enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) { struct sas_task *task = TO_SAS_TASK(cmd); unsigned long flags; + enum blk_eh_timer_return rtn; + + if (sas_ata_timed_out(cmd, task, &rtn)) + return rtn; if (!task) { cmd->request->timeout /= 2; |