Commit bd056d7e authored by Tejun Heo's avatar Tejun Heo

[PATCH] libata: separate out rw ATA taskfile building into ata_build_rw_tf()

Separate out rw ATA taskfile building from ata_scsi_rw_xlat() into
ata_build_rw_tf().  This will be used to improve media error handling.
Signed-off-by: default avatarTejun Heo <htejun@gmail.com>
parent 2432697b
...@@ -199,7 +199,8 @@ static const u8 ata_rw_cmds[] = { ...@@ -199,7 +199,8 @@ static const u8 ata_rw_cmds[] = {
/** /**
* ata_rwcmd_protocol - set taskfile r/w commands and protocol * ata_rwcmd_protocol - set taskfile r/w commands and protocol
* @qc: command to examine and configure * @tf: command to examine and configure
* @dev: device tf belongs to
* *
* Examine the device configuration and tf->flags to calculate * Examine the device configuration and tf->flags to calculate
* the proper read/write commands and protocol to use. * the proper read/write commands and protocol to use.
...@@ -207,10 +208,8 @@ static const u8 ata_rw_cmds[] = { ...@@ -207,10 +208,8 @@ static const u8 ata_rw_cmds[] = {
* LOCKING: * LOCKING:
* caller. * caller.
*/ */
int ata_rwcmd_protocol(struct ata_queued_cmd *qc) static int ata_rwcmd_protocol(struct ata_taskfile *tf, struct ata_device *dev)
{ {
struct ata_taskfile *tf = &qc->tf;
struct ata_device *dev = qc->dev;
u8 cmd; u8 cmd;
int index, fua, lba48, write; int index, fua, lba48, write;
...@@ -222,7 +221,7 @@ int ata_rwcmd_protocol(struct ata_queued_cmd *qc) ...@@ -222,7 +221,7 @@ int ata_rwcmd_protocol(struct ata_queued_cmd *qc)
if (dev->flags & ATA_DFLAG_PIO) { if (dev->flags & ATA_DFLAG_PIO) {
tf->protocol = ATA_PROT_PIO; tf->protocol = ATA_PROT_PIO;
index = dev->multi_count ? 0 : 8; index = dev->multi_count ? 0 : 8;
} else if (lba48 && (qc->ap->flags & ATA_FLAG_PIO_LBA48)) { } else if (lba48 && (dev->ap->flags & ATA_FLAG_PIO_LBA48)) {
/* Unable to use DMA due to host limitation */ /* Unable to use DMA due to host limitation */
tf->protocol = ATA_PROT_PIO; tf->protocol = ATA_PROT_PIO;
index = dev->multi_count ? 0 : 8; index = dev->multi_count ? 0 : 8;
...@@ -282,6 +281,130 @@ u64 ata_tf_read_block(struct ata_taskfile *tf, struct ata_device *dev) ...@@ -282,6 +281,130 @@ u64 ata_tf_read_block(struct ata_taskfile *tf, struct ata_device *dev)
return block; return block;
} }
/**
* ata_build_rw_tf - Build ATA taskfile for given read/write request
* @tf: Target ATA taskfile
* @dev: ATA device @tf belongs to
* @block: Block address
* @n_block: Number of blocks
* @tf_flags: RW/FUA etc...
* @tag: tag
*
* LOCKING:
* None.
*
* Build ATA taskfile @tf for read/write request described by
* @block, @n_block, @tf_flags and @tag on @dev.
*
* RETURNS:
*
* 0 on success, -ERANGE if the request is too large for @dev,
* -EINVAL if the request is invalid.
*/
int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev,
u64 block, u32 n_block, unsigned int tf_flags,
unsigned int tag)
{
tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
tf->flags |= tf_flags;
if ((dev->flags & (ATA_DFLAG_PIO | ATA_DFLAG_NCQ_OFF |
ATA_DFLAG_NCQ)) == ATA_DFLAG_NCQ) {
/* yay, NCQ */
if (!lba_48_ok(block, n_block))
return -ERANGE;
tf->protocol = ATA_PROT_NCQ;
tf->flags |= ATA_TFLAG_LBA | ATA_TFLAG_LBA48;
if (tf->flags & ATA_TFLAG_WRITE)
tf->command = ATA_CMD_FPDMA_WRITE;
else
tf->command = ATA_CMD_FPDMA_READ;
tf->nsect = tag << 3;
tf->hob_feature = (n_block >> 8) & 0xff;
tf->feature = n_block & 0xff;
tf->hob_lbah = (block >> 40) & 0xff;
tf->hob_lbam = (block >> 32) & 0xff;
tf->hob_lbal = (block >> 24) & 0xff;
tf->lbah = (block >> 16) & 0xff;
tf->lbam = (block >> 8) & 0xff;
tf->lbal = block & 0xff;
tf->device = 1 << 6;
if (tf->flags & ATA_TFLAG_FUA)
tf->device |= 1 << 7;
} else if (dev->flags & ATA_DFLAG_LBA) {
tf->flags |= ATA_TFLAG_LBA;
if (lba_28_ok(block, n_block)) {
/* use LBA28 */
tf->device |= (block >> 24) & 0xf;
} else if (lba_48_ok(block, n_block)) {
if (!(dev->flags & ATA_DFLAG_LBA48))
return -ERANGE;
/* use LBA48 */
tf->flags |= ATA_TFLAG_LBA48;
tf->hob_nsect = (n_block >> 8) & 0xff;
tf->hob_lbah = (block >> 40) & 0xff;
tf->hob_lbam = (block >> 32) & 0xff;
tf->hob_lbal = (block >> 24) & 0xff;
} else
/* request too large even for LBA48 */
return -ERANGE;
if (unlikely(ata_rwcmd_protocol(tf, dev) < 0))
return -EINVAL;
tf->nsect = n_block & 0xff;
tf->lbah = (block >> 16) & 0xff;
tf->lbam = (block >> 8) & 0xff;
tf->lbal = block & 0xff;
tf->device |= ATA_LBA;
} else {
/* CHS */
u32 sect, head, cyl, track;
/* The request -may- be too large for CHS addressing. */
if (!lba_28_ok(block, n_block))
return -ERANGE;
if (unlikely(ata_rwcmd_protocol(tf, dev) < 0))
return -EINVAL;
/* Convert LBA to CHS */
track = (u32)block / dev->sectors;
cyl = track / dev->heads;
head = track % dev->heads;
sect = (u32)block % dev->sectors + 1;
DPRINTK("block %u track %u cyl %u head %u sect %u\n",
(u32)block, track, cyl, head, sect);
/* Check whether the converted CHS can fit.
Cylinder: 0-65535
Head: 0-15
Sector: 1-255*/
if ((cyl >> 16) || (head >> 4) || (sect >> 8) || (!sect))
return -ERANGE;
tf->nsect = n_block & 0xff; /* Sector count 0 means 256 sectors */
tf->lbal = sect;
tf->lbam = cyl;
tf->lbah = cyl >> 8;
tf->device |= head;
}
return 0;
}
/** /**
* ata_pack_xfermask - Pack pio, mwdma and udma masks into xfer_mask * ata_pack_xfermask - Pack pio, mwdma and udma masks into xfer_mask
* @pio_mask: pio_mask * @pio_mask: pio_mask
......
...@@ -1265,17 +1265,14 @@ static unsigned int ata_scsi_verify_xlat(struct ata_queued_cmd *qc, const u8 *sc ...@@ -1265,17 +1265,14 @@ static unsigned int ata_scsi_verify_xlat(struct ata_queued_cmd *qc, const u8 *sc
static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, const u8 *scsicmd) static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, const u8 *scsicmd)
{ {
struct ata_taskfile *tf = &qc->tf; unsigned int tf_flags = 0;
struct ata_device *dev = qc->dev;
u64 block; u64 block;
u32 n_block; u32 n_block;
int rc;
qc->flags |= ATA_QCFLAG_IO;
tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
if (scsicmd[0] == WRITE_10 || scsicmd[0] == WRITE_6 || if (scsicmd[0] == WRITE_10 || scsicmd[0] == WRITE_6 ||
scsicmd[0] == WRITE_16) scsicmd[0] == WRITE_16)
tf->flags |= ATA_TFLAG_WRITE; tf_flags |= ATA_TFLAG_WRITE;
/* Calculate the SCSI LBA, transfer length and FUA. */ /* Calculate the SCSI LBA, transfer length and FUA. */
switch (scsicmd[0]) { switch (scsicmd[0]) {
...@@ -1283,7 +1280,7 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, const u8 *scsicm ...@@ -1283,7 +1280,7 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, const u8 *scsicm
case WRITE_10: case WRITE_10:
scsi_10_lba_len(scsicmd, &block, &n_block); scsi_10_lba_len(scsicmd, &block, &n_block);
if (unlikely(scsicmd[1] & (1 << 3))) if (unlikely(scsicmd[1] & (1 << 3)))
tf->flags |= ATA_TFLAG_FUA; tf_flags |= ATA_TFLAG_FUA;
break; break;
case READ_6: case READ_6:
case WRITE_6: case WRITE_6:
...@@ -1299,7 +1296,7 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, const u8 *scsicm ...@@ -1299,7 +1296,7 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, const u8 *scsicm
case WRITE_16: case WRITE_16:
scsi_16_lba_len(scsicmd, &block, &n_block); scsi_16_lba_len(scsicmd, &block, &n_block);
if (unlikely(scsicmd[1] & (1 << 3))) if (unlikely(scsicmd[1] & (1 << 3)))
tf->flags |= ATA_TFLAG_FUA; tf_flags |= ATA_TFLAG_FUA;
break; break;
default: default:
DPRINTK("no-byte command\n"); DPRINTK("no-byte command\n");
...@@ -1317,106 +1314,17 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, const u8 *scsicm ...@@ -1317,106 +1314,17 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, const u8 *scsicm
*/ */
goto nothing_to_do; goto nothing_to_do;
if ((dev->flags & (ATA_DFLAG_PIO | ATA_DFLAG_NCQ_OFF | qc->flags |= ATA_QCFLAG_IO;
ATA_DFLAG_NCQ)) == ATA_DFLAG_NCQ) {
/* yay, NCQ */
if (!lba_48_ok(block, n_block))
goto out_of_range;
tf->protocol = ATA_PROT_NCQ;
tf->flags |= ATA_TFLAG_LBA | ATA_TFLAG_LBA48;
if (tf->flags & ATA_TFLAG_WRITE)
tf->command = ATA_CMD_FPDMA_WRITE;
else
tf->command = ATA_CMD_FPDMA_READ;
qc->nsect = n_block;
tf->nsect = qc->tag << 3;
tf->hob_feature = (n_block >> 8) & 0xff;
tf->feature = n_block & 0xff;
tf->hob_lbah = (block >> 40) & 0xff;
tf->hob_lbam = (block >> 32) & 0xff;
tf->hob_lbal = (block >> 24) & 0xff;
tf->lbah = (block >> 16) & 0xff;
tf->lbam = (block >> 8) & 0xff;
tf->lbal = block & 0xff;
tf->device = 1 << 6;
if (tf->flags & ATA_TFLAG_FUA)
tf->device |= 1 << 7;
} else if (dev->flags & ATA_DFLAG_LBA) {
tf->flags |= ATA_TFLAG_LBA;
if (lba_28_ok(block, n_block)) {
/* use LBA28 */
tf->device |= (block >> 24) & 0xf;
} else if (lba_48_ok(block, n_block)) {
if (!(dev->flags & ATA_DFLAG_LBA48))
goto out_of_range;
/* use LBA48 */
tf->flags |= ATA_TFLAG_LBA48;
tf->hob_nsect = (n_block >> 8) & 0xff;
tf->hob_lbah = (block >> 40) & 0xff;
tf->hob_lbam = (block >> 32) & 0xff;
tf->hob_lbal = (block >> 24) & 0xff;
} else
/* request too large even for LBA48 */
goto out_of_range;
if (unlikely(ata_rwcmd_protocol(qc) < 0))
goto invalid_fld;
qc->nsect = n_block;
tf->nsect = n_block & 0xff;
tf->lbah = (block >> 16) & 0xff;
tf->lbam = (block >> 8) & 0xff;
tf->lbal = block & 0xff;
tf->device |= ATA_LBA;
} else {
/* CHS */
u32 sect, head, cyl, track;
/* The request -may- be too large for CHS addressing. */
if (!lba_28_ok(block, n_block))
goto out_of_range;
if (unlikely(ata_rwcmd_protocol(qc) < 0))
goto invalid_fld;
/* Convert LBA to CHS */
track = (u32)block / dev->sectors;
cyl = track / dev->heads;
head = track % dev->heads;
sect = (u32)block % dev->sectors + 1;
DPRINTK("block %u track %u cyl %u head %u sect %u\n",
(u32)block, track, cyl, head, sect);
/* Check whether the converted CHS can fit.
Cylinder: 0-65535
Head: 0-15
Sector: 1-255*/
if ((cyl >> 16) || (head >> 4) || (sect >> 8) || (!sect))
goto out_of_range;
qc->nsect = n_block; qc->nsect = n_block;
tf->nsect = n_block & 0xff; /* Sector count 0 means 256 sectors */
tf->lbal = sect;
tf->lbam = cyl;
tf->lbah = cyl >> 8;
tf->device |= head;
}
rc = ata_build_rw_tf(&qc->tf, qc->dev, block, n_block, tf_flags,
qc->tag);
if (likely(rc == 0))
return 0; return 0;
if (rc == -ERANGE)
goto out_of_range;
/* treat all other errors as -EINVAL, fall through */
invalid_fld: invalid_fld:
ata_scsi_set_sense(qc->scsicmd, ILLEGAL_REQUEST, 0x24, 0x0); ata_scsi_set_sense(qc->scsicmd, ILLEGAL_REQUEST, 0x24, 0x0);
/* "Invalid field in cbd" */ /* "Invalid field in cbd" */
......
...@@ -51,7 +51,9 @@ extern int atapi_enabled; ...@@ -51,7 +51,9 @@ extern int atapi_enabled;
extern int atapi_dmadir; extern int atapi_dmadir;
extern int libata_fua; extern int libata_fua;
extern struct ata_queued_cmd *ata_qc_new_init(struct ata_device *dev); extern struct ata_queued_cmd *ata_qc_new_init(struct ata_device *dev);
extern int ata_rwcmd_protocol(struct ata_queued_cmd *qc); extern int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev,
u64 block, u32 n_block, unsigned int tf_flags,
unsigned int tag);
extern u64 ata_tf_read_block(struct ata_taskfile *tf, struct ata_device *dev); extern u64 ata_tf_read_block(struct ata_taskfile *tf, struct ata_device *dev);
extern void ata_dev_disable(struct ata_device *dev); extern void ata_dev_disable(struct ata_device *dev);
extern void ata_port_flush_task(struct ata_port *ap); extern void ata_port_flush_task(struct ata_port *ap);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment