Commit 7b745c84a9f4ad62db4b67053fbceb5d706451af
1 parent
6e5459353d
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
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>
Showing 1 changed file with 114 additions and 0 deletions Side-by-side Diff
drivers/target/target_core_file.c
... | ... | @@ -190,6 +190,11 @@ |
190 | 190 | |
191 | 191 | fd_dev->fd_dev_id = fd_host->fd_host_dev_id_count++; |
192 | 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 | 199 | pr_debug("CORE_FILE[%u] - Added TCM FILEIO Device ID: %u at %s," |
195 | 200 | " %llu total bytes\n", fd_host->fd_host_id, fd_dev->fd_dev_id, |
196 | 201 | |
... | ... | @@ -328,7 +333,115 @@ |
328 | 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 | + | |
331 | 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 | + | |
444 | +static sense_reason_t | |
332 | 445 | fd_execute_rw(struct se_cmd *cmd) |
333 | 446 | { |
334 | 447 | struct scatterlist *sgl = cmd->t_data_sg; |
... | ... | @@ -486,6 +599,7 @@ |
486 | 599 | static struct sbc_ops fd_sbc_ops = { |
487 | 600 | .execute_rw = fd_execute_rw, |
488 | 601 | .execute_sync_cache = fd_execute_sync_cache, |
602 | + .execute_write_same = fd_execute_write_same, | |
489 | 603 | }; |
490 | 604 | |
491 | 605 | static sense_reason_t |