|
@@ -190,6 +190,11 @@ 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,
|
|
@@ -328,6 +333,114 @@ fd_execute_sync_cache(struct se_cmd *cmd)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static unsigned char *
|
|
|
+fd_setup_write_same_buf(struct se_cmd *cmd, struct scatterlist *sg,
|
|
|
+ unsigned int len)
|
|
|
+{
|
|
|
+ struct se_device *se_dev = cmd->se_dev;
|
|
|
+ unsigned int block_size = se_dev->dev_attrib.block_size;
|
|
|
+ unsigned int i = 0, end;
|
|
|
+ unsigned char *buf, *p, *kmap_buf;
|
|
|
+
|
|
|
+ buf = kzalloc(min_t(unsigned int, len, PAGE_SIZE), GFP_KERNEL);
|
|
|
+ if (!buf) {
|
|
|
+ pr_err("Unable to allocate fd_execute_write_same buf\n");
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ kmap_buf = kmap(sg_page(sg)) + sg->offset;
|
|
|
+ if (!kmap_buf) {
|
|
|
+ pr_err("kmap() failed in fd_setup_write_same\n");
|
|
|
+ kfree(buf);
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+ /*
|
|
|
+ * Fill local *buf to contain multiple WRITE_SAME blocks up to
|
|
|
+ * min(len, PAGE_SIZE)
|
|
|
+ */
|
|
|
+ p = buf;
|
|
|
+ end = min_t(unsigned int, len, PAGE_SIZE);
|
|
|
+
|
|
|
+ while (i < end) {
|
|
|
+ memcpy(p, kmap_buf, block_size);
|
|
|
+
|
|
|
+ i += block_size;
|
|
|
+ p += block_size;
|
|
|
+ }
|
|
|
+ kunmap(sg_page(sg));
|
|
|
+
|
|
|
+ return buf;
|
|
|
+}
|
|
|
+
|
|
|
+static sense_reason_t
|
|
|
+fd_execute_write_same(struct se_cmd *cmd)
|
|
|
+{
|
|
|
+ struct se_device *se_dev = cmd->se_dev;
|
|
|
+ struct fd_dev *fd_dev = FD_DEV(se_dev);
|
|
|
+ struct file *f = fd_dev->fd_file;
|
|
|
+ struct scatterlist *sg;
|
|
|
+ struct iovec *iov;
|
|
|
+ mm_segment_t old_fs;
|
|
|
+ sector_t nolb = spc_get_write_same_sectors(cmd);
|
|
|
+ loff_t pos = cmd->t_task_lba * se_dev->dev_attrib.block_size;
|
|
|
+ unsigned int len, len_tmp, iov_num;
|
|
|
+ int i, rc;
|
|
|
+ unsigned char *buf;
|
|
|
+
|
|
|
+ if (!nolb) {
|
|
|
+ target_complete_cmd(cmd, SAM_STAT_GOOD);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ sg = &cmd->t_data_sg[0];
|
|
|
+
|
|
|
+ if (cmd->t_data_nents > 1 ||
|
|
|
+ sg->length != cmd->se_dev->dev_attrib.block_size) {
|
|
|
+ pr_err("WRITE_SAME: Illegal SGL t_data_nents: %u length: %u"
|
|
|
+ " block_size: %u\n", cmd->t_data_nents, sg->length,
|
|
|
+ cmd->se_dev->dev_attrib.block_size);
|
|
|
+ return TCM_INVALID_CDB_FIELD;
|
|
|
+ }
|
|
|
+
|
|
|
+ len = len_tmp = nolb * se_dev->dev_attrib.block_size;
|
|
|
+ iov_num = DIV_ROUND_UP(len, PAGE_SIZE);
|
|
|
+
|
|
|
+ buf = fd_setup_write_same_buf(cmd, sg, len);
|
|
|
+ if (!buf)
|
|
|
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
|
|
|
+
|
|
|
+ iov = vzalloc(sizeof(struct iovec) * iov_num);
|
|
|
+ if (!iov) {
|
|
|
+ pr_err("Unable to allocate fd_execute_write_same iovecs\n");
|
|
|
+ kfree(buf);
|
|
|
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
|
|
|
+ }
|
|
|
+ /*
|
|
|
+ * Map the single fabric received scatterlist block now populated
|
|
|
+ * in *buf into each iovec for I/O submission.
|
|
|
+ */
|
|
|
+ for (i = 0; i < iov_num; i++) {
|
|
|
+ iov[i].iov_base = buf;
|
|
|
+ iov[i].iov_len = min_t(unsigned int, len_tmp, PAGE_SIZE);
|
|
|
+ len_tmp -= iov[i].iov_len;
|
|
|
+ }
|
|
|
+
|
|
|
+ old_fs = get_fs();
|
|
|
+ set_fs(get_ds());
|
|
|
+ rc = vfs_writev(f, &iov[0], iov_num, &pos);
|
|
|
+ set_fs(old_fs);
|
|
|
+
|
|
|
+ vfree(iov);
|
|
|
+ kfree(buf);
|
|
|
+
|
|
|
+ if (rc < 0 || rc != len) {
|
|
|
+ pr_err("vfs_writev() returned %d for write same\n", rc);
|
|
|
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ target_complete_cmd(cmd, SAM_STAT_GOOD);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static sense_reason_t
|
|
|
fd_execute_rw(struct se_cmd *cmd)
|
|
|
{
|
|
@@ -486,6 +599,7 @@ static sector_t fd_get_blocks(struct se_device *dev)
|
|
|
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,
|
|
|
};
|
|
|
|
|
|
static sense_reason_t
|