summaryrefslogtreecommitdiff
path: root/block
diff options
context:
space:
mode:
authorRichard Zhu <Richard.Zhu@freescale.com>2015-08-28 09:07:15 +0800
committerNitin Garg <nitin.garg@freescale.com>2015-09-17 09:24:24 -0500
commit3d5ca5ece0dbc2ffd351470944744bd947298c3d (patch)
tree1f6bff0d6afe811b0e2d54a71bf5f6742568895a /block
parent1ca4dffee79055ea95c59e27bab50bc5080310f5 (diff)
MLK-11444 ata: imx: cmd buf corruption errata bug fix
errata: When a read command returns less data than specified in the PRDs (for example, there are two PRDs for this command, but the device returns a number of bytes which is less than in the first PRD), the second PRD of this command is not read out of the PRD FIFO, causing the next command to use this PRD erroneously. workaround - forces sg_tablesize = 1 - modified the sg_io function in block/scsi_ioctl.c to use a 64k buffer allocated with dma_alloc_coherent during the probe in ahci_imx - In order to fix the scsi/sata hang, when CD_ROM and HDD are accessed simultaneously after the workaround is applied. Do not go to sleep in scsi_eh_handler, when there is host failed. Signed-off-by: Richard Zhu <Richard.Zhu@freescale.com>
Diffstat (limited to 'block')
-rw-r--r--block/blk-map.c15
-rw-r--r--block/scsi_ioctl.c27
2 files changed, 37 insertions, 5 deletions
diff --git a/block/blk-map.c b/block/blk-map.c
index ae4ae1047fd9..4ae0a59b34ea 100644
--- a/block/blk-map.c
+++ b/block/blk-map.c
@@ -275,6 +275,12 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+#ifdef CONFIG_AHCI_IMX
+extern void *sg_io_buffer_hack;
+#else
+#define sg_io_buffer_hack NULL
+#endif
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
@@ -302,7 +308,14 @@ int blk_rq_map_kern(struct request_queue *q, struct request *rq, void *kbuf,
if (!len || !kbuf)
return -EINVAL;
- do_copy = !blk_rq_aligned(q, addr, len) || object_is_on_stack(kbuf);
+#ifdef CONFIG_AHCI_IMX
+ if (kbuf == sg_io_buffer_hack)
+ do_copy = 0;
+ else
+#endif
+ do_copy = !blk_rq_aligned(q, addr, len)
+ || object_is_on_stack(kbuf);
+
if (do_copy)
bio = bio_copy_kern(q, kbuf, len, gfp_mask, reading);
else
diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c
index 4044cf789c7a..be3081a45ca8 100644
--- a/block/scsi_ioctl.c
+++ b/block/scsi_ioctl.c
@@ -246,6 +246,12 @@ static int blk_fill_sghdr_rq(struct request_queue *q, struct request *rq,
return 0;
}
+#ifdef CONFIG_AHCI_IMX
+extern void *sg_io_buffer_hack;
+#else
+#define sg_io_buffer_hack NULL
+#endif
+
static int blk_complete_sghdr_rq(struct request *rq, struct sg_io_hdr *hdr,
struct bio *bio)
{
@@ -274,7 +280,12 @@ static int blk_complete_sghdr_rq(struct request *rq, struct sg_io_hdr *hdr,
ret = -EFAULT;
}
- r = blk_rq_unmap_user(bio);
+ if (sg_io_buffer_hack && !hdr->iovec_count)
+ r = copy_to_user(hdr->dxferp, sg_io_buffer_hack,
+ hdr->dxfer_len);
+ else
+ r = blk_rq_unmap_user(bio);
+
if (!ret)
ret = r;
blk_put_request(rq);
@@ -300,6 +311,9 @@ static int sg_io(struct request_queue *q, struct gendisk *bd_disk,
if (hdr->dxfer_len > (queue_max_hw_sectors(q) << 9))
return -EIO;
+ if (sg_io_buffer_hack && hdr->dxfer_len > 0x10000)
+ return -EIO;
+
if (hdr->dxfer_len)
switch (hdr->dxfer_direction) {
default:
@@ -347,9 +361,14 @@ static int sg_io(struct request_queue *q, struct gendisk *bd_disk,
hdr->iovec_count,
iov_data_len, GFP_KERNEL);
kfree(iov);
- } else if (hdr->dxfer_len)
- ret = blk_rq_map_user(q, rq, NULL, hdr->dxferp, hdr->dxfer_len,
- GFP_KERNEL);
+ } else if (hdr->dxfer_len) {
+ if (sg_io_buffer_hack)
+ ret = blk_rq_map_kern(q, rq, sg_io_buffer_hack,
+ hdr->dxfer_len, GFP_KERNEL);
+ else
+ ret = blk_rq_map_user(q, rq, NULL, hdr->dxferp,
+ hdr->dxfer_len, GFP_KERNEL);
+ }
if (ret)
goto out;