queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
 }
 
-/**
- * sd_setup_discard_cmnd - unmap blocks on thinly provisioned device
- * @sdp: scsi device to operate on
- * @rq: Request to prepare
- *
- * Will issue either UNMAP or WRITE SAME(16) depending on preference
- * indicated by target device.
- **/
-static int sd_setup_discard_cmnd(struct scsi_cmnd *cmd)
+static int sd_setup_unmap_cmnd(struct scsi_cmnd *cmd)
 {
-       struct request *rq = cmd->request;
        struct scsi_device *sdp = cmd->device;
-       struct scsi_disk *sdkp = scsi_disk(rq->rq_disk);
-       sector_t sector = blk_rq_pos(rq);
-       unsigned int nr_sectors = blk_rq_sectors(rq);
-       unsigned int len;
-       int ret;
+       struct request *rq = cmd->request;
+       u64 sector = blk_rq_pos(rq) >> (ilog2(sdp->sector_size) - 9);
+       u32 nr_sectors = blk_rq_sectors(rq) >> (ilog2(sdp->sector_size) - 9);
+       unsigned int data_len = 24;
        char *buf;
-       struct page *page;
 
-       sector >>= ilog2(sdp->sector_size) - 9;
-       nr_sectors >>= ilog2(sdp->sector_size) - 9;
-
-       page = alloc_page(GFP_ATOMIC | __GFP_ZERO);
-       if (!page)
+       rq->special_vec.bv_page = alloc_page(GFP_ATOMIC | __GFP_ZERO);
+       if (!rq->special_vec.bv_page)
                return BLKPREP_DEFER;
+       rq->special_vec.bv_offset = 0;
+       rq->special_vec.bv_len = data_len;
+       rq->rq_flags |= RQF_SPECIAL_PAYLOAD;
 
-       switch (sdkp->provisioning_mode) {
-       case SD_LBP_UNMAP:
-               buf = page_address(page);
-
-               cmd->cmd_len = 10;
-               cmd->cmnd[0] = UNMAP;
-               cmd->cmnd[8] = 24;
-
-               put_unaligned_be16(6 + 16, &buf[0]);
-               put_unaligned_be16(16, &buf[2]);
-               put_unaligned_be64(sector, &buf[8]);
-               put_unaligned_be32(nr_sectors, &buf[16]);
+       cmd->cmd_len = 10;
+       cmd->cmnd[0] = UNMAP;
+       cmd->cmnd[8] = 24;
 
-               len = 24;
-               break;
+       buf = page_address(rq->special_vec.bv_page);
+       put_unaligned_be16(6 + 16, &buf[0]);
+       put_unaligned_be16(16, &buf[2]);
+       put_unaligned_be64(sector, &buf[8]);
+       put_unaligned_be32(nr_sectors, &buf[16]);
 
-       case SD_LBP_WS16:
-               cmd->cmd_len = 16;
-               cmd->cmnd[0] = WRITE_SAME_16;
-               cmd->cmnd[1] = 0x8; /* UNMAP */
-               put_unaligned_be64(sector, &cmd->cmnd[2]);
-               put_unaligned_be32(nr_sectors, &cmd->cmnd[10]);
+       cmd->allowed = SD_MAX_RETRIES;
+       cmd->transfersize = data_len;
+       rq->timeout = SD_TIMEOUT;
+       scsi_req(rq)->resid_len = data_len;
 
-               len = sdkp->device->sector_size;
-               break;
+       return scsi_init_io(cmd);
+}
 
-       case SD_LBP_WS10:
-       case SD_LBP_ZERO:
-               cmd->cmd_len = 10;
-               cmd->cmnd[0] = WRITE_SAME;
-               if (sdkp->provisioning_mode == SD_LBP_WS10)
-                       cmd->cmnd[1] = 0x8; /* UNMAP */
-               put_unaligned_be32(sector, &cmd->cmnd[2]);
-               put_unaligned_be16(nr_sectors, &cmd->cmnd[7]);
+static int sd_setup_write_same16_cmnd(struct scsi_cmnd *cmd)
+{
+       struct scsi_device *sdp = cmd->device;
+       struct request *rq = cmd->request;
+       u64 sector = blk_rq_pos(rq) >> (ilog2(sdp->sector_size) - 9);
+       u32 nr_sectors = blk_rq_sectors(rq) >> (ilog2(sdp->sector_size) - 9);
+       u32 data_len = sdp->sector_size;
 
-               len = sdkp->device->sector_size;
-               break;
+       rq->special_vec.bv_page = alloc_page(GFP_ATOMIC | __GFP_ZERO);
+       if (!rq->special_vec.bv_page)
+               return BLKPREP_DEFER;
+       rq->special_vec.bv_offset = 0;
+       rq->special_vec.bv_len = data_len;
+       rq->rq_flags |= RQF_SPECIAL_PAYLOAD;
 
-       default:
-               ret = BLKPREP_INVALID;
-               goto out;
-       }
+       cmd->cmd_len = 16;
+       cmd->cmnd[0] = WRITE_SAME_16;
+       cmd->cmnd[1] = 0x8; /* UNMAP */
+       put_unaligned_be64(sector, &cmd->cmnd[2]);
+       put_unaligned_be32(nr_sectors, &cmd->cmnd[10]);
 
+       cmd->allowed = SD_MAX_RETRIES;
+       cmd->transfersize = data_len;
        rq->timeout = SD_TIMEOUT;
+       scsi_req(rq)->resid_len = data_len;
 
-       cmd->transfersize = len;
-       cmd->allowed = SD_MAX_RETRIES;
+       return scsi_init_io(cmd);
+}
 
-       rq->special_vec.bv_page = page;
-       rq->special_vec.bv_offset = 0;
-       rq->special_vec.bv_len = len;
+static int sd_setup_write_same10_cmnd(struct scsi_cmnd *cmd, bool unmap)
+{
+       struct scsi_device *sdp = cmd->device;
+       struct request *rq = cmd->request;
+       u64 sector = blk_rq_pos(rq) >> (ilog2(sdp->sector_size) - 9);
+       u32 nr_sectors = blk_rq_sectors(rq) >> (ilog2(sdp->sector_size) - 9);
+       u32 data_len = sdp->sector_size;
 
+       rq->special_vec.bv_page = alloc_page(GFP_ATOMIC | __GFP_ZERO);
+       if (!rq->special_vec.bv_page)
+               return BLKPREP_DEFER;
+       rq->special_vec.bv_offset = 0;
+       rq->special_vec.bv_len = data_len;
        rq->rq_flags |= RQF_SPECIAL_PAYLOAD;
-       scsi_req(rq)->resid_len = len;
 
-       ret = scsi_init_io(cmd);
-out:
-       if (ret != BLKPREP_OK)
-               __free_page(page);
-       return ret;
+       cmd->cmd_len = 10;
+       cmd->cmnd[0] = WRITE_SAME;
+       if (unmap)
+               cmd->cmnd[1] = 0x8; /* UNMAP */
+       put_unaligned_be32(sector, &cmd->cmnd[2]);
+       put_unaligned_be16(nr_sectors, &cmd->cmnd[7]);
+
+       cmd->allowed = SD_MAX_RETRIES;
+       cmd->transfersize = data_len;
+       rq->timeout = SD_TIMEOUT;
+       scsi_req(rq)->resid_len = data_len;
+
+       return scsi_init_io(cmd);
 }
 
 static void sd_config_write_same(struct scsi_disk *sdkp)
 
        switch (req_op(rq)) {
        case REQ_OP_DISCARD:
-               return sd_setup_discard_cmnd(cmd);
+               switch (scsi_disk(rq->rq_disk)->provisioning_mode) {
+               case SD_LBP_UNMAP:
+                       return sd_setup_unmap_cmnd(cmd);
+               case SD_LBP_WS16:
+                       return sd_setup_write_same16_cmnd(cmd);
+               case SD_LBP_WS10:
+                       return sd_setup_write_same10_cmnd(cmd, true);
+               case SD_LBP_ZERO:
+                       return sd_setup_write_same10_cmnd(cmd, false);
+               default:
+                       return BLKPREP_INVALID;
+               }
        case REQ_OP_WRITE_SAME:
                return sd_setup_write_same_cmnd(cmd);
        case REQ_OP_FLUSH: