From 4fcf812ca392303aa79dd50e96e83a29faa13bd0 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 29 Jul 2011 17:26:39 -0700 Subject: [SCSI] libsas: export sas_alloc_task() Now that isci has added a 3rd open coded user of this functionality just share the libsas version. Acked-by: Jack Wang Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- include/scsi/libsas.h | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) (limited to 'include/scsi/libsas.h') diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index ee866060f8a4..2517254b8403 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -555,36 +555,14 @@ struct sas_task { struct work_struct abort_work; }; -extern struct kmem_cache *sas_task_cache; - #define SAS_TASK_STATE_PENDING 1 #define SAS_TASK_STATE_DONE 2 #define SAS_TASK_STATE_ABORTED 4 #define SAS_TASK_NEED_DEV_RESET 8 #define SAS_TASK_AT_INITIATOR 16 -static inline struct sas_task *sas_alloc_task(gfp_t flags) -{ - struct sas_task *task = kmem_cache_zalloc(sas_task_cache, flags); - - if (task) { - INIT_LIST_HEAD(&task->list); - spin_lock_init(&task->task_state_lock); - task->task_state_flags = SAS_TASK_STATE_PENDING; - init_timer(&task->timer); - init_completion(&task->completion); - } - - return task; -} - -static inline void sas_free_task(struct sas_task *task) -{ - if (task) { - BUG_ON(!list_empty(&task->list)); - kmem_cache_free(sas_task_cache, task); - } -} +extern struct sas_task *sas_alloc_task(gfp_t flags); +extern void sas_free_task(struct sas_task *task); struct sas_domain_function_template { /* The class calls these to notify the LLDD of an event. */ -- cgit v1.2.3 From 8ec6552f4a77d15f446b00aed364e3c12d38aa6c Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 1 Sep 2011 21:18:20 -0700 Subject: [SCSI] libsas: sgpio write support Add SFF-8485 v0.7 / SAS-1 smp-write-gpio register support to libsas. Defer SAS-2 support unless/until it defines an sgpio interface. Minimum implementation needed to get the lights blinking. try_test_sas_gpio_gp_bit() provides a common method to parse the incoming write data (raw bitstream), and the to_sas_gpio_gp_bit() helper routine can be used as a basis for the set/clear operations for the 'read' implementation. Host implementations parse as many bits (ODx.[012]) as are locally supported and report the number of registers successfully written. If the submitted data overruns the internal number of registers available report the write as a success with the number of bytes remaining reported in ->resid_len. Example (assuming an active backplane) set the "identify" pattern for the first 21 devices: smp_write_gpio --count=2 --data=92,49,24,92,24,92,49,24 -t 4 --index=1 /dev/bsg/sas_hostX Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_host_smp.c | 103 ++++++++++++++++++++++++++++++++++++- include/scsi/libsas.h | 11 ++++ include/scsi/sas.h | 8 +++ 3 files changed, 120 insertions(+), 2 deletions(-) (limited to 'include/scsi/libsas.h') diff --git a/drivers/scsi/libsas/sas_host_smp.c b/drivers/scsi/libsas/sas_host_smp.c index 04ad8dd1a74c..e1aa17840c5b 100644 --- a/drivers/scsi/libsas/sas_host_smp.c +++ b/drivers/scsi/libsas/sas_host_smp.c @@ -51,6 +51,91 @@ static void sas_host_smp_discover(struct sas_ha_struct *sas_ha, u8 *resp_data, resp_data[15] = rphy->identify.target_port_protocols; } +/** + * to_sas_gpio_gp_bit - given the gpio frame data find the byte/bit position of 'od' + * @od: od bit to find + * @data: incoming bitstream (from frame) + * @index: requested data register index (from frame) + * @count: total number of registers in the bitstream (from frame) + * @bit: bit position of 'od' in the returned byte + * + * returns NULL if 'od' is not in 'data' + * + * From SFF-8485 v0.7: + * "In GPIO_TX[1], bit 0 of byte 3 contains the first bit (i.e., OD0.0) + * and bit 7 of byte 0 contains the 32nd bit (i.e., OD10.1). + * + * In GPIO_TX[2], bit 0 of byte 3 contains the 33rd bit (i.e., OD10.2) + * and bit 7 of byte 0 contains the 64th bit (i.e., OD21.0)." + * + * The general-purpose (raw-bitstream) RX registers have the same layout + * although 'od' is renamed 'id' for 'input data'. + * + * SFF-8489 defines the behavior of the LEDs in response to the 'od' values. + */ +static u8 *to_sas_gpio_gp_bit(unsigned int od, u8 *data, u8 index, u8 count, u8 *bit) +{ + unsigned int reg; + u8 byte; + + /* gp registers start at index 1 */ + if (index == 0) + return NULL; + + index--; /* make index 0-based */ + if (od < index * 32) + return NULL; + + od -= index * 32; + reg = od >> 5; + + if (reg >= count) + return NULL; + + od &= (1 << 5) - 1; + byte = 3 - (od >> 3); + *bit = od & ((1 << 3) - 1); + + return &data[reg * 4 + byte]; +} + +int try_test_sas_gpio_gp_bit(unsigned int od, u8 *data, u8 index, u8 count) +{ + u8 *byte; + u8 bit; + + byte = to_sas_gpio_gp_bit(od, data, index, count, &bit); + if (!byte) + return -1; + + return (*byte >> bit) & 1; +} +EXPORT_SYMBOL(try_test_sas_gpio_gp_bit); + +static int sas_host_smp_write_gpio(struct sas_ha_struct *sas_ha, u8 *resp_data, + u8 reg_type, u8 reg_index, u8 reg_count, + u8 *req_data) +{ + struct sas_internal *i = to_sas_internal(sas_ha->core.shost->transportt); + int written; + + if (i->dft->lldd_write_gpio == NULL) { + resp_data[2] = SMP_RESP_FUNC_UNK; + return 0; + } + + written = i->dft->lldd_write_gpio(sas_ha, reg_type, reg_index, + reg_count, req_data); + + if (written < 0) { + resp_data[2] = SMP_RESP_FUNC_FAILED; + written = 0; + } else + resp_data[2] = SMP_RESP_FUNC_ACC; + + return written; +} + static void sas_report_phy_sata(struct sas_ha_struct *sas_ha, u8 *resp_data, u8 phy_id) { @@ -230,9 +315,23 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req, /* Can't implement; hosts have no routes */ break; - case SMP_WRITE_GPIO_REG: - /* FIXME: need GPIO support in the transport class */ + case SMP_WRITE_GPIO_REG: { + /* SFF-8485 v0.7 */ + const int base_frame_size = 11; + int to_write = req_data[4]; + + if (blk_rq_bytes(req) < base_frame_size + to_write * 4 || + req->resid_len < base_frame_size + to_write * 4) { + resp_data[2] = SMP_RESP_INV_FRM_LEN; + break; + } + + to_write = sas_host_smp_write_gpio(sas_ha, resp_data, req_data[2], + req_data[3], to_write, &req_data[8]); + req->resid_len -= base_frame_size + to_write * 4; + rsp->resid_len -= 8; break; + } case SMP_CONF_ROUTE_INFO: /* Can't implement; hosts have no routes */ diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 2517254b8403..af0a1deac930 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -405,6 +405,13 @@ static inline void sas_phy_disconnected(struct asd_sas_phy *phy) phy->linkrate = SAS_LINK_RATE_UNKNOWN; } +static inline unsigned int to_sas_gpio_od(int device, int bit) +{ + return 3 * device + bit; +} + +int try_test_sas_gpio_gp_bit(unsigned int od, u8 *data, u8 index, u8 count); + /* ---------- Tasks ---------- */ /* service_response | SAS_TASK_COMPLETE | SAS_TASK_UNDELIVERED | @@ -592,6 +599,10 @@ struct sas_domain_function_template { /* Phy management */ int (*lldd_control_phy)(struct asd_sas_phy *, enum phy_func, void *); + + /* GPIO support */ + int (*lldd_write_gpio)(struct sas_ha_struct *, u8 reg_type, + u8 reg_index, u8 reg_count, u8 *write_data); }; extern int sas_register_ha(struct sas_ha_struct *); diff --git a/include/scsi/sas.h b/include/scsi/sas.h index e9fd02281381..a3001add0c66 100644 --- a/include/scsi/sas.h +++ b/include/scsi/sas.h @@ -195,6 +195,14 @@ enum sas_open_rej_reason { SAS_OREJ_RSVD_RETRY = 18, }; +enum sas_gpio_reg_type { + SAS_GPIO_REG_CFG = 0, + SAS_GPIO_REG_RX = 1, + SAS_GPIO_REG_RX_GP = 2, + SAS_GPIO_REG_TX = 3, + SAS_GPIO_REG_TX_GP = 4, +}; + struct dev_to_host_fis { u8 fis_type; /* 0x34 */ u8 flags; -- cgit v1.2.3 From ffaac8f45bfb2dffb78179baa5740de34058eef8 Mon Sep 17 00:00:00 2001 From: Luben Tuikov Date: Thu, 22 Sep 2011 09:41:36 -0700 Subject: [SCSI] libsas: Allow expander T-T attachments Allow expander table-to-table attachments for expanders that support it. Signed-off-by: Luben Tuikov Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_expander.c | 20 ++++++++++++++------ include/scsi/libsas.h | 3 +++ include/scsi/sas.h | 14 ++++++++++++-- 3 files changed, 29 insertions(+), 8 deletions(-) (limited to 'include/scsi/libsas.h') diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index f84084bba2f0..e8d0115a025a 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -329,6 +329,7 @@ static void ex_assign_report_general(struct domain_device *dev, dev->ex_dev.ex_change_count = be16_to_cpu(rg->change_count); dev->ex_dev.max_route_indexes = be16_to_cpu(rg->route_indexes); dev->ex_dev.num_phys = min(rg->num_phys, (u8)MAX_EXPANDER_PHYS); + dev->ex_dev.t2t_supp = rg->t2t_supp; dev->ex_dev.conf_route_table = rg->conf_route_table; dev->ex_dev.configuring = rg->configuring; memcpy(dev->ex_dev.enclosure_logical_id, rg->enclosure_logical_id, 8); @@ -1133,15 +1134,17 @@ static void sas_print_parent_topology_bug(struct domain_device *child, }; struct domain_device *parent = child->parent; - sas_printk("%s ex %016llx phy 0x%x <--> %s ex %016llx phy 0x%x " - "has %c:%c routing link!\n", + sas_printk("%s ex %016llx (T2T supp:%d) phy 0x%x <--> %s ex %016llx " + "(T2T supp:%d) phy 0x%x has %c:%c routing link!\n", ex_type[parent->dev_type], SAS_ADDR(parent->sas_addr), + parent->ex_dev.t2t_supp, parent_phy->phy_id, ex_type[child->dev_type], SAS_ADDR(child->sas_addr), + child->ex_dev.t2t_supp, child_phy->phy_id, ra_char[parent_phy->routing_attr], @@ -1238,10 +1241,15 @@ static int sas_check_parent_topology(struct domain_device *child) sas_print_parent_topology_bug(child, parent_phy, child_phy); res = -ENODEV; } - } else if (parent_phy->routing_attr == TABLE_ROUTING && - child_phy->routing_attr != SUBTRACTIVE_ROUTING) { - sas_print_parent_topology_bug(child, parent_phy, child_phy); - res = -ENODEV; + } else if (parent_phy->routing_attr == TABLE_ROUTING) { + if (child_phy->routing_attr == SUBTRACTIVE_ROUTING || + (child_phy->routing_attr == TABLE_ROUTING && + child_ex->t2t_supp && parent_ex->t2t_supp)) { + /* All good */; + } else { + sas_print_parent_topology_bug(child, parent_phy, child_phy); + res = -ENODEV; + } } break; case FANOUT_DEV: diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index af0a1deac930..ac9d80c93c68 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -142,8 +142,11 @@ struct expander_device { u16 ex_change_count; u16 max_route_indexes; u8 num_phys; + + u8 t2t_supp:1; u8 configuring:1; u8 conf_route_table:1; + u8 enclosure_logical_id[8]; struct ex_phy *ex_phy; diff --git a/include/scsi/sas.h b/include/scsi/sas.h index a3001add0c66..07d504f3981c 100644 --- a/include/scsi/sas.h +++ b/include/scsi/sas.h @@ -349,7 +349,12 @@ struct report_general_resp { u8 conf_route_table:1; u8 configuring:1; - u8 _r_b:6; + u8 config_others:1; + u8 orej_retry_supp:1; + u8 stp_cont_awt:1; + u8 self_config:1; + u8 zone_config:1; + u8 t2t_supp:1; u8 _r_c; @@ -536,7 +541,12 @@ struct report_general_resp { u8 _r_a; u8 num_phys; - u8 _r_b:6; + u8 t2t_supp:1; + u8 zone_config:1; + u8 self_config:1; + u8 stp_cont_awt:1; + u8 orej_retry_supp:1; + u8 config_others:1; u8 configuring:1; u8 conf_route_table:1; -- cgit v1.2.3 From d962480e9a061505637bb16543d49e247349e25d Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 23 Sep 2011 16:43:45 -0700 Subject: [SCSI] libsas: fix try_test_sas_gpio_gp_bit() build error If the user has disabled CONFIG_SCSI_SAS_HOST_SMP then libsas drivers will not be receiving smp-gpio frames and do not need this lookup code. Reported-by: Randy Dunlap Tested-by: Randy Dunlap Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- include/scsi/libsas.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include/scsi/libsas.h') diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index ac9d80c93c68..d455fa96dc8c 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -413,7 +413,14 @@ static inline unsigned int to_sas_gpio_od(int device, int bit) return 3 * device + bit; } +#ifdef CONFIG_SCSI_SAS_HOST_SMP int try_test_sas_gpio_gp_bit(unsigned int od, u8 *data, u8 index, u8 count); +#else +static inline int try_test_sas_gpio_gp_bit(unsigned int od, u8 *data, u8 index, u8 count) +{ + return -1; +} +#endif /* ---------- Tasks ---------- */ /* -- cgit v1.2.3 From b50102d3e9a43a75379407c2080f696f61cb286b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 30 Sep 2011 18:52:19 -0700 Subject: [SCSI] isci: atapi support Based on original implementation from Jiangbi Liu and Maciej Trela. ATAPI transfers happen in two-to-three stages. The two stage atapi commands are those that include a dma data transfer. The data transfer portion of these operations is handled by the hardware packet-dma acceleration. The three-stage commands do not have a data transfer and are handled without hardware assistance in raw frame mode. stage1: transmit host-to-device fis to notify the device of an incoming atapi cdb. Upon reception of the pio-setup-fis repost the task_context to perform the dma transfer of the cdb+data (go to stage3), or repost the task_context to transmit the cdb as a raw frame (go to stage 2). stage2: wait for hardware notification of the cdb transmission and then go to stage 3. stage3: wait for the arrival of the terminating device-to-host fis and terminate the command. To keep the implementation simple we only support ATAPI packet-dma protocol (for commands with data) to avoid needing to handle the data transfer manually (like we do for SATA-PIO). This may affect compatibility for a small number of devices (see ATA_HORKAGE_ATAPI_MOD16_DMA). If the data-transfer underruns, or encounters an error the device-to-host fis is expected to arrive in the unsolicited frame queue to pass to libata for disposition. However, in the DONE_UNEXP_FIS (data underrun) case it appears we need to craft a response. In the DONE_REG_ERR case we do receive the UF and propagate it to libsas. Signed-off-by: Maciej Trela Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/isci/remote_device.c | 24 +++ drivers/scsi/isci/remote_device.h | 9 + drivers/scsi/isci/request.c | 328 ++++++++++++++++++++++++++++++++++-- drivers/scsi/isci/request.h | 28 ++- drivers/scsi/libsas/sas_scsi_host.c | 2 +- include/scsi/libsas.h | 5 + 6 files changed, 380 insertions(+), 16 deletions(-) (limited to 'include/scsi/libsas.h') diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index b6e6368c2665..fbf9ce28c3f5 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -386,6 +386,18 @@ static bool is_remote_device_ready(struct isci_remote_device *idev) } } +/* + * called once the remote node context has transisitioned to a ready + * state (after suspending RX and/or TX due to early D2H fis) + */ +static void atapi_remote_device_resume_done(void *_dev) +{ + struct isci_remote_device *idev = _dev; + struct isci_request *ireq = idev->working_request; + + sci_change_state(&ireq->sm, SCI_REQ_COMPLETED); +} + enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev, u32 event_code) { @@ -432,6 +444,16 @@ enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev, if (status != SCI_SUCCESS) return status; + if (state == SCI_STP_DEV_ATAPI_ERROR) { + /* For ATAPI error state resume the RNC right away. */ + if (scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX || + scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX) { + return sci_remote_node_context_resume(&idev->rnc, + atapi_remote_device_resume_done, + idev); + } + } + if (state == SCI_STP_DEV_IDLE) { /* We pick up suspension events to handle specifically to this @@ -625,6 +647,7 @@ enum sci_status sci_remote_device_complete_io(struct isci_host *ihost, case SCI_STP_DEV_CMD: case SCI_STP_DEV_NCQ: case SCI_STP_DEV_NCQ_ERROR: + case SCI_STP_DEV_ATAPI_ERROR: status = common_complete_io(iport, idev, ireq); if (status != SCI_SUCCESS) break; @@ -1020,6 +1043,7 @@ static const struct sci_base_state sci_remote_device_state_table[] = { [SCI_STP_DEV_NCQ_ERROR] = { .enter_state = sci_stp_remote_device_ready_ncq_error_substate_enter, }, + [SCI_STP_DEV_ATAPI_ERROR] = { }, [SCI_STP_DEV_AWAIT_RESET] = { }, [SCI_SMP_DEV_IDLE] = { .enter_state = sci_smp_remote_device_ready_idle_substate_enter, diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h index 57ccfc3d6ad3..e1747ea0d0ea 100644 --- a/drivers/scsi/isci/remote_device.h +++ b/drivers/scsi/isci/remote_device.h @@ -243,6 +243,15 @@ enum sci_remote_device_states { */ SCI_STP_DEV_NCQ_ERROR, + /** + * This is the ATAPI error state for the STP ATAPI remote device. + * This state is entered when ATAPI device sends error status FIS + * without data while the device object is in CMD state. + * A suspension event is expected in this state. + * The device object will resume right away. + */ + SCI_STP_DEV_ATAPI_ERROR, + /** * This is the READY substate indicates the device is waiting for the RESET task * coming to be recovered from certain hardware specific error. diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index 225b196800a2..23a55043a923 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -481,7 +481,29 @@ static void sci_stp_optimized_request_construct(struct isci_request *ireq, } } +static void sci_atapi_construct(struct isci_request *ireq) +{ + struct host_to_dev_fis *h2d_fis = &ireq->stp.cmd; + struct sas_task *task; + + /* To simplify the implementation we take advantage of the + * silicon's partial acceleration of atapi protocol (dma data + * transfers), so we promote all commands to dma protocol. This + * breaks compatibility with ATA_HORKAGE_ATAPI_MOD16_DMA drives. + */ + h2d_fis->features |= ATAPI_PKT_DMA; + + scu_stp_raw_request_construct_task_context(ireq); + + task = isci_request_access_task(ireq); + if (task->data_dir == DMA_NONE) + task->total_xfer_len = 0; + /* clear the response so we can detect arrivial of an + * unsolicited h2d fis + */ + ireq->stp.rsp.fis_type = 0; +} static enum sci_status sci_io_request_construct_sata(struct isci_request *ireq, @@ -491,6 +513,7 @@ sci_io_request_construct_sata(struct isci_request *ireq, { enum sci_status status = SCI_SUCCESS; struct sas_task *task = isci_request_access_task(ireq); + struct domain_device *dev = ireq->target_device->domain_dev; /* check for management protocols */ if (ireq->ttype == tmf_task) { @@ -519,6 +542,13 @@ sci_io_request_construct_sata(struct isci_request *ireq, } + /* ATAPI */ + if (dev->sata_dev.command_set == ATAPI_COMMAND_SET && + task->ata_task.fis.command == ATA_CMD_PACKET) { + sci_atapi_construct(ireq); + return SCI_SUCCESS; + } + /* non data */ if (task->data_dir == DMA_NONE) { scu_stp_raw_request_construct_task_context(ireq); @@ -627,7 +657,7 @@ enum sci_status sci_task_request_construct_sata(struct isci_request *ireq) /** * sci_req_tx_bytes - bytes transferred when reply underruns request - * @sci_req: request that was terminated early + * @ireq: request that was terminated early */ #define SCU_TASK_CONTEXT_SRAM 0x200000 static u32 sci_req_tx_bytes(struct isci_request *ireq) @@ -729,6 +759,10 @@ sci_io_request_terminate(struct isci_request *ireq) case SCI_REQ_STP_SOFT_RESET_WAIT_H2D_ASSERTED: case SCI_REQ_STP_SOFT_RESET_WAIT_H2D_DIAG: case SCI_REQ_STP_SOFT_RESET_WAIT_D2H: + case SCI_REQ_ATAPI_WAIT_H2D: + case SCI_REQ_ATAPI_WAIT_PIO_SETUP: + case SCI_REQ_ATAPI_WAIT_D2H: + case SCI_REQ_ATAPI_WAIT_TC_COMP: sci_change_state(&ireq->sm, SCI_REQ_ABORTING); return SCI_SUCCESS; case SCI_REQ_TASK_WAIT_TC_RESP: @@ -1194,8 +1228,8 @@ static enum sci_status sci_stp_request_pio_data_out_transmit_data(struct isci_re { struct isci_stp_request *stp_req = &ireq->stp.req; struct scu_sgl_element_pair *sgl_pair; + enum sci_status status = SCI_SUCCESS; struct scu_sgl_element *sgl; - enum sci_status status; u32 offset; u32 len = 0; @@ -1249,7 +1283,7 @@ static enum sci_status sci_stp_request_pio_data_out_transmit_data(struct isci_re */ static enum sci_status sci_stp_request_pio_data_in_copy_data_buffer(struct isci_stp_request *stp_req, - u8 *data_buf, u32 len) + u8 *data_buf, u32 len) { struct isci_request *ireq; u8 *src_addr; @@ -1423,6 +1457,128 @@ static enum sci_status sci_stp_request_udma_general_frame_handler(struct isci_re return status; } +static enum sci_status process_unsolicited_fis(struct isci_request *ireq, + u32 frame_index) +{ + struct isci_host *ihost = ireq->owning_controller; + enum sci_status status; + struct dev_to_host_fis *frame_header; + u32 *frame_buffer; + + status = sci_unsolicited_frame_control_get_header(&ihost->uf_control, + frame_index, + (void **)&frame_header); + + if (status != SCI_SUCCESS) + return status; + + if (frame_header->fis_type != FIS_REGD2H) { + dev_err(&ireq->isci_host->pdev->dev, + "%s ERROR: invalid fis type 0x%X\n", + __func__, frame_header->fis_type); + return SCI_FAILURE; + } + + sci_unsolicited_frame_control_get_buffer(&ihost->uf_control, + frame_index, + (void **)&frame_buffer); + + sci_controller_copy_sata_response(&ireq->stp.rsp, + (u32 *)frame_header, + frame_buffer); + + /* Frame has been decoded return it to the controller */ + sci_controller_release_frame(ihost, frame_index); + + return status; +} + +static enum sci_status atapi_d2h_reg_frame_handler(struct isci_request *ireq, + u32 frame_index) +{ + struct sas_task *task = isci_request_access_task(ireq); + enum sci_status status; + + status = process_unsolicited_fis(ireq, frame_index); + + if (status == SCI_SUCCESS) { + if (ireq->stp.rsp.status & ATA_ERR) + status = SCI_IO_FAILURE_RESPONSE_VALID; + } else { + status = SCI_IO_FAILURE_RESPONSE_VALID; + } + + if (status != SCI_SUCCESS) { + ireq->scu_status = SCU_TASK_DONE_CHECK_RESPONSE; + ireq->sci_status = status; + } else { + ireq->scu_status = SCU_TASK_DONE_GOOD; + ireq->sci_status = SCI_SUCCESS; + } + + /* the d2h ufi is the end of non-data commands */ + if (task->data_dir == DMA_NONE) + sci_change_state(&ireq->sm, SCI_REQ_COMPLETED); + + return status; +} + +static void scu_atapi_reconstruct_raw_frame_task_context(struct isci_request *ireq) +{ + struct ata_device *dev = sas_to_ata_dev(ireq->target_device->domain_dev); + void *atapi_cdb = ireq->ttype_ptr.io_task_ptr->ata_task.atapi_packet; + struct scu_task_context *task_context = ireq->tc; + + /* fill in the SCU Task Context for a DATA fis containing CDB in Raw Frame + * type. The TC for previous Packet fis was already there, we only need to + * change the H2D fis content. + */ + memset(&ireq->stp.cmd, 0, sizeof(struct host_to_dev_fis)); + memcpy(((u8 *)&ireq->stp.cmd + sizeof(u32)), atapi_cdb, ATAPI_CDB_LEN); + memset(&(task_context->type.stp), 0, sizeof(struct stp_task_context)); + task_context->type.stp.fis_type = FIS_DATA; + task_context->transfer_length_bytes = dev->cdb_len; +} + +static void scu_atapi_construct_task_context(struct isci_request *ireq) +{ + struct ata_device *dev = sas_to_ata_dev(ireq->target_device->domain_dev); + struct sas_task *task = isci_request_access_task(ireq); + struct scu_task_context *task_context = ireq->tc; + int cdb_len = dev->cdb_len; + + /* reference: SSTL 1.13.4.2 + * task_type, sata_direction + */ + if (task->data_dir == DMA_TO_DEVICE) { + task_context->task_type = SCU_TASK_TYPE_PACKET_DMA_OUT; + task_context->sata_direction = 0; + } else { + /* todo: for NO_DATA command, we need to send out raw frame. */ + task_context->task_type = SCU_TASK_TYPE_PACKET_DMA_IN; + task_context->sata_direction = 1; + } + + memset(&task_context->type.stp, 0, sizeof(task_context->type.stp)); + task_context->type.stp.fis_type = FIS_DATA; + + memset(&ireq->stp.cmd, 0, sizeof(ireq->stp.cmd)); + memcpy(&ireq->stp.cmd.lbal, task->ata_task.atapi_packet, cdb_len); + task_context->ssp_command_iu_length = cdb_len / sizeof(u32); + + /* task phase is set to TX_CMD */ + task_context->task_phase = 0x1; + + /* retry counter */ + task_context->stp_retry_count = 0; + + /* data transfer size. */ + task_context->transfer_length_bytes = task->total_xfer_len; + + /* setup sgl */ + sci_request_build_sgl(ireq); +} + enum sci_status sci_io_request_frame_handler(struct isci_request *ireq, u32 frame_index) @@ -1835,6 +1991,24 @@ sci_io_request_frame_handler(struct isci_request *ireq, return status; } + case SCI_REQ_ATAPI_WAIT_PIO_SETUP: { + struct sas_task *task = isci_request_access_task(ireq); + + sci_controller_release_frame(ihost, frame_index); + ireq->target_device->working_request = ireq; + if (task->data_dir == DMA_NONE) { + sci_change_state(&ireq->sm, SCI_REQ_ATAPI_WAIT_TC_COMP); + scu_atapi_reconstruct_raw_frame_task_context(ireq); + } else { + sci_change_state(&ireq->sm, SCI_REQ_ATAPI_WAIT_D2H); + scu_atapi_construct_task_context(ireq); + } + + sci_controller_continue_io(ireq); + return SCI_SUCCESS; + } + case SCI_REQ_ATAPI_WAIT_D2H: + return atapi_d2h_reg_frame_handler(ireq, frame_index); case SCI_REQ_ABORTING: /* * TODO: Is it even possible to get an unsolicited frame in the @@ -1966,6 +2140,112 @@ stp_request_soft_reset_await_h2d_diagnostic_tc_event(struct isci_request *ireq, return SCI_SUCCESS; } +static enum sci_status atapi_raw_completion(struct isci_request *ireq, u32 completion_code, + enum sci_base_request_states next) +{ + enum sci_status status = SCI_SUCCESS; + + switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) { + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_GOOD): + ireq->scu_status = SCU_TASK_DONE_GOOD; + ireq->sci_status = SCI_SUCCESS; + sci_change_state(&ireq->sm, next); + break; + default: + /* All other completion status cause the IO to be complete. + * If a NAK was received, then it is up to the user to retry + * the request. + */ + ireq->scu_status = SCU_NORMALIZE_COMPLETION_STATUS(completion_code); + ireq->sci_status = SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR; + + sci_change_state(&ireq->sm, SCI_REQ_COMPLETED); + break; + } + + return status; +} + +static enum sci_status atapi_data_tc_completion_handler(struct isci_request *ireq, + u32 completion_code) +{ + struct isci_remote_device *idev = ireq->target_device; + struct dev_to_host_fis *d2h = &ireq->stp.rsp; + enum sci_status status = SCI_SUCCESS; + + switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) { + case (SCU_TASK_DONE_GOOD << SCU_COMPLETION_TL_STATUS_SHIFT): + sci_change_state(&ireq->sm, SCI_REQ_COMPLETED); + break; + + case (SCU_TASK_DONE_UNEXP_FIS << SCU_COMPLETION_TL_STATUS_SHIFT): { + u16 len = sci_req_tx_bytes(ireq); + + /* likely non-error data underrrun, workaround missing + * d2h frame from the controller + */ + if (d2h->fis_type != FIS_REGD2H) { + d2h->fis_type = FIS_REGD2H; + d2h->flags = (1 << 6); + d2h->status = 0x50; + d2h->error = 0; + d2h->lbal = 0; + d2h->byte_count_low = len & 0xff; + d2h->byte_count_high = len >> 8; + d2h->device = 0xa0; + d2h->lbal_exp = 0; + d2h->lbam_exp = 0; + d2h->lbah_exp = 0; + d2h->_r_a = 0; + d2h->sector_count = 0x3; + d2h->sector_count_exp = 0; + d2h->_r_b = 0; + d2h->_r_c = 0; + d2h->_r_d = 0; + } + + ireq->scu_status = SCU_TASK_DONE_GOOD; + ireq->sci_status = SCI_SUCCESS_IO_DONE_EARLY; + status = ireq->sci_status; + + /* the hw will have suspended the rnc, so complete the + * request upon pending resume + */ + sci_change_state(&idev->sm, SCI_STP_DEV_ATAPI_ERROR); + break; + } + case (SCU_TASK_DONE_EXCESS_DATA << SCU_COMPLETION_TL_STATUS_SHIFT): + /* In this case, there is no UF coming after. + * compelte the IO now. + */ + ireq->scu_status = SCU_TASK_DONE_GOOD; + ireq->sci_status = SCI_SUCCESS; + sci_change_state(&ireq->sm, SCI_REQ_COMPLETED); + break; + + default: + if (d2h->fis_type == FIS_REGD2H) { + /* UF received change the device state to ATAPI_ERROR */ + status = ireq->sci_status; + sci_change_state(&idev->sm, SCI_STP_DEV_ATAPI_ERROR); + } else { + /* If receiving any non-sucess TC status, no UF + * received yet, then an UF for the status fis + * is coming after (XXX: suspect this is + * actually a protocol error or a bug like the + * DONE_UNEXP_FIS case) + */ + ireq->scu_status = SCU_TASK_DONE_CHECK_RESPONSE; + ireq->sci_status = SCI_FAILURE_IO_RESPONSE_VALID; + + sci_change_state(&ireq->sm, SCI_REQ_ATAPI_WAIT_D2H); + } + break; + } + + return status; +} + enum sci_status sci_io_request_tc_completion(struct isci_request *ireq, u32 completion_code) @@ -2017,6 +2297,17 @@ sci_io_request_tc_completion(struct isci_request *ireq, return request_aborting_state_tc_event(ireq, completion_code); + case SCI_REQ_ATAPI_WAIT_H2D: + return atapi_raw_completion(ireq, completion_code, + SCI_REQ_ATAPI_WAIT_PIO_SETUP); + + case SCI_REQ_ATAPI_WAIT_TC_COMP: + return atapi_raw_completion(ireq, completion_code, + SCI_REQ_ATAPI_WAIT_D2H); + + case SCI_REQ_ATAPI_WAIT_D2H: + return atapi_data_tc_completion_handler(ireq, completion_code); + default: dev_warn(&ihost->pdev->dev, "%s: SCIC IO Request given task completion " @@ -2423,6 +2714,8 @@ static void isci_process_stp_response(struct sas_task *task, struct dev_to_host_ */ if (fis->status & ATA_DF) ts->stat = SAS_PROTO_RESPONSE; + else if (fis->status & ATA_ERR) + ts->stat = SAM_STAT_CHECK_CONDITION; else ts->stat = SAM_STAT_GOOD; @@ -2782,6 +3075,7 @@ static void sci_request_started_state_enter(struct sci_base_state_machine *sm) { struct isci_request *ireq = container_of(sm, typeof(*ireq), sm); struct domain_device *dev = ireq->target_device->domain_dev; + enum sci_base_request_states state; struct sas_task *task; /* XXX as hch said always creating an internal sas_task for tmf @@ -2793,26 +3087,30 @@ static void sci_request_started_state_enter(struct sci_base_state_machine *sm) * substates */ if (!task && dev->dev_type == SAS_END_DEV) { - sci_change_state(sm, SCI_REQ_TASK_WAIT_TC_COMP); + state = SCI_REQ_TASK_WAIT_TC_COMP; } else if (!task && (isci_request_access_tmf(ireq)->tmf_code == isci_tmf_sata_srst_high || isci_request_access_tmf(ireq)->tmf_code == isci_tmf_sata_srst_low)) { - sci_change_state(sm, SCI_REQ_STP_SOFT_RESET_WAIT_H2D_ASSERTED); + state = SCI_REQ_STP_SOFT_RESET_WAIT_H2D_ASSERTED; } else if (task && task->task_proto == SAS_PROTOCOL_SMP) { - sci_change_state(sm, SCI_REQ_SMP_WAIT_RESP); + state = SCI_REQ_SMP_WAIT_RESP; } else if (task && sas_protocol_ata(task->task_proto) && !task->ata_task.use_ncq) { - u32 state; - - if (task->data_dir == DMA_NONE) + if (dev->sata_dev.command_set == ATAPI_COMMAND_SET && + task->ata_task.fis.command == ATA_CMD_PACKET) { + state = SCI_REQ_ATAPI_WAIT_H2D; + } else if (task->data_dir == DMA_NONE) { state = SCI_REQ_STP_NON_DATA_WAIT_H2D; - else if (task->ata_task.dma_xfer) + } else if (task->ata_task.dma_xfer) { state = SCI_REQ_STP_UDMA_WAIT_TC_COMP; - else /* PIO */ + } else /* PIO */ { state = SCI_REQ_STP_PIO_WAIT_H2D; - - sci_change_state(sm, state); + } + } else { + /* SSP or NCQ are fully accelerated, no substates */ + return; } + sci_change_state(sm, state); } static void sci_request_completed_state_enter(struct sci_base_state_machine *sm) @@ -2904,6 +3202,10 @@ static const struct sci_base_state sci_request_state_table[] = { [SCI_REQ_TASK_WAIT_TC_RESP] = { }, [SCI_REQ_SMP_WAIT_RESP] = { }, [SCI_REQ_SMP_WAIT_TC_COMP] = { }, + [SCI_REQ_ATAPI_WAIT_H2D] = { }, + [SCI_REQ_ATAPI_WAIT_PIO_SETUP] = { }, + [SCI_REQ_ATAPI_WAIT_D2H] = { }, + [SCI_REQ_ATAPI_WAIT_TC_COMP] = { }, [SCI_REQ_COMPLETED] = { .enter_state = sci_request_completed_state_enter, }, diff --git a/drivers/scsi/isci/request.h b/drivers/scsi/isci/request.h index 58d70b6606ef..f720b97b7bb5 100644 --- a/drivers/scsi/isci/request.h +++ b/drivers/scsi/isci/request.h @@ -96,7 +96,6 @@ enum sci_request_protocol { * to wait for another fis or if the transfer is complete. Upon * receipt of a d2h fis this will be the status field of that fis. * @sgl - track pio transfer progress as we iterate through the sgl - * @device_cdb_len - atapi device advertises it's transfer constraints at setup */ struct isci_stp_request { u32 pio_len; @@ -107,7 +106,6 @@ struct isci_stp_request { u8 set; u32 offset; } sgl; - u32 device_cdb_len; }; struct isci_request { @@ -248,6 +246,32 @@ enum sci_base_request_states { */ SCI_REQ_STP_PIO_DATA_OUT, + /* + * While in this state the IO request object is waiting for the TC + * completion notification for the H2D Register FIS + */ + SCI_REQ_ATAPI_WAIT_H2D, + + /* + * While in this state the IO request object is waiting for either a + * PIO Setup. + */ + SCI_REQ_ATAPI_WAIT_PIO_SETUP, + + /* + * The non-data IO transit to this state in this state after receiving + * TC completion. While in this state IO request object is waiting for + * D2H status frame as UF. + */ + SCI_REQ_ATAPI_WAIT_D2H, + + /* + * When transmitting raw frames hardware reports task context completion + * after every frame submission, so in the non-accelerated case we need + * to expect the completion for the "cdb" frame. + */ + SCI_REQ_ATAPI_WAIT_TC_COMP, + /* * The AWAIT_TC_COMPLETION sub-state indicates that the started raw * task management request is waiting for the transmission of the diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index ebd1417339e9..b2c4a7731656 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -801,7 +801,7 @@ void sas_slave_destroy(struct scsi_device *scsi_dev) struct domain_device *dev = sdev_to_domain_dev(scsi_dev); if (dev_is_sata(dev)) - dev->sata_dev.ap->link.device[0].class = ATA_DEV_NONE; + sas_to_ata_dev(dev)->class = ATA_DEV_NONE; } int sas_change_queue_depth(struct scsi_device *sdev, int depth, int reason) diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index d455fa96dc8c..c4b7cd0b85e5 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -389,6 +389,11 @@ sdev_to_domain_dev(struct scsi_device *sdev) { return starget_to_domain_dev(sdev->sdev_target); } +static inline struct ata_device *sas_to_ata_dev(struct domain_device *dev) +{ + return &dev->sata_dev.ap->link.device[0]; +} + static inline struct domain_device * cmd_to_domain_dev(struct scsi_cmnd *cmd) { -- cgit v1.2.3 From 1a34c0640137eed8dabdac3a68a7a84116ac9e0d Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 21 Sep 2011 22:05:34 -0700 Subject: [SCSI] libsas: fix port->dev_list locking port->dev_list maintains a list of devices attached to a given sas root port. It needs to be mutated under a lock as contexts outside of the single-threaded-libsas-workqueue access the list via sas_find_dev_by_rphy(). Fixup locations where the list was being mutated without a lock. This is a follow-up to commit 5911e963 "[SCSI] libsas: remove expander from dev list on error", where Luben noted [1]: > 2/ We have unlocked list manipulations in sas_ex_discover_end_dev(), > sas_unregister_common_dev(), and sas_ex_discover_end_dev() Yes, I can see that and that is very unfortunate. [1]: http://marc.info/?l=linux-scsi&m=131480962006471&w=2 Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_discover.c | 13 ++++++++----- drivers/scsi/libsas/sas_expander.c | 15 +++++++++------ include/scsi/libsas.h | 2 +- 3 files changed, 18 insertions(+), 12 deletions(-) (limited to 'include/scsi/libsas.h') diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index f5831930df9b..54a5199ceb56 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -219,17 +219,20 @@ out_err2: /* ---------- Device registration and unregistration ---------- */ -static inline void sas_unregister_common_dev(struct domain_device *dev) +static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_device *dev) { sas_notify_lldd_dev_gone(dev); if (!dev->parent) dev->port->port_dev = NULL; else list_del_init(&dev->siblings); + + spin_lock_irq(&port->dev_list_lock); list_del_init(&dev->dev_list_node); + spin_unlock_irq(&port->dev_list_lock); } -void sas_unregister_dev(struct domain_device *dev) +void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) { if (dev->rphy) { sas_remove_children(&dev->rphy->dev); @@ -241,15 +244,15 @@ void sas_unregister_dev(struct domain_device *dev) kfree(dev->ex_dev.ex_phy); dev->ex_dev.ex_phy = NULL; } - sas_unregister_common_dev(dev); + sas_unregister_common_dev(port, dev); } void sas_unregister_domain_devices(struct asd_sas_port *port) { struct domain_device *dev, *n; - list_for_each_entry_safe_reverse(dev,n,&port->dev_list,dev_list_node) - sas_unregister_dev(dev); + list_for_each_entry_safe_reverse(dev, n, &port->dev_list, dev_list_node) + sas_unregister_dev(port, dev); port->port->rphy = NULL; diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index b172f1773081..88bbe8e1e844 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -754,7 +754,10 @@ static struct domain_device *sas_ex_discover_end_dev( out_list_del: sas_rphy_free(child->rphy); child->rphy = NULL; + + spin_lock_irq(&parent->port->dev_list_lock); list_del(&child->dev_list_node); + spin_unlock_irq(&parent->port->dev_list_lock); out_free: sas_port_delete(phy->port); out_err: @@ -1739,7 +1742,7 @@ out: return res; } -static void sas_unregister_ex_tree(struct domain_device *dev) +static void sas_unregister_ex_tree(struct asd_sas_port *port, struct domain_device *dev) { struct expander_device *ex = &dev->ex_dev; struct domain_device *child, *n; @@ -1748,11 +1751,11 @@ static void sas_unregister_ex_tree(struct domain_device *dev) child->gone = 1; if (child->dev_type == EDGE_DEV || child->dev_type == FANOUT_DEV) - sas_unregister_ex_tree(child); + sas_unregister_ex_tree(port, child); else - sas_unregister_dev(child); + sas_unregister_dev(port, child); } - sas_unregister_dev(dev); + sas_unregister_dev(port, dev); } static void sas_unregister_devs_sas_addr(struct domain_device *parent, @@ -1769,9 +1772,9 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent, child->gone = 1; if (child->dev_type == EDGE_DEV || child->dev_type == FANOUT_DEV) - sas_unregister_ex_tree(child); + sas_unregister_ex_tree(parent->port, child); else - sas_unregister_dev(child); + sas_unregister_dev(parent->port, child); break; } } diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index c4b7cd0b85e5..6a308d42d98f 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -656,7 +656,7 @@ int sas_discover_event(struct asd_sas_port *, enum discover_event ev); int sas_discover_sata(struct domain_device *); int sas_discover_end_dev(struct domain_device *); -void sas_unregister_dev(struct domain_device *); +void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *); void sas_init_dev(struct domain_device *); -- cgit v1.2.3