Merge branch 'for-2.6.36' of git://git.kernel.dk/linux-2.6-block
[pandora-kernel.git] / drivers / scsi / sd.c
index cc8a1d1..8e2e893 100644 (file)
@@ -46,6 +46,7 @@
 #include <linux/blkdev.h>
 #include <linux/blkpg.h>
 #include <linux/delay.h>
+#include <linux/smp_lock.h>
 #include <linux/mutex.h>
 #include <linux/string_helpers.h>
 #include <linux/async.h>
@@ -411,54 +412,85 @@ static void sd_prot_op(struct scsi_cmnd *scmd, unsigned int dif)
 }
 
 /**
- * sd_prepare_discard - unmap blocks on thinly provisioned device
+ * scsi_setup_discard_cmnd - unmap blocks on thinly provisioned device
+ * @sdp: scsi device to operate one
  * @rq: Request to prepare
  *
  * Will issue either UNMAP or WRITE SAME(16) depending on preference
  * indicated by target device.
  **/
-static int sd_prepare_discard(struct request *rq)
+static int scsi_setup_discard_cmnd(struct scsi_device *sdp, struct request *rq)
 {
        struct scsi_disk *sdkp = scsi_disk(rq->rq_disk);
        struct bio *bio = rq->bio;
        sector_t sector = bio->bi_sector;
-       unsigned int num = bio_sectors(bio);
+       unsigned int nr_sectors = bio_sectors(bio);
+       unsigned int len;
+       int ret;
+       struct page *page;
 
        if (sdkp->device->sector_size == 4096) {
                sector >>= 3;
-               num >>= 3;
+               nr_sectors >>= 3;
        }
 
-       rq->cmd_type = REQ_TYPE_BLOCK_PC;
        rq->timeout = SD_TIMEOUT;
 
        memset(rq->cmd, 0, rq->cmd_len);
 
+       page = alloc_page(GFP_ATOMIC | __GFP_ZERO);
+       if (!page)
+               return BLKPREP_DEFER;
+
        if (sdkp->unmap) {
-               char *buf = kmap_atomic(bio_page(bio), KM_USER0);
+               char *buf = page_address(page);
 
+               rq->cmd_len = 10;
                rq->cmd[0] = UNMAP;
                rq->cmd[8] = 24;
-               rq->cmd_len = 10;
-
-               /* Ensure that data length matches payload */
-               rq->__data_len = bio->bi_size = bio->bi_io_vec->bv_len = 24;
 
                put_unaligned_be16(6 + 16, &buf[0]);
                put_unaligned_be16(16, &buf[2]);
                put_unaligned_be64(sector, &buf[8]);
-               put_unaligned_be32(num, &buf[16]);
+               put_unaligned_be32(nr_sectors, &buf[16]);
 
-               kunmap_atomic(buf, KM_USER0);
+               len = 24;
        } else {
+               rq->cmd_len = 16;
                rq->cmd[0] = WRITE_SAME_16;
                rq->cmd[1] = 0x8; /* UNMAP */
                put_unaligned_be64(sector, &rq->cmd[2]);
-               put_unaligned_be32(num, &rq->cmd[10]);
-               rq->cmd_len = 16;
+               put_unaligned_be32(nr_sectors, &rq->cmd[10]);
+
+               len = sdkp->device->sector_size;
        }
 
-       return BLKPREP_OK;
+       blk_add_request_payload(rq, page, len);
+       ret = scsi_setup_blk_pc_cmnd(sdp, rq);
+       rq->buffer = page_address(page);
+       if (ret != BLKPREP_OK) {
+               __free_page(page);
+               rq->buffer = NULL;
+       }
+       return ret;
+}
+
+static int scsi_setup_flush_cmnd(struct scsi_device *sdp, struct request *rq)
+{
+       rq->timeout = SD_TIMEOUT;
+       rq->retries = SD_MAX_RETRIES;
+       rq->cmd[0] = SYNCHRONIZE_CACHE;
+       rq->cmd_len = 10;
+
+       return scsi_setup_blk_pc_cmnd(sdp, rq);
+}
+
+static void sd_unprep_fn(struct request_queue *q, struct request *rq)
+{
+       if (rq->cmd_flags & REQ_DISCARD) {
+               free_page((unsigned long)rq->buffer);
+               rq->buffer = NULL;
+       }
 }
 
 /**
@@ -485,10 +517,13 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
         * Discard request come in as REQ_TYPE_FS but we turn them into
         * block PC requests to make life easier.
         */
-       if (blk_discard_rq(rq))
-               ret = sd_prepare_discard(rq);
-
-       if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
+       if (rq->cmd_flags & REQ_DISCARD) {
+               ret = scsi_setup_discard_cmnd(sdp, rq);
+               goto out;
+       } else if (rq->cmd_flags & REQ_FLUSH) {
+               ret = scsi_setup_flush_cmnd(sdp, rq);
+               goto out;
+       } else if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
                ret = scsi_setup_blk_pc_cmnd(sdp, rq);
                goto out;
        } else if (rq->cmd_type != REQ_TYPE_FS) {
@@ -636,7 +671,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
                SCpnt->cmnd[0] = VARIABLE_LENGTH_CMD;
                SCpnt->cmnd[7] = 0x18;
                SCpnt->cmnd[9] = (rq_data_dir(rq) == READ) ? READ_32 : WRITE_32;
-               SCpnt->cmnd[10] = protect | (blk_fua_rq(rq) ? 0x8 : 0);
+               SCpnt->cmnd[10] = protect | ((rq->cmd_flags & REQ_FUA) ? 0x8 : 0);
 
                /* LBA */
                SCpnt->cmnd[12] = sizeof(block) > 4 ? (unsigned char) (block >> 56) & 0xff : 0;
@@ -661,7 +696,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
                SCpnt->cmnd[31] = (unsigned char) this_count & 0xff;
        } else if (block > 0xffffffff) {
                SCpnt->cmnd[0] += READ_16 - READ_6;
-               SCpnt->cmnd[1] = protect | (blk_fua_rq(rq) ? 0x8 : 0);
+               SCpnt->cmnd[1] = protect | ((rq->cmd_flags & REQ_FUA) ? 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;
@@ -682,7 +717,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
                        this_count = 0xffff;
 
                SCpnt->cmnd[0] += READ_10 - READ_6;
-               SCpnt->cmnd[1] = protect | (blk_fua_rq(rq) ? 0x8 : 0);
+               SCpnt->cmnd[1] = protect | ((rq->cmd_flags & REQ_FUA) ? 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;
@@ -691,7 +726,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
                SCpnt->cmnd[7] = (unsigned char) (this_count >> 8) & 0xff;
                SCpnt->cmnd[8] = (unsigned char) this_count & 0xff;
        } else {
-               if (unlikely(blk_fua_rq(rq))) {
+               if (unlikely(rq->cmd_flags & REQ_FUA)) {
                        /*
                         * This happens only if this drive failed
                         * 10byte rw command with ILLEGAL_REQUEST
@@ -745,6 +780,8 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
  *     or from within the kernel (e.g. as a result of a mount(1) ).
  *     In the latter case @inode and @filp carry an abridged amount
  *     of information as noted above.
+ *
+ *     Locking: called with bdev->bd_mutex held.
  **/
 static int sd_open(struct block_device *bdev, fmode_t mode)
 {
@@ -799,7 +836,7 @@ static int sd_open(struct block_device *bdev, fmode_t mode)
        if (!scsi_device_online(sdev))
                goto error_out;
 
-       if (!sdkp->openers++ && sdev->removable) {
+       if ((atomic_inc_return(&sdkp->openers) == 1) && sdev->removable) {
                if (scsi_block_when_processing_errors(sdev))
                        scsi_set_medium_removal(sdev, SCSI_REMOVAL_PREVENT);
        }
@@ -823,6 +860,8 @@ error_autopm:
  *
  *     Note: may block (uninterruptible) if error recovery is underway
  *     on this disk.
+ *
+ *     Locking: called with bdev->bd_mutex held.
  **/
 static int sd_release(struct gendisk *disk, fmode_t mode)
 {
@@ -831,7 +870,7 @@ static int sd_release(struct gendisk *disk, fmode_t mode)
 
        SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_release\n"));
 
-       if (!--sdkp->openers && sdev->removable) {
+       if (atomic_dec_return(&sdkp->openers) && sdev->removable) {
                if (scsi_block_when_processing_errors(sdev))
                        scsi_set_medium_removal(sdev, SCSI_REMOVAL_ALLOW);
        }
@@ -904,7 +943,7 @@ static int sd_ioctl(struct block_device *bdev, fmode_t mode,
        error = scsi_nonblockable_ioctl(sdp, cmd, p,
                                        (mode & FMODE_NDELAY) != 0);
        if (!scsi_block_when_processing_errors(sdp) || !error)
-               return error;
+               goto out;
 
        /*
         * Send SCSI addressing ioctls directly to mid level, send other
@@ -914,13 +953,17 @@ static int sd_ioctl(struct block_device *bdev, fmode_t mode,
        switch (cmd) {
                case SCSI_IOCTL_GET_IDLUN:
                case SCSI_IOCTL_GET_BUS_NUMBER:
-                       return scsi_ioctl(sdp, cmd, p);
+                       error = scsi_ioctl(sdp, cmd, p);
+                       break;
                default:
                        error = scsi_cmd_ioctl(disk->queue, disk, mode, cmd, p);
                        if (error != -ENOTTY)
-                               return error;
+                               break;
+                       error = scsi_ioctl(sdp, cmd, p);
+                       break;
        }
-       return scsi_ioctl(sdp, cmd, p);
+out:
+       return error;
 }
 
 static void set_media_not_present(struct scsi_disk *sdkp)
@@ -1045,15 +1088,6 @@ static int sd_sync_cache(struct scsi_disk *sdkp)
        return 0;
 }
 
-static void sd_prepare_flush(struct request_queue *q, struct request *rq)
-{
-       rq->cmd_type = REQ_TYPE_BLOCK_PC;
-       rq->timeout = SD_TIMEOUT;
-       rq->retries = SD_MAX_RETRIES;
-       rq->cmd[0] = SYNCHRONIZE_CACHE;
-       rq->cmd_len = 10;
-}
-
 static void sd_rescan(struct device *dev)
 {
        struct scsi_disk *sdkp = scsi_disk_get_from_dev(dev);
@@ -1103,7 +1137,7 @@ static const struct block_device_operations sd_fops = {
        .owner                  = THIS_MODULE,
        .open                   = sd_open,
        .release                = sd_release,
-       .locked_ioctl           = sd_ioctl,
+       .ioctl                  = sd_ioctl,
        .getgeo                 = sd_getgeo,
 #ifdef CONFIG_COMPAT
        .compat_ioctl           = sd_compat_ioctl,
@@ -1120,7 +1154,7 @@ static unsigned int sd_completed_bytes(struct scsi_cmnd *scmd)
        u64 bad_lba;
        int info_valid;
 
-       if (!blk_fs_request(scmd->request))
+       if (scmd->request->cmd_type != REQ_TYPE_FS)
                return 0;
 
        info_valid = scsi_get_sense_info_fld(scmd->sense_buffer,
@@ -1171,6 +1205,12 @@ static int sd_done(struct scsi_cmnd *SCpnt)
        int sense_valid = 0;
        int sense_deferred = 0;
 
+       if (SCpnt->request->cmd_flags & REQ_DISCARD) {
+               if (!result)
+                       scsi_set_resid(SCpnt, 0);
+               return good_bytes;
+       }
+
        if (result) {
                sense_valid = scsi_command_normalize_sense(SCpnt, &sshdr);
                if (sense_valid)
@@ -2121,7 +2161,7 @@ static int sd_revalidate_disk(struct gendisk *disk)
        else
                ordered = QUEUE_ORDERED_DRAIN;
 
-       blk_queue_ordered(sdkp->disk->queue, ordered, sd_prepare_flush);
+       blk_queue_ordered(sdkp->disk->queue, ordered);
 
        set_capacity(disk, sdkp->capacity);
        kfree(buffer);
@@ -2234,6 +2274,7 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
        sd_revalidate_disk(gd);
 
        blk_queue_prep_rq(sdp->request_queue, sd_prep_fn);
+       blk_queue_unprep_rq(sdp->request_queue, sd_unprep_fn);
 
        gd->driverfs_dev = &sdp->sdev_gendev;
        gd->flags = GENHD_FL_EXT_DEVT;
@@ -2313,7 +2354,7 @@ static int sd_probe(struct device *dev)
        sdkp->driver = &sd_template;
        sdkp->disk = gd;
        sdkp->index = index;
-       sdkp->openers = 0;
+       atomic_set(&sdkp->openers, 0);
        sdkp->previous_state = 1;
 
        if (!sdp->request_queue->rq_timeout) {
@@ -2372,6 +2413,7 @@ static int sd_remove(struct device *dev)
 
        async_synchronize_full();
        blk_queue_prep_rq(sdkp->device->request_queue, scsi_prep_fn);
+       blk_queue_unprep_rq(sdkp->device->request_queue, NULL);
        device_del(&sdkp->dev);
        del_gendisk(sdkp->disk);
        sd_shutdown(dev);