diff --git a/block/blk-map.c b/block/blk-map.c index 3a62e471d81b..774db8d3cced 100644 --- a/block/blk-map.c +++ b/block/blk-map.c @@ -205,6 +205,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 passthrough requests * @q: request queue where request should be inserted @@ -232,7 +238,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 f5e0ad65e86a..6c894f178e05 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c @@ -239,6 +239,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) { @@ -268,7 +274,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; @@ -292,6 +303,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: @@ -338,9 +352,14 @@ static int sg_io(struct request_queue *q, struct gendisk *bd_disk, ret = blk_rq_map_user_iov(q, rq, NULL, &i, 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_free_cdb; diff --git a/drivers/ata/ahci_imx.c b/drivers/ata/ahci_imx.c index bfc617cc8ac5..1cc0f88fc04e 100644 --- a/drivers/ata/ahci_imx.c +++ b/drivers/ata/ahci_imx.c @@ -108,6 +108,8 @@ struct imx_ahci_priv { u32 imped_ratio; }; +void *sg_io_buffer_hack; + static int ahci_imx_hotplug; module_param_named(hotplug, ahci_imx_hotplug, int, 0644); MODULE_PARM_DESC(hotplug, "AHCI IMX hot-plug support (0=Don't support, 1=support)"); @@ -1170,6 +1172,24 @@ static int imx_ahci_probe(struct platform_device *pdev) reg_val = clk_get_rate(imxpriv->ahb_clk) / 1000; writel(reg_val, hpriv->mmio + IMX_TIMER1MS); + /* + * Due to IP bug on the Synopsis 3.00 SATA version, + * which is present on mx6q, and not on mx53, + * we should use sg_tablesize = 1 for reliable operation + */ + if (imxpriv->type == AHCI_IMX6Q || imxpriv->type == AHCI_IMX6QP) { + dma_addr_t dma; + + ahci_platform_sht.sg_tablesize = 1; + + sg_io_buffer_hack = dma_alloc_coherent(dev, 0x10000, + &dma, GFP_KERNEL); + if (!sg_io_buffer_hack) { + ret = -ENOMEM; + goto disable_sata; + } + } + ret = ahci_platform_init_host(pdev, hpriv, &ahci_imx_port_info, &ahci_platform_sht); if (ret) diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index ae2fa170f6ad..dcab391507c6 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -63,6 +63,13 @@ static int scsi_eh_try_stu(struct scsi_cmnd *scmd); static int scsi_try_to_abort_cmd(struct scsi_host_template *, struct scsi_cmnd *); +#ifdef CONFIG_AHCI_IMX +extern void *sg_io_buffer_hack; +#else +#define sg_io_buffer_hack NULL +#endif + +/* called with shost->host_lock held */ void scsi_eh_wakeup(struct Scsi_Host *shost) { lockdep_assert_held(shost->host_lock); @@ -72,6 +79,11 @@ void scsi_eh_wakeup(struct Scsi_Host *shost) wake_up_process(shost->ehandler); SCSI_LOG_ERROR_RECOVERY(5, shost_printk(KERN_INFO, shost, "Waking error handler thread\n")); + } else if ((shost->host_failed > 0) || (sg_io_buffer_hack != NULL)) { + trace_scsi_eh_wakeup(shost); + wake_up_process(shost->ehandler); + SCSI_LOG_ERROR_RECOVERY(5, shost_printk(KERN_INFO, shost, + "Waking error handler thread\n")); } } @@ -2188,8 +2200,15 @@ int scsi_error_handler(void *data) if (kthread_should_stop()) break; + /* + * Do not go to sleep, when there is host_failed when the + * one-PRD per command workaroud is triggered. + * Because that ata/scsi subsystem maybe hang, when CD_ROM + * and HDD are accessed simultaneously. + */ if ((shost->host_failed == 0 && shost->host_eh_scheduled == 0) || - shost->host_failed != scsi_host_busy(shost)) { + ((shost->host_failed != atomic_read(&shost->host_busy)) && + (sg_io_buffer_hack == NULL) && (shost->host_failed > 0))) { SCSI_LOG_ERROR_RECOVERY(1, shost_printk(KERN_INFO, shost, "scsi_eh_%d: sleeping\n",