Skip to content

Commit

Permalink
[SCSI] pm80xx: NCQ error handling changes
Browse files Browse the repository at this point in the history
Handled NCQ errors in the low level driver as the FW
is not providing the faulty tag for NCQ errors for libsas
to recover.

[jejb: fix checkpatch issues]
Signed-off-by: Anand Kumar S <[email protected]>
Acked-by: Jack Wang <[email protected]>
Reviewed-by: Hannes Reinecke <[email protected]>
Signed-off-by: James Bottomley <[email protected]>
  • Loading branch information
Sakthivel K authored and James Bottomley committed May 10, 2013
1 parent a33a015 commit c6b9ef5
Show file tree
Hide file tree
Showing 5 changed files with 544 additions and 28 deletions.
272 changes: 261 additions & 11 deletions drivers/scsi/pm8001/pm8001_hwi.c
Original file line number Diff line number Diff line change
Expand Up @@ -1707,6 +1707,123 @@ int pm8001_handle_event(struct pm8001_hba_info *pm8001_ha, void *data,
return ret;
}

static void pm8001_send_abort_all(struct pm8001_hba_info *pm8001_ha,
struct pm8001_device *pm8001_ha_dev)
{
int res;
u32 ccb_tag;
struct pm8001_ccb_info *ccb;
struct sas_task *task = NULL;
struct task_abort_req task_abort;
struct inbound_queue_table *circularQ;
u32 opc = OPC_INB_SATA_ABORT;
int ret;

if (!pm8001_ha_dev) {
PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("dev is null\n"));
return;
}

task = sas_alloc_slow_task(GFP_ATOMIC);

if (!task) {
PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("cannot "
"allocate task\n"));
return;
}

task->task_done = pm8001_task_done;

res = pm8001_tag_alloc(pm8001_ha, &ccb_tag);
if (res)
return;

ccb = &pm8001_ha->ccb_info[ccb_tag];
ccb->device = pm8001_ha_dev;
ccb->ccb_tag = ccb_tag;
ccb->task = task;

circularQ = &pm8001_ha->inbnd_q_tbl[0];

memset(&task_abort, 0, sizeof(task_abort));
task_abort.abort_all = cpu_to_le32(1);
task_abort.device_id = cpu_to_le32(pm8001_ha_dev->device_id);
task_abort.tag = cpu_to_le32(ccb_tag);

ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &task_abort, 0);

}

static void pm8001_send_read_log(struct pm8001_hba_info *pm8001_ha,
struct pm8001_device *pm8001_ha_dev)
{
struct sata_start_req sata_cmd;
int res;
u32 ccb_tag;
struct pm8001_ccb_info *ccb;
struct sas_task *task = NULL;
struct host_to_dev_fis fis;
struct domain_device *dev;
struct inbound_queue_table *circularQ;
u32 opc = OPC_INB_SATA_HOST_OPSTART;

task = sas_alloc_slow_task(GFP_ATOMIC);

if (!task) {
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk("cannot allocate task !!!\n"));
return;
}
task->task_done = pm8001_task_done;

res = pm8001_tag_alloc(pm8001_ha, &ccb_tag);
if (res) {
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk("cannot allocate tag !!!\n"));
return;
}

/* allocate domain device by ourselves as libsas
* is not going to provide any
*/
dev = kzalloc(sizeof(struct domain_device), GFP_ATOMIC);
if (!dev) {
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk("Domain device cannot be allocated\n"));
sas_free_task(task);
return;
} else {
task->dev = dev;
task->dev->lldd_dev = pm8001_ha_dev;
}

ccb = &pm8001_ha->ccb_info[ccb_tag];
ccb->device = pm8001_ha_dev;
ccb->ccb_tag = ccb_tag;
ccb->task = task;
pm8001_ha_dev->id |= NCQ_READ_LOG_FLAG;
pm8001_ha_dev->id |= NCQ_2ND_RLE_FLAG;

memset(&sata_cmd, 0, sizeof(sata_cmd));
circularQ = &pm8001_ha->inbnd_q_tbl[0];

/* construct read log FIS */
memset(&fis, 0, sizeof(struct host_to_dev_fis));
fis.fis_type = 0x27;
fis.flags = 0x80;
fis.command = ATA_CMD_READ_LOG_EXT;
fis.lbal = 0x10;
fis.sector_count = 0x1;

sata_cmd.tag = cpu_to_le32(ccb_tag);
sata_cmd.device_id = cpu_to_le32(pm8001_ha_dev->device_id);
sata_cmd.ncqtag_atap_dir_m |= ((0x1 << 7) | (0x5 << 9));
memcpy(&sata_cmd.sata_fis, &fis, sizeof(struct host_to_dev_fis));

res = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &sata_cmd, 0);

}

/**
* mpi_ssp_completion- process the event that FW response to the SSP request.
* @pm8001_ha: our hba card information
Expand Down Expand Up @@ -1941,7 +2058,7 @@ mpi_ssp_completion(struct pm8001_hba_info *pm8001_ha , void *piomb)
break;
}
PM8001_IO_DBG(pm8001_ha,
pm8001_printk("scsi_status = %x \n ",
pm8001_printk("scsi_status = %x\n ",
psspPayload->ssp_resp_iu.status));
spin_lock_irqsave(&t->task_state_lock, flags);
t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
Expand Down Expand Up @@ -2170,23 +2287,64 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
status = le32_to_cpu(psataPayload->status);
tag = le32_to_cpu(psataPayload->tag);

if (!tag) {
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk("tag null\n"));
return;
}
ccb = &pm8001_ha->ccb_info[tag];
param = le32_to_cpu(psataPayload->param);
t = ccb->task;
if (ccb) {
t = ccb->task;
pm8001_dev = ccb->device;
} else {
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk("ccb null\n"));
return;
}

if (t) {
if (t->dev && (t->dev->lldd_dev))
pm8001_dev = t->dev->lldd_dev;
} else {
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk("task null\n"));
return;
}

if ((pm8001_dev && !(pm8001_dev->id & NCQ_READ_LOG_FLAG))
&& unlikely(!t || !t->lldd_task || !t->dev)) {
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk("task or dev null\n"));
return;
}

ts = &t->task_status;
pm8001_dev = ccb->device;
if (status)
if (!ts) {
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk("sata IO status 0x%x\n", status));
if (unlikely(!t || !t->lldd_task || !t->dev))
pm8001_printk("ts null\n"));
return;
}

switch (status) {
case IO_SUCCESS:
PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS\n"));
if (param == 0) {
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAM_STAT_GOOD;
/* check if response is for SEND READ LOG */
if (pm8001_dev &&
(pm8001_dev->id & NCQ_READ_LOG_FLAG)) {
/* set new bit for abort_all */
pm8001_dev->id |= NCQ_ABORT_ALL_FLAG;
/* clear bit for read log */
pm8001_dev->id = pm8001_dev->id & 0x7FFFFFFF;
pm8001_send_abort_all(pm8001_ha, pm8001_dev);
/* Free the tag */
pm8001_tag_free(pm8001_ha, tag);
sas_free_task(t);
return;
}
} else {
u8 len;
ts->resp = SAS_TASK_COMPLETE;
Expand Down Expand Up @@ -2497,6 +2655,29 @@ static void mpi_sata_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
u32 dev_id = le32_to_cpu(psataPayload->device_id);
unsigned long flags;

ccb = &pm8001_ha->ccb_info[tag];

if (ccb) {
t = ccb->task;
pm8001_dev = ccb->device;
} else {
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk("No CCB !!!. returning\n"));
}
if (event)
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk("SATA EVENT 0x%x\n", event));

/* Check if this is NCQ error */
if (event == IO_XFER_ERROR_ABORTED_NCQ_MODE) {
/* find device using device id */
pm8001_dev = pm8001_find_dev(pm8001_ha, dev_id);
/* send read log extension */
if (pm8001_dev)
pm8001_send_read_log(pm8001_ha, pm8001_dev);
return;
}

ccb = &pm8001_ha->ccb_info[tag];
t = ccb->task;
pm8001_dev = ccb->device;
Expand Down Expand Up @@ -3512,19 +3693,29 @@ int pm8001_mpi_task_abort_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
u32 status ;
u32 tag, scp;
struct task_status_struct *ts;
struct pm8001_device *pm8001_dev;

struct task_abort_resp *pPayload =
(struct task_abort_resp *)(piomb + 4);

status = le32_to_cpu(pPayload->status);
tag = le32_to_cpu(pPayload->tag);
if (!tag) {
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk(" TAG NULL. RETURNING !!!"));
return -1;
}

scp = le32_to_cpu(pPayload->scp);
ccb = &pm8001_ha->ccb_info[tag];
t = ccb->task;
PM8001_IO_DBG(pm8001_ha,
pm8001_printk(" status = 0x%x\n", status));
if (t == NULL)
pm8001_dev = ccb->device; /* retrieve device */

if (!t) {
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk(" TASK NULL. RETURNING !!!"));
return -1;
}
ts = &t->task_status;
if (status != 0)
PM8001_FAIL_DBG(pm8001_ha,
Expand All @@ -3548,7 +3739,15 @@ int pm8001_mpi_task_abort_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
spin_unlock_irqrestore(&t->task_state_lock, flags);
pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
mb();
t->task_done(t);

if ((pm8001_dev->id & NCQ_ABORT_ALL_FLAG) && t) {
pm8001_tag_free(pm8001_ha, tag);
sas_free_task(t);
/* clear the flag */
pm8001_dev->id &= 0xBFFFFFFF;
} else
t->task_done(t);

return 0;
}

Expand Down Expand Up @@ -4133,6 +4332,7 @@ static int pm8001_chip_sata_req(struct pm8001_hba_info *pm8001_ha,
u32 ATAP = 0x0;
u32 dir;
struct inbound_queue_table *circularQ;
unsigned long flags;
u32 opc = OPC_INB_SATA_HOST_OPSTART;
memset(&sata_cmd, 0, sizeof(sata_cmd));
circularQ = &pm8001_ha->inbnd_q_tbl[0];
Expand All @@ -4153,8 +4353,10 @@ static int pm8001_chip_sata_req(struct pm8001_hba_info *pm8001_ha,
PM8001_IO_DBG(pm8001_ha, pm8001_printk("FPDMA\n"));
}
}
if (task->ata_task.use_ncq && pm8001_get_ncq_tag(task, &hdr_tag))
if (task->ata_task.use_ncq && pm8001_get_ncq_tag(task, &hdr_tag)) {
task->ata_task.fis.sector_count |= (u8) (hdr_tag << 3);
ncg_tag = hdr_tag;
}
dir = data_dir_flags[task->data_dir] << 8;
sata_cmd.tag = cpu_to_le32(tag);
sata_cmd.device_id = cpu_to_le32(pm8001_ha_dev->device_id);
Expand Down Expand Up @@ -4185,6 +4387,54 @@ static int pm8001_chip_sata_req(struct pm8001_hba_info *pm8001_ha,
sata_cmd.len = cpu_to_le32(task->total_xfer_len);
sata_cmd.esgl = 0;
}

/* Check for read log for failed drive and return */
if (sata_cmd.sata_fis.command == 0x2f) {
if (pm8001_ha_dev && ((pm8001_ha_dev->id & NCQ_READ_LOG_FLAG) ||
(pm8001_ha_dev->id & NCQ_ABORT_ALL_FLAG) ||
(pm8001_ha_dev->id & NCQ_2ND_RLE_FLAG))) {
struct task_status_struct *ts;

pm8001_ha_dev->id &= 0xDFFFFFFF;
ts = &task->task_status;

spin_lock_irqsave(&task->task_state_lock, flags);
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAM_STAT_GOOD;
task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
task->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
task->task_state_flags |= SAS_TASK_STATE_DONE;
if (unlikely((task->task_state_flags &
SAS_TASK_STATE_ABORTED))) {
spin_unlock_irqrestore(&task->task_state_lock,
flags);
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk("task 0x%p resp 0x%x "
" stat 0x%x but aborted by upper layer "
"\n", task, ts->resp, ts->stat));
pm8001_ccb_task_free(pm8001_ha, task, ccb, tag);
} else if (task->uldd_task) {
spin_unlock_irqrestore(&task->task_state_lock,
flags);
pm8001_ccb_task_free(pm8001_ha, task, ccb, tag);
mb();/* ditto */
spin_unlock_irq(&pm8001_ha->lock);
task->task_done(task);
spin_lock_irq(&pm8001_ha->lock);
return 0;
} else if (!task->uldd_task) {
spin_unlock_irqrestore(&task->task_state_lock,
flags);
pm8001_ccb_task_free(pm8001_ha, task, ccb, tag);
mb();/*ditto*/
spin_unlock_irq(&pm8001_ha->lock);
task->task_done(task);
spin_lock_irq(&pm8001_ha->lock);
return 0;
}
}
}

ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &sata_cmd, 0);
return ret;
}
Expand Down
Loading

0 comments on commit c6b9ef5

Please sign in to comment.