[BLOCK] scsi: add FUA support to sd
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / scsi / sd.c
index 03fcbab30033976abcd4d21f5a2bde5016510ecd..32d4d8d7b9f393e6524153ac7a71c9c00784116c 100644 (file)
@@ -102,6 +102,7 @@ struct scsi_disk {
        u8              write_prot;
        unsigned        WCE : 1;        /* state of disk WCE bit */
        unsigned        RCD : 1;        /* state of disk RCD bit, unused */
+       unsigned        DPOFUA : 1;     /* state of disk DPOFUA bit */
 };
 
 static DEFINE_IDR(sd_index_idr);
@@ -121,8 +122,7 @@ static void sd_shutdown(struct device *dev);
 static void sd_rescan(struct device *);
 static int sd_init_command(struct scsi_cmnd *);
 static int sd_issue_flush(struct device *, sector_t *);
-static void sd_end_flush(request_queue_t *, struct request *);
-static int sd_prepare_flush(request_queue_t *, struct request *);
+static void sd_prepare_flush(request_queue_t *, struct request *);
 static void sd_read_capacity(struct scsi_disk *sdkp, char *diskname,
                             unsigned char *buffer);
 
@@ -137,8 +137,6 @@ static struct scsi_driver sd_template = {
        .rescan                 = sd_rescan,
        .init_command           = sd_init_command,
        .issue_flush            = sd_issue_flush,
-       .prepare_flush          = sd_prepare_flush,
-       .end_flush              = sd_end_flush,
 };
 
 /*
@@ -245,7 +243,7 @@ static int sd_init_command(struct scsi_cmnd * SCpnt)
         * SG_IO from block layer already setup, just copy cdb basically
         */
        if (blk_pc_request(rq)) {
-               scsi_setup_blk_pc_cmnd(SCpnt, SD_PASSTHROUGH_RETRIES);
+               scsi_setup_blk_pc_cmnd(SCpnt);
                if (rq->timeout)
                        timeout = rq->timeout;
 
@@ -346,6 +344,7 @@ static int sd_init_command(struct scsi_cmnd * SCpnt)
        
        if (block > 0xffffffff) {
                SCpnt->cmnd[0] += READ_16 - READ_6;
+               SCpnt->cmnd[1] |= blk_fua_rq(rq) ? 0x8 : 0;
                SCpnt->cmnd[2] = sizeof(block) > 4 ? (unsigned char) (block >> 56) & 0xff : 0;
                SCpnt->cmnd[3] = sizeof(block) > 4 ? (unsigned char) (block >> 48) & 0xff : 0;
                SCpnt->cmnd[4] = sizeof(block) > 4 ? (unsigned char) (block >> 40) & 0xff : 0;
@@ -365,6 +364,7 @@ static int sd_init_command(struct scsi_cmnd * SCpnt)
                        this_count = 0xffff;
 
                SCpnt->cmnd[0] += READ_10 - READ_6;
+               SCpnt->cmnd[1] |= blk_fua_rq(rq) ? 0x8 : 0;
                SCpnt->cmnd[2] = (unsigned char) (block >> 24) & 0xff;
                SCpnt->cmnd[3] = (unsigned char) (block >> 16) & 0xff;
                SCpnt->cmnd[4] = (unsigned char) (block >> 8) & 0xff;
@@ -373,6 +373,17 @@ static int sd_init_command(struct scsi_cmnd * SCpnt)
                SCpnt->cmnd[7] = (unsigned char) (this_count >> 8) & 0xff;
                SCpnt->cmnd[8] = (unsigned char) this_count & 0xff;
        } else {
+               if (unlikely(blk_fua_rq(rq))) {
+                       /*
+                        * This happens only if this drive failed
+                        * 10byte rw command with ILLEGAL_REQUEST
+                        * during operation and thus turned off
+                        * use_10_for_rw.
+                        */
+                       printk(KERN_ERR "sd: FUA write on READ/WRITE(6) drive\n");
+                       return 0;
+               }
+
                SCpnt->cmnd[1] |= (unsigned char) ((block >> 16) & 0x1f);
                SCpnt->cmnd[2] = (unsigned char) ((block >> 8) & 0xff);
                SCpnt->cmnd[3] = (unsigned char) block & 0xff;
@@ -729,42 +740,13 @@ static int sd_issue_flush(struct device *dev, sector_t *error_sector)
        return ret;
 }
 
-static void sd_end_flush(request_queue_t *q, struct request *flush_rq)
-{
-       struct request *rq = flush_rq->end_io_data;
-       struct scsi_cmnd *cmd = rq->special;
-       unsigned int bytes = rq->hard_nr_sectors << 9;
-
-       if (!flush_rq->errors) {
-               spin_unlock(q->queue_lock);
-               scsi_io_completion(cmd, bytes, 0);
-               spin_lock(q->queue_lock);
-       } else if (blk_barrier_postflush(rq)) {
-               spin_unlock(q->queue_lock);
-               scsi_io_completion(cmd, 0, bytes);
-               spin_lock(q->queue_lock);
-       } else {
-               /*
-                * force journal abort of barriers
-                */
-               end_that_request_first(rq, -EOPNOTSUPP, rq->hard_nr_sectors);
-               end_that_request_last(rq);
-       }
-}
-
-static int sd_prepare_flush(request_queue_t *q, struct request *rq)
+static void sd_prepare_flush(request_queue_t *q, struct request *rq)
 {
-       struct scsi_device *sdev = q->queuedata;
-       struct scsi_disk *sdkp = dev_get_drvdata(&sdev->sdev_gendev);
-
-       if (!sdkp || !sdkp->WCE)
-               return 0;
-
        memset(rq->cmd, 0, sizeof(rq->cmd));
-       rq->flags |= REQ_BLOCK_PC | REQ_SOFTBARRIER;
+       rq->flags |= REQ_BLOCK_PC;
        rq->timeout = SD_TIMEOUT;
        rq->cmd[0] = SYNCHRONIZE_CACHE;
-       return 1;
+       rq->cmd_len = 10;
 }
 
 static void sd_rescan(struct device *dev)
@@ -1427,10 +1409,18 @@ sd_read_cache_type(struct scsi_disk *sdkp, char *diskname,
                        sdkp->RCD = 0;
                }
 
+               sdkp->DPOFUA = (data.device_specific & 0x10) != 0;
+               if (sdkp->DPOFUA && !sdkp->device->use_10_for_rw) {
+                       printk(KERN_NOTICE "SCSI device %s: uses "
+                              "READ/WRITE(6), disabling FUA\n", diskname);
+                       sdkp->DPOFUA = 0;
+               }
+
                ct =  sdkp->RCD + 2*sdkp->WCE;
 
-               printk(KERN_NOTICE "SCSI device %s: drive cache: %s\n",
-                      diskname, types[ct]);
+               printk(KERN_NOTICE "SCSI device %s: drive cache: %s%s\n",
+                      diskname, types[ct],
+                      sdkp->DPOFUA ? " w/ FUA" : "");
 
                return;
        }
@@ -1462,6 +1452,7 @@ static int sd_revalidate_disk(struct gendisk *disk)
        struct scsi_disk *sdkp = scsi_disk(disk);
        struct scsi_device *sdp = sdkp->device;
        unsigned char *buffer;
+       unsigned ordered;
 
        SCSI_LOG_HLQUEUE(3, printk("sd_revalidate_disk: disk=%s\n", disk->disk_name));
 
@@ -1495,12 +1486,24 @@ static int sd_revalidate_disk(struct gendisk *disk)
         */
        if (sdkp->media_present) {
                sd_read_capacity(sdkp, disk->disk_name, buffer);
-               if (sdp->removable)
-                       sd_read_write_protect_flag(sdkp, disk->disk_name,
-                                                  buffer);
+               sd_read_write_protect_flag(sdkp, disk->disk_name, buffer);
                sd_read_cache_type(sdkp, disk->disk_name, buffer);
        }
-               
+
+       /*
+        * We now have all cache related info, determine how we deal
+        * with ordered requests.  Note that as the current SCSI
+        * dispatch function can alter request order, we cannot use
+        * QUEUE_ORDERED_TAG_* even when ordered tag is supported.
+        */
+       if (sdkp->WCE)
+               ordered = sdkp->DPOFUA
+                       ? QUEUE_ORDERED_DRAIN_FUA : QUEUE_ORDERED_DRAIN_FLUSH;
+       else
+               ordered = QUEUE_ORDERED_DRAIN;
+
+       blk_queue_ordered(sdkp->disk->queue, ordered, sd_prepare_flush);
+
        set_capacity(disk, sdkp->capacity);
        kfree(buffer);
 
@@ -1600,6 +1603,7 @@ static int sd_probe(struct device *dev)
        strcpy(gd->devfs_name, sdp->devfs_name);
 
        gd->private_data = &sdkp->driver;
+       gd->queue = sdkp->device->request_queue;
 
        sd_revalidate_disk(gd);
 
@@ -1607,7 +1611,6 @@ static int sd_probe(struct device *dev)
        gd->flags = GENHD_FL_DRIVERFS;
        if (sdp->removable)
                gd->flags |= GENHD_FL_REMOVABLE;
-       gd->queue = sdkp->device->request_queue;
 
        dev_set_drvdata(dev, sdkp);
        add_disk(gd);