Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 75012
b: refs/heads/master
c: 140b5e5
h: refs/heads/master
v: v3
  • Loading branch information
Tejun Heo authored and Jeff Garzik committed Dec 18, 2007
1 parent 4c512f3 commit c1b0f71
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 23 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: f2dfc1a12bb1a029df62b018a8e1882e91041025
refs/heads/master: 140b5e59119a172a91b5fa13d54ca4f79bbefee1
87 changes: 65 additions & 22 deletions trunk/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 trunk/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 c1b0f71

Please sign in to comment.