From 4fe74b1cb051dc9d47a80e263c388cf1651783d4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sun, 5 Feb 2012 12:16:00 +0100 Subject: [SCSI] virtio-scsi: SCSI driver for QEMU based virtual machines The virtio-scsi HBA is the basis of an alternative storage stack for QEMU-based virtual machines (including KVM). Compared to virtio-blk it is more scalable, because it supports many LUNs on a single PCI slot), more powerful (it more easily supports passthrough of host devices to the guest) and more easily extensible (new SCSI features implemented by QEMU should not require updating the driver in the guest). Acked-by: Rusty Russell Signed-off-by: Paolo Bonzini Signed-off-by: James Bottomley --- include/linux/virtio_ids.h | 1 + include/linux/virtio_scsi.h | 114 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 include/linux/virtio_scsi.h (limited to 'include/linux') diff --git a/include/linux/virtio_ids.h b/include/linux/virtio_ids.h index 85bb0bb66ffc..c5d8455c68c0 100644 --- a/include/linux/virtio_ids.h +++ b/include/linux/virtio_ids.h @@ -34,6 +34,7 @@ #define VIRTIO_ID_CONSOLE 3 /* virtio console */ #define VIRTIO_ID_RNG 4 /* virtio ring */ #define VIRTIO_ID_BALLOON 5 /* virtio balloon */ +#define VIRTIO_ID_SCSI 8 /* virtio scsi */ #define VIRTIO_ID_9P 9 /* 9p virtio console */ #endif /* _LINUX_VIRTIO_IDS_H */ diff --git a/include/linux/virtio_scsi.h b/include/linux/virtio_scsi.h new file mode 100644 index 000000000000..8ddeafdc0546 --- /dev/null +++ b/include/linux/virtio_scsi.h @@ -0,0 +1,114 @@ +#ifndef _LINUX_VIRTIO_SCSI_H +#define _LINUX_VIRTIO_SCSI_H +/* This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. */ + +#define VIRTIO_SCSI_CDB_SIZE 32 +#define VIRTIO_SCSI_SENSE_SIZE 96 + +/* SCSI command request, followed by data-out */ +struct virtio_scsi_cmd_req { + u8 lun[8]; /* Logical Unit Number */ + u64 tag; /* Command identifier */ + u8 task_attr; /* Task attribute */ + u8 prio; + u8 crn; + u8 cdb[VIRTIO_SCSI_CDB_SIZE]; +} __packed; + +/* Response, followed by sense data and data-in */ +struct virtio_scsi_cmd_resp { + u32 sense_len; /* Sense data length */ + u32 resid; /* Residual bytes in data buffer */ + u16 status_qualifier; /* Status qualifier */ + u8 status; /* Command completion status */ + u8 response; /* Response values */ + u8 sense[VIRTIO_SCSI_SENSE_SIZE]; +} __packed; + +/* Task Management Request */ +struct virtio_scsi_ctrl_tmf_req { + u32 type; + u32 subtype; + u8 lun[8]; + u64 tag; +} __packed; + +struct virtio_scsi_ctrl_tmf_resp { + u8 response; +} __packed; + +/* Asynchronous notification query/subscription */ +struct virtio_scsi_ctrl_an_req { + u32 type; + u8 lun[8]; + u32 event_requested; +} __packed; + +struct virtio_scsi_ctrl_an_resp { + u32 event_actual; + u8 response; +} __packed; + +struct virtio_scsi_event { + u32 event; + u8 lun[8]; + u32 reason; +} __packed; + +struct virtio_scsi_config { + u32 num_queues; + u32 seg_max; + u32 max_sectors; + u32 cmd_per_lun; + u32 event_info_size; + u32 sense_size; + u32 cdb_size; + u16 max_channel; + u16 max_target; + u32 max_lun; +} __packed; + +/* Response codes */ +#define VIRTIO_SCSI_S_OK 0 +#define VIRTIO_SCSI_S_OVERRUN 1 +#define VIRTIO_SCSI_S_ABORTED 2 +#define VIRTIO_SCSI_S_BAD_TARGET 3 +#define VIRTIO_SCSI_S_RESET 4 +#define VIRTIO_SCSI_S_BUSY 5 +#define VIRTIO_SCSI_S_TRANSPORT_FAILURE 6 +#define VIRTIO_SCSI_S_TARGET_FAILURE 7 +#define VIRTIO_SCSI_S_NEXUS_FAILURE 8 +#define VIRTIO_SCSI_S_FAILURE 9 +#define VIRTIO_SCSI_S_FUNCTION_SUCCEEDED 10 +#define VIRTIO_SCSI_S_FUNCTION_REJECTED 11 +#define VIRTIO_SCSI_S_INCORRECT_LUN 12 + +/* Controlq type codes. */ +#define VIRTIO_SCSI_T_TMF 0 +#define VIRTIO_SCSI_T_AN_QUERY 1 +#define VIRTIO_SCSI_T_AN_SUBSCRIBE 2 + +/* Valid TMF subtypes. */ +#define VIRTIO_SCSI_T_TMF_ABORT_TASK 0 +#define VIRTIO_SCSI_T_TMF_ABORT_TASK_SET 1 +#define VIRTIO_SCSI_T_TMF_CLEAR_ACA 2 +#define VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET 3 +#define VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET 4 +#define VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET 5 +#define VIRTIO_SCSI_T_TMF_QUERY_TASK 6 +#define VIRTIO_SCSI_T_TMF_QUERY_TASK_SET 7 + +/* Events. */ +#define VIRTIO_SCSI_T_EVENTS_MISSED 0x80000000 +#define VIRTIO_SCSI_T_NO_EVENT 0 +#define VIRTIO_SCSI_T_TRANSPORT_RESET 1 +#define VIRTIO_SCSI_T_ASYNC_NOTIFY 2 + +#define VIRTIO_SCSI_S_SIMPLE 0 +#define VIRTIO_SCSI_S_ORDERED 1 +#define VIRTIO_SCSI_S_HEAD 2 +#define VIRTIO_SCSI_S_ACA 3 + + +#endif /* _LINUX_VIRTIO_SCSI_H */ -- cgit v1.2.3 From 81c757bc696284f39f07766f0c2ca67af64ce9bd Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 2 Dec 2011 16:07:01 -0800 Subject: [SCSI] libsas: execute transport link resets with libata-eh via host workqueue Link resets leave ata affiliations intact, so arrange for libsas to make an effort to avoid dropping the device due to a slow-to-recover link. Towards this end carry out reset in the host workqueue so that it can check for ata devices and kick the reset request to libata. Hard resets, in contrast, bypass libata since they are meant for associating an ata device with another initiator in the domain (tears down affiliations). Need to add a new transport_sas_phy_reset() since the current sas_phy_reset() is a utility function to libsas lldds. They are not prepared for it to loop back into eh. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/ata/libata-eh.c | 1 + drivers/ata/libata.h | 1 - drivers/scsi/libsas/sas_ata.c | 11 ++++++++ drivers/scsi/libsas/sas_expander.c | 2 +- drivers/scsi/libsas/sas_init.c | 56 +++++++++++++++++++++++++++++++++++++- drivers/scsi/libsas/sas_internal.h | 1 + include/linux/libata.h | 1 + include/scsi/sas_ata.h | 4 +++ 8 files changed, 74 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index a9b282038000..c61316e9d2f7 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -863,6 +863,7 @@ void ata_port_wait_eh(struct ata_port *ap) goto retry; } } +EXPORT_SYMBOL_GPL(ata_port_wait_eh); static int ata_eh_nr_in_flight(struct ata_port *ap) { diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 814486d35c44..1fab235ee516 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -151,7 +151,6 @@ extern void ata_eh_acquire(struct ata_port *ap); extern void ata_eh_release(struct ata_port *ap); extern enum blk_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd); extern void ata_scsi_error(struct Scsi_Host *host); -extern void ata_port_wait_eh(struct ata_port *ap); extern void ata_eh_fastdrain_timerfn(unsigned long arg); extern void ata_qc_schedule_eh(struct ata_queued_cmd *qc); extern void ata_dev_disable(struct ata_device *dev); diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 48cadf88c399..03930a04a679 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -698,3 +698,14 @@ void sas_ata_schedule_reset(struct domain_device *dev) ata_port_schedule_eh(ap); spin_unlock_irqrestore(ap->lock, flags); } + +void sas_ata_wait_eh(struct domain_device *dev) +{ + struct ata_port *ap; + + if (!dev_is_sata(dev)) + return; + + ap = dev->sata_dev.ap; + ata_port_wait_eh(ap); +} diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index f4894b0f537b..d3c1a29b8a2a 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -228,7 +228,7 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, } /* check if we have an existing attached ata device on this expander phy */ -static struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id) +struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id) { struct ex_phy *ex_phy = &ex_dev->ex_dev.ex_phy[phy_id]; struct domain_device *dev; diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index cb65adf4ab16..a15fb861daba 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -195,6 +196,59 @@ static int sas_get_linkerrors(struct sas_phy *phy) return sas_smp_get_phy_events(phy); } +/** + * transport_sas_phy_reset - reset a phy and permit libata to manage the link + * + * phy reset request via sysfs in host workqueue context so we know we + * can block on eh and safely traverse the domain_device topology + */ +static int transport_sas_phy_reset(struct sas_phy *phy, int hard_reset) +{ + int ret; + enum phy_func reset_type; + + if (hard_reset) + reset_type = PHY_FUNC_HARD_RESET; + else + reset_type = PHY_FUNC_LINK_RESET; + + if (scsi_is_sas_phy_local(phy)) { + struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); + struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); + struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; + struct sas_internal *i = + to_sas_internal(sas_ha->core.shost->transportt); + struct domain_device *dev = NULL; + + if (asd_phy->port) + dev = asd_phy->port->port_dev; + + /* validate that dev has been probed */ + if (dev) + dev = sas_find_dev_by_rphy(dev->rphy); + + if (dev && dev_is_sata(dev) && !hard_reset) { + sas_ata_schedule_reset(dev); + sas_ata_wait_eh(dev); + ret = 0; + } else + ret = i->dft->lldd_control_phy(asd_phy, reset_type, NULL); + } else { + struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent); + struct domain_device *ddev = sas_find_dev_by_rphy(rphy); + struct domain_device *ata_dev = sas_ex_to_ata(ddev, phy->number); + + if (ata_dev && !hard_reset) { + sas_ata_schedule_reset(ata_dev); + sas_ata_wait_eh(ata_dev); + ret = 0; + } else + ret = sas_smp_phy_control(ddev, phy->number, reset_type, NULL); + } + + return ret; +} + int sas_phy_enable(struct sas_phy *phy, int enable) { int ret; @@ -300,7 +354,7 @@ static void phy_reset_work(struct work_struct *work) { struct sas_phy_data *d = container_of(work, typeof(*d), reset_work); - d->reset_result = sas_phy_reset(d->phy, d->hard_reset); + d->reset_result = transport_sas_phy_reset(d->phy, d->hard_reset); } static int sas_phy_setup(struct sas_phy *phy) diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 9ba65e0c6f91..ae9698d9d857 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -85,6 +85,7 @@ int sas_smp_phy_control(struct domain_device *dev, int phy_id, int sas_smp_get_phy_events(struct sas_phy *phy); struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy); +struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id); void sas_hae_reset(struct work_struct *work); diff --git a/include/linux/libata.h b/include/linux/libata.h index cafc09a64fe4..aa4270477563 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1147,6 +1147,7 @@ static inline int ata_acpi_cbl_80wire(struct ata_port *ap, * EH - drivers/ata/libata-eh.c */ extern void ata_port_schedule_eh(struct ata_port *ap); +extern void ata_port_wait_eh(struct ata_port *ap); extern int ata_link_abort(struct ata_link *link); extern int ata_port_abort(struct ata_port *ap); extern int ata_port_freeze(struct ata_port *ap); diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h index c0bcd30eec56..da3f37727387 100644 --- a/include/scsi/sas_ata.h +++ b/include/scsi/sas_ata.h @@ -45,6 +45,7 @@ int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, struct list_head *done_q); void sas_probe_sata(struct work_struct *work); void sas_ata_schedule_reset(struct domain_device *dev); +void sas_ata_wait_eh(struct domain_device *dev); #else @@ -79,6 +80,9 @@ static inline void sas_ata_schedule_reset(struct domain_device *dev) { } +static inline void sas_ata_wait_eh(struct domain_device *dev) +{ +} #endif #endif /* _SAS_ATA_H_ */ -- cgit v1.2.3 From 9508a66f898d46e726a318469312b45e0b1d078b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 18 Jan 2012 20:47:01 -0800 Subject: [SCSI] libsas: async ata scanning libsas ata error handling is already async but this does not help the scan case. Move initial link recovery out from under host->scan_mutex, and delay synchronization with eh until after all port probe/recovery work has been queued. Device ordering is maintained with scan order by still calling sas_rphy_add() in order of domain discovery. Since we now scan the domain list when invoking libata-eh we need to be careful to check for fully initialized ata ports. Acked-by: Jack Wang Acked-by: Jeff Garzik Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/ata/libata-core.c | 34 +++++++++-------- drivers/ata/libata-scsi.c | 13 +++++++ drivers/ata/libata.h | 1 + drivers/scsi/aic94xx/aic94xx_init.c | 1 - drivers/scsi/isci/init.c | 1 - drivers/scsi/libsas/sas_ata.c | 74 +++++++++++++++++++++++++++++++------ drivers/scsi/libsas/sas_discover.c | 22 +++++------ drivers/scsi/libsas/sas_internal.h | 9 +++++ drivers/scsi/libsas/sas_scsi_host.c | 18 --------- drivers/scsi/mvsas/mv_init.c | 1 - drivers/scsi/pm8001/pm8001_init.c | 1 - include/linux/libata.h | 1 + include/scsi/libsas.h | 1 - include/scsi/sas_ata.h | 12 +++--- 14 files changed, 123 insertions(+), 66 deletions(-) (limited to 'include/linux') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index c06e0ec11556..e0bda9ff89cd 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -5936,29 +5936,31 @@ void ata_host_init(struct ata_host *host, struct device *dev, host->ops = ops; } -int ata_port_probe(struct ata_port *ap) +void __ata_port_probe(struct ata_port *ap) { - int rc = 0; + struct ata_eh_info *ehi = &ap->link.eh_info; + unsigned long flags; - /* probe */ - if (ap->ops->error_handler) { - struct ata_eh_info *ehi = &ap->link.eh_info; - unsigned long flags; + /* kick EH for boot probing */ + spin_lock_irqsave(ap->lock, flags); - /* kick EH for boot probing */ - spin_lock_irqsave(ap->lock, flags); + ehi->probe_mask |= ATA_ALL_DEVICES; + ehi->action |= ATA_EH_RESET; + ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET; - ehi->probe_mask |= ATA_ALL_DEVICES; - ehi->action |= ATA_EH_RESET; - ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET; + ap->pflags &= ~ATA_PFLAG_INITIALIZING; + ap->pflags |= ATA_PFLAG_LOADING; + ata_port_schedule_eh(ap); - ap->pflags &= ~ATA_PFLAG_INITIALIZING; - ap->pflags |= ATA_PFLAG_LOADING; - ata_port_schedule_eh(ap); + spin_unlock_irqrestore(ap->lock, flags); +} - spin_unlock_irqrestore(ap->lock, flags); +int ata_port_probe(struct ata_port *ap) +{ + int rc = 0; - /* wait for EH to finish */ + if (ap->ops->error_handler) { + __ata_port_probe(ap); ata_port_wait_eh(ap); } else { DPRINTK("ata%u: bus probe begin\n", ap->print_id); diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 508a60bfe5c1..1ee00c8b5b04 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3838,6 +3838,19 @@ void ata_sas_port_stop(struct ata_port *ap) } EXPORT_SYMBOL_GPL(ata_sas_port_stop); +int ata_sas_async_port_init(struct ata_port *ap) +{ + int rc = ap->ops->port_start(ap); + + if (!rc) { + ap->print_id = ata_print_id++; + __ata_port_probe(ap); + } + + return rc; +} +EXPORT_SYMBOL_GPL(ata_sas_async_port_init); + /** * ata_sas_port_init - Initialize a SATA device * @ap: SATA port to initialize diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 1fab235ee516..2e26fcaf635b 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -105,6 +105,7 @@ extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg); extern struct ata_port *ata_port_alloc(struct ata_host *host); extern const char *sata_spd_string(unsigned int spd); extern int ata_port_probe(struct ata_port *ap); +extern void __ata_port_probe(struct ata_port *ap); /* libata-acpi.c */ #ifdef CONFIG_ATA_ACPI diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c index eea988a04f92..ff80552ead84 100644 --- a/drivers/scsi/aic94xx/aic94xx_init.c +++ b/drivers/scsi/aic94xx/aic94xx_init.c @@ -81,7 +81,6 @@ static struct scsi_host_template aic94xx_sht = { .use_clustering = ENABLE_CLUSTERING, .eh_device_reset_handler = sas_eh_device_reset_handler, .eh_bus_reset_handler = sas_eh_bus_reset_handler, - .slave_alloc = sas_slave_alloc, .target_destroy = sas_target_destroy, .ioctl = sas_ioctl, }; diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index c3fe39bcacd5..c9af456e7dfe 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -165,7 +165,6 @@ static struct scsi_host_template isci_sht = { .sg_tablesize = SG_ALL, .max_sectors = SCSI_DEFAULT_MAX_SECTORS, .use_clustering = ENABLE_CLUSTERING, - .slave_alloc = sas_slave_alloc, .target_destroy = sas_target_destroy, .ioctl = sas_ioctl, .shost_attrs = isci_host_attrs, diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index a9ec1643ee93..eb8b77c86169 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -585,11 +585,10 @@ static struct ata_port_info sata_port_info = { .port_ops = &sas_sata_ops }; -int sas_ata_init_host_and_port(struct domain_device *found_dev, - struct scsi_target *starget) +int sas_ata_init_host_and_port(struct domain_device *found_dev) { - struct Scsi_Host *shost = dev_to_shost(&starget->dev); - struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); + struct sas_ha_struct *ha = found_dev->port->ha; + struct Scsi_Host *shost = ha->core.shost; struct ata_port *ap; ata_host_init(&found_dev->sata_dev.ata_host, @@ -607,6 +606,8 @@ int sas_ata_init_host_and_port(struct domain_device *found_dev, ap->private_data = found_dev; ap->cbl = ATA_CBL_SATA; ap->scsi_host = shost; + /* publish initialized ata port */ + smp_wmb(); found_dev->sata_dev.ap = ap; return 0; @@ -683,6 +684,38 @@ static void sas_get_ata_command_set(struct domain_device *dev) dev->sata_dev.command_set = ATAPI_COMMAND_SET; } +void sas_probe_sata(struct asd_sas_port *port) +{ + struct domain_device *dev, *n; + int err; + + mutex_lock(&port->ha->disco_mutex); + list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { + if (!dev_is_sata(dev)) + continue; + + err = sas_ata_init_host_and_port(dev); + if (err) + sas_fail_probe(dev, __func__, err); + else + ata_sas_async_port_init(dev->sata_dev.ap); + } + mutex_unlock(&port->ha->disco_mutex); + + list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { + if (!dev_is_sata(dev)) + continue; + + sas_ata_wait_eh(dev); + + /* if libata could not bring the link up, don't surface + * the device + */ + if (ata_dev_disabled(sas_to_ata_dev(dev))) + sas_fail_probe(dev, __func__, -ENODEV); + } +} + /** * sas_discover_sata -- discover an STP/SATA domain device * @dev: pointer to struct domain_device of interest @@ -724,11 +757,23 @@ static void async_sas_ata_eh(void *data, async_cookie_t cookie) sas_put_device(dev); } +static bool sas_ata_dev_eh_valid(struct domain_device *dev) +{ + struct ata_port *ap; + + if (!dev_is_sata(dev)) + return false; + ap = dev->sata_dev.ap; + /* consume fully initialized ata ports */ + smp_rmb(); + return !!ap; +} + void sas_ata_strategy_handler(struct Scsi_Host *shost) { - struct scsi_device *sdev; struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); LIST_HEAD(async); + int i; /* it's ok to defer revalidation events during ata eh, these * disks are in one of three states: @@ -740,14 +785,21 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost) */ sas_disable_revalidation(sas_ha); - shost_for_each_device(sdev, shost) { - struct domain_device *ddev = sdev_to_domain_dev(sdev); - - if (!dev_is_sata(ddev)) - continue; + spin_lock_irq(&sas_ha->phy_port_lock); + for (i = 0; i < sas_ha->num_phys; i++) { + struct asd_sas_port *port = sas_ha->sas_port[i]; + struct domain_device *dev; - async_schedule_domain(async_sas_ata_eh, ddev, &async); + spin_lock(&port->dev_list_lock); + list_for_each_entry(dev, &port->dev_list, dev_list_node) { + if (!sas_ata_dev_eh_valid(dev)) + continue; + async_schedule_domain(async_sas_ata_eh, dev, &async); + } + spin_unlock(&port->dev_list_lock); } + spin_unlock_irq(&sas_ha->phy_port_lock); + async_synchronize_full_domain(&async); sas_enable_revalidation(sas_ha); diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 18fa364aa00f..0d58a8beaa3d 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -207,22 +207,22 @@ static void sas_probe_devices(struct work_struct *work) clear_bit(DISCE_PROBE, &port->disc.pending); - list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { - int err; - + /* devices must be domain members before link recovery and probe */ + list_for_each_entry(dev, &port->disco_list, disco_list_node) { spin_lock_irq(&port->dev_list_lock); list_add_tail(&dev->dev_list_node, &port->dev_list); spin_unlock_irq(&port->dev_list_lock); + } - err = sas_rphy_add(dev->rphy); + sas_probe_sata(port); - if (err) { - SAS_DPRINTK("%s: for %s device %16llx returned %d\n", - __func__, dev->parent ? "exp-attached" : - "direct-attached", - SAS_ADDR(dev->sas_addr), err); - sas_unregister_dev(port, dev); - } else + list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { + int err; + + err = sas_rphy_add(dev->rphy); + if (err) + sas_fail_probe(dev, __func__, err); + else list_del_init(&dev->disco_list_node); } } diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index e028d7a44202..d0d9bf10f79c 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -113,6 +113,15 @@ static inline int sas_smp_host_handler(struct Scsi_Host *shost, } #endif +static inline void sas_fail_probe(struct domain_device *dev, const char *func, int err) +{ + SAS_DPRINTK("%s: for %s device %16llx returned %d\n", + func, dev->parent ? "exp-attached" : + "direct-attached", + SAS_ADDR(dev->sas_addr), err); + sas_unregister_dev(dev->port, dev); +} + static inline void sas_fill_in_rphy(struct domain_device *dev, struct sas_rphy *rphy) { diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index e58ca50517d5..3701ff7e7267 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -762,17 +762,10 @@ int sas_target_alloc(struct scsi_target *starget) { struct sas_rphy *rphy = dev_to_rphy(starget->dev.parent); struct domain_device *found_dev = sas_find_dev_by_rphy(rphy); - int res; if (!found_dev) return -ENODEV; - if (dev_is_sata(found_dev)) { - res = sas_ata_init_host_and_port(found_dev, starget); - if (res) - return res; - } - kref_get(&found_dev->kref); starget->hostdata = found_dev; return 0; @@ -1012,16 +1005,6 @@ void sas_task_abort(struct sas_task *task) } } -int sas_slave_alloc(struct scsi_device *scsi_dev) -{ - struct domain_device *dev = sdev_to_domain_dev(scsi_dev); - - if (dev_is_sata(dev)) - return ata_sas_port_init(dev->sata_dev.ap); - - return 0; -} - void sas_target_destroy(struct scsi_target *starget) { struct domain_device *found_dev = starget->hostdata; @@ -1082,6 +1065,5 @@ EXPORT_SYMBOL_GPL(sas_task_abort); EXPORT_SYMBOL_GPL(sas_phy_reset); EXPORT_SYMBOL_GPL(sas_eh_device_reset_handler); EXPORT_SYMBOL_GPL(sas_eh_bus_reset_handler); -EXPORT_SYMBOL_GPL(sas_slave_alloc); EXPORT_SYMBOL_GPL(sas_target_destroy); EXPORT_SYMBOL_GPL(sas_ioctl); diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c index d45878b31254..cc59dff3810b 100644 --- a/drivers/scsi/mvsas/mv_init.c +++ b/drivers/scsi/mvsas/mv_init.c @@ -73,7 +73,6 @@ static struct scsi_host_template mvs_sht = { .use_clustering = ENABLE_CLUSTERING, .eh_device_reset_handler = sas_eh_device_reset_handler, .eh_bus_reset_handler = sas_eh_bus_reset_handler, - .slave_alloc = sas_slave_alloc, .target_destroy = sas_target_destroy, .ioctl = sas_ioctl, .shost_attrs = mvst_host_attrs, diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c index bd165ea61919..36efaa7c3a54 100644 --- a/drivers/scsi/pm8001/pm8001_init.c +++ b/drivers/scsi/pm8001/pm8001_init.c @@ -75,7 +75,6 @@ static struct scsi_host_template pm8001_sht = { .use_clustering = ENABLE_CLUSTERING, .eh_device_reset_handler = sas_eh_device_reset_handler, .eh_bus_reset_handler = sas_eh_bus_reset_handler, - .slave_alloc = sas_slave_alloc, .target_destroy = sas_target_destroy, .ioctl = sas_ioctl, .shost_attrs = pm8001_host_attrs, diff --git a/include/linux/libata.h b/include/linux/libata.h index aa4270477563..42378d637ffb 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -996,6 +996,7 @@ extern int ata_sas_scsi_ioctl(struct ata_port *ap, struct scsi_device *dev, extern void ata_sas_port_destroy(struct ata_port *); extern struct ata_port *ata_sas_port_alloc(struct ata_host *, struct ata_port_info *, struct Scsi_Host *); +extern int ata_sas_async_port_init(struct ata_port *); extern int ata_sas_port_init(struct ata_port *); extern int ata_sas_port_start(struct ata_port *ap); extern void ata_sas_port_stop(struct ata_port *ap); diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 4a42be34fad0..20153d58e4e6 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -646,7 +646,6 @@ int sas_phy_reset(struct sas_phy *phy, int hard_reset); int sas_queue_up(struct sas_task *task); extern int sas_queuecommand(struct Scsi_Host * ,struct scsi_cmnd *); extern int sas_target_alloc(struct scsi_target *); -extern int sas_slave_alloc(struct scsi_device *); extern int sas_slave_configure(struct scsi_device *); extern int sas_change_queue_depth(struct scsi_device *, int new_depth, int reason); diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h index 1556eff4cc44..cdccd2eb7b6c 100644 --- a/include/scsi/sas_ata.h +++ b/include/scsi/sas_ata.h @@ -37,15 +37,14 @@ static inline int dev_is_sata(struct domain_device *dev) } int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy); -int sas_ata_init_host_and_port(struct domain_device *found_dev, - struct scsi_target *starget); - +int sas_ata_init_host_and_port(struct domain_device *found_dev); void sas_ata_task_abort(struct sas_task *task); void sas_ata_strategy_handler(struct Scsi_Host *shost); void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, struct list_head *done_q); void sas_ata_schedule_reset(struct domain_device *dev); void sas_ata_wait_eh(struct domain_device *dev); +void sas_probe_sata(struct asd_sas_port *port); #else @@ -53,8 +52,7 @@ static inline int dev_is_sata(struct domain_device *dev) { return 0; } -static inline int sas_ata_init_host_and_port(struct domain_device *found_dev, - struct scsi_target *starget) +static inline int sas_ata_init_host_and_port(struct domain_device *found_dev) { return 0; } @@ -79,6 +77,10 @@ static inline void sas_ata_wait_eh(struct domain_device *dev) { } +static inline void sas_probe_sata(struct asd_sas_port *port) +{ +} + static inline int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy) { return 0; -- cgit v1.2.3