aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/isci
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/isci')
-rw-r--r--drivers/scsi/isci/host.c102
-rw-r--r--drivers/scsi/isci/host.h23
-rw-r--r--drivers/scsi/isci/init.c54
-rw-r--r--drivers/scsi/isci/phy.c13
-rw-r--r--drivers/scsi/isci/port.c148
-rw-r--r--drivers/scsi/isci/port.h6
-rw-r--r--drivers/scsi/isci/probe_roms.h4
-rw-r--r--drivers/scsi/isci/registers.h122
-rw-r--r--drivers/scsi/isci/remote_device.c109
-rw-r--r--drivers/scsi/isci/remote_device.h14
-rw-r--r--drivers/scsi/isci/request.c383
-rw-r--r--drivers/scsi/isci/request.h34
-rw-r--r--drivers/scsi/isci/task.c730
-rw-r--r--drivers/scsi/isci/task.h54
14 files changed, 884 insertions, 912 deletions
diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c
index ef46d83..e7fe9c4 100644
--- a/drivers/scsi/isci/host.c
+++ b/drivers/scsi/isci/host.c
@@ -1094,6 +1094,7 @@ static void isci_host_completion_routine(unsigned long data)
struct isci_request *request;
struct isci_request *next_request;
struct sas_task *task;
+ u16 active;
INIT_LIST_HEAD(&completed_request_list);
INIT_LIST_HEAD(&errored_request_list);
@@ -1184,6 +1185,13 @@ static void isci_host_completion_routine(unsigned long data)
}
}
+ /* the coalesence timeout doubles at each encoding step, so
+ * update it based on the ilog2 value of the outstanding requests
+ */
+ active = isci_tci_active(ihost);
+ writel(SMU_ICC_GEN_VAL(NUMBER, active) |
+ SMU_ICC_GEN_VAL(TIMER, ISCI_COALESCE_BASE + ilog2(active)),
+ &ihost->smu_registers->interrupt_coalesce_control);
}
/**
@@ -1255,6 +1263,10 @@ void isci_host_deinit(struct isci_host *ihost)
{
int i;
+ /* disable output data selects */
+ for (i = 0; i < isci_gpio_count(ihost); i++)
+ writel(SGPIO_HW_CONTROL, &ihost->scu_registers->peg0.sgpio.output_data_select[i]);
+
isci_host_change_state(ihost, isci_stopping);
for (i = 0; i < SCI_MAX_PORTS; i++) {
struct isci_port *iport = &ihost->ports[i];
@@ -1273,6 +1285,12 @@ void isci_host_deinit(struct isci_host *ihost)
spin_unlock_irq(&ihost->scic_lock);
wait_for_stop(ihost);
+
+ /* disable sgpio: where the above wait should give time for the
+ * enclosure to sample the gpios going inactive
+ */
+ writel(0, &ihost->scu_registers->peg0.sgpio.interface_control);
+
sci_controller_reset(ihost);
/* Cancel any/all outstanding port timers */
@@ -1332,7 +1350,7 @@ static void isci_user_parameters_get(struct sci_user_parameters *u)
u->stp_max_occupancy_timeout = stp_max_occ_to;
u->ssp_max_occupancy_timeout = ssp_max_occ_to;
u->no_outbound_task_timeout = no_outbound_task_to;
- u->max_number_concurrent_device_spin_up = max_concurr_spinup;
+ u->max_concurr_spinup = max_concurr_spinup;
}
static void sci_controller_initial_state_enter(struct sci_base_state_machine *sm)
@@ -1474,7 +1492,7 @@ static void sci_controller_ready_state_enter(struct sci_base_state_machine *sm)
struct isci_host *ihost = container_of(sm, typeof(*ihost), sm);
/* set the default interrupt coalescence number and timeout value. */
- sci_controller_set_interrupt_coalescence(ihost, 0x10, 250);
+ sci_controller_set_interrupt_coalescence(ihost, 0, 0);
}
static void sci_controller_ready_state_exit(struct sci_base_state_machine *sm)
@@ -1643,7 +1661,7 @@ static void sci_controller_set_default_config_parameters(struct isci_host *ihost
ihost->oem_parameters.controller.mode_type = SCIC_PORT_AUTOMATIC_CONFIGURATION_MODE;
/* Default to APC mode. */
- ihost->oem_parameters.controller.max_concurrent_dev_spin_up = 1;
+ ihost->oem_parameters.controller.max_concurr_spin_up = 1;
/* Default to no SSC operation. */
ihost->oem_parameters.controller.do_enable_ssc = false;
@@ -1769,7 +1787,8 @@ int sci_oem_parameters_validate(struct sci_oem_params *oem)
} else
return -EINVAL;
- if (oem->controller.max_concurrent_dev_spin_up > MAX_CONCURRENT_DEVICE_SPIN_UP_COUNT)
+ if (oem->controller.max_concurr_spin_up > MAX_CONCURRENT_DEVICE_SPIN_UP_COUNT ||
+ oem->controller.max_concurr_spin_up < 1)
return -EINVAL;
return 0;
@@ -1792,6 +1811,16 @@ static enum sci_status sci_oem_parameters_set(struct isci_host *ihost)
return SCI_FAILURE_INVALID_STATE;
}
+static u8 max_spin_up(struct isci_host *ihost)
+{
+ if (ihost->user_parameters.max_concurr_spinup)
+ return min_t(u8, ihost->user_parameters.max_concurr_spinup,
+ MAX_CONCURRENT_DEVICE_SPIN_UP_COUNT);
+ else
+ return min_t(u8, ihost->oem_parameters.controller.max_concurr_spin_up,
+ MAX_CONCURRENT_DEVICE_SPIN_UP_COUNT);
+}
+
static void power_control_timeout(unsigned long data)
{
struct sci_timer *tmr = (struct sci_timer *)data;
@@ -1821,8 +1850,7 @@ static void power_control_timeout(unsigned long data)
if (iphy == NULL)
continue;
- if (ihost->power_control.phys_granted_power >=
- ihost->oem_parameters.controller.max_concurrent_dev_spin_up)
+ if (ihost->power_control.phys_granted_power >= max_spin_up(ihost))
break;
ihost->power_control.requesters[i] = NULL;
@@ -1847,8 +1875,7 @@ void sci_controller_power_control_queue_insert(struct isci_host *ihost,
{
BUG_ON(iphy == NULL);
- if (ihost->power_control.phys_granted_power <
- ihost->oem_parameters.controller.max_concurrent_dev_spin_up) {
+ if (ihost->power_control.phys_granted_power < max_spin_up(ihost)) {
ihost->power_control.phys_granted_power++;
sci_phy_consume_power_handler(iphy);
@@ -2357,6 +2384,12 @@ int isci_host_init(struct isci_host *ihost)
for (i = 0; i < SCI_MAX_PHYS; i++)
isci_phy_init(&ihost->phys[i], ihost, i);
+ /* enable sgpio */
+ writel(1, &ihost->scu_registers->peg0.sgpio.interface_control);
+ for (i = 0; i < isci_gpio_count(ihost); i++)
+ writel(SGPIO_HW_CONTROL, &ihost->scu_registers->peg0.sgpio.output_data_select[i]);
+ writel(0, &ihost->scu_registers->peg0.sgpio.vendor_specific_code);
+
for (i = 0; i < SCI_MAX_REMOTE_DEVICES; i++) {
struct isci_remote_device *idev = &ihost->devices[i];
@@ -2752,3 +2785,56 @@ enum sci_task_status sci_controller_start_task(struct isci_host *ihost,
return status;
}
+
+static int sci_write_gpio_tx_gp(struct isci_host *ihost, u8 reg_index, u8 reg_count, u8 *write_data)
+{
+ int d;
+
+ /* no support for TX_GP_CFG */
+ if (reg_index == 0)
+ return -EINVAL;
+
+ for (d = 0; d < isci_gpio_count(ihost); d++) {
+ u32 val = 0x444; /* all ODx.n clear */
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ int bit = (i << 2) + 2;
+
+ bit = try_test_sas_gpio_gp_bit(to_sas_gpio_od(d, i),
+ write_data, reg_index,
+ reg_count);
+ if (bit < 0)
+ break;
+
+ /* if od is set, clear the 'invert' bit */
+ val &= ~(bit << ((i << 2) + 2));
+ }
+
+ if (i < 3)
+ break;
+ writel(val, &ihost->scu_registers->peg0.sgpio.output_data_select[d]);
+ }
+
+ /* unless reg_index is > 1, we should always be able to write at
+ * least one register
+ */
+ return d > 0;
+}
+
+int isci_gpio_write(struct sas_ha_struct *sas_ha, u8 reg_type, u8 reg_index,
+ u8 reg_count, u8 *write_data)
+{
+ struct isci_host *ihost = sas_ha->lldd_ha;
+ int written;
+
+ switch (reg_type) {
+ case SAS_GPIO_REG_TX_GP:
+ written = sci_write_gpio_tx_gp(ihost, reg_index, reg_count, write_data);
+ break;
+ default:
+ written = -EINVAL;
+ }
+
+ return written;
+}
diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h
index 062101a..ef84778 100644
--- a/drivers/scsi/isci/host.h
+++ b/drivers/scsi/isci/host.h
@@ -309,9 +309,8 @@ static inline struct isci_pci_info *to_pci_info(struct pci_dev *pdev)
}
#define for_each_isci_host(id, ihost, pdev) \
- for (id = 0, ihost = to_pci_info(pdev)->hosts[id]; \
- id < ARRAY_SIZE(to_pci_info(pdev)->hosts) && ihost; \
- ihost = to_pci_info(pdev)->hosts[++id])
+ for (id = 0; id < SCI_MAX_CONTROLLERS && \
+ (ihost = to_pci_info(pdev)->hosts[id]); id++)
static inline enum isci_status isci_host_get_state(struct isci_host *isci_host)
{
@@ -369,6 +368,9 @@ static inline struct isci_host *dev_to_ihost(struct domain_device *dev)
#define ISCI_TAG_SEQ(tag) (((tag) >> 12) & (SCI_MAX_SEQ-1))
#define ISCI_TAG_TCI(tag) ((tag) & (SCI_MAX_IO_REQUESTS-1))
+/* interrupt coalescing baseline: 9 == 3 to 5us interrupt delay per command */
+#define ISCI_COALESCE_BASE 9
+
/* expander attached sata devices require 3 rnc slots */
static inline int sci_remote_device_node_count(struct isci_remote_device *idev)
{
@@ -437,6 +439,18 @@ static inline bool is_c0(struct pci_dev *pdev)
return false;
}
+/* set hw control for 'activity', even though active enclosures seem to drive
+ * the activity led on their own. Skip setting FSENG control on 'status' due
+ * to unexpected operation and 'error' due to not being a supported automatic
+ * FSENG output
+ */
+#define SGPIO_HW_CONTROL 0x00000443
+
+static inline int isci_gpio_count(struct isci_host *ihost)
+{
+ return ARRAY_SIZE(ihost->scu_registers->peg0.sgpio.output_data_select);
+}
+
void sci_controller_post_request(struct isci_host *ihost,
u32 request);
void sci_controller_release_frame(struct isci_host *ihost,
@@ -539,4 +553,7 @@ void sci_port_configuration_agent_construct(
enum sci_status sci_port_configuration_agent_initialize(
struct isci_host *ihost,
struct sci_port_configuration_agent *port_agent);
+
+int isci_gpio_write(struct sas_ha_struct *, u8 reg_type, u8 reg_index,
+ u8 reg_count, u8 *write_data);
#endif
diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c
index 0365d58..3784388 100644
--- a/drivers/scsi/isci/init.c
+++ b/drivers/scsi/isci/init.c
@@ -59,10 +59,19 @@
#include <linux/firmware.h>
#include <linux/efi.h>
#include <asm/string.h>
+#include <scsi/scsi_host.h>
#include "isci.h"
#include "task.h"
#include "probe_roms.h"
+#define MAJ 1
+#define MIN 0
+#define BUILD 0
+#define DRV_VERSION __stringify(MAJ) "." __stringify(MIN) "." \
+ __stringify(BUILD)
+
+MODULE_VERSION(DRV_VERSION);
+
static struct scsi_transport_template *isci_transport_template;
static DEFINE_PCI_DEVICE_TABLE(isci_id_table) = {
@@ -109,10 +118,26 @@ unsigned char phy_gen = 3;
module_param(phy_gen, byte, 0);
MODULE_PARM_DESC(phy_gen, "PHY generation (1: 1.5Gbps 2: 3.0Gbps 3: 6.0Gbps)");
-unsigned char max_concurr_spinup = 1;
+unsigned char max_concurr_spinup;
module_param(max_concurr_spinup, byte, 0);
MODULE_PARM_DESC(max_concurr_spinup, "Max concurrent device spinup");
+static ssize_t isci_show_id(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = container_of(dev, typeof(*shost), shost_dev);
+ struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
+ struct isci_host *ihost = container_of(sas_ha, typeof(*ihost), sas_ha);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", ihost->id);
+}
+
+static DEVICE_ATTR(isci_id, S_IRUGO, isci_show_id, NULL);
+
+struct device_attribute *isci_host_attrs[] = {
+ &dev_attr_isci_id,
+ NULL
+};
+
static struct scsi_host_template isci_sht = {
.module = THIS_MODULE,
@@ -138,6 +163,7 @@ static struct scsi_host_template isci_sht = {
.slave_alloc = sas_slave_alloc,
.target_destroy = sas_target_destroy,
.ioctl = sas_ioctl,
+ .shost_attrs = isci_host_attrs,
};
static struct sas_domain_function_template isci_transport_ops = {
@@ -166,6 +192,9 @@ static struct sas_domain_function_template isci_transport_ops = {
/* Phy management */
.lldd_control_phy = isci_phy_control,
+
+ /* GPIO support */
+ .lldd_write_gpio = isci_gpio_write,
};
@@ -232,17 +261,6 @@ static int isci_register_sas_ha(struct isci_host *isci_host)
return 0;
}
-static ssize_t isci_show_id(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct Scsi_Host *shost = container_of(dev, typeof(*shost), shost_dev);
- struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
- struct isci_host *ihost = container_of(sas_ha, typeof(*ihost), sas_ha);
-
- return snprintf(buf, PAGE_SIZE, "%d\n", ihost->id);
-}
-
-static DEVICE_ATTR(isci_id, S_IRUGO, isci_show_id, NULL);
-
static void isci_unregister(struct isci_host *isci_host)
{
struct Scsi_Host *shost;
@@ -251,7 +269,6 @@ static void isci_unregister(struct isci_host *isci_host)
return;
shost = isci_host->shost;
- device_remove_file(&shost->shost_dev, &dev_attr_isci_id);
sas_unregister_ha(&isci_host->sas_ha);
@@ -415,14 +432,8 @@ static struct isci_host *isci_host_alloc(struct pci_dev *pdev, int id)
if (err)
goto err_shost_remove;
- err = device_create_file(&shost->shost_dev, &dev_attr_isci_id);
- if (err)
- goto err_unregister_ha;
-
return isci_host;
- err_unregister_ha:
- sas_unregister_ha(&(isci_host->sas_ha));
err_shost_remove:
scsi_remove_host(shost);
err_shost:
@@ -448,7 +459,7 @@ static int __devinit isci_pci_probe(struct pci_dev *pdev, const struct pci_devic
return -ENOMEM;
pci_set_drvdata(pdev, pci_info);
- if (efi_enabled)
+ if (efi_enabled(EFI_RUNTIME_SERVICES))
orom = isci_get_efi_var(pdev);
if (!orom)
@@ -539,7 +550,8 @@ static __init int isci_init(void)
{
int err;
- pr_info("%s: Intel(R) C600 SAS Controller Driver\n", DRV_NAME);
+ pr_info("%s: Intel(R) C600 SAS Controller Driver - version %s\n",
+ DRV_NAME, DRV_VERSION);
isci_transport_template = sas_domain_attach_transport(&isci_transport_ops);
if (!isci_transport_template)
diff --git a/drivers/scsi/isci/phy.c b/drivers/scsi/isci/phy.c
index 430fc8f..35f50c2 100644
--- a/drivers/scsi/isci/phy.c
+++ b/drivers/scsi/isci/phy.c
@@ -708,7 +708,7 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code)
__func__,
event_code);
- return SCI_FAILURE;;
+ return SCI_FAILURE;
}
return SCI_SUCCESS;
case SCI_PHY_SUB_AWAIT_SATA_SPEED_EN:
@@ -1313,6 +1313,17 @@ int isci_phy_control(struct asd_sas_phy *sas_phy,
ret = isci_port_perform_hard_reset(ihost, iport, iphy);
break;
+ case PHY_FUNC_GET_EVENTS: {
+ struct scu_link_layer_registers __iomem *r;
+ struct sas_phy *phy = sas_phy->phy;
+
+ r = iphy->link_layer_registers;
+ phy->running_disparity_error_count = readl(&r->running_disparity_error_count);
+ phy->loss_of_dword_sync_count = readl(&r->loss_of_sync_error_count);
+ phy->phy_reset_problem_count = readl(&r->phy_reset_problem_count);
+ phy->invalid_dword_count = readl(&r->invalid_dword_counter);
+ break;
+ }
default:
dev_dbg(&ihost->pdev->dev,
diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c
index 8f6f9b7..ac7f277 100644
--- a/drivers/scsi/isci/port.c
+++ b/drivers/scsi/isci/port.c
@@ -145,48 +145,15 @@ static void sci_port_bcn_enable(struct isci_port *iport)
}
}
-/* called under sci_lock to stabilize phy:port associations */
-void isci_port_bcn_enable(struct isci_host *ihost, struct isci_port *iport)
-{
- int i;
-
- clear_bit(IPORT_BCN_BLOCKED, &iport->flags);
- wake_up(&ihost->eventq);
-
- if (!test_and_clear_bit(IPORT_BCN_PENDING, &iport->flags))
- return;
-
- for (i = 0; i < ARRAY_SIZE(iport->phy_table); i++) {
- struct isci_phy *iphy = iport->phy_table[i];
-
- if (!iphy)
- continue;
-
- ihost->sas_ha.notify_port_event(&iphy->sas_phy,
- PORTE_BROADCAST_RCVD);
- break;
- }
-}
-
static void isci_port_bc_change_received(struct isci_host *ihost,
struct isci_port *iport,
struct isci_phy *iphy)
{
- if (iport && test_bit(IPORT_BCN_BLOCKED, &iport->flags)) {
- dev_dbg(&ihost->pdev->dev,
- "%s: disabled BCN; isci_phy = %p, sas_phy = %p\n",
- __func__, iphy, &iphy->sas_phy);
- set_bit(IPORT_BCN_PENDING, &iport->flags);
- atomic_inc(&iport->event);
- wake_up(&ihost->eventq);
- } else {
- dev_dbg(&ihost->pdev->dev,
- "%s: isci_phy = %p, sas_phy = %p\n",
- __func__, iphy, &iphy->sas_phy);
+ dev_dbg(&ihost->pdev->dev,
+ "%s: isci_phy = %p, sas_phy = %p\n",
+ __func__, iphy, &iphy->sas_phy);
- ihost->sas_ha.notify_port_event(&iphy->sas_phy,
- PORTE_BROADCAST_RCVD);
- }
+ ihost->sas_ha.notify_port_event(&iphy->sas_phy, PORTE_BROADCAST_RCVD);
sci_port_bcn_enable(iport);
}
@@ -278,9 +245,6 @@ static void isci_port_link_down(struct isci_host *isci_host,
/* check to see if this is the last phy on this port. */
if (isci_phy->sas_phy.port &&
isci_phy->sas_phy.port->num_phys == 1) {
- atomic_inc(&isci_port->event);
- isci_port_bcn_enable(isci_host, isci_port);
-
/* change the state for all devices on this port. The
* next task sent to this device will be returned as
* SAS_TASK_UNDELIVERED, and the scsi mid layer will
@@ -294,8 +258,8 @@ static void isci_port_link_down(struct isci_host *isci_host,
__func__, isci_device);
set_bit(IDEV_GONE, &isci_device->flags);
}
+ isci_port_change_state(isci_port, isci_stopping);
}
- isci_port_change_state(isci_port, isci_stopping);
}
/* Notify libsas of the borken link, this will trigger calls to our
@@ -350,6 +314,34 @@ static void isci_port_stop_complete(struct isci_host *ihost,
dev_dbg(&ihost->pdev->dev, "Port stop complete\n");
}
+
+static bool is_port_ready_state(enum sci_port_states state)
+{
+ switch (state) {
+ case SCI_PORT_READY:
+ case SCI_PORT_SUB_WAITING:
+ case SCI_PORT_SUB_OPERATIONAL:
+ case SCI_PORT_SUB_CONFIGURING:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* flag dummy rnc hanling when exiting a ready state */
+static void port_state_machine_change(struct isci_port *iport,
+ enum sci_port_states state)
+{
+ struct sci_base_state_machine *sm = &iport->sm;
+ enum sci_port_states old_state = sm->current_state_id;
+
+ if (is_port_ready_state(old_state) && !is_port_ready_state(state))
+ iport->ready_exit = true;
+
+ sci_change_state(sm, state);
+ iport->ready_exit = false;
+}
+
/**
* isci_port_hard_reset_complete() - This function is called by the sci core
* when the hard reset complete notification has been received.
@@ -368,6 +360,26 @@ static void isci_port_hard_reset_complete(struct isci_port *isci_port,
/* Save the status of the hard reset from the port. */
isci_port->hard_reset_status = completion_status;
+ if (completion_status != SCI_SUCCESS) {
+
+ /* The reset failed. The port state is now SCI_PORT_FAILED. */
+ if (isci_port->active_phy_mask == 0) {
+
+ /* Generate the link down now to the host, since it
+ * was intercepted by the hard reset state machine when
+ * it really happened.
+ */
+ isci_port_link_down(isci_port->isci_host,
+ &isci_port->isci_host->phys[
+ isci_port->last_active_phy],
+ isci_port);
+ }
+ /* Advance the port state so that link state changes will be
+ * noticed.
+ */
+ port_state_machine_change(isci_port, SCI_PORT_SUB_WAITING);
+
+ }
complete_all(&isci_port->hard_reset_complete);
}
@@ -657,6 +669,8 @@ void sci_port_deactivate_phy(struct isci_port *iport, struct isci_phy *iphy,
struct isci_host *ihost = iport->owning_controller;
iport->active_phy_mask &= ~(1 << iphy->phy_index);
+ if (!iport->active_phy_mask)
+ iport->last_active_phy = iphy->phy_index;
iphy->max_negotiated_speed = SAS_LINK_RATE_UNKNOWN;
@@ -683,33 +697,6 @@ static void sci_port_invalid_link_up(struct isci_port *iport, struct isci_phy *i
}
}
-static bool is_port_ready_state(enum sci_port_states state)
-{
- switch (state) {
- case SCI_PORT_READY:
- case SCI_PORT_SUB_WAITING:
- case SCI_PORT_SUB_OPERATIONAL:
- case SCI_PORT_SUB_CONFIGURING:
- return true;
- default:
- return false;
- }
-}
-
-/* flag dummy rnc hanling when exiting a ready state */
-static void port_state_machine_change(struct isci_port *iport,
- enum sci_port_states state)
-{
- struct sci_base_state_machine *sm = &iport->sm;
- enum sci_port_states old_state = sm->current_state_id;
-
- if (is_port_ready_state(old_state) && !is_port_ready_state(state))
- iport->ready_exit = true;
-
- sci_change_state(sm, state);
- iport->ready_exit = false;
-}
-
/**
* sci_port_general_link_up_handler - phy can be assigned to port?
* @sci_port: sci_port object for which has a phy that has gone link up.
@@ -1622,7 +1609,8 @@ void sci_port_construct(struct isci_port *iport, u8 index,
iport->logical_port_index = SCIC_SDS_DUMMY_PORT;
iport->physical_port_index = index;
iport->active_phy_mask = 0;
- iport->ready_exit = false;
+ iport->last_active_phy = 0;
+ iport->ready_exit = false;
iport->owning_controller = ihost;
@@ -1648,7 +1636,6 @@ void isci_port_init(struct isci_port *iport, struct isci_host *ihost, int index)
init_completion(&iport->start_complete);
iport->isci_host = ihost;
isci_port_change_state(iport, isci_freed);
- atomic_set(&iport->event, 0);
}
/**
@@ -1676,7 +1663,7 @@ int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *ipor
{
unsigned long flags;
enum sci_status status;
- int idx, ret = TMF_RESP_FUNC_COMPLETE;
+ int ret = TMF_RESP_FUNC_COMPLETE;
dev_dbg(&ihost->pdev->dev, "%s: iport = %p\n",
__func__, iport);
@@ -1697,8 +1684,13 @@ int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *ipor
"%s: iport = %p; hard reset completion\n",
__func__, iport);
- if (iport->hard_reset_status != SCI_SUCCESS)
+ if (iport->hard_reset_status != SCI_SUCCESS) {
ret = TMF_RESP_FUNC_FAILED;
+
+ dev_err(&ihost->pdev->dev,
+ "%s: iport = %p; hard reset failed (0x%x)\n",
+ __func__, iport, iport->hard_reset_status);
+ }
} else {
ret = TMF_RESP_FUNC_FAILED;
@@ -1718,18 +1710,6 @@ int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *ipor
"%s: iport = %p; hard reset failed "
"(0x%x) - driving explicit link fail for all phys\n",
__func__, iport, iport->hard_reset_status);
-
- /* Down all phys in the port. */
- spin_lock_irqsave(&ihost->scic_lock, flags);
- for (idx = 0; idx < SCI_MAX_PHYS; ++idx) {
- struct isci_phy *iphy = iport->phy_table[idx];
-
- if (!iphy)
- continue;
- sci_phy_stop(iphy);
- sci_phy_start(iphy);
- }
- spin_unlock_irqrestore(&ihost->scic_lock, flags);
}
return ret;
}
diff --git a/drivers/scsi/isci/port.h b/drivers/scsi/isci/port.h
index b50ecd4..cb5ffbc 100644
--- a/drivers/scsi/isci/port.h
+++ b/drivers/scsi/isci/port.h
@@ -77,7 +77,6 @@ enum isci_status {
/**
* struct isci_port - isci direct attached sas port object
- * @event: counts bcns and port stop events (for bcn filtering)
* @ready_exit: several states constitute 'ready'. When exiting ready we
* need to take extra port-teardown actions that are
* skipped when exiting to another 'ready' state.
@@ -92,10 +91,6 @@ enum isci_status {
*/
struct isci_port {
enum isci_status status;
- #define IPORT_BCN_BLOCKED 0
- #define IPORT_BCN_PENDING 1
- unsigned long flags;
- atomic_t event;
struct isci_host *isci_host;
struct asd_sas_port sas_port;
struct list_head remote_dev_list;
@@ -109,6 +104,7 @@ struct isci_port {
u8 logical_port_index;
u8 physical_port_index;
u8 active_phy_mask;
+ u8 last_active_phy;
u16 reserved_rni;
u16 reserved_tag;
u32 started_request_count;
diff --git a/drivers/scsi/isci/probe_roms.h b/drivers/scsi/isci/probe_roms.h
index dc007e6..2c75248 100644
--- a/drivers/scsi/isci/probe_roms.h
+++ b/drivers/scsi/isci/probe_roms.h
@@ -112,7 +112,7 @@ struct sci_user_parameters {
* This field specifies the maximum number of direct attached devices
* that can have power supplied to them simultaneously.
*/
- u8 max_number_concurrent_device_spin_up;
+ u8 max_concurr_spinup;
/**
* This field specifies the number of seconds to allow a phy to consume
@@ -219,7 +219,7 @@ struct sci_bios_oem_param_block_hdr {
struct sci_oem_params {
struct {
uint8_t mode_type;
- uint8_t max_concurrent_dev_spin_up;
+ uint8_t max_concurr_spin_up;
uint8_t do_enable_ssc;
uint8_t reserved;
} controller;
diff --git a/drivers/scsi/isci/registers.h b/drivers/scsi/isci/registers.h
index 00afc73..eaa541a 100644
--- a/drivers/scsi/isci/registers.h
+++ b/drivers/scsi/isci/registers.h
@@ -875,122 +875,6 @@ struct scu_iit_entry {
#define SCU_PTSxSR_GEN_BIT(name) \
SCU_GEN_BIT(SCU_PTSG_PORT_TASK_SCHEDULER_STATUS_ ## name)
-
-/*
- * *****************************************************************************
- * * SGPIO Register shift and mask values
- * ***************************************************************************** */
-#define SCU_SGPIO_CONTROL_SGPIO_ENABLE_SHIFT (0)
-#define SCU_SGPIO_CONTROL_SGPIO_ENABLE_MASK (0x00000001)
-#define SCU_SGPIO_CONTROL_SGPIO_SERIAL_CLOCK_SELECT_SHIFT (1)
-#define SCU_SGPIO_CONTROL_SGPIO_SERIAL_CLOCK_SELECT_MASK (0x00000002)
-#define SCU_SGPIO_CONTROL_SGPIO_SERIAL_SHIFT_WIDTH_SELECT_SHIFT (2)
-#define SCU_SGPIO_CONTROL_SGPIO_SERIAL_SHIFT_WIDTH_SELECT_MASK (0x00000004)
-#define SCU_SGPIO_CONTROL_SGPIO_TEST_BIT_SHIFT (15)
-#define SCU_SGPIO_CONTROL_SGPIO_TEST_BIT_MASK (0x00008000)
-#define SCU_SGPIO_CONTROL_SGPIO_RESERVED_MASK (0xFFFF7FF8)
-
-#define SCU_SGICRx_GEN_BIT(name) \
- SCU_GEN_BIT(SCU_SGPIO_CONTROL_SGPIO_ ## name)
-
-#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R0_SHIFT (0)
-#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R0_MASK (0x0000000F)
-#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R1_SHIFT (4)
-#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R1_MASK (0x000000F0)
-#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R2_SHIFT (8)
-#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R2_MASK (0x00000F00)
-#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R3_SHIFT (12)
-#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R3_MASK (0x0000F000)
-#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_RESERVED_MASK (0xFFFF0000)
-
-#define SCU_SGPBRx_GEN_VAL(name, value) \
- SCU_GEN_VALUE(SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_ ## name, value)
-
-#define SCU_SGPIO_START_DRIVE_LOWER_R0_SHIFT (0)
-#define SCU_SGPIO_START_DRIVE_LOWER_R0_MASK (0x00000003)
-#define SCU_SGPIO_START_DRIVE_LOWER_R1_SHIFT (4)
-#define SCU_SGPIO_START_DRIVE_LOWER_R1_MASK (0x00000030)
-#define SCU_SGPIO_START_DRIVE_LOWER_R2_SHIFT (8)
-#define SCU_SGPIO_START_DRIVE_LOWER_R2_MASK (0x00000300)
-#define SCU_SGPIO_START_DRIVE_LOWER_R3_SHIFT (12)
-#define SCU_SGPIO_START_DRIVE_LOWER_R3_MASK (0x00003000)
-#define SCU_SGPIO_START_DRIVE_LOWER_RESERVED_MASK (0xFFFF8888)
-
-#define SCU_SGSDLRx_GEN_VAL(name, value) \
- SCU_GEN_VALUE(SCU_SGPIO_START_DRIVE_LOWER_ ## name, value)
-
-#define SCU_SGPIO_START_DRIVE_UPPER_R0_SHIFT (0)
-#define SCU_SGPIO_START_DRIVE_UPPER_R0_MASK (0x00000003)
-#define SCU_SGPIO_START_DRIVE_UPPER_R1_SHIFT (4)
-#define SCU_SGPIO_START_DRIVE_UPPER_R1_MASK (0x00000030)
-#define SCU_SGPIO_START_DRIVE_UPPER_R2_SHIFT (8)
-#define SCU_SGPIO_START_DRIVE_UPPER_R2_MASK (0x00000300)
-#define SCU_SGPIO_START_DRIVE_UPPER_R3_SHIFT (12)
-#define SCU_SGPIO_START_DRIVE_UPPER_R3_MASK (0x00003000)
-#define SCU_SGPIO_START_DRIVE_UPPER_RESERVED_MASK (0xFFFF8888)
-
-#define SCU_SGSDURx_GEN_VAL(name, value) \
- SCU_GEN_VALUE(SCU_SGPIO_START_DRIVE_LOWER_ ## name, value)
-
-#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D0_SHIFT (0)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D0_MASK (0x00000003)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D1_SHIFT (4)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D1_MASK (0x00000030)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D2_SHIFT (8)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D2_MASK (0x00000300)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D3_SHIFT (12)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D3_MASK (0x00003000)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_RESERVED_MASK (0xFFFF8888)
-
-#define SCU_SGSIDLRx_GEN_VAL(name, value) \
- SCU_GEN_VALUE(SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_ ## name, value)
-
-#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D0_SHIFT (0)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D0_MASK (0x00000003)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D1_SHIFT (4)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D1_MASK (0x00000030)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D2_SHIFT (8)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D2_MASK (0x00000300)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D3_SHIFT (12)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D3_MASK (0x00003000)
-#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_RESERVED_MASK (0xFFFF8888)
-
-#define SCU_SGSIDURx_GEN_VAL(name, value) \
- SCU_GEN_VALUE(SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_ ## name, value)
-
-#define SCU_SGPIO_VENDOR_SPECIFIC_CODE_SHIFT (0)
-#define SCU_SGPIO_VENDOR_SPECIFIC_CODE_MASK (0x0000000F)
-#define SCU_SGPIO_VENDOR_SPECIFIC_CODE_RESERVED_MASK (0xFFFFFFF0)
-
-#define SCU_SGVSCR_GEN_VAL(value) \
- SCU_GEN_VALUE(SCU_SGPIO_VENDOR_SPECIFIC_CODE ## name, value)
-
-#define SCU_SGPIO_OUPUT_DATA_SELECT_INPUT_DATA0_SHIFT (0)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_INPUT_DATA0_MASK (0x00000003)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_INVERT_INPUT_DATA0_SHIFT (2)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_INVERT_INPUT_DATA0_MASK (0x00000004)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_JOG_ENABLE_DATA0_SHIFT (3)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_JOG_ENABLE_DATA0_MASK (0x00000008)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_INPUT_DATA1_SHIFT (4)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_INPUT_DATA1_MASK (0x00000030)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_INVERT_INPUT_DATA1_SHIFT (6)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_INVERT_INPUT_DATA1_MASK (0x00000040)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_JOG_ENABLE_DATA1_SHIFT (7)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_JOG_ENABLE_DATA1_MASK (0x00000080)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_INPUT_DATA2_SHIFT (8)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_INPUT_DATA2_MASK (0x00000300)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_INVERT_INPUT_DATA2_SHIFT (10)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_INVERT_INPUT_DATA2_MASK (0x00000400)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_JOG_ENABLE_DATA2_SHIFT (11)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_JOG_ENABLE_DATA2_MASK (0x00000800)
-#define SCU_SGPIO_OUPUT_DATA_SELECT_RESERVED_MASK (0xFFFFF000)
-
-#define SCU_SGODSR_GEN_VAL(name, value) \
- SCU_GEN_VALUE(SCU_SGPIO_OUPUT_DATA_SELECT_ ## name, value)
-
-#define SCU_SGODSR_GEN_BIT(name) \
- SCU_GEN_BIT(SCU_SGPIO_OUPUT_DATA_SELECT_ ## name)
-
/*
* *****************************************************************************
* * SMU Registers
@@ -1529,10 +1413,12 @@ struct scu_sgpio_registers {
u32 serial_input_upper;
/* 0x0018 SGPIO_SGVSCR */
u32 vendor_specific_code;
+/* 0x001C Reserved */
+ u32 reserved_001c;
/* 0x0020 SGPIO_SGODSR */
- u32 ouput_data_select[8];
+ u32 output_data_select[8];
/* Remainder of memory space 256 bytes */
- u32 reserved_1444_14ff[0x31];
+ u32 reserved_1444_14ff[0x30];
};
diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c
index b6e6368..b207cd3 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,
@@ -1414,88 +1438,3 @@ int isci_remote_device_found(struct domain_device *domain_dev)
return status == SCI_SUCCESS ? 0 : -ENODEV;
}
-/**
- * isci_device_is_reset_pending() - This function will check if there is any
- * pending reset condition on the device.
- * @request: This parameter is the isci_device object.
- *
- * true if there is a reset pending for the device.
- */
-bool isci_device_is_reset_pending(
- struct isci_host *isci_host,
- struct isci_remote_device *isci_device)
-{
- struct isci_request *isci_request;
- struct isci_request *tmp_req;
- bool reset_is_pending = false;
- unsigned long flags;
-
- dev_dbg(&isci_host->pdev->dev,
- "%s: isci_device = %p\n", __func__, isci_device);
-
- spin_lock_irqsave(&isci_host->scic_lock, flags);
-
- /* Check for reset on all pending requests. */
- list_for_each_entry_safe(isci_request, tmp_req,
- &isci_device->reqs_in_process, dev_node) {
- dev_dbg(&isci_host->pdev->dev,
- "%s: isci_device = %p request = %p\n",
- __func__, isci_device, isci_request);
-
- if (isci_request->ttype == io_task) {
- struct sas_task *task = isci_request_access_task(
- isci_request);
-
- spin_lock(&task->task_state_lock);
- if (task->task_state_flags & SAS_TASK_NEED_DEV_RESET)
- reset_is_pending = true;
- spin_unlock(&task->task_state_lock);
- }
- }
-
- spin_unlock_irqrestore(&isci_host->scic_lock, flags);
-
- dev_dbg(&isci_host->pdev->dev,
- "%s: isci_device = %p reset_is_pending = %d\n",
- __func__, isci_device, reset_is_pending);
-
- return reset_is_pending;
-}
-
-/**
- * isci_device_clear_reset_pending() - This function will clear if any pending
- * reset condition flags on the device.
- * @request: This parameter is the isci_device object.
- *
- * true if there is a reset pending for the device.
- */
-void isci_device_clear_reset_pending(struct isci_host *ihost, struct isci_remote_device *idev)
-{
- struct isci_request *isci_request;
- struct isci_request *tmp_req;
- unsigned long flags = 0;
-
- dev_dbg(&ihost->pdev->dev, "%s: idev=%p, ihost=%p\n",
- __func__, idev, ihost);
-
- spin_lock_irqsave(&ihost->scic_lock, flags);
-
- /* Clear reset pending on all pending requests. */
- list_for_each_entry_safe(isci_request, tmp_req,
- &idev->reqs_in_process, dev_node) {
- dev_dbg(&ihost->pdev->dev, "%s: idev = %p request = %p\n",
- __func__, idev, isci_request);
-
- if (isci_request->ttype == io_task) {
-
- unsigned long flags2;
- struct sas_task *task = isci_request_access_task(
- isci_request);
-
- spin_lock_irqsave(&task->task_state_lock, flags2);
- task->task_state_flags &= ~SAS_TASK_NEED_DEV_RESET;
- spin_unlock_irqrestore(&task->task_state_lock, flags2);
- }
- }
- spin_unlock_irqrestore(&ihost->scic_lock, flags);
-}
diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h
index 57ccfc3..483ee50 100644
--- a/drivers/scsi/isci/remote_device.h
+++ b/drivers/scsi/isci/remote_device.h
@@ -132,10 +132,7 @@ void isci_remote_device_nuke_requests(struct isci_host *ihost,
struct isci_remote_device *idev);
void isci_remote_device_gone(struct domain_device *domain_dev);
int isci_remote_device_found(struct domain_device *domain_dev);
-bool isci_device_is_reset_pending(struct isci_host *ihost,
- struct isci_remote_device *idev);
-void isci_device_clear_reset_pending(struct isci_host *ihost,
- struct isci_remote_device *idev);
+
/**
* sci_remote_device_stop() - This method will stop both transmission and
* reception of link activity for the supplied remote device. This method
@@ -244,6 +241,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 b70f999..f3cbecc 100644
--- a/drivers/scsi/isci/request.c
+++ b/drivers/scsi/isci/request.c
@@ -191,7 +191,7 @@ static void sci_task_request_build_ssp_task_iu(struct isci_request *ireq)
task_iu->task_func = isci_tmf->tmf_code;
task_iu->task_tag =
- (ireq->ttype == tmf_task) ?
+ (test_bit(IREQ_TMF, &ireq->flags)) ?
isci_tmf->io_tag :
SCI_CONTROLLER_INVALID_IO_TAG;
}
@@ -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,9 +513,10 @@ 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) {
+ if (test_bit(IREQ_TMF, &ireq->flags)) {
struct isci_tmf *tmf = isci_request_access_tmf(ireq);
if (tmf->tmf_code == isci_tmf_sata_srst_high ||
@@ -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);
@@ -602,7 +632,7 @@ enum sci_status sci_task_request_construct_sata(struct isci_request *ireq)
enum sci_status status = SCI_SUCCESS;
/* check for management protocols */
- if (ireq->ttype == tmf_task) {
+ if (test_bit(IREQ_TMF, &ireq->flags)) {
struct isci_tmf *tmf = isci_request_access_tmf(ireq);
if (tmf->tmf_code == isci_tmf_sata_srst_high ||
@@ -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
@@ -1900,10 +2074,9 @@ static enum sci_status stp_request_udma_await_tc_event(struct isci_request *ireq
case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_MAX_PLD_ERR):
case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_LL_R_ERR):
case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_CMD_LL_R_ERR):
- case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_CRC_ERR):
sci_remote_device_suspend(ireq->target_device,
SCU_EVENT_SPECIFIC(SCU_NORMALIZE_COMPLETION_STATUS(completion_code)));
- /* Fall through to the default case */
+ /* Fall through to the default case */
default:
/* All other completion status cause the IO to be complete. */
ireq->scu_status = SCU_NORMALIZE_COMPLETION_STATUS(completion_code);
@@ -1966,6 +2139,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 +2296,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 "
@@ -2340,14 +2630,8 @@ static void isci_task_save_for_upper_layer_completion(
switch (task_notification_selection) {
case isci_perform_normal_io_completion:
-
/* Normal notification (task_done) */
- dev_dbg(&host->pdev->dev,
- "%s: Normal - task = %p, response=%d (%d), status=%d (%d)\n",
- __func__,
- task,
- task->task_status.resp, response,
- task->task_status.stat, status);
+
/* Add to the completed list. */
list_add(&request->completed_node,
&host->requests_to_complete);
@@ -2360,13 +2644,6 @@ static void isci_task_save_for_upper_layer_completion(
/* No notification to libsas because this request is
* already in the abort path.
*/
- dev_dbg(&host->pdev->dev,
- "%s: Aborted - task = %p, response=%d (%d), status=%d (%d)\n",
- __func__,
- task,
- task->task_status.resp, response,
- task->task_status.stat, status);
-
/* Wake up whatever process was waiting for this
* request to complete.
*/
@@ -2383,30 +2660,22 @@ static void isci_task_save_for_upper_layer_completion(
case isci_perform_error_io_completion:
/* Use sas_task_abort */
- dev_dbg(&host->pdev->dev,
- "%s: Error - task = %p, response=%d (%d), status=%d (%d)\n",
- __func__,
- task,
- task->task_status.resp, response,
- task->task_status.stat, status);
/* Add to the aborted list. */
list_add(&request->completed_node,
&host->requests_to_errorback);
break;
default:
- dev_dbg(&host->pdev->dev,
- "%s: Unknown - task = %p, response=%d (%d), status=%d (%d)\n",
- __func__,
- task,
- task->task_status.resp, response,
- task->task_status.stat, status);
-
/* Add to the error to libsas list. */
list_add(&request->completed_node,
&host->requests_to_errorback);
break;
}
+ dev_dbg(&host->pdev->dev,
+ "%s: %d - task = %p, response=%d (%d), status=%d (%d)\n",
+ __func__, task_notification_selection, task,
+ (task) ? task->task_status.resp : 0, response,
+ (task) ? task->task_status.stat : 0, status);
}
static void isci_process_stp_response(struct sas_task *task, struct dev_to_host_fis *fis)
@@ -2423,6 +2692,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;
@@ -2436,9 +2707,9 @@ static void isci_request_io_request_complete(struct isci_host *ihost,
struct sas_task *task = isci_request_access_task(request);
struct ssp_response_iu *resp_iu;
unsigned long task_flags;
- struct isci_remote_device *idev = isci_lookup_device(task->dev);
- enum service_response response = SAS_TASK_UNDELIVERED;
- enum exec_status status = SAS_ABORTED_TASK;
+ struct isci_remote_device *idev = request->target_device;
+ enum service_response response = SAS_TASK_UNDELIVERED;
+ enum exec_status status = SAS_ABORTED_TASK;
enum isci_request_status request_status;
enum isci_completion_selection complete_to_host
= isci_perform_normal_io_completion;
@@ -2769,7 +3040,6 @@ static void isci_request_io_request_complete(struct isci_host *ihost,
/* complete the io request to the core. */
sci_controller_complete_io(ihost, request->target_device, request);
- isci_put_device(idev);
/* set terminated handle so it cannot be completed or
* terminated again, and to cause any calls into abort
@@ -2782,37 +3052,42 @@ 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
* requests would simplify the driver
*/
- task = ireq->ttype == io_task ? isci_request_access_task(ireq) : NULL;
+ task = (test_bit(IREQ_TMF, &ireq->flags)) ? NULL : isci_request_access_task(ireq);
/* all unaccelerated request types (non ssp or ncq) handled with
* 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 +3179,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,
},
@@ -3263,7 +3542,7 @@ static struct isci_request *isci_io_request_from_tag(struct isci_host *ihost,
ireq = isci_request_from_tag(ihost, tag);
ireq->ttype_ptr.io_task_ptr = task;
- ireq->ttype = io_task;
+ clear_bit(IREQ_TMF, &ireq->flags);
task->lldd_task = ireq;
return ireq;
@@ -3277,7 +3556,7 @@ struct isci_request *isci_tmf_request_from_tag(struct isci_host *ihost,
ireq = isci_request_from_tag(ihost, tag);
ireq->ttype_ptr.tmf_task_ptr = isci_tmf;
- ireq->ttype = tmf_task;
+ set_bit(IREQ_TMF, &ireq->flags);
return ireq;
}
diff --git a/drivers/scsi/isci/request.h b/drivers/scsi/isci/request.h
index 58d70b6..be38933 100644
--- a/drivers/scsi/isci/request.h
+++ b/drivers/scsi/isci/request.h
@@ -77,11 +77,6 @@ enum isci_request_status {
dead = 0x07
};
-enum task_type {
- io_task = 0,
- tmf_task = 1
-};
-
enum sci_request_protocol {
SCIC_NO_PROTOCOL,
SCIC_SMP_PROTOCOL,
@@ -96,7 +91,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 +101,6 @@ struct isci_stp_request {
u8 set;
u32 offset;
} sgl;
- u32 device_cdb_len;
};
struct isci_request {
@@ -118,7 +111,6 @@ struct isci_request {
#define IREQ_ACTIVE 3
unsigned long flags;
/* XXX kill ttype and ttype_ptr, allocate full sas_task */
- enum task_type ttype;
union ttype_ptr_union {
struct sas_task *io_task_ptr; /* When ttype==io_task */
struct isci_tmf *tmf_task_ptr; /* When ttype==tmf_task */
@@ -249,6 +241,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
* initial frame (i.e. command, task, etc.).
diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c
index d6bcdd0..e294d11 100644
--- a/drivers/scsi/isci/task.c
+++ b/drivers/scsi/isci/task.c
@@ -212,16 +212,27 @@ int isci_task_execute_task(struct sas_task *task, int num, gfp_t gfp_flags)
task->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
spin_unlock_irqrestore(&task->task_state_lock, flags);
- /* Indicate QUEUE_FULL so that the scsi
- * midlayer retries. if the request
- * failed for remote device reasons,
- * it gets returned as
- * SAS_TASK_UNDELIVERED next time
- * through.
- */
- isci_task_refuse(ihost, task,
- SAS_TASK_COMPLETE,
- SAS_QUEUE_FULL);
+ if (test_bit(IDEV_GONE, &idev->flags)) {
+
+ /* Indicate that the device
+ * is gone.
+ */
+ isci_task_refuse(ihost, task,
+ SAS_TASK_UNDELIVERED,
+ SAS_DEVICE_UNKNOWN);
+ } else {
+ /* Indicate QUEUE_FULL so that
+ * the scsi midlayer retries.
+ * If the request failed for
+ * remote device reasons, it
+ * gets returned as
+ * SAS_TASK_UNDELIVERED next
+ * time through.
+ */
+ isci_task_refuse(ihost, task,
+ SAS_TASK_COMPLETE,
+ SAS_QUEUE_FULL);
+ }
}
}
}
@@ -243,7 +254,7 @@ static enum sci_status isci_sata_management_task_request_build(struct isci_reque
struct isci_tmf *isci_tmf;
enum sci_status status;
- if (tmf_task != ireq->ttype)
+ if (!test_bit(IREQ_TMF, &ireq->flags))
return SCI_FAILURE;
isci_tmf = isci_request_access_tmf(ireq);
@@ -327,6 +338,60 @@ static struct isci_request *isci_task_request_build(struct isci_host *ihost,
return ireq;
}
+/**
+* isci_request_mark_zombie() - This function must be called with scic_lock held.
+*/
+static void isci_request_mark_zombie(struct isci_host *ihost, struct isci_request *ireq)
+{
+ struct completion *tmf_completion = NULL;
+ struct completion *req_completion;
+
+ /* Set the request state to "dead". */
+ ireq->status = dead;
+
+ req_completion = ireq->io_request_completion;
+ ireq->io_request_completion = NULL;
+
+ if (test_bit(IREQ_TMF, &ireq->flags)) {
+ /* Break links with the TMF request. */
+ struct isci_tmf *tmf = isci_request_access_tmf(ireq);
+
+ /* In the case where a task request is dying,
+ * the thread waiting on the complete will sit and
+ * timeout unless we wake it now. Since the TMF
+ * has a default error status, complete it here
+ * to wake the waiting thread.
+ */
+ if (tmf) {
+ tmf_completion = tmf->complete;
+ tmf->complete = NULL;
+ }
+ ireq->ttype_ptr.tmf_task_ptr = NULL;
+ dev_dbg(&ihost->pdev->dev, "%s: tmf_code %d, managed tag %#x\n",
+ __func__, tmf->tmf_code, tmf->io_tag);
+ } else {
+ /* Break links with the sas_task - the callback is done
+ * elsewhere.
+ */
+ struct sas_task *task = isci_request_access_task(ireq);
+
+ if (task)
+ task->lldd_task = NULL;
+
+ ireq->ttype_ptr.io_task_ptr = NULL;
+ }
+
+ dev_warn(&ihost->pdev->dev, "task context unrecoverable (tag: %#x)\n",
+ ireq->io_tag);
+
+ /* Don't force waiting threads to timeout. */
+ if (req_completion)
+ complete(req_completion);
+
+ if (tmf_completion != NULL)
+ complete(tmf_completion);
+}
+
static int isci_task_execute_tmf(struct isci_host *ihost,
struct isci_remote_device *idev,
struct isci_tmf *tmf, unsigned long timeout_ms)
@@ -364,6 +429,7 @@ static int isci_task_execute_tmf(struct isci_host *ihost,
/* Assign the pointer to the TMF's completion kernel wait structure. */
tmf->complete = &completion;
+ tmf->status = SCI_FAILURE_TIMEOUT;
ireq = isci_task_request_build(ihost, idev, tag, tmf);
if (!ireq)
@@ -399,18 +465,35 @@ static int isci_task_execute_tmf(struct isci_host *ihost,
msecs_to_jiffies(timeout_ms));
if (timeleft == 0) {
+ /* The TMF did not complete - this could be because
+ * of an unplug. Terminate the TMF request now.
+ */
spin_lock_irqsave(&ihost->scic_lock, flags);
if (tmf->cb_state_func != NULL)
- tmf->cb_state_func(isci_tmf_timed_out, tmf, tmf->cb_data);
+ tmf->cb_state_func(isci_tmf_timed_out, tmf,
+ tmf->cb_data);
- sci_controller_terminate_request(ihost,
- idev,
- ireq);
+ sci_controller_terminate_request(ihost, idev, ireq);
spin_unlock_irqrestore(&ihost->scic_lock, flags);
- wait_for_completion(tmf->complete);
+ timeleft = wait_for_completion_timeout(
+ &completion,
+ msecs_to_jiffies(ISCI_TERMINATION_TIMEOUT_MSEC));
+
+ if (!timeleft) {
+ /* Strange condition - the termination of the TMF
+ * request timed-out.
+ */
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+
+ /* If the TMF status has not changed, kill it. */
+ if (tmf->status == SCI_FAILURE_TIMEOUT)
+ isci_request_mark_zombie(ihost, ireq);
+
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+ }
}
isci_print_tmf(tmf);
@@ -501,48 +584,17 @@ static enum isci_request_status isci_task_validate_request_to_abort(
return old_state;
}
-/**
-* isci_request_cleanup_completed_loiterer() - This function will take care of
-* the final cleanup on any request which has been explicitly terminated.
-* @isci_host: This parameter specifies the ISCI host object
-* @isci_device: This is the device to which the request is pending.
-* @isci_request: This parameter specifies the terminated request object.
-* @task: This parameter is the libsas I/O request.
-*/
-static void isci_request_cleanup_completed_loiterer(
- struct isci_host *isci_host,
- struct isci_remote_device *isci_device,
- struct isci_request *isci_request,
- struct sas_task *task)
+static int isci_request_is_dealloc_managed(enum isci_request_status stat)
{
- unsigned long flags;
-
- dev_dbg(&isci_host->pdev->dev,
- "%s: isci_device=%p, request=%p, task=%p\n",
- __func__, isci_device, isci_request, task);
-
- if (task != NULL) {
-
- spin_lock_irqsave(&task->task_state_lock, flags);
- task->lldd_task = NULL;
-
- task->task_state_flags &= ~SAS_TASK_NEED_DEV_RESET;
-
- isci_set_task_doneflags(task);
-
- /* If this task is not in the abort path, call task_done. */
- if (!(task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
-
- spin_unlock_irqrestore(&task->task_state_lock, flags);
- task->task_done(task);
- } else
- spin_unlock_irqrestore(&task->task_state_lock, flags);
- }
-
- if (isci_request != NULL) {
- spin_lock_irqsave(&isci_host->scic_lock, flags);
- list_del_init(&isci_request->dev_node);
- spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+ switch (stat) {
+ case aborted:
+ case aborting:
+ case terminating:
+ case completed:
+ case dead:
+ return true;
+ default:
+ return false;
}
}
@@ -563,11 +615,9 @@ static void isci_terminate_request_core(struct isci_host *ihost,
enum sci_status status = SCI_SUCCESS;
bool was_terminated = false;
bool needs_cleanup_handling = false;
- enum isci_request_status request_status;
unsigned long flags;
unsigned long termination_completed = 1;
struct completion *io_request_completion;
- struct sas_task *task;
dev_dbg(&ihost->pdev->dev,
"%s: device = %p; request = %p\n",
@@ -577,10 +627,6 @@ static void isci_terminate_request_core(struct isci_host *ihost,
io_request_completion = isci_request->io_request_completion;
- task = (isci_request->ttype == io_task)
- ? isci_request_access_task(isci_request)
- : NULL;
-
/* Note that we are not going to control
* the target to abort the request.
*/
@@ -619,42 +665,27 @@ static void isci_terminate_request_core(struct isci_host *ihost,
__func__, isci_request, io_request_completion);
/* Wait here for the request to complete. */
- #define TERMINATION_TIMEOUT_MSEC 500
termination_completed
= wait_for_completion_timeout(
io_request_completion,
- msecs_to_jiffies(TERMINATION_TIMEOUT_MSEC));
+ msecs_to_jiffies(ISCI_TERMINATION_TIMEOUT_MSEC));
if (!termination_completed) {
/* The request to terminate has timed out. */
- spin_lock_irqsave(&ihost->scic_lock,
- flags);
+ spin_lock_irqsave(&ihost->scic_lock, flags);
/* Check for state changes. */
- if (!test_bit(IREQ_TERMINATED, &isci_request->flags)) {
+ if (!test_bit(IREQ_TERMINATED,
+ &isci_request->flags)) {
/* The best we can do is to have the
* request die a silent death if it
* ever really completes.
- *
- * Set the request state to "dead",
- * and clear the task pointer so that
- * an actual completion event callback
- * doesn't do anything.
*/
- isci_request->status = dead;
- isci_request->io_request_completion
- = NULL;
-
- if (isci_request->ttype == io_task) {
-
- /* Break links with the
- * sas_task.
- */
- isci_request->ttype_ptr.io_task_ptr
- = NULL;
- }
+ isci_request_mark_zombie(ihost,
+ isci_request);
+ needs_cleanup_handling = true;
} else
termination_completed = 1;
@@ -691,29 +722,28 @@ static void isci_terminate_request_core(struct isci_host *ihost,
* needs to be detached and freed here.
*/
spin_lock_irqsave(&isci_request->state_lock, flags);
- request_status = isci_request->status;
-
- if ((isci_request->ttype == io_task) /* TMFs are in their own thread */
- && ((request_status == aborted)
- || (request_status == aborting)
- || (request_status == terminating)
- || (request_status == completed)
- || (request_status == dead)
- )
- ) {
-
- /* The completion routine won't free a request in
- * the aborted/aborting/etc. states, so we do
- * it here.
- */
- needs_cleanup_handling = true;
- }
+
+ needs_cleanup_handling
+ = isci_request_is_dealloc_managed(
+ isci_request->status);
+
spin_unlock_irqrestore(&isci_request->state_lock, flags);
}
- if (needs_cleanup_handling)
- isci_request_cleanup_completed_loiterer(
- ihost, idev, isci_request, task);
+ if (needs_cleanup_handling) {
+
+ dev_dbg(&ihost->pdev->dev,
+ "%s: cleanup isci_device=%p, request=%p\n",
+ __func__, idev, isci_request);
+
+ if (isci_request != NULL) {
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ isci_free_tag(ihost, isci_request->io_tag);
+ isci_request_change_state(isci_request, unallocated);
+ list_del_init(&isci_request->dev_node);
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+ }
+ }
}
}
@@ -772,7 +802,9 @@ void isci_terminate_pending_requests(struct isci_host *ihost,
dev_dbg(&ihost->pdev->dev,
"%s: idev=%p request=%p; task=%p old_state=%d\n",
__func__, idev, ireq,
- ireq->ttype == io_task ? isci_request_access_task(ireq) : NULL,
+ (!test_bit(IREQ_TMF, &ireq->flags)
+ ? isci_request_access_task(ireq)
+ : NULL),
old_state);
/* If the old_state is started:
@@ -889,22 +921,14 @@ int isci_task_lu_reset(struct domain_device *domain_device, u8 *lun)
"%s: domain_device=%p, isci_host=%p; isci_device=%p\n",
__func__, domain_device, isci_host, isci_device);
- if (isci_device)
- set_bit(IDEV_EH, &isci_device->flags);
+ if (!isci_device) {
+ /* If the device is gone, stop the escalations. */
+ dev_dbg(&isci_host->pdev->dev, "%s: No dev\n", __func__);
- /* If there is a device reset pending on any request in the
- * device's list, fail this LUN reset request in order to
- * escalate to the device reset.
- */
- if (!isci_device ||
- isci_device_is_reset_pending(isci_host, isci_device)) {
- dev_dbg(&isci_host->pdev->dev,
- "%s: No dev (%p), or "
- "RESET PENDING: domain_device=%p\n",
- __func__, isci_device, domain_device);
- ret = TMF_RESP_FUNC_FAILED;
+ ret = TMF_RESP_FUNC_COMPLETE;
goto out;
}
+ set_bit(IDEV_EH, &isci_device->flags);
/* Send the task management part of the reset. */
if (sas_protocol_ata(domain_device->tproto)) {
@@ -1013,7 +1037,8 @@ int isci_task_abort_task(struct sas_task *task)
struct isci_tmf tmf;
int ret = TMF_RESP_FUNC_FAILED;
unsigned long flags;
- bool any_dev_reset = false;
+ int perform_termination = 0;
+ int target_done_already = 0;
/* Get the isci_request reference from the task. Note that
* this check does not depend on the pending request list
@@ -1028,96 +1053,43 @@ int isci_task_abort_task(struct sas_task *task)
/* If task is already done, the request isn't valid */
if (!(task->task_state_flags & SAS_TASK_STATE_DONE) &&
(task->task_state_flags & SAS_TASK_AT_INITIATOR) &&
- old_request)
+ old_request) {
isci_device = isci_lookup_device(task->dev);
-
+ target_done_already = test_bit(IREQ_COMPLETE_IN_TARGET,
+ &old_request->flags);
+ }
spin_unlock(&task->task_state_lock);
spin_unlock_irqrestore(&isci_host->scic_lock, flags);
dev_dbg(&isci_host->pdev->dev,
- "%s: task = %p\n", __func__, task);
-
- if (!isci_device || !old_request)
- goto out;
-
- set_bit(IDEV_EH, &isci_device->flags);
-
- /* This version of the driver will fail abort requests for
- * SATA/STP. Failing the abort request this way will cause the
- * SCSI error handler thread to escalate to LUN reset
- */
- if (sas_protocol_ata(task->task_proto)) {
- dev_dbg(&isci_host->pdev->dev,
- " task %p is for a STP/SATA device;"
- " returning TMF_RESP_FUNC_FAILED\n"
- " to cause a LUN reset...\n", task);
- goto out;
- }
-
- dev_dbg(&isci_host->pdev->dev,
- "%s: old_request == %p\n", __func__, old_request);
+ "%s: dev = %p, task = %p, old_request == %p\n",
+ __func__, isci_device, task, old_request);
- any_dev_reset = isci_device_is_reset_pending(isci_host, isci_device);
-
- spin_lock_irqsave(&task->task_state_lock, flags);
-
- any_dev_reset = any_dev_reset || (task->task_state_flags & SAS_TASK_NEED_DEV_RESET);
+ if (isci_device)
+ set_bit(IDEV_EH, &isci_device->flags);
- /* If the extraction of the request reference from the task
- * failed, then the request has been completed (or if there is a
- * pending reset then this abort request function must be failed
- * in order to escalate to the target reset).
+ /* Device reset conditions signalled in task_state_flags are the
+ * responsbility of libsas to observe at the start of the error
+ * handler thread.
*/
- if ((old_request == NULL) || any_dev_reset) {
-
- /* If the device reset task flag is set, fail the task
- * management request. Otherwise, the original request
- * has completed.
- */
- if (any_dev_reset) {
-
- /* Turn off the task's DONE to make sure this
- * task is escalated to a target reset.
- */
- task->task_state_flags &= ~SAS_TASK_STATE_DONE;
-
- /* Make the reset happen as soon as possible. */
- task->task_state_flags |= SAS_TASK_NEED_DEV_RESET;
-
- spin_unlock_irqrestore(&task->task_state_lock, flags);
-
- /* Fail the task management request in order to
- * escalate to the target reset.
- */
- ret = TMF_RESP_FUNC_FAILED;
-
- dev_dbg(&isci_host->pdev->dev,
- "%s: Failing task abort in order to "
- "escalate to target reset because\n"
- "SAS_TASK_NEED_DEV_RESET is set for "
- "task %p on dev %p\n",
- __func__, task, isci_device);
-
-
- } else {
- /* The request has already completed and there
- * is nothing to do here other than to set the task
- * done bit, and indicate that the task abort function
- * was sucessful.
- */
- isci_set_task_doneflags(task);
-
- spin_unlock_irqrestore(&task->task_state_lock, flags);
+ if (!isci_device || !old_request) {
+ /* The request has already completed and there
+ * is nothing to do here other than to set the task
+ * done bit, and indicate that the task abort function
+ * was sucessful.
+ */
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ task->task_state_flags |= SAS_TASK_STATE_DONE;
+ task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR |
+ SAS_TASK_STATE_PENDING);
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
- ret = TMF_RESP_FUNC_COMPLETE;
+ ret = TMF_RESP_FUNC_COMPLETE;
- dev_dbg(&isci_host->pdev->dev,
- "%s: abort task not needed for %p\n",
- __func__, task);
- }
+ dev_dbg(&isci_host->pdev->dev,
+ "%s: abort task not needed for %p\n",
+ __func__, task);
goto out;
- } else {
- spin_unlock_irqrestore(&task->task_state_lock, flags);
}
spin_lock_irqsave(&isci_host->scic_lock, flags);
@@ -1146,24 +1118,44 @@ int isci_task_abort_task(struct sas_task *task)
goto out;
}
if (task->task_proto == SAS_PROTOCOL_SMP ||
- test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) {
+ sas_protocol_ata(task->task_proto) ||
+ target_done_already) {
spin_unlock_irqrestore(&isci_host->scic_lock, flags);
dev_dbg(&isci_host->pdev->dev,
- "%s: SMP request (%d)"
+ "%s: %s request"
" or complete_in_target (%d), thus no TMF\n",
- __func__, (task->task_proto == SAS_PROTOCOL_SMP),
+ __func__,
+ ((task->task_proto == SAS_PROTOCOL_SMP)
+ ? "SMP"
+ : (sas_protocol_ata(task->task_proto)
+ ? "SATA/STP"
+ : "<other>")
+ ),
test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags));
- /* Set the state on the task. */
- isci_task_all_done(task);
-
- ret = TMF_RESP_FUNC_COMPLETE;
+ if (test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) {
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ task->task_state_flags |= SAS_TASK_STATE_DONE;
+ task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR |
+ SAS_TASK_STATE_PENDING);
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+ ret = TMF_RESP_FUNC_COMPLETE;
+ } else {
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR |
+ SAS_TASK_STATE_PENDING);
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+ }
- /* Stopping and SMP devices are not sent a TMF, and are not
- * reset, but the outstanding I/O request is terminated below.
+ /* STP and SMP devices are not sent a TMF, but the
+ * outstanding I/O request is terminated below. This is
+ * because SATA/STP and SMP discovery path timeouts directly
+ * call the abort task interface for cleanup.
*/
+ perform_termination = 1;
+
} else {
/* Fill in the tmf stucture */
isci_task_build_abort_task_tmf(&tmf, isci_tmf_ssp_task_abort,
@@ -1172,22 +1164,24 @@ int isci_task_abort_task(struct sas_task *task)
spin_unlock_irqrestore(&isci_host->scic_lock, flags);
- #define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* half second timeout. */
+ #define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* 1/2 second timeout */
ret = isci_task_execute_tmf(isci_host, isci_device, &tmf,
ISCI_ABORT_TASK_TIMEOUT_MS);
- if (ret != TMF_RESP_FUNC_COMPLETE)
+ if (ret == TMF_RESP_FUNC_COMPLETE)
+ perform_termination = 1;
+ else
dev_dbg(&isci_host->pdev->dev,
- "%s: isci_task_send_tmf failed\n",
- __func__);
+ "%s: isci_task_send_tmf failed\n", __func__);
}
- if (ret == TMF_RESP_FUNC_COMPLETE) {
+ if (perform_termination) {
set_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags);
/* Clean up the request on our side, and wait for the aborted
* I/O to complete.
*/
- isci_terminate_request_core(isci_host, isci_device, old_request);
+ isci_terminate_request_core(isci_host, isci_device,
+ old_request);
}
/* Make sure we do not leave a reference to aborted_io_completion */
@@ -1288,7 +1282,8 @@ isci_task_request_complete(struct isci_host *ihost,
enum sci_task_status completion_status)
{
struct isci_tmf *tmf = isci_request_access_tmf(ireq);
- struct completion *tmf_complete;
+ struct completion *tmf_complete = NULL;
+ struct completion *request_complete = ireq->io_request_completion;
dev_dbg(&ihost->pdev->dev,
"%s: request = %p, status=%d\n",
@@ -1296,278 +1291,53 @@ isci_task_request_complete(struct isci_host *ihost,
isci_request_change_state(ireq, completed);
- tmf->status = completion_status;
set_bit(IREQ_COMPLETE_IN_TARGET, &ireq->flags);
- if (tmf->proto == SAS_PROTOCOL_SSP) {
- memcpy(&tmf->resp.resp_iu,
- &ireq->ssp.rsp,
- SSP_RESP_IU_MAX_SIZE);
- } else if (tmf->proto == SAS_PROTOCOL_SATA) {
- memcpy(&tmf->resp.d2h_fis,
- &ireq->stp.rsp,
- sizeof(struct dev_to_host_fis));
+ if (tmf) {
+ tmf->status = completion_status;
+
+ if (tmf->proto == SAS_PROTOCOL_SSP) {
+ memcpy(&tmf->resp.resp_iu,
+ &ireq->ssp.rsp,
+ SSP_RESP_IU_MAX_SIZE);
+ } else if (tmf->proto == SAS_PROTOCOL_SATA) {
+ memcpy(&tmf->resp.d2h_fis,
+ &ireq->stp.rsp,
+ sizeof(struct dev_to_host_fis));
+ }
+ /* PRINT_TMF( ((struct isci_tmf *)request->task)); */
+ tmf_complete = tmf->complete;
}
-
- /* PRINT_TMF( ((struct isci_tmf *)request->task)); */
- tmf_complete = tmf->complete;
-
sci_controller_complete_io(ihost, ireq->target_device, ireq);
/* set the 'terminated' flag handle to make sure it cannot be terminated
* or completed again.
*/
set_bit(IREQ_TERMINATED, &ireq->flags);
- isci_request_change_state(ireq, unallocated);
- list_del_init(&ireq->dev_node);
-
- /* The task management part completes last. */
- complete(tmf_complete);
-}
-
-static void isci_smp_task_timedout(unsigned long _task)
-{
- struct sas_task *task = (void *) _task;
- unsigned long flags;
-
- spin_lock_irqsave(&task->task_state_lock, flags);
- if (!(task->task_state_flags & SAS_TASK_STATE_DONE))
- task->task_state_flags |= SAS_TASK_STATE_ABORTED;
- spin_unlock_irqrestore(&task->task_state_lock, flags);
-
- complete(&task->completion);
-}
-
-static void isci_smp_task_done(struct sas_task *task)
-{
- if (!del_timer(&task->timer))
- return;
- complete(&task->completion);
-}
-
-static struct sas_task *isci_alloc_task(void)
-{
- struct sas_task *task = kzalloc(sizeof(*task), GFP_KERNEL);
-
- 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 void isci_free_task(struct isci_host *ihost, struct sas_task *task)
-{
- if (task) {
- BUG_ON(!list_empty(&task->list));
- kfree(task);
- }
-}
-
-static int isci_smp_execute_task(struct isci_host *ihost,
- struct domain_device *dev, void *req,
- int req_size, void *resp, int resp_size)
-{
- int res, retry;
- struct sas_task *task = NULL;
-
- for (retry = 0; retry < 3; retry++) {
- task = isci_alloc_task();
- if (!task)
- return -ENOMEM;
-
- task->dev = dev;
- task->task_proto = dev->tproto;
- sg_init_one(&task->smp_task.smp_req, req, req_size);
- sg_init_one(&task->smp_task.smp_resp, resp, resp_size);
-
- task->task_done = isci_smp_task_done;
-
- task->timer.data = (unsigned long) task;
- task->timer.function = isci_smp_task_timedout;
- task->timer.expires = jiffies + 10*HZ;
- add_timer(&task->timer);
-
- res = isci_task_execute_task(task, 1, GFP_KERNEL);
-
- if (res) {
- del_timer(&task->timer);
- dev_dbg(&ihost->pdev->dev,
- "%s: executing SMP task failed:%d\n",
- __func__, res);
- goto ex_err;
- }
-
- wait_for_completion(&task->completion);
- res = -ECOMM;
- if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
- dev_dbg(&ihost->pdev->dev,
- "%s: smp task timed out or aborted\n",
- __func__);
- isci_task_abort_task(task);
- if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
- dev_dbg(&ihost->pdev->dev,
- "%s: SMP task aborted and not done\n",
- __func__);
- goto ex_err;
- }
- }
- if (task->task_status.resp == SAS_TASK_COMPLETE &&
- task->task_status.stat == SAM_STAT_GOOD) {
- res = 0;
- break;
- }
- if (task->task_status.resp == SAS_TASK_COMPLETE &&
- task->task_status.stat == SAS_DATA_UNDERRUN) {
- /* no error, but return the number of bytes of
- * underrun */
- res = task->task_status.residual;
- break;
- }
- if (task->task_status.resp == SAS_TASK_COMPLETE &&
- task->task_status.stat == SAS_DATA_OVERRUN) {
- res = -EMSGSIZE;
- break;
- } else {
- dev_dbg(&ihost->pdev->dev,
- "%s: task to dev %016llx response: 0x%x "
- "status 0x%x\n", __func__,
- SAS_ADDR(dev->sas_addr),
- task->task_status.resp,
- task->task_status.stat);
- isci_free_task(ihost, task);
- task = NULL;
- }
- }
-ex_err:
- BUG_ON(retry == 3 && task != NULL);
- isci_free_task(ihost, task);
- return res;
-}
-
-#define DISCOVER_REQ_SIZE 16
-#define DISCOVER_RESP_SIZE 56
-
-int isci_smp_get_phy_attached_dev_type(struct isci_host *ihost,
- struct domain_device *dev,
- int phy_id, int *adt)
-{
- struct smp_resp *disc_resp;
- u8 *disc_req;
- int res;
-
- disc_resp = kzalloc(DISCOVER_RESP_SIZE, GFP_KERNEL);
- if (!disc_resp)
- return -ENOMEM;
-
- disc_req = kzalloc(DISCOVER_REQ_SIZE, GFP_KERNEL);
- if (disc_req) {
- disc_req[0] = SMP_REQUEST;
- disc_req[1] = SMP_DISCOVER;
- disc_req[9] = phy_id;
- } else {
- kfree(disc_resp);
- return -ENOMEM;
- }
- res = isci_smp_execute_task(ihost, dev, disc_req, DISCOVER_REQ_SIZE,
- disc_resp, DISCOVER_RESP_SIZE);
- if (!res) {
- if (disc_resp->result != SMP_RESP_FUNC_ACC)
- res = disc_resp->result;
- else
- *adt = disc_resp->disc.attached_dev_type;
+ /* As soon as something is in the terminate path, deallocation is
+ * managed there. Note that the final non-managed state of a task
+ * request is "completed".
+ */
+ if ((ireq->status == completed) ||
+ !isci_request_is_dealloc_managed(ireq->status)) {
+ isci_request_change_state(ireq, unallocated);
+ isci_free_tag(ihost, ireq->io_tag);
+ list_del_init(&ireq->dev_node);
}
- kfree(disc_req);
- kfree(disc_resp);
-
- return res;
-}
-static void isci_wait_for_smp_phy_reset(struct isci_remote_device *idev, int phy_num)
-{
- struct domain_device *dev = idev->domain_dev;
- struct isci_port *iport = idev->isci_port;
- struct isci_host *ihost = iport->isci_host;
- int res, iteration = 0, attached_device_type;
- #define STP_WAIT_MSECS 25000
- unsigned long tmo = msecs_to_jiffies(STP_WAIT_MSECS);
- unsigned long deadline = jiffies + tmo;
- enum {
- SMP_PHYWAIT_PHYDOWN,
- SMP_PHYWAIT_PHYUP,
- SMP_PHYWAIT_DONE
- } phy_state = SMP_PHYWAIT_PHYDOWN;
-
- /* While there is time, wait for the phy to go away and come back */
- while (time_is_after_jiffies(deadline) && phy_state != SMP_PHYWAIT_DONE) {
- int event = atomic_read(&iport->event);
-
- ++iteration;
-
- tmo = wait_event_timeout(ihost->eventq,
- event != atomic_read(&iport->event) ||
- !test_bit(IPORT_BCN_BLOCKED, &iport->flags),
- tmo);
- /* link down, stop polling */
- if (!test_bit(IPORT_BCN_BLOCKED, &iport->flags))
- break;
+ /* "request_complete" is set if the task was being terminated. */
+ if (request_complete)
+ complete(request_complete);
- dev_dbg(&ihost->pdev->dev,
- "%s: iport %p, iteration %d,"
- " phase %d: time_remaining %lu, bcns = %d\n",
- __func__, iport, iteration, phy_state,
- tmo, test_bit(IPORT_BCN_PENDING, &iport->flags));
-
- res = isci_smp_get_phy_attached_dev_type(ihost, dev, phy_num,
- &attached_device_type);
- tmo = deadline - jiffies;
-
- if (res) {
- dev_dbg(&ihost->pdev->dev,
- "%s: iteration %d, phase %d:"
- " SMP error=%d, time_remaining=%lu\n",
- __func__, iteration, phy_state, res, tmo);
- break;
- }
- dev_dbg(&ihost->pdev->dev,
- "%s: iport %p, iteration %d,"
- " phase %d: time_remaining %lu, bcns = %d, "
- "attdevtype = %x\n",
- __func__, iport, iteration, phy_state,
- tmo, test_bit(IPORT_BCN_PENDING, &iport->flags),
- attached_device_type);
-
- switch (phy_state) {
- case SMP_PHYWAIT_PHYDOWN:
- /* Has the device gone away? */
- if (!attached_device_type)
- phy_state = SMP_PHYWAIT_PHYUP;
-
- break;
-
- case SMP_PHYWAIT_PHYUP:
- /* Has the device come back? */
- if (attached_device_type)
- phy_state = SMP_PHYWAIT_DONE;
- break;
-
- case SMP_PHYWAIT_DONE:
- break;
- }
-
- }
- dev_dbg(&ihost->pdev->dev, "%s: done\n", __func__);
+ /* The task management part completes last. */
+ if (tmf_complete)
+ complete(tmf_complete);
}
static int isci_reset_device(struct isci_host *ihost,
struct isci_remote_device *idev)
{
struct sas_phy *phy = sas_find_local_phy(idev->domain_dev);
- struct isci_port *iport = idev->isci_port;
enum sci_status status;
unsigned long flags;
int rc;
@@ -1587,13 +1357,6 @@ static int isci_reset_device(struct isci_host *ihost,
}
spin_unlock_irqrestore(&ihost->scic_lock, flags);
- /* Make sure all pending requests are able to be fully terminated. */
- isci_device_clear_reset_pending(ihost, idev);
-
- /* If this is a device on an expander, disable BCN processing. */
- if (!scsi_is_sas_phy_local(phy))
- set_bit(IPORT_BCN_BLOCKED, &iport->flags);
-
rc = sas_phy_reset(phy, true);
/* Terminate in-progress I/O now. */
@@ -1604,21 +1367,6 @@ static int isci_reset_device(struct isci_host *ihost,
status = sci_remote_device_reset_complete(idev);
spin_unlock_irqrestore(&ihost->scic_lock, flags);
- /* If this is a device on an expander, bring the phy back up. */
- if (!scsi_is_sas_phy_local(phy)) {
- /* A phy reset will cause the device to go away then reappear.
- * Since libsas will take action on incoming BCNs (eg. remove
- * a device going through an SMP phy-control driven reset),
- * we need to wait until the phy comes back up before letting
- * discovery proceed in libsas.
- */
- isci_wait_for_smp_phy_reset(idev, phy->number);
-
- spin_lock_irqsave(&ihost->scic_lock, flags);
- isci_port_bcn_enable(ihost, idev->isci_port);
- spin_unlock_irqrestore(&ihost->scic_lock, flags);
- }
-
if (status != SCI_SUCCESS) {
dev_dbg(&ihost->pdev->dev,
"%s: sci_remote_device_reset_complete(%p) "
diff --git a/drivers/scsi/isci/task.h b/drivers/scsi/isci/task.h
index 4a7fa90..bc78c0a 100644
--- a/drivers/scsi/isci/task.h
+++ b/drivers/scsi/isci/task.h
@@ -58,6 +58,8 @@
#include <scsi/sas_ata.h>
#include "host.h"
+#define ISCI_TERMINATION_TIMEOUT_MSEC 500
+
struct isci_request;
/**
@@ -224,35 +226,6 @@ enum isci_completion_selection {
isci_perform_error_io_completion /* Use sas_task_abort */
};
-static inline void isci_set_task_doneflags(
- struct sas_task *task)
-{
- /* Since no futher action will be taken on this task,
- * make sure to mark it complete from the lldd perspective.
- */
- task->task_state_flags |= SAS_TASK_STATE_DONE;
- task->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
- task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
-}
-/**
- * isci_task_all_done() - This function clears the task bits to indicate the
- * LLDD is done with the task.
- *
- *
- */
-static inline void isci_task_all_done(
- struct sas_task *task)
-{
- unsigned long flags;
-
- /* Since no futher action will be taken on this task,
- * make sure to mark it complete from the lldd perspective.
- */
- spin_lock_irqsave(&task->task_state_lock, flags);
- isci_set_task_doneflags(task);
- spin_unlock_irqrestore(&task->task_state_lock, flags);
-}
-
/**
* isci_task_set_completion_status() - This function sets the completion status
* for the request.
@@ -286,6 +259,25 @@ isci_task_set_completion_status(
task->task_status.resp = response;
task->task_status.stat = status;
+ switch (task->task_proto) {
+
+ case SAS_PROTOCOL_SATA:
+ case SAS_PROTOCOL_STP:
+ case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
+
+ if (task_notification_selection
+ == isci_perform_error_io_completion) {
+ /* SATA/STP I/O has it's own means of scheduling device
+ * error handling on the normal path.
+ */
+ task_notification_selection
+ = isci_perform_normal_io_completion;
+ }
+ break;
+ default:
+ break;
+ }
+
switch (task_notification_selection) {
case isci_perform_error_io_completion:
@@ -315,7 +307,9 @@ isci_task_set_completion_status(
/* Fall through to the normal case... */
case isci_perform_normal_io_completion:
/* Normal notification (task_done) */
- isci_set_task_doneflags(task);
+ task->task_state_flags |= SAS_TASK_STATE_DONE;
+ task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR |
+ SAS_TASK_STATE_PENDING);
break;
default:
WARN_ONCE(1, "unknown task_notification_selection: %d\n",