diff options
author | Nicholas Bellinger <nab@linux-iscsi.org> | 2013-02-19 20:30:34 -0500 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2013-02-20 15:29:04 -0500 |
commit | 7b745c84a9f4ad62db4b67053fbceb5d706451af (patch) | |
tree | 2c8886e676130abfa4682531eb5c01d57feb6e47 | |
parent | 6e5459353de4ac80924e94fafa8b3e31a086c5dd (diff) |
target/file: Add WRITE_SAME w/ UNMAP=0 emulation support
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.
(asias: convert to vzalloc)
Cc: Martin K. Petersen <martin.petersen@oracle.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Asias He <asias@redhat.com>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
-rw-r--r-- | drivers/target/target_core_file.c | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c index b9c88497e8f0..94383a56bd74 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) | |||
190 | 190 | ||
191 | fd_dev->fd_dev_id = fd_host->fd_host_dev_id_count++; | 191 | fd_dev->fd_dev_id = fd_host->fd_host_dev_id_count++; |
192 | fd_dev->fd_queue_depth = dev->queue_depth; | 192 | fd_dev->fd_queue_depth = dev->queue_depth; |
193 | /* | ||
194 | * Limit WRITE_SAME w/ UNMAP=0 emulation to 8k Number of LBAs (NoLB) | ||
195 | * based upon struct iovec limit for vfs_writev() | ||
196 | */ | ||
197 | dev->dev_attrib.max_write_same_len = 0x1000; | ||
193 | 198 | ||
194 | pr_debug("CORE_FILE[%u] - Added TCM FILEIO Device ID: %u at %s," | 199 | pr_debug("CORE_FILE[%u] - Added TCM FILEIO Device ID: %u at %s," |
195 | " %llu total bytes\n", fd_host->fd_host_id, fd_dev->fd_dev_id, | 200 | " %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) | |||
328 | return 0; | 333 | return 0; |
329 | } | 334 | } |
330 | 335 | ||
336 | static unsigned char * | ||
337 | fd_setup_write_same_buf(struct se_cmd *cmd, struct scatterlist *sg, | ||
338 | unsigned int len) | ||
339 | { | ||
340 | struct se_device *se_dev = cmd->se_dev; | ||
341 | unsigned int block_size = se_dev->dev_attrib.block_size; | ||
342 | unsigned int i = 0, end; | ||
343 | unsigned char *buf, *p, *kmap_buf; | ||
344 | |||
345 | buf = kzalloc(min_t(unsigned int, len, PAGE_SIZE), GFP_KERNEL); | ||
346 | if (!buf) { | ||
347 | pr_err("Unable to allocate fd_execute_write_same buf\n"); | ||
348 | return NULL; | ||
349 | } | ||
350 | |||
351 | kmap_buf = kmap(sg_page(sg)) + sg->offset; | ||
352 | if (!kmap_buf) { | ||
353 | pr_err("kmap() failed in fd_setup_write_same\n"); | ||
354 | kfree(buf); | ||
355 | return NULL; | ||
356 | } | ||
357 | /* | ||
358 | * Fill local *buf to contain multiple WRITE_SAME blocks up to | ||
359 | * min(len, PAGE_SIZE) | ||
360 | */ | ||
361 | p = buf; | ||
362 | end = min_t(unsigned int, len, PAGE_SIZE); | ||
363 | |||
364 | while (i < end) { | ||
365 | memcpy(p, kmap_buf, block_size); | ||
366 | |||
367 | i += block_size; | ||
368 | p += block_size; | ||
369 | } | ||
370 | kunmap(sg_page(sg)); | ||
371 | |||
372 | return buf; | ||
373 | } | ||
374 | |||
375 | static sense_reason_t | ||
376 | fd_execute_write_same(struct se_cmd *cmd) | ||
377 | { | ||
378 | struct se_device *se_dev = cmd->se_dev; | ||
379 | struct fd_dev *fd_dev = FD_DEV(se_dev); | ||
380 | struct file *f = fd_dev->fd_file; | ||
381 | struct scatterlist *sg; | ||
382 | struct iovec *iov; | ||
383 | mm_segment_t old_fs; | ||
384 | sector_t nolb = spc_get_write_same_sectors(cmd); | ||
385 | loff_t pos = cmd->t_task_lba * se_dev->dev_attrib.block_size; | ||
386 | unsigned int len, len_tmp, iov_num; | ||
387 | int i, rc; | ||
388 | unsigned char *buf; | ||
389 | |||
390 | if (!nolb) { | ||
391 | target_complete_cmd(cmd, SAM_STAT_GOOD); | ||
392 | return 0; | ||
393 | } | ||
394 | sg = &cmd->t_data_sg[0]; | ||
395 | |||
396 | if (cmd->t_data_nents > 1 || | ||
397 | sg->length != cmd->se_dev->dev_attrib.block_size) { | ||
398 | pr_err("WRITE_SAME: Illegal SGL t_data_nents: %u length: %u" | ||
399 | " block_size: %u\n", cmd->t_data_nents, sg->length, | ||
400 | cmd->se_dev->dev_attrib.block_size); | ||
401 | return TCM_INVALID_CDB_FIELD; | ||
402 | } | ||
403 | |||
404 | len = len_tmp = nolb * se_dev->dev_attrib.block_size; | ||
405 | iov_num = DIV_ROUND_UP(len, PAGE_SIZE); | ||
406 | |||
407 | buf = fd_setup_write_same_buf(cmd, sg, len); | ||
408 | if (!buf) | ||
409 | return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; | ||
410 | |||
411 | iov = vzalloc(sizeof(struct iovec) * iov_num); | ||
412 | if (!iov) { | ||
413 | pr_err("Unable to allocate fd_execute_write_same iovecs\n"); | ||
414 | kfree(buf); | ||
415 | return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; | ||
416 | } | ||
417 | /* | ||
418 | * Map the single fabric received scatterlist block now populated | ||
419 | * in *buf into each iovec for I/O submission. | ||
420 | */ | ||
421 | for (i = 0; i < iov_num; i++) { | ||
422 | iov[i].iov_base = buf; | ||
423 | iov[i].iov_len = min_t(unsigned int, len_tmp, PAGE_SIZE); | ||
424 | len_tmp -= iov[i].iov_len; | ||
425 | } | ||
426 | |||
427 | old_fs = get_fs(); | ||
428 | set_fs(get_ds()); | ||
429 | rc = vfs_writev(f, &iov[0], iov_num, &pos); | ||
430 | set_fs(old_fs); | ||
431 | |||
432 | vfree(iov); | ||
433 | kfree(buf); | ||
434 | |||
435 | if (rc < 0 || rc != len) { | ||
436 | pr_err("vfs_writev() returned %d for write same\n", rc); | ||
437 | return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; | ||
438 | } | ||
439 | |||
440 | target_complete_cmd(cmd, SAM_STAT_GOOD); | ||
441 | return 0; | ||
442 | } | ||
443 | |||
331 | static sense_reason_t | 444 | static sense_reason_t |
332 | fd_execute_rw(struct se_cmd *cmd) | 445 | fd_execute_rw(struct se_cmd *cmd) |
333 | { | 446 | { |
@@ -486,6 +599,7 @@ static sector_t fd_get_blocks(struct se_device *dev) | |||
486 | static struct sbc_ops fd_sbc_ops = { | 599 | static struct sbc_ops fd_sbc_ops = { |
487 | .execute_rw = fd_execute_rw, | 600 | .execute_rw = fd_execute_rw, |
488 | .execute_sync_cache = fd_execute_sync_cache, | 601 | .execute_sync_cache = fd_execute_sync_cache, |
602 | .execute_write_same = fd_execute_write_same, | ||
489 | }; | 603 | }; |
490 | 604 | ||
491 | static sense_reason_t | 605 | static sense_reason_t |