aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoland Dreier <roland@purestorage.com>2012-10-31 12:16:48 -0400
committerNicholas Bellinger <nab@linux-iscsi.org>2012-11-06 23:55:46 -0500
commitd4b2b867193c157f0ac8c10137e287a90ed4e5d5 (patch)
treef1bd32ea59f0b69ac1d1c028912d42514196d013
parent1f981de55a46777ca0da93af5faf7d3f6e7e7000 (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.c235
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
642static int spc_modesense_rwrecovery(unsigned char *p) 642static 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
651out:
647 return 12; 652 return 12;
648} 653}
649 654
650static int spc_modesense_control(struct se_device *dev, unsigned char *p) 655static 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
742out:
732 return 12; 743 return 12;
733} 744}
734 745
735static int spc_modesense_caching(struct se_device *dev, unsigned char *p) 746static 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
759out:
743 return 20; 760 return 20;
744} 761}
745 762
763static 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
746static void spc_modesense_write_protect(unsigned char *buf, int type) 773static 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
799static 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
808static 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
772static int spc_emulate_modesense(struct se_cmd *cmd) 823static 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
947set_length:
948 if (ten)
949 put_unaligned_be16(length - 2, buf);
950 else
951 buf[0] = length - 1;
952
953out:
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}