提交 140b5e59 编写于 作者: T Tejun Heo 提交者: Jeff Garzik

libata: fix ATAPI draining

With ATAPI transfer chunk size properly programmed, libata PIO HSM
should be able to handle full spurious data chunks.  Also, it's a good
idea to suppress trailing data warning for misc ATAPI commands as
there can be many of them per command - for example, if the chunk size
is 16 and the drive tries to transfer 510 bytes, there can be 31
trailing data messages.

This patch makes the following updates to libata ATAPI PIO HSM
implementation.

* Make it drain full spurious chunks.

* Suppress trailing data warning message for misc commands.

* Put limit on how many bytes can be drained.

* If odd, round up consumed bytes and the number of bytes to be
  drained.  This gets the number of bytes to drain right for drivers
  which do 16bit PIO.

This patch is partial backport of improve-ATAPI-data-xfer patchset
pending for #upstream.
Signed-off-by: NTejun Heo <htejun@gmail.com>
Signed-off-by: NJeff Garzik <jeff@garzik.org>
上级 f2dfc1a1
...@@ -64,6 +64,7 @@ ...@@ -64,6 +64,7 @@
#include <linux/libata.h> #include <linux/libata.h>
#include <asm/semaphore.h> #include <asm/semaphore.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <linux/cdrom.h>
#include "libata.h" #include "libata.h"
...@@ -4651,6 +4652,43 @@ int ata_check_atapi_dma(struct ata_queued_cmd *qc) ...@@ -4651,6 +4652,43 @@ int ata_check_atapi_dma(struct ata_queued_cmd *qc)
return 0; return 0;
} }
/**
* atapi_qc_may_overflow - Check whether data transfer may overflow
* @qc: ATA command in question
*
* ATAPI commands which transfer variable length data to host
* might overflow due to application error or hardare bug. This
* function checks whether overflow should be drained and ignored
* for @qc.
*
* LOCKING:
* None.
*
* RETURNS:
* 1 if @qc may overflow; otherwise, 0.
*/
static int atapi_qc_may_overflow(struct ata_queued_cmd *qc)
{
if (qc->tf.protocol != ATA_PROT_ATAPI &&
qc->tf.protocol != ATA_PROT_ATAPI_DMA)
return 0;
if (qc->tf.flags & ATA_TFLAG_WRITE)
return 0;
switch (qc->cdb[0]) {
case READ_10:
case READ_12:
case WRITE_10:
case WRITE_12:
case GPCMD_READ_CD:
case GPCMD_READ_CD_MSF:
return 0;
}
return 1;
}
/** /**
* ata_std_qc_defer - Check whether a qc needs to be deferred * ata_std_qc_defer - Check whether a qc needs to be deferred
* @qc: ATA command in question * @qc: ATA command in question
...@@ -5139,23 +5177,19 @@ static void atapi_send_cdb(struct ata_port *ap, struct ata_queued_cmd *qc) ...@@ -5139,23 +5177,19 @@ static void atapi_send_cdb(struct ata_port *ap, struct ata_queued_cmd *qc)
* Inherited from caller. * Inherited from caller.
* *
*/ */
static int __atapi_pio_bytes(struct ata_queued_cmd *qc, unsigned int bytes)
static void __atapi_pio_bytes(struct ata_queued_cmd *qc, unsigned int bytes)
{ {
int do_write = (qc->tf.flags & ATA_TFLAG_WRITE); int do_write = (qc->tf.flags & ATA_TFLAG_WRITE);
struct scatterlist *sg = qc->__sg;
struct scatterlist *lsg = sg_last(qc->__sg, qc->n_elem);
struct ata_port *ap = qc->ap; struct ata_port *ap = qc->ap;
struct ata_eh_info *ehi = &qc->dev->link->eh_info;
struct scatterlist *sg;
struct page *page; struct page *page;
unsigned char *buf; unsigned char *buf;
unsigned int offset, count; unsigned int offset, count;
int no_more_sg = 0;
if (qc->curbytes + bytes >= qc->nbytes)
ap->hsm_task_state = HSM_ST_LAST;
next_sg: next_sg:
if (unlikely(no_more_sg)) { sg = qc->cursg;
if (unlikely(!sg)) {
/* /*
* The end of qc->sg is reached and the device expects * The end of qc->sg is reached and the device expects
* more data to transfer. In order not to overrun qc->sg * more data to transfer. In order not to overrun qc->sg
...@@ -5164,21 +5198,28 @@ static void __atapi_pio_bytes(struct ata_queued_cmd *qc, unsigned int bytes) ...@@ -5164,21 +5198,28 @@ static void __atapi_pio_bytes(struct ata_queued_cmd *qc, unsigned int bytes)
* - for write case, padding zero data to the device * - for write case, padding zero data to the device
*/ */
u16 pad_buf[1] = { 0 }; u16 pad_buf[1] = { 0 };
unsigned int words = bytes >> 1;
unsigned int i; unsigned int i;
if (words) /* warning if bytes > 1 */ if (bytes > qc->curbytes - qc->nbytes + ATAPI_MAX_DRAIN) {
ata_dev_printk(qc->dev, KERN_WARNING, ata_ehi_push_desc(ehi, "too much trailing data "
"%u bytes trailing data\n", bytes); "buf=%u cur=%u bytes=%u",
qc->nbytes, qc->curbytes, bytes);
return -1;
}
/* overflow is exptected for misc ATAPI commands */
if (bytes && !atapi_qc_may_overflow(qc))
ata_dev_printk(qc->dev, KERN_WARNING, "ATAPI %u bytes "
"trailing data (cdb=%02x nbytes=%u)\n",
bytes, qc->cdb[0], qc->nbytes);
for (i = 0; i < words; i++) for (i = 0; i < (bytes + 1) / 2; i++)
ap->ops->data_xfer(qc->dev, (unsigned char *)pad_buf, 2, do_write); ap->ops->data_xfer(qc->dev, (unsigned char *)pad_buf, 2, do_write);
ap->hsm_task_state = HSM_ST_LAST; qc->curbytes += bytes;
return;
}
sg = qc->cursg; return 0;
}
page = sg_page(sg); page = sg_page(sg);
offset = sg->offset + qc->cursg_ofs; offset = sg->offset + qc->cursg_ofs;
...@@ -5213,19 +5254,20 @@ static void __atapi_pio_bytes(struct ata_queued_cmd *qc, unsigned int bytes) ...@@ -5213,19 +5254,20 @@ static void __atapi_pio_bytes(struct ata_queued_cmd *qc, unsigned int bytes)
} }
bytes -= count; bytes -= count;
if ((count & 1) && bytes)
bytes--;
qc->curbytes += count; qc->curbytes += count;
qc->cursg_ofs += count; qc->cursg_ofs += count;
if (qc->cursg_ofs == sg->length) { if (qc->cursg_ofs == sg->length) {
if (qc->cursg == lsg)
no_more_sg = 1;
qc->cursg = sg_next(qc->cursg); qc->cursg = sg_next(qc->cursg);
qc->cursg_ofs = 0; qc->cursg_ofs = 0;
} }
if (bytes) if (bytes)
goto next_sg; goto next_sg;
return 0;
} }
/** /**
...@@ -5268,7 +5310,8 @@ static void atapi_pio_bytes(struct ata_queued_cmd *qc) ...@@ -5268,7 +5310,8 @@ static void atapi_pio_bytes(struct ata_queued_cmd *qc)
VPRINTK("ata%u: xfering %d bytes\n", ap->print_id, bytes); VPRINTK("ata%u: xfering %d bytes\n", ap->print_id, bytes);
__atapi_pio_bytes(qc, bytes); if (__atapi_pio_bytes(qc, bytes))
goto err_out;
ata_altstatus(ap); /* flush */ ata_altstatus(ap); /* flush */
return; return;
......
...@@ -119,6 +119,8 @@ enum { ...@@ -119,6 +119,8 @@ enum {
ATA_DEF_BUSY_WAIT = 10000, ATA_DEF_BUSY_WAIT = 10000,
ATA_SHORT_PAUSE = (HZ >> 6) + 1, ATA_SHORT_PAUSE = (HZ >> 6) + 1,
ATAPI_MAX_DRAIN = 16 << 10,
ATA_SHT_EMULATED = 1, ATA_SHT_EMULATED = 1,
ATA_SHT_CMD_PER_LUN = 1, ATA_SHT_CMD_PER_LUN = 1,
ATA_SHT_THIS_ID = -1, ATA_SHT_THIS_ID = -1,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册