On 02/20/2013 09:43 AM, Nicholas A. Bellinger wrote: > From: Nicholas Bellinger <nab@xxxxxxxxxxxxxxx> > > Hi mkp, hch, & Co, > > Please review this patch to add support for WRITE_SAME w/ UNMAP=0 > emulation into FILEIO, as I'd really like to include this in the > upcoming for-3.9 target merge. > > Thank you, > > --nab > > --------------------------------------------------------------------- > > This patch adds support for emulation of WRITE_SAME w/ UNMAP=0 within > fd_execute_write_same() backend code. > > The emulation uses vfs_writev() to submit a locally populated buffer > from the received WRITE_SAME scatterlist block for duplication, and by > default enforces a limit of max_write_same_len=0x1000 (8192) sectors up > to the limit of 1024 iovec entries for the single call to vfs_writev(). > > It also sets max_write_same_len to the operational default at setup -> > fd_configure_device() time. > > Tested with 512, 1k, 2k, and 4k block_sizes. > > Cc: Martin K. Petersen <martin.petersen@xxxxxxxxxx> > Cc: Christoph Hellwig <hch@xxxxxx> > Cc: Asias He <asias@xxxxxxxxxx> > Signed-off-by: Nicholas Bellinger <nab@xxxxxxxxxxxxxxx> > --- > drivers/target/target_core_file.c | 115 +++++++++++++++++++++++++++++++++++++ > 1 files changed, 115 insertions(+), 0 deletions(-) > > diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c > index b9c8849..0b71509 100644 > --- a/drivers/target/target_core_file.c > +++ b/drivers/target/target_core_file.c > @@ -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,115 @@ 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 = vmalloc(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; > + } > + memset(iov, 0, sizeof(struct iovec) * iov_num); Use vzalloc()? > + /* > + * Map the single fabric received scatterlist block into each > + * iovec for 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 +600,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 > -- Asias -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html