diff options
author | Roland Dreier <roland@purestorage.com> | 2012-10-31 12:16:48 -0400 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2012-11-06 23:55:46 -0500 |
commit | d4b2b867193c157f0ac8c10137e287a90ed4e5d5 (patch) | |
tree | f1bd32ea59f0b69ac1d1c028912d42514196d013 | |
parent | 1f981de55a46777ca0da93af5faf7d3f6e7e7000 (diff) |
target: Refactor MODE SENSE emulation
Convert spc_emulate_modesense() to use a table of mode pages, rather
than a switch statement. This makes it possible to add more pages
sanely -- in particular we no longer need to make sure we keep the
0x3f (return all mode pages) case in sync.
While we're touching this code, make our MODE SENSE emulation a bit
better in a couple of ways:
- When the initiator passes PC == 1 asking for changeable values,
return all 0s to show we don't support setting anything.
- Return a block descriptor for disk devices.
(nab: fix up device attribute references to use dev->dev_attrib
in for-next code)
Signed-off-by: Roland Dreier <roland@purestorage.com>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
-rw-r--r-- | drivers/target/target_core_spc.c | 235 |
1 files changed, 176 insertions, 59 deletions
diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c index 71d440f0573e..8ccfe00c51d7 100644 --- a/drivers/target/target_core_spc.c +++ b/drivers/target/target_core_spc.c | |||
@@ -639,18 +639,28 @@ out: | |||
639 | return ret; | 639 | return ret; |
640 | } | 640 | } |
641 | 641 | ||
642 | static int spc_modesense_rwrecovery(unsigned char *p) | 642 | static int spc_modesense_rwrecovery(struct se_device *dev, u8 pc, u8 *p) |
643 | { | 643 | { |
644 | p[0] = 0x01; | 644 | p[0] = 0x01; |
645 | p[1] = 0x0a; | 645 | p[1] = 0x0a; |
646 | 646 | ||
647 | /* No changeable values for now */ | ||
648 | if (pc == 1) | ||
649 | goto out; | ||
650 | |||
651 | out: | ||
647 | return 12; | 652 | return 12; |
648 | } | 653 | } |
649 | 654 | ||
650 | static int spc_modesense_control(struct se_device *dev, unsigned char *p) | 655 | static int spc_modesense_control(struct se_device *dev, u8 pc, u8 *p) |
651 | { | 656 | { |
652 | p[0] = 0x0a; | 657 | p[0] = 0x0a; |
653 | p[1] = 0x0a; | 658 | p[1] = 0x0a; |
659 | |||
660 | /* No changeable values for now */ | ||
661 | if (pc == 1) | ||
662 | goto out; | ||
663 | |||
654 | p[2] = 2; | 664 | p[2] = 2; |
655 | /* | 665 | /* |
656 | * From spc4r23, 7.4.7 Control mode page | 666 | * From spc4r23, 7.4.7 Control mode page |
@@ -729,20 +739,37 @@ static int spc_modesense_control(struct se_device *dev, unsigned char *p) | |||
729 | p[9] = 0xff; | 739 | p[9] = 0xff; |
730 | p[11] = 30; | 740 | p[11] = 30; |
731 | 741 | ||
742 | out: | ||
732 | return 12; | 743 | return 12; |
733 | } | 744 | } |
734 | 745 | ||
735 | static int spc_modesense_caching(struct se_device *dev, unsigned char *p) | 746 | static int spc_modesense_caching(struct se_device *dev, u8 pc, u8 *p) |
736 | { | 747 | { |
737 | p[0] = 0x08; | 748 | p[0] = 0x08; |
738 | p[1] = 0x12; | 749 | p[1] = 0x12; |
750 | |||
751 | /* No changeable values for now */ | ||
752 | if (pc == 1) | ||
753 | goto out; | ||
754 | |||
739 | if (dev->dev_attrib.emulate_write_cache > 0) | 755 | if (dev->dev_attrib.emulate_write_cache > 0) |
740 | p[2] = 0x04; /* Write Cache Enable */ | 756 | p[2] = 0x04; /* Write Cache Enable */ |
741 | p[12] = 0x20; /* Disabled Read Ahead */ | 757 | p[12] = 0x20; /* Disabled Read Ahead */ |
742 | 758 | ||
759 | out: | ||
743 | return 20; | 760 | return 20; |
744 | } | 761 | } |
745 | 762 | ||
763 | static struct { | ||
764 | uint8_t page; | ||
765 | uint8_t subpage; | ||
766 | int (*emulate)(struct se_device *, u8, unsigned char *); | ||
767 | } modesense_handlers[] = { | ||
768 | { .page = 0x01, .subpage = 0x00, .emulate = spc_modesense_rwrecovery }, | ||
769 | { .page = 0x08, .subpage = 0x00, .emulate = spc_modesense_caching }, | ||
770 | { .page = 0x0a, .subpage = 0x00, .emulate = spc_modesense_control }, | ||
771 | }; | ||
772 | |||
746 | static void spc_modesense_write_protect(unsigned char *buf, int type) | 773 | static void spc_modesense_write_protect(unsigned char *buf, int type) |
747 | { | 774 | { |
748 | /* | 775 | /* |
@@ -769,77 +796,167 @@ static void spc_modesense_dpofua(unsigned char *buf, int type) | |||
769 | } | 796 | } |
770 | } | 797 | } |
771 | 798 | ||
799 | static int spc_modesense_blockdesc(unsigned char *buf, u64 blocks, u32 block_size) | ||
800 | { | ||
801 | *buf++ = 8; | ||
802 | put_unaligned_be32(min(blocks, 0xffffffffull), buf); | ||
803 | buf += 4; | ||
804 | put_unaligned_be32(block_size, buf); | ||
805 | return 9; | ||
806 | } | ||
807 | |||
808 | static int spc_modesense_long_blockdesc(unsigned char *buf, u64 blocks, u32 block_size) | ||
809 | { | ||
810 | if (blocks <= 0xffffffff) | ||
811 | return spc_modesense_blockdesc(buf + 3, blocks, block_size) + 3; | ||
812 | |||
813 | *buf++ = 1; /* LONGLBA */ | ||
814 | buf += 2; | ||
815 | *buf++ = 16; | ||
816 | put_unaligned_be64(blocks, buf); | ||
817 | buf += 12; | ||
818 | put_unaligned_be32(block_size, buf); | ||
819 | |||
820 | return 17; | ||
821 | } | ||
822 | |||
772 | static int spc_emulate_modesense(struct se_cmd *cmd) | 823 | static int spc_emulate_modesense(struct se_cmd *cmd) |
773 | { | 824 | { |
774 | struct se_device *dev = cmd->se_dev; | 825 | struct se_device *dev = cmd->se_dev; |
775 | char *cdb = cmd->t_task_cdb; | 826 | char *cdb = cmd->t_task_cdb; |
776 | unsigned char *rbuf; | 827 | unsigned char *buf, *map_buf; |
777 | int type = dev->transport->get_device_type(dev); | 828 | int type = dev->transport->get_device_type(dev); |
778 | int ten = (cmd->t_task_cdb[0] == MODE_SENSE_10); | 829 | int ten = (cmd->t_task_cdb[0] == MODE_SENSE_10); |
779 | u32 offset = ten ? 8 : 4; | 830 | bool dbd = !!(cdb[1] & 0x08); |
831 | bool llba = ten ? !!(cdb[1] & 0x10) : false; | ||
832 | u8 pc = cdb[2] >> 6; | ||
833 | u8 page = cdb[2] & 0x3f; | ||
834 | u8 subpage = cdb[3]; | ||
780 | int length = 0; | 835 | int length = 0; |
781 | unsigned char buf[SE_MODE_PAGE_BUF]; | 836 | int ret; |
837 | int i; | ||
782 | 838 | ||
783 | memset(buf, 0, SE_MODE_PAGE_BUF); | 839 | map_buf = transport_kmap_data_sg(cmd); |
784 | 840 | ||
785 | switch (cdb[2] & 0x3f) { | 841 | /* |
786 | case 0x01: | 842 | * If SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC is not set, then we |
787 | length = spc_modesense_rwrecovery(&buf[offset]); | 843 | * know we actually allocated a full page. Otherwise, if the |
788 | break; | 844 | * data buffer is too small, allocate a temporary buffer so we |
789 | case 0x08: | 845 | * don't have to worry about overruns in all our INQUIRY |
790 | length = spc_modesense_caching(dev, &buf[offset]); | 846 | * emulation handling. |
791 | break; | 847 | */ |
792 | case 0x0a: | 848 | if (cmd->data_length < SE_MODE_PAGE_BUF && |
793 | length = spc_modesense_control(dev, &buf[offset]); | 849 | (cmd->se_cmd_flags & SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC)) { |
794 | break; | 850 | buf = kzalloc(SE_MODE_PAGE_BUF, GFP_KERNEL); |
795 | case 0x3f: | 851 | if (!buf) { |
796 | length = spc_modesense_rwrecovery(&buf[offset]); | 852 | transport_kunmap_data_sg(cmd); |
797 | length += spc_modesense_caching(dev, &buf[offset+length]); | 853 | cmd->scsi_sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; |
798 | length += spc_modesense_control(dev, &buf[offset+length]); | 854 | return -ENOMEM; |
799 | break; | 855 | } |
800 | default: | 856 | } else { |
801 | pr_err("MODE SENSE: unimplemented page/subpage: 0x%02x/0x%02x\n", | 857 | buf = map_buf; |
802 | cdb[2] & 0x3f, cdb[3]); | ||
803 | cmd->scsi_sense_reason = TCM_UNKNOWN_MODE_PAGE; | ||
804 | return -EINVAL; | ||
805 | } | 858 | } |
806 | offset += length; | 859 | |
807 | 860 | length = ten ? 2 : 1; | |
808 | if (ten) { | 861 | |
809 | offset -= 2; | 862 | /* DEVICE-SPECIFIC PARAMETER */ |
810 | buf[0] = (offset >> 8) & 0xff; | 863 | if ((cmd->se_lun->lun_access & TRANSPORT_LUNFLAGS_READ_ONLY) || |
811 | buf[1] = offset & 0xff; | 864 | (cmd->se_deve && |
812 | offset += 2; | 865 | (cmd->se_deve->lun_flags & TRANSPORT_LUNFLAGS_READ_ONLY))) |
813 | 866 | spc_modesense_write_protect(&buf[length], type); | |
814 | if ((cmd->se_lun->lun_access & TRANSPORT_LUNFLAGS_READ_ONLY) || | 867 | |
815 | (cmd->se_deve && | 868 | if ((dev->dev_attrib.emulate_write_cache > 0) && |
816 | (cmd->se_deve->lun_flags & TRANSPORT_LUNFLAGS_READ_ONLY))) | 869 | (dev->dev_attrib.emulate_fua_write > 0)) |
817 | spc_modesense_write_protect(&buf[3], type); | 870 | spc_modesense_dpofua(&buf[length], type); |
818 | 871 | ||
819 | if ((dev->dev_attrib.emulate_write_cache > 0) && | 872 | ++length; |
820 | (dev->dev_attrib.emulate_fua_write > 0)) | 873 | |
821 | spc_modesense_dpofua(&buf[3], type); | 874 | /* BLOCK DESCRIPTOR */ |
875 | |||
876 | /* | ||
877 | * For now we only include a block descriptor for disk (SBC) | ||
878 | * devices; other command sets use a slightly different format. | ||
879 | */ | ||
880 | if (!dbd && type == TYPE_DISK) { | ||
881 | u64 blocks = dev->transport->get_blocks(dev); | ||
882 | u32 block_size = dev->dev_attrib.block_size; | ||
883 | |||
884 | if (ten) { | ||
885 | if (llba) { | ||
886 | length += spc_modesense_long_blockdesc(&buf[length], | ||
887 | blocks, block_size); | ||
888 | } else { | ||
889 | length += 3; | ||
890 | length += spc_modesense_blockdesc(&buf[length], | ||
891 | blocks, block_size); | ||
892 | } | ||
893 | } else { | ||
894 | length += spc_modesense_blockdesc(&buf[length], blocks, | ||
895 | block_size); | ||
896 | } | ||
822 | } else { | 897 | } else { |
823 | offset -= 1; | 898 | if (ten) |
824 | buf[0] = offset & 0xff; | 899 | length += 4; |
825 | offset += 1; | 900 | else |
826 | 901 | length += 1; | |
827 | if ((cmd->se_lun->lun_access & TRANSPORT_LUNFLAGS_READ_ONLY) || | ||
828 | (cmd->se_deve && | ||
829 | (cmd->se_deve->lun_flags & TRANSPORT_LUNFLAGS_READ_ONLY))) | ||
830 | spc_modesense_write_protect(&buf[2], type); | ||
831 | |||
832 | if ((dev->dev_attrib.emulate_write_cache > 0) && | ||
833 | (dev->dev_attrib.emulate_fua_write > 0)) | ||
834 | spc_modesense_dpofua(&buf[2], type); | ||
835 | } | 902 | } |
836 | 903 | ||
837 | rbuf = transport_kmap_data_sg(cmd); | 904 | if (page == 0x3f) { |
838 | if (rbuf) { | 905 | if (subpage != 0x00 && subpage != 0xff) { |
839 | memcpy(rbuf, buf, min(offset, cmd->data_length)); | 906 | cmd->scsi_sense_reason = TCM_INVALID_CDB_FIELD; |
840 | transport_kunmap_data_sg(cmd); | 907 | length = -EINVAL; |
908 | goto out; | ||
909 | } | ||
910 | |||
911 | for (i = 0; i < ARRAY_SIZE(modesense_handlers); ++i) { | ||
912 | /* | ||
913 | * Tricky way to say all subpage 00h for | ||
914 | * subpage==0, all subpages for subpage==0xff | ||
915 | * (and we just checked above that those are | ||
916 | * the only two possibilities). | ||
917 | */ | ||
918 | if ((modesense_handlers[i].subpage & ~subpage) == 0) { | ||
919 | ret = modesense_handlers[i].emulate(dev, pc, &buf[length]); | ||
920 | if (!ten && length + ret >= 255) | ||
921 | break; | ||
922 | length += ret; | ||
923 | } | ||
924 | } | ||
925 | |||
926 | goto set_length; | ||
927 | } | ||
928 | |||
929 | for (i = 0; i < ARRAY_SIZE(modesense_handlers); ++i) | ||
930 | if (modesense_handlers[i].page == page && | ||
931 | modesense_handlers[i].subpage == subpage) { | ||
932 | length += modesense_handlers[i].emulate(dev, pc, &buf[length]); | ||
933 | goto set_length; | ||
934 | } | ||
935 | |||
936 | /* | ||
937 | * We don't intend to implement: | ||
938 | * - obsolete page 03h "format parameters" (checked by Solaris) | ||
939 | */ | ||
940 | if (page != 0x03) | ||
941 | pr_err("MODE SENSE: unimplemented page/subpage: 0x%02x/0x%02x\n", | ||
942 | page, subpage); | ||
943 | |||
944 | cmd->scsi_sense_reason = TCM_UNKNOWN_MODE_PAGE; | ||
945 | return -EINVAL; | ||
946 | |||
947 | set_length: | ||
948 | if (ten) | ||
949 | put_unaligned_be16(length - 2, buf); | ||
950 | else | ||
951 | buf[0] = length - 1; | ||
952 | |||
953 | out: | ||
954 | if (buf != map_buf) { | ||
955 | memcpy(map_buf, buf, cmd->data_length); | ||
956 | kfree(buf); | ||
841 | } | 957 | } |
842 | 958 | ||
959 | transport_kunmap_data_sg(cmd); | ||
843 | target_complete_cmd(cmd, GOOD); | 960 | target_complete_cmd(cmd, GOOD); |
844 | return 0; | 961 | return 0; |
845 | } | 962 | } |