diff options
Diffstat (limited to 'drivers/scsi/sd.c')
-rw-r--r-- | drivers/scsi/sd.c | 85 |
1 files changed, 45 insertions, 40 deletions
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 3d3ad7d1b77..32d4d8d7b9f 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -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, }; /* @@ -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)); @@ -1498,7 +1489,21 @@ static int sd_revalidate_disk(struct gendisk *disk) 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); @@ -1598,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); @@ -1605,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); |