diff options
Diffstat (limited to 'drivers/target')
-rw-r--r-- | drivers/target/target_core_file.c | 111 |
1 files changed, 111 insertions, 0 deletions
diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c index e1878bfd97b7..7e02304a1024 100644 --- a/drivers/target/target_core_file.c +++ b/drivers/target/target_core_file.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include <linux/falloc.h> | 33 | #include <linux/falloc.h> |
34 | #include <scsi/scsi.h> | 34 | #include <scsi/scsi.h> |
35 | #include <scsi/scsi_host.h> | 35 | #include <scsi/scsi_host.h> |
36 | #include <asm/unaligned.h> | ||
36 | 37 | ||
37 | #include <target/target_core_base.h> | 38 | #include <target/target_core_base.h> |
38 | #include <target/target_core_backend.h> | 39 | #include <target/target_core_backend.h> |
@@ -525,6 +526,115 @@ fd_execute_write_same_unmap(struct se_cmd *cmd) | |||
525 | } | 526 | } |
526 | 527 | ||
527 | static sense_reason_t | 528 | static sense_reason_t |
529 | fd_execute_unmap(struct se_cmd *cmd) | ||
530 | { | ||
531 | struct se_device *dev = cmd->se_dev; | ||
532 | struct fd_dev *fd_dev = FD_DEV(dev); | ||
533 | struct file *file = fd_dev->fd_file; | ||
534 | struct inode *inode = file->f_mapping->host; | ||
535 | unsigned char *buf, *ptr = NULL; | ||
536 | sector_t lba; | ||
537 | int size; | ||
538 | u32 range; | ||
539 | sense_reason_t ret = 0; | ||
540 | int dl, bd_dl, err; | ||
541 | |||
542 | /* We never set ANC_SUP */ | ||
543 | if (cmd->t_task_cdb[1]) | ||
544 | return TCM_INVALID_CDB_FIELD; | ||
545 | |||
546 | if (cmd->data_length == 0) { | ||
547 | target_complete_cmd(cmd, SAM_STAT_GOOD); | ||
548 | return 0; | ||
549 | } | ||
550 | |||
551 | if (cmd->data_length < 8) { | ||
552 | pr_warn("UNMAP parameter list length %u too small\n", | ||
553 | cmd->data_length); | ||
554 | return TCM_PARAMETER_LIST_LENGTH_ERROR; | ||
555 | } | ||
556 | |||
557 | buf = transport_kmap_data_sg(cmd); | ||
558 | if (!buf) | ||
559 | return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; | ||
560 | |||
561 | dl = get_unaligned_be16(&buf[0]); | ||
562 | bd_dl = get_unaligned_be16(&buf[2]); | ||
563 | |||
564 | size = cmd->data_length - 8; | ||
565 | if (bd_dl > size) | ||
566 | pr_warn("UNMAP parameter list length %u too small, ignoring bd_dl %u\n", | ||
567 | cmd->data_length, bd_dl); | ||
568 | else | ||
569 | size = bd_dl; | ||
570 | |||
571 | if (size / 16 > dev->dev_attrib.max_unmap_block_desc_count) { | ||
572 | ret = TCM_INVALID_PARAMETER_LIST; | ||
573 | goto err; | ||
574 | } | ||
575 | |||
576 | /* First UNMAP block descriptor starts at 8 byte offset */ | ||
577 | ptr = &buf[8]; | ||
578 | pr_debug("UNMAP: Sub: %s Using dl: %u bd_dl: %u size: %u" | ||
579 | " ptr: %p\n", dev->transport->name, dl, bd_dl, size, ptr); | ||
580 | |||
581 | while (size >= 16) { | ||
582 | lba = get_unaligned_be64(&ptr[0]); | ||
583 | range = get_unaligned_be32(&ptr[8]); | ||
584 | pr_debug("UNMAP: Using lba: %llu and range: %u\n", | ||
585 | (unsigned long long)lba, range); | ||
586 | |||
587 | if (range > dev->dev_attrib.max_unmap_lba_count) { | ||
588 | ret = TCM_INVALID_PARAMETER_LIST; | ||
589 | goto err; | ||
590 | } | ||
591 | |||
592 | if (lba + range > dev->transport->get_blocks(dev) + 1) { | ||
593 | ret = TCM_ADDRESS_OUT_OF_RANGE; | ||
594 | goto err; | ||
595 | } | ||
596 | |||
597 | if (S_ISBLK(inode->i_mode)) { | ||
598 | /* The backend is block device, use discard */ | ||
599 | struct block_device *bdev = inode->i_bdev; | ||
600 | |||
601 | err = blkdev_issue_discard(bdev, lba, range, GFP_KERNEL, 0); | ||
602 | if (err < 0) { | ||
603 | pr_err("FILEIO: blkdev_issue_discard() failed: %d\n", | ||
604 | err); | ||
605 | ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; | ||
606 | goto err; | ||
607 | } | ||
608 | } else { | ||
609 | /* The backend is normal file, use fallocate */ | ||
610 | loff_t pos = lba * dev->dev_attrib.block_size; | ||
611 | unsigned int len = range * dev->dev_attrib.block_size; | ||
612 | int mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE; | ||
613 | |||
614 | if (!file->f_op->fallocate) { | ||
615 | ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; | ||
616 | goto err; | ||
617 | } | ||
618 | err = file->f_op->fallocate(file, mode, pos, len); | ||
619 | if (err < 0) { | ||
620 | pr_warn("FILEIO: fallocate() failed: %d\n", err); | ||
621 | ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; | ||
622 | goto err; | ||
623 | } | ||
624 | } | ||
625 | |||
626 | ptr += 16; | ||
627 | size -= 16; | ||
628 | } | ||
629 | |||
630 | err: | ||
631 | transport_kunmap_data_sg(cmd); | ||
632 | if (!ret) | ||
633 | target_complete_cmd(cmd, GOOD); | ||
634 | return ret; | ||
635 | } | ||
636 | |||
637 | static sense_reason_t | ||
528 | fd_execute_rw(struct se_cmd *cmd) | 638 | fd_execute_rw(struct se_cmd *cmd) |
529 | { | 639 | { |
530 | struct scatterlist *sgl = cmd->t_data_sg; | 640 | struct scatterlist *sgl = cmd->t_data_sg; |
@@ -684,6 +794,7 @@ static struct sbc_ops fd_sbc_ops = { | |||
684 | .execute_sync_cache = fd_execute_sync_cache, | 794 | .execute_sync_cache = fd_execute_sync_cache, |
685 | .execute_write_same = fd_execute_write_same, | 795 | .execute_write_same = fd_execute_write_same, |
686 | .execute_write_same_unmap = fd_execute_write_same_unmap, | 796 | .execute_write_same_unmap = fd_execute_write_same_unmap, |
797 | .execute_unmap = fd_execute_unmap, | ||
687 | }; | 798 | }; |
688 | 799 | ||
689 | static sense_reason_t | 800 | static sense_reason_t |