diff options
Diffstat (limited to 'drivers/target/target_core_sbc.c')
-rw-r--r-- | drivers/target/target_core_sbc.c | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index 60d4b5185f32..bbc5b0ee2bdc 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c | |||
@@ -596,3 +596,88 @@ u32 sbc_get_device_type(struct se_device *dev) | |||
596 | return TYPE_DISK; | 596 | return TYPE_DISK; |
597 | } | 597 | } |
598 | EXPORT_SYMBOL(sbc_get_device_type); | 598 | EXPORT_SYMBOL(sbc_get_device_type); |
599 | |||
600 | sense_reason_t | ||
601 | sbc_execute_unmap(struct se_cmd *cmd, | ||
602 | sense_reason_t (*do_unmap_fn)(struct se_cmd *, void *, | ||
603 | sector_t, sector_t), | ||
604 | void *priv) | ||
605 | { | ||
606 | struct se_device *dev = cmd->se_dev; | ||
607 | unsigned char *buf, *ptr = NULL; | ||
608 | sector_t lba; | ||
609 | int size; | ||
610 | u32 range; | ||
611 | sense_reason_t ret = 0; | ||
612 | int dl, bd_dl; | ||
613 | |||
614 | /* We never set ANC_SUP */ | ||
615 | if (cmd->t_task_cdb[1]) | ||
616 | return TCM_INVALID_CDB_FIELD; | ||
617 | |||
618 | if (cmd->data_length == 0) { | ||
619 | target_complete_cmd(cmd, SAM_STAT_GOOD); | ||
620 | return 0; | ||
621 | } | ||
622 | |||
623 | if (cmd->data_length < 8) { | ||
624 | pr_warn("UNMAP parameter list length %u too small\n", | ||
625 | cmd->data_length); | ||
626 | return TCM_PARAMETER_LIST_LENGTH_ERROR; | ||
627 | } | ||
628 | |||
629 | buf = transport_kmap_data_sg(cmd); | ||
630 | if (!buf) | ||
631 | return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; | ||
632 | |||
633 | dl = get_unaligned_be16(&buf[0]); | ||
634 | bd_dl = get_unaligned_be16(&buf[2]); | ||
635 | |||
636 | size = cmd->data_length - 8; | ||
637 | if (bd_dl > size) | ||
638 | pr_warn("UNMAP parameter list length %u too small, ignoring bd_dl %u\n", | ||
639 | cmd->data_length, bd_dl); | ||
640 | else | ||
641 | size = bd_dl; | ||
642 | |||
643 | if (size / 16 > dev->dev_attrib.max_unmap_block_desc_count) { | ||
644 | ret = TCM_INVALID_PARAMETER_LIST; | ||
645 | goto err; | ||
646 | } | ||
647 | |||
648 | /* First UNMAP block descriptor starts at 8 byte offset */ | ||
649 | ptr = &buf[8]; | ||
650 | pr_debug("UNMAP: Sub: %s Using dl: %u bd_dl: %u size: %u" | ||
651 | " ptr: %p\n", dev->transport->name, dl, bd_dl, size, ptr); | ||
652 | |||
653 | while (size >= 16) { | ||
654 | lba = get_unaligned_be64(&ptr[0]); | ||
655 | range = get_unaligned_be32(&ptr[8]); | ||
656 | pr_debug("UNMAP: Using lba: %llu and range: %u\n", | ||
657 | (unsigned long long)lba, range); | ||
658 | |||
659 | if (range > dev->dev_attrib.max_unmap_lba_count) { | ||
660 | ret = TCM_INVALID_PARAMETER_LIST; | ||
661 | goto err; | ||
662 | } | ||
663 | |||
664 | if (lba + range > dev->transport->get_blocks(dev) + 1) { | ||
665 | ret = TCM_ADDRESS_OUT_OF_RANGE; | ||
666 | goto err; | ||
667 | } | ||
668 | |||
669 | ret = do_unmap_fn(cmd, priv, lba, range); | ||
670 | if (ret) | ||
671 | goto err; | ||
672 | |||
673 | ptr += 16; | ||
674 | size -= 16; | ||
675 | } | ||
676 | |||
677 | err: | ||
678 | transport_kunmap_data_sg(cmd); | ||
679 | if (!ret) | ||
680 | target_complete_cmd(cmd, GOOD); | ||
681 | return ret; | ||
682 | } | ||
683 | EXPORT_SYMBOL(sbc_execute_unmap); | ||