]> Pileus Git - ~andy/linux/commitdiff
[BLOCK] add FUA support to libata
authorTejun Heo <htejun@gmail.com>
Fri, 6 Jan 2006 08:56:18 +0000 (09:56 +0100)
committerJens Axboe <axboe@suse.de>
Fri, 6 Jan 2006 08:56:18 +0000 (09:56 +0100)
Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jens Axboe <axboe@suse.de>
drivers/scsi/libata-core.c
drivers/scsi/libata-scsi.c
drivers/scsi/libata.h
include/linux/ata.h
include/linux/libata.h

index 9ea1025879147ca524999e9595c53413ea808b1f..bdfb0a88cd6f675975cc44a172484c8e9085b2f2 100644 (file)
@@ -562,16 +562,28 @@ static const u8 ata_rw_cmds[] = {
        ATA_CMD_WRITE_MULTI,
        ATA_CMD_READ_MULTI_EXT,
        ATA_CMD_WRITE_MULTI_EXT,
+       0,
+       0,
+       0,
+       ATA_CMD_WRITE_MULTI_FUA_EXT,
        /* pio */
        ATA_CMD_PIO_READ,
        ATA_CMD_PIO_WRITE,
        ATA_CMD_PIO_READ_EXT,
        ATA_CMD_PIO_WRITE_EXT,
+       0,
+       0,
+       0,
+       0,
        /* dma */
        ATA_CMD_READ,
        ATA_CMD_WRITE,
        ATA_CMD_READ_EXT,
-       ATA_CMD_WRITE_EXT
+       ATA_CMD_WRITE_EXT,
+       0,
+       0,
+       0,
+       ATA_CMD_WRITE_FUA_EXT
 };
 
 /**
@@ -584,25 +596,32 @@ static const u8 ata_rw_cmds[] = {
  *     LOCKING:
  *     caller.
  */
-void ata_rwcmd_protocol(struct ata_queued_cmd *qc)
+int ata_rwcmd_protocol(struct ata_queued_cmd *qc)
 {
        struct ata_taskfile *tf = &qc->tf;
        struct ata_device *dev = qc->dev;
+       u8 cmd;
 
-       int index, lba48, write;
+       int index, fua, lba48, write;
  
+       fua = (tf->flags & ATA_TFLAG_FUA) ? 4 : 0;
        lba48 = (tf->flags & ATA_TFLAG_LBA48) ? 2 : 0;
        write = (tf->flags & ATA_TFLAG_WRITE) ? 1 : 0;
 
        if (dev->flags & ATA_DFLAG_PIO) {
                tf->protocol = ATA_PROT_PIO;
-               index = dev->multi_count ? 0 : 4;
+               index = dev->multi_count ? 0 : 8;
        } else {
                tf->protocol = ATA_PROT_DMA;
-               index = 8;
+               index = 16;
        }
 
-       tf->command = ata_rw_cmds[index + lba48 + write];
+       cmd = ata_rw_cmds[index + fua + lba48 + write];
+       if (cmd) {
+               tf->command = cmd;
+               return 0;
+       }
+       return -1;
 }
 
 static const char * const xfer_mode_str[] = {
index e0439be4b5731fd2b8390c479f0c756043030af7..2c644cbb6e9c4bb966a9bbeb217597490e03a703 100644 (file)
@@ -1080,11 +1080,13 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, const u8 *scsicm
            scsicmd[0] == WRITE_16)
                tf->flags |= ATA_TFLAG_WRITE;
 
-       /* Calculate the SCSI LBA and transfer length. */
+       /* Calculate the SCSI LBA, transfer length and FUA. */
        switch (scsicmd[0]) {
        case READ_10:
        case WRITE_10:
                scsi_10_lba_len(scsicmd, &block, &n_block);
+               if (unlikely(scsicmd[1] & (1 << 3)))
+                       tf->flags |= ATA_TFLAG_FUA;
                break;
        case READ_6:
        case WRITE_6:
@@ -1099,6 +1101,8 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, const u8 *scsicm
        case READ_16:
        case WRITE_16:
                scsi_16_lba_len(scsicmd, &block, &n_block);
+               if (unlikely(scsicmd[1] & (1 << 3)))
+                       tf->flags |= ATA_TFLAG_FUA;
                break;
        default:
                DPRINTK("no-byte command\n");
@@ -1142,7 +1146,8 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, const u8 *scsicm
                        tf->device |= (block >> 24) & 0xf;
                }
 
-               ata_rwcmd_protocol(qc);
+               if (unlikely(ata_rwcmd_protocol(qc) < 0))
+                       goto invalid_fld;
 
                qc->nsect = n_block;
                tf->nsect = n_block & 0xff;
@@ -1160,7 +1165,8 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, const u8 *scsicm
                if ((block >> 28) || (n_block > 256))
                        goto out_of_range;
 
-               ata_rwcmd_protocol(qc);
+               if (unlikely(ata_rwcmd_protocol(qc) < 0))
+                       goto invalid_fld;
 
                /* Convert LBA to CHS */
                track = (u32)block / dev->sectors;
@@ -1695,6 +1701,7 @@ static unsigned int ata_msense_rw_recovery(u8 **ptr_io, const u8 *last)
 unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf,
                                  unsigned int buflen)
 {
+       struct ata_device *dev = args->dev;
        u8 *scsicmd = args->cmd->cmnd, *p, *last;
        const u8 sat_blk_desc[] = {
                0, 0, 0, 0,     /* number of blocks: sat unspecified */
@@ -1703,6 +1710,7 @@ unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf,
        };
        u8 pg, spg;
        unsigned int ebd, page_control, six_byte, output_len, alloc_len, minlen;
+       u8 dpofua;
 
        VPRINTK("ENTER\n");
 
@@ -1771,9 +1779,17 @@ unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf,
 
        if (minlen < 1)
                return 0;
+
+       dpofua = 0;
+       if (ata_id_has_fua(args->id) && dev->flags & ATA_DFLAG_LBA48 &&
+           (!(dev->flags & ATA_DFLAG_PIO) || dev->multi_count))
+               dpofua = 1 << 4;
+
        if (six_byte) {
                output_len--;
                rbuf[0] = output_len;
+               if (minlen > 2)
+                       rbuf[2] |= dpofua;
                if (ebd) {
                        if (minlen > 3)
                                rbuf[3] = sizeof(sat_blk_desc);
@@ -1786,6 +1802,8 @@ unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf,
                rbuf[0] = output_len >> 8;
                if (minlen > 1)
                        rbuf[1] = output_len;
+               if (minlen > 3)
+                       rbuf[3] |= dpofua;
                if (ebd) {
                        if (minlen > 7)
                                rbuf[7] = sizeof(sat_blk_desc);
@@ -2446,7 +2464,7 @@ int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
                if (xlat_func)
                        ata_scsi_translate(ap, dev, cmd, done, xlat_func);
                else
-                       ata_scsi_simulate(dev->id, cmd, done);
+                       ata_scsi_simulate(ap, dev, cmd, done);
        } else
                ata_scsi_translate(ap, dev, cmd, done, atapi_xlat);
 
@@ -2469,14 +2487,16 @@ out_unlock:
  *     spin_lock_irqsave(host_set lock)
  */
 
-void ata_scsi_simulate(u16 *id,
+void ata_scsi_simulate(struct ata_port *ap, struct ata_device *dev,
                      struct scsi_cmnd *cmd,
                      void (*done)(struct scsi_cmnd *))
 {
        struct ata_scsi_args args;
        const u8 *scsicmd = cmd->cmnd;
 
-       args.id = id;
+       args.ap = ap;
+       args.dev = dev;
+       args.id = dev->id;
        args.cmd = cmd;
        args.done = done;
 
index 251e53bdc6e03af366fae3c912047b554a4a30b8..e03ce48b7b4b633c8752319470cdb56105054659 100644 (file)
@@ -32,6 +32,8 @@
 #define DRV_VERSION    "1.20"  /* must be exactly four chars */
 
 struct ata_scsi_args {
+       struct ata_port         *ap;
+       struct ata_device       *dev;
        u16                     *id;
        struct scsi_cmnd        *cmd;
        void                    (*done)(struct scsi_cmnd *);
@@ -41,7 +43,7 @@ struct ata_scsi_args {
 extern int atapi_enabled;
 extern struct ata_queued_cmd *ata_qc_new_init(struct ata_port *ap,
                                      struct ata_device *dev);
-extern void ata_rwcmd_protocol(struct ata_queued_cmd *qc);
+extern int ata_rwcmd_protocol(struct ata_queued_cmd *qc);
 extern void ata_qc_free(struct ata_queued_cmd *qc);
 extern int ata_qc_issue(struct ata_queued_cmd *qc);
 extern int ata_check_atapi_dma(struct ata_queued_cmd *qc);
index d2873b732bb1255ac08515d21e3ca832f601a058..f63dad4165b1d98f907306258a2cb3297406dcbd 100644 (file)
@@ -129,6 +129,7 @@ enum {
        ATA_CMD_READ_EXT        = 0x25,
        ATA_CMD_WRITE           = 0xCA,
        ATA_CMD_WRITE_EXT       = 0x35,
+       ATA_CMD_WRITE_FUA_EXT   = 0x3D,
        ATA_CMD_PIO_READ        = 0x20,
        ATA_CMD_PIO_READ_EXT    = 0x24,
        ATA_CMD_PIO_WRITE       = 0x30,
@@ -137,6 +138,7 @@ enum {
        ATA_CMD_READ_MULTI_EXT  = 0x29,
        ATA_CMD_WRITE_MULTI     = 0xC5,
        ATA_CMD_WRITE_MULTI_EXT = 0x39,
+       ATA_CMD_WRITE_MULTI_FUA_EXT = 0xCE,
        ATA_CMD_SET_FEATURES    = 0xEF,
        ATA_CMD_PACKET          = 0xA0,
        ATA_CMD_VERIFY          = 0x40,
@@ -192,6 +194,7 @@ enum {
        ATA_TFLAG_DEVICE        = (1 << 2), /* enable r/w to device reg */
        ATA_TFLAG_WRITE         = (1 << 3), /* data dir: host->dev==1 (write) */
        ATA_TFLAG_LBA           = (1 << 4), /* enable LBA */
+       ATA_TFLAG_FUA           = (1 << 5), /* enable FUA */
 };
 
 enum ata_tf_protocols {
@@ -245,7 +248,8 @@ struct ata_taskfile {
 #define ata_id_is_sata(id)     ((id)[93] == 0)
 #define ata_id_rahead_enabled(id) ((id)[85] & (1 << 6))
 #define ata_id_wcache_enabled(id) ((id)[85] & (1 << 5))
-#define ata_id_has_flush(id) ((id)[83] & (1 << 12))
+#define ata_id_has_fua(id)     ((id)[84] & (1 << 6))
+#define ata_id_has_flush(id)   ((id)[83] & (1 << 12))
 #define ata_id_has_flush_ext(id) ((id)[83] & (1 << 13))
 #define ata_id_has_lba48(id)   ((id)[83] & (1 << 10))
 #define ata_id_has_wcache(id)  ((id)[82] & (1 << 5))
index e828e172ccbf1f2d80b6838c43000d0379117abb..6db2c0845731e09a50df4d4209039705575c6f16 100644 (file)
@@ -480,7 +480,8 @@ extern u8   ata_bmdma_status(struct ata_port *ap);
 extern void ata_bmdma_irq_clear(struct ata_port *ap);
 extern void ata_qc_complete(struct ata_queued_cmd *qc);
 extern void ata_eng_timeout(struct ata_port *ap);
-extern void ata_scsi_simulate(u16 *id, struct scsi_cmnd *cmd,
+extern void ata_scsi_simulate(struct ata_port *ap, struct ata_device *dev,
+                             struct scsi_cmnd *cmd,
                              void (*done)(struct scsi_cmnd *));
 extern int ata_std_bios_param(struct scsi_device *sdev,
                              struct block_device *bdev,