Skip to content

Commit

Permalink
libata: fix ATAPI draining
Browse files Browse the repository at this point in the history
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: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
  • Loading branch information
Tejun Heo authored and Jeff Garzik committed Dec 18, 2007
1 parent f2dfc1a commit 140b5e5
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 22 deletions.
87 changes: 65 additions & 22 deletions drivers/ata/libata-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
#include <linux/libata.h>
#include <asm/semaphore.h>
#include <asm/byteorder.h>
#include <linux/cdrom.h>

#include "libata.h"

Expand Down Expand Up @@ -4651,6 +4652,43 @@ int ata_check_atapi_dma(struct ata_queued_cmd *qc)
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
* @qc: ATA command in question
Expand Down Expand Up @@ -5139,23 +5177,19 @@ static void atapi_send_cdb(struct ata_port *ap, struct ata_queued_cmd *qc)
* Inherited from caller.
*
*/

static void __atapi_pio_bytes(struct ata_queued_cmd *qc, unsigned int bytes)
static int __atapi_pio_bytes(struct ata_queued_cmd *qc, unsigned int bytes)
{
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_eh_info *ehi = &qc->dev->link->eh_info;
struct scatterlist *sg;
struct page *page;
unsigned char *buf;
unsigned int offset, count;
int no_more_sg = 0;

if (qc->curbytes + bytes >= qc->nbytes)
ap->hsm_task_state = HSM_ST_LAST;

next_sg:
if (unlikely(no_more_sg)) {
sg = qc->cursg;
if (unlikely(!sg)) {
/*
* The end of qc->sg is reached and the device expects
* more data to transfer. In order not to overrun qc->sg
Expand All @@ -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
*/
u16 pad_buf[1] = { 0 };
unsigned int words = bytes >> 1;
unsigned int i;

if (words) /* warning if bytes > 1 */
ata_dev_printk(qc->dev, KERN_WARNING,
"%u bytes trailing data\n", bytes);
if (bytes > qc->curbytes - qc->nbytes + ATAPI_MAX_DRAIN) {
ata_ehi_push_desc(ehi, "too much trailing data "
"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->hsm_task_state = HSM_ST_LAST;
return;
}
qc->curbytes += bytes;

sg = qc->cursg;
return 0;
}

page = sg_page(sg);
offset = sg->offset + qc->cursg_ofs;
Expand Down Expand Up @@ -5213,19 +5254,20 @@ static void __atapi_pio_bytes(struct ata_queued_cmd *qc, unsigned int bytes)
}

bytes -= count;
if ((count & 1) && bytes)
bytes--;
qc->curbytes += count;
qc->cursg_ofs += count;

if (qc->cursg_ofs == sg->length) {
if (qc->cursg == lsg)
no_more_sg = 1;

qc->cursg = sg_next(qc->cursg);
qc->cursg_ofs = 0;
}

if (bytes)
goto next_sg;

return 0;
}

/**
Expand Down Expand Up @@ -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);

__atapi_pio_bytes(qc, bytes);
if (__atapi_pio_bytes(qc, bytes))
goto err_out;
ata_altstatus(ap); /* flush */

return;
Expand Down
2 changes: 2 additions & 0 deletions include/linux/libata.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ enum {
ATA_DEF_BUSY_WAIT = 10000,
ATA_SHORT_PAUSE = (HZ >> 6) + 1,

ATAPI_MAX_DRAIN = 16 << 10,

ATA_SHT_EMULATED = 1,
ATA_SHT_CMD_PER_LUN = 1,
ATA_SHT_THIS_ID = -1,
Expand Down

0 comments on commit 140b5e5

Please sign in to comment.