target/file: Add WRITE_SAME w/ UNMAP=1 emulation support
authorAsias He <asias@redhat.com>
Mon, 25 Feb 2013 06:03:42 +0000 (14:03 +0800)
committerNicholas Bellinger <nab@linux-iscsi.org>
Thu, 25 Apr 2013 08:05:23 +0000 (01:05 -0700)
This patch adds support for emulation of WRITE_SAME w/ UNMAP=1 within
fd_execute_write_same_unmap() backend code.

If the FILEIO backend is normal file, the emulation uses fallocate to
punch hole to reclaim the free space used by the file. If the FILEIO
backend is block device, the emulation uses blkdev_issue_discard().

Tested with 512, 1k, 2k, and 4k block_sizes.

Changes in v2:
- Set the various dev->dev_attrib.*unmap* values (nab)

Cc: Christoph Hellwig <hch@lst.de>
Cc: Martin K. Petersen <martin.petersen@oracle.com>
Cc: Nicholas Bellinger <nab@linux-iscsi.org>
Signed-off-by: Asias He <asias@redhat.com>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
drivers/target/target_core_file.c

index 17a6acbc3ab0e300b1db04717ecf68804358319f..e1878bfd97b76e6fa0b47ab5c27a5e4c33cbee30 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/module.h>
+#include <linux/falloc.h>
 #include <scsi/scsi.h>
 #include <scsi/scsi_host.h>
 
@@ -166,6 +167,30 @@ static int fd_configure_device(struct se_device *dev)
                        " block_device blocks: %llu logical_block_size: %d\n",
                        dev_size, div_u64(dev_size, fd_dev->fd_block_size),
                        fd_dev->fd_block_size);
+               /*
+                * Check if the underlying struct block_device request_queue supports
+                * the QUEUE_FLAG_DISCARD bit for UNMAP/WRITE_SAME in SCSI + TRIM
+                * in ATA and we need to set TPE=1
+                */
+               if (blk_queue_discard(q)) {
+                       dev->dev_attrib.max_unmap_lba_count =
+                               q->limits.max_discard_sectors;
+                       /*
+                        * Currently hardcoded to 1 in Linux/SCSI code..
+                        */
+                       dev->dev_attrib.max_unmap_block_desc_count = 1;
+                       dev->dev_attrib.unmap_granularity =
+                               q->limits.discard_granularity >> 9;
+                       dev->dev_attrib.unmap_granularity_alignment =
+                               q->limits.discard_alignment;
+                       pr_debug("IFILE: BLOCK Discard support available,"
+                                       " disabled by default\n");
+               }
+               /*
+                * Enable write same emulation for IBLOCK and use 0xFFFF as
+                * the smaller WRITE_SAME(10) only has a two-byte block count.
+                */
+               dev->dev_attrib.max_write_same_len = 0xFFFF;
        } else {
                if (!(fd_dev->fbd_flags & FBDF_HAS_SIZE)) {
                        pr_err("FILEIO: Missing fd_dev_size="
@@ -176,6 +201,23 @@ static int fd_configure_device(struct se_device *dev)
 
                dev->dev_attrib.hw_block_size = FD_BLOCKSIZE;
                dev->dev_attrib.hw_max_sectors = FD_MAX_SECTORS;
+
+               /*
+                * Limit UNMAP emulation to 8k Number of LBAs (NoLB)
+                */
+               dev->dev_attrib.max_unmap_lba_count = 0x2000;
+               /*
+                * Currently hardcoded to 1 in Linux/SCSI code..
+                */
+               dev->dev_attrib.max_unmap_block_desc_count = 1;
+               dev->dev_attrib.unmap_granularity = 1;
+               dev->dev_attrib.unmap_granularity_alignment = 0;
+
+               /*
+                * Limit WRITE_SAME w/ UNMAP=0 emulation to 8k Number of LBAs (NoLB)
+                * based upon struct iovec limit for vfs_writev()
+                */
+               dev->dev_attrib.max_write_same_len = 0x1000;
        }
 
        fd_dev->fd_block_size = dev->dev_attrib.hw_block_size;
@@ -190,11 +232,6 @@ static int fd_configure_device(struct se_device *dev)
 
        fd_dev->fd_dev_id = fd_host->fd_host_dev_id_count++;
        fd_dev->fd_queue_depth = dev->queue_depth;
-       /*
-        * Limit WRITE_SAME w/ UNMAP=0 emulation to 8k Number of LBAs (NoLB)
-        * based upon struct iovec limit for vfs_writev()
-        */
-       dev->dev_attrib.max_write_same_len = 0x1000;
 
        pr_debug("CORE_FILE[%u] - Added TCM FILEIO Device ID: %u at %s,"
                " %llu total bytes\n", fd_host->fd_host_id, fd_dev->fd_dev_id,
@@ -441,6 +478,52 @@ fd_execute_write_same(struct se_cmd *cmd)
        return 0;
 }
 
+static sense_reason_t
+fd_execute_write_same_unmap(struct se_cmd *cmd)
+{
+       struct se_device *se_dev = cmd->se_dev;
+       struct fd_dev *fd_dev = FD_DEV(se_dev);
+       struct file *file = fd_dev->fd_file;
+       struct inode *inode = file->f_mapping->host;
+       sector_t nolb = sbc_get_write_same_sectors(cmd);
+       int ret;
+
+       if (!nolb) {
+               target_complete_cmd(cmd, SAM_STAT_GOOD);
+               return 0;
+       }
+
+       if (S_ISBLK(inode->i_mode)) {
+               /* The backend is block device, use discard */
+               struct block_device *bdev = inode->i_bdev;
+
+               ret = blkdev_issue_discard(bdev, cmd->t_task_lba,
+                               nolb, GFP_KERNEL, 0);
+               if (ret < 0) {
+                       pr_warn("FILEIO: blkdev_issue_discard() failed: %d\n",
+                               ret);
+                       return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+               }
+       } else {
+               /* The backend is normal file, use fallocate */
+               loff_t pos = cmd->t_task_lba * se_dev->dev_attrib.block_size;
+               unsigned int len = nolb * se_dev->dev_attrib.block_size;
+               int mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE;
+
+               if (!file->f_op->fallocate)
+                       return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+
+               ret = file->f_op->fallocate(file, mode, pos, len);
+               if (ret < 0) {
+                       pr_warn("FILEIO: fallocate() failed: %d\n", ret);
+                       return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+               }
+       }
+
+       target_complete_cmd(cmd, GOOD);
+       return 0;
+}
+
 static sense_reason_t
 fd_execute_rw(struct se_cmd *cmd)
 {
@@ -600,6 +683,7 @@ static struct sbc_ops fd_sbc_ops = {
        .execute_rw             = fd_execute_rw,
        .execute_sync_cache     = fd_execute_sync_cache,
        .execute_write_same     = fd_execute_write_same,
+       .execute_write_same_unmap = fd_execute_write_same_unmap,
 };
 
 static sense_reason_t