diff options
-rw-r--r-- | drivers/scsi/scsi_transport_sas.c | 199 | ||||
-rw-r--r-- | include/scsi/scsi_transport_sas.h | 19 |
2 files changed, 184 insertions, 34 deletions
diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index 434f3954aa87..5de29b941c6b 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c | |||
@@ -39,6 +39,7 @@ | |||
39 | #define SAS_HOST_ATTRS 0 | 39 | #define SAS_HOST_ATTRS 0 |
40 | #define SAS_PORT_ATTRS 17 | 40 | #define SAS_PORT_ATTRS 17 |
41 | #define SAS_RPORT_ATTRS 7 | 41 | #define SAS_RPORT_ATTRS 7 |
42 | #define SAS_END_DEV_ATTRS 3 | ||
42 | 43 | ||
43 | struct sas_internal { | 44 | struct sas_internal { |
44 | struct scsi_transport_template t; | 45 | struct scsi_transport_template t; |
@@ -47,9 +48,11 @@ struct sas_internal { | |||
47 | struct class_device_attribute private_host_attrs[SAS_HOST_ATTRS]; | 48 | struct class_device_attribute private_host_attrs[SAS_HOST_ATTRS]; |
48 | struct class_device_attribute private_phy_attrs[SAS_PORT_ATTRS]; | 49 | struct class_device_attribute private_phy_attrs[SAS_PORT_ATTRS]; |
49 | struct class_device_attribute private_rphy_attrs[SAS_RPORT_ATTRS]; | 50 | struct class_device_attribute private_rphy_attrs[SAS_RPORT_ATTRS]; |
51 | struct class_device_attribute private_end_dev_attrs[SAS_END_DEV_ATTRS]; | ||
50 | 52 | ||
51 | struct transport_container phy_attr_cont; | 53 | struct transport_container phy_attr_cont; |
52 | struct transport_container rphy_attr_cont; | 54 | struct transport_container rphy_attr_cont; |
55 | struct transport_container end_dev_attr_cont; | ||
53 | 56 | ||
54 | /* | 57 | /* |
55 | * The array of null terminated pointers to attributes | 58 | * The array of null terminated pointers to attributes |
@@ -58,6 +61,7 @@ struct sas_internal { | |||
58 | struct class_device_attribute *host_attrs[SAS_HOST_ATTRS + 1]; | 61 | struct class_device_attribute *host_attrs[SAS_HOST_ATTRS + 1]; |
59 | struct class_device_attribute *phy_attrs[SAS_PORT_ATTRS + 1]; | 62 | struct class_device_attribute *phy_attrs[SAS_PORT_ATTRS + 1]; |
60 | struct class_device_attribute *rphy_attrs[SAS_RPORT_ATTRS + 1]; | 63 | struct class_device_attribute *rphy_attrs[SAS_RPORT_ATTRS + 1]; |
64 | struct class_device_attribute *end_dev_attrs[SAS_END_DEV_ATTRS + 1]; | ||
61 | }; | 65 | }; |
62 | #define to_sas_internal(tmpl) container_of(tmpl, struct sas_internal, t) | 66 | #define to_sas_internal(tmpl) container_of(tmpl, struct sas_internal, t) |
63 | 67 | ||
@@ -588,6 +592,73 @@ sas_rphy_simple_attr(identify.sas_address, sas_address, "0x%016llx\n", | |||
588 | unsigned long long); | 592 | unsigned long long); |
589 | sas_rphy_simple_attr(identify.phy_identifier, phy_identifier, "%d\n", u8); | 593 | sas_rphy_simple_attr(identify.phy_identifier, phy_identifier, "%d\n", u8); |
590 | 594 | ||
595 | /* only need 8 bytes of data plus header (4 or 8) */ | ||
596 | #define BUF_SIZE 64 | ||
597 | |||
598 | int sas_read_port_mode_page(struct scsi_device *sdev) | ||
599 | { | ||
600 | char *buffer = kzalloc(BUF_SIZE, GFP_KERNEL), *msdata; | ||
601 | struct sas_rphy *rphy = target_to_rphy(sdev->sdev_target); | ||
602 | struct sas_end_device *rdev; | ||
603 | struct scsi_mode_data mode_data; | ||
604 | int res, error; | ||
605 | |||
606 | BUG_ON(rphy->identify.device_type != SAS_END_DEVICE); | ||
607 | |||
608 | rdev = rphy_to_end_device(rphy); | ||
609 | |||
610 | if (!buffer) | ||
611 | return -ENOMEM; | ||
612 | |||
613 | res = scsi_mode_sense(sdev, 1, 0x19, buffer, BUF_SIZE, 30*HZ, 3, | ||
614 | &mode_data, NULL); | ||
615 | |||
616 | error = -EINVAL; | ||
617 | if (!scsi_status_is_good(res)) | ||
618 | goto out; | ||
619 | |||
620 | msdata = buffer + mode_data.header_length + | ||
621 | mode_data.block_descriptor_length; | ||
622 | |||
623 | if (msdata - buffer > BUF_SIZE - 8) | ||
624 | goto out; | ||
625 | |||
626 | error = 0; | ||
627 | |||
628 | rdev->ready_led_meaning = msdata[2] & 0x10 ? 1 : 0; | ||
629 | rdev->I_T_nexus_loss_timeout = (msdata[4] << 8) + msdata[5]; | ||
630 | rdev->initiator_response_timeout = (msdata[6] << 8) + msdata[7]; | ||
631 | |||
632 | out: | ||
633 | kfree(buffer); | ||
634 | return error; | ||
635 | } | ||
636 | EXPORT_SYMBOL(sas_read_port_mode_page); | ||
637 | |||
638 | #define sas_end_dev_show_simple(field, name, format_string, cast) \ | ||
639 | static ssize_t \ | ||
640 | show_sas_end_dev_##name(struct class_device *cdev, char *buf) \ | ||
641 | { \ | ||
642 | struct sas_rphy *rphy = transport_class_to_rphy(cdev); \ | ||
643 | struct sas_end_device *rdev = rphy_to_end_device(rphy); \ | ||
644 | \ | ||
645 | return snprintf(buf, 20, format_string, cast rdev->field); \ | ||
646 | } | ||
647 | |||
648 | #define sas_end_dev_simple_attr(field, name, format_string, type) \ | ||
649 | sas_end_dev_show_simple(field, name, format_string, (type)) \ | ||
650 | static SAS_CLASS_DEVICE_ATTR(end_dev, name, S_IRUGO, \ | ||
651 | show_sas_end_dev_##name, NULL) | ||
652 | |||
653 | sas_end_dev_simple_attr(ready_led_meaning, ready_led_meaning, "%d\n", int); | ||
654 | sas_end_dev_simple_attr(I_T_nexus_loss_timeout, I_T_nexus_loss_timeout, | ||
655 | "%d\n", int); | ||
656 | sas_end_dev_simple_attr(initiator_response_timeout, initiator_response_timeout, | ||
657 | "%d\n", int); | ||
658 | |||
659 | static DECLARE_TRANSPORT_CLASS(sas_end_dev_class, | ||
660 | "sas_end_device", NULL, NULL, NULL); | ||
661 | |||
591 | static DECLARE_TRANSPORT_CLASS(sas_rphy_class, | 662 | static DECLARE_TRANSPORT_CLASS(sas_rphy_class, |
592 | "sas_rphy", NULL, NULL, NULL); | 663 | "sas_rphy", NULL, NULL, NULL); |
593 | 664 | ||
@@ -610,6 +681,31 @@ static int sas_rphy_match(struct attribute_container *cont, struct device *dev) | |||
610 | return &i->rphy_attr_cont.ac == cont; | 681 | return &i->rphy_attr_cont.ac == cont; |
611 | } | 682 | } |
612 | 683 | ||
684 | static int sas_end_dev_match(struct attribute_container *cont, | ||
685 | struct device *dev) | ||
686 | { | ||
687 | struct Scsi_Host *shost; | ||
688 | struct sas_internal *i; | ||
689 | struct sas_rphy *rphy; | ||
690 | |||
691 | if (!scsi_is_sas_rphy(dev)) | ||
692 | return 0; | ||
693 | shost = dev_to_shost(dev->parent->parent); | ||
694 | rphy = dev_to_rphy(dev); | ||
695 | |||
696 | if (!shost->transportt) | ||
697 | return 0; | ||
698 | if (shost->transportt->host_attrs.ac.class != | ||
699 | &sas_host_class.class) | ||
700 | return 0; | ||
701 | |||
702 | i = to_sas_internal(shost->transportt); | ||
703 | return &i->end_dev_attr_cont.ac == cont && | ||
704 | rphy->identify.device_type == SAS_END_DEVICE && | ||
705 | /* FIXME: remove contained eventually */ | ||
706 | rphy->contained; | ||
707 | } | ||
708 | |||
613 | static void sas_rphy_release(struct device *dev) | 709 | static void sas_rphy_release(struct device *dev) |
614 | { | 710 | { |
615 | struct sas_rphy *rphy = dev_to_rphy(dev); | 711 | struct sas_rphy *rphy = dev_to_rphy(dev); |
@@ -650,6 +746,40 @@ struct sas_rphy *sas_rphy_alloc(struct sas_phy *parent) | |||
650 | EXPORT_SYMBOL(sas_rphy_alloc); | 746 | EXPORT_SYMBOL(sas_rphy_alloc); |
651 | 747 | ||
652 | /** | 748 | /** |
749 | * sas_end_device_alloc - allocate an rphy for an end device | ||
750 | * | ||
751 | * Allocates an SAS remote PHY structure, connected to @parent. | ||
752 | * | ||
753 | * Returns: | ||
754 | * SAS PHY allocated or %NULL if the allocation failed. | ||
755 | */ | ||
756 | struct sas_rphy *sas_end_device_alloc(struct sas_phy *parent) | ||
757 | { | ||
758 | struct Scsi_Host *shost = dev_to_shost(&parent->dev); | ||
759 | struct sas_end_device *rdev; | ||
760 | |||
761 | rdev = kzalloc(sizeof(*rdev), GFP_KERNEL); | ||
762 | if (!rdev) { | ||
763 | put_device(&parent->dev); | ||
764 | return NULL; | ||
765 | } | ||
766 | |||
767 | device_initialize(&rdev->rphy.dev); | ||
768 | rdev->rphy.dev.parent = get_device(&parent->dev); | ||
769 | rdev->rphy.dev.release = sas_rphy_release; | ||
770 | sprintf(rdev->rphy.dev.bus_id, "rphy-%d:%d-%d", | ||
771 | shost->host_no, parent->port_identifier, parent->number); | ||
772 | rdev->rphy.identify.device_type = SAS_END_DEVICE; | ||
773 | /* FIXME: mark the rphy as being contained in a larger structure */ | ||
774 | rdev->rphy.contained = 1; | ||
775 | transport_setup_device(&rdev->rphy.dev); | ||
776 | |||
777 | return &rdev->rphy; | ||
778 | } | ||
779 | EXPORT_SYMBOL(sas_end_device_alloc); | ||
780 | |||
781 | |||
782 | /** | ||
653 | * sas_rphy_add -- add a SAS remote PHY to the device hierachy | 783 | * sas_rphy_add -- add a SAS remote PHY to the device hierachy |
654 | * @rphy: The remote PHY to be added | 784 | * @rphy: The remote PHY to be added |
655 | * | 785 | * |
@@ -807,51 +937,35 @@ static int sas_user_scan(struct Scsi_Host *shost, uint channel, | |||
807 | * Setup / Teardown code | 937 | * Setup / Teardown code |
808 | */ | 938 | */ |
809 | 939 | ||
810 | #define SETUP_RPORT_ATTRIBUTE(field) \ | 940 | #define SETUP_TEMPLATE(attrb, field, perm, test) \ |
811 | i->private_rphy_attrs[count] = class_device_attr_##field; \ | 941 | i->private_##attrb[count] = class_device_attr_##field; \ |
812 | i->private_rphy_attrs[count].attr.mode = S_IRUGO; \ | 942 | i->private_##attrb[count].attr.mode = perm; \ |
813 | i->private_rphy_attrs[count].store = NULL; \ | 943 | i->private_##attrb[count].store = NULL; \ |
814 | i->rphy_attrs[count] = &i->private_rphy_attrs[count]; \ | 944 | i->attrb[count] = &i->private_##attrb[count]; \ |
815 | count++ | 945 | if (test) \ |
946 | count++ | ||
947 | |||
948 | |||
949 | #define SETUP_RPORT_ATTRIBUTE(field) \ | ||
950 | SETUP_TEMPLATE(rphy_attrs, field, S_IRUGO, 1) | ||
816 | 951 | ||
817 | #define SETUP_OPTIONAL_RPORT_ATTRIBUTE(field, func) \ | 952 | #define SETUP_OPTIONAL_RPORT_ATTRIBUTE(field, func) \ |
818 | i->private_rphy_attrs[count] = class_device_attr_##field; \ | 953 | SETUP_TEMPLATE(rphy_attrs, field, S_IRUGO, i->f->func) |
819 | i->private_rphy_attrs[count].attr.mode = S_IRUGO; \ | ||
820 | i->private_rphy_attrs[count].store = NULL; \ | ||
821 | i->rphy_attrs[count] = &i->private_rphy_attrs[count]; \ | ||
822 | if (i->f->func) \ | ||
823 | count++ | ||
824 | 954 | ||
825 | #define SETUP_PORT_ATTRIBUTE(field) \ | 955 | #define SETUP_PORT_ATTRIBUTE(field) \ |
826 | i->private_phy_attrs[count] = class_device_attr_##field; \ | 956 | SETUP_TEMPLATE(phy_attrs, field, S_IRUGO, 1) |
827 | i->private_phy_attrs[count].attr.mode = S_IRUGO; \ | ||
828 | i->private_phy_attrs[count].store = NULL; \ | ||
829 | i->phy_attrs[count] = &i->private_phy_attrs[count]; \ | ||
830 | count++ | ||
831 | 957 | ||
832 | #define SETUP_OPTIONAL_PORT_ATTRIBUTE(field, func) \ | 958 | #define SETUP_OPTIONAL_PORT_ATTRIBUTE(field, func) \ |
833 | i->private_phy_attrs[count] = class_device_attr_##field; \ | 959 | SETUP_TEMPLATE(phy_attrs, field, S_IRUGO, i->f->func) |
834 | i->private_phy_attrs[count].attr.mode = S_IRUGO; \ | ||
835 | i->private_phy_attrs[count].store = NULL; \ | ||
836 | i->phy_attrs[count] = &i->private_phy_attrs[count]; \ | ||
837 | if (i->f->func) \ | ||
838 | count++ | ||
839 | 960 | ||
840 | #define SETUP_PORT_ATTRIBUTE_WRONLY(field) \ | 961 | #define SETUP_PORT_ATTRIBUTE_WRONLY(field) \ |
841 | i->private_phy_attrs[count] = class_device_attr_##field; \ | 962 | SETUP_TEMPLATE(phy_attrs, field, S_IWUGO, 1) |
842 | i->private_phy_attrs[count].attr.mode = S_IWUGO; \ | ||
843 | i->private_phy_attrs[count].show = NULL; \ | ||
844 | i->phy_attrs[count] = &i->private_phy_attrs[count]; \ | ||
845 | count++ | ||
846 | 963 | ||
847 | #define SETUP_OPTIONAL_PORT_ATTRIBUTE_WRONLY(field, func) \ | 964 | #define SETUP_OPTIONAL_PORT_ATTRIBUTE_WRONLY(field, func) \ |
848 | i->private_phy_attrs[count] = class_device_attr_##field; \ | 965 | SETUP_TEMPLATE(phy_attrs, field, S_IWUGO, i->f->func) |
849 | i->private_phy_attrs[count].attr.mode = S_IWUGO; \ | ||
850 | i->private_phy_attrs[count].show = NULL; \ | ||
851 | i->phy_attrs[count] = &i->private_phy_attrs[count]; \ | ||
852 | if (i->f->func) \ | ||
853 | count++ | ||
854 | 966 | ||
967 | #define SETUP_END_DEV_ATTRIBUTE(field) \ | ||
968 | SETUP_TEMPLATE(end_dev_attrs, field, S_IRUGO, 1) | ||
855 | 969 | ||
856 | /** | 970 | /** |
857 | * sas_attach_transport -- instantiate SAS transport template | 971 | * sas_attach_transport -- instantiate SAS transport template |
@@ -885,6 +999,11 @@ sas_attach_transport(struct sas_function_template *ft) | |||
885 | i->rphy_attr_cont.ac.match = sas_rphy_match; | 999 | i->rphy_attr_cont.ac.match = sas_rphy_match; |
886 | transport_container_register(&i->rphy_attr_cont); | 1000 | transport_container_register(&i->rphy_attr_cont); |
887 | 1001 | ||
1002 | i->end_dev_attr_cont.ac.class = &sas_end_dev_class.class; | ||
1003 | i->end_dev_attr_cont.ac.attrs = &i->end_dev_attrs[0]; | ||
1004 | i->end_dev_attr_cont.ac.match = sas_end_dev_match; | ||
1005 | transport_container_register(&i->end_dev_attr_cont); | ||
1006 | |||
888 | i->f = ft; | 1007 | i->f = ft; |
889 | 1008 | ||
890 | count = 0; | 1009 | count = 0; |
@@ -923,6 +1042,12 @@ sas_attach_transport(struct sas_function_template *ft) | |||
923 | get_bay_identifier); | 1042 | get_bay_identifier); |
924 | i->rphy_attrs[count] = NULL; | 1043 | i->rphy_attrs[count] = NULL; |
925 | 1044 | ||
1045 | count = 0; | ||
1046 | SETUP_END_DEV_ATTRIBUTE(end_dev_ready_led_meaning); | ||
1047 | SETUP_END_DEV_ATTRIBUTE(end_dev_I_T_nexus_loss_timeout); | ||
1048 | SETUP_END_DEV_ATTRIBUTE(end_dev_initiator_response_timeout); | ||
1049 | i->end_dev_attrs[count] = NULL; | ||
1050 | |||
926 | return &i->t; | 1051 | return &i->t; |
927 | } | 1052 | } |
928 | EXPORT_SYMBOL(sas_attach_transport); | 1053 | EXPORT_SYMBOL(sas_attach_transport); |
@@ -956,9 +1081,14 @@ static __init int sas_transport_init(void) | |||
956 | error = transport_class_register(&sas_rphy_class); | 1081 | error = transport_class_register(&sas_rphy_class); |
957 | if (error) | 1082 | if (error) |
958 | goto out_unregister_phy; | 1083 | goto out_unregister_phy; |
1084 | error = transport_class_register(&sas_end_dev_class); | ||
1085 | if (error) | ||
1086 | goto out_unregister_rphy; | ||
959 | 1087 | ||
960 | return 0; | 1088 | return 0; |
961 | 1089 | ||
1090 | out_unregister_rphy: | ||
1091 | transport_class_unregister(&sas_rphy_class); | ||
962 | out_unregister_phy: | 1092 | out_unregister_phy: |
963 | transport_class_unregister(&sas_phy_class); | 1093 | transport_class_unregister(&sas_phy_class); |
964 | out_unregister_transport: | 1094 | out_unregister_transport: |
@@ -973,6 +1103,7 @@ static void __exit sas_transport_exit(void) | |||
973 | transport_class_unregister(&sas_host_class); | 1103 | transport_class_unregister(&sas_host_class); |
974 | transport_class_unregister(&sas_phy_class); | 1104 | transport_class_unregister(&sas_phy_class); |
975 | transport_class_unregister(&sas_rphy_class); | 1105 | transport_class_unregister(&sas_rphy_class); |
1106 | transport_class_unregister(&sas_end_dev_class); | ||
976 | } | 1107 | } |
977 | 1108 | ||
978 | MODULE_AUTHOR("Christoph Hellwig"); | 1109 | MODULE_AUTHOR("Christoph Hellwig"); |
diff --git a/include/scsi/scsi_transport_sas.h b/include/scsi/scsi_transport_sas.h index 95e2132d485e..8fded431cf46 100644 --- a/include/scsi/scsi_transport_sas.h +++ b/include/scsi/scsi_transport_sas.h | |||
@@ -82,6 +82,10 @@ struct sas_rphy { | |||
82 | struct sas_identify identify; | 82 | struct sas_identify identify; |
83 | struct list_head list; | 83 | struct list_head list; |
84 | u32 scsi_target_id; | 84 | u32 scsi_target_id; |
85 | /* temporary expedient: mark the rphy as being contained | ||
86 | * within a type specific rphy | ||
87 | * FIXME: pull this out when everything uses the containers */ | ||
88 | unsigned contained:1; | ||
85 | }; | 89 | }; |
86 | 90 | ||
87 | #define dev_to_rphy(d) \ | 91 | #define dev_to_rphy(d) \ |
@@ -90,6 +94,19 @@ struct sas_rphy { | |||
90 | dev_to_rphy((cdev)->dev) | 94 | dev_to_rphy((cdev)->dev) |
91 | #define rphy_to_shost(rphy) \ | 95 | #define rphy_to_shost(rphy) \ |
92 | dev_to_shost((rphy)->dev.parent) | 96 | dev_to_shost((rphy)->dev.parent) |
97 | #define target_to_rphy(targ) \ | ||
98 | dev_to_rphy((targ)->dev.parent) | ||
99 | |||
100 | struct sas_end_device { | ||
101 | struct sas_rphy rphy; | ||
102 | /* flags */ | ||
103 | unsigned ready_led_meaning:1; | ||
104 | /* parameters */ | ||
105 | u16 I_T_nexus_loss_timeout; | ||
106 | u16 initiator_response_timeout; | ||
107 | }; | ||
108 | #define rphy_to_end_device(r) \ | ||
109 | container_of((r), struct sas_end_device, rphy) | ||
93 | 110 | ||
94 | 111 | ||
95 | /* The functions by which the transport class and the driver communicate */ | 112 | /* The functions by which the transport class and the driver communicate */ |
@@ -110,6 +127,7 @@ extern void sas_phy_delete(struct sas_phy *); | |||
110 | extern int scsi_is_sas_phy(const struct device *); | 127 | extern int scsi_is_sas_phy(const struct device *); |
111 | 128 | ||
112 | extern struct sas_rphy *sas_rphy_alloc(struct sas_phy *); | 129 | extern struct sas_rphy *sas_rphy_alloc(struct sas_phy *); |
130 | extern struct sas_rphy *sas_end_device_alloc(struct sas_phy *); | ||
113 | void sas_rphy_free(struct sas_rphy *); | 131 | void sas_rphy_free(struct sas_rphy *); |
114 | extern int sas_rphy_add(struct sas_rphy *); | 132 | extern int sas_rphy_add(struct sas_rphy *); |
115 | extern void sas_rphy_delete(struct sas_rphy *); | 133 | extern void sas_rphy_delete(struct sas_rphy *); |
@@ -118,5 +136,6 @@ extern int scsi_is_sas_rphy(const struct device *); | |||
118 | extern struct scsi_transport_template * | 136 | extern struct scsi_transport_template * |
119 | sas_attach_transport(struct sas_function_template *); | 137 | sas_attach_transport(struct sas_function_template *); |
120 | extern void sas_release_transport(struct scsi_transport_template *); | 138 | extern void sas_release_transport(struct scsi_transport_template *); |
139 | int sas_read_port_mode_page(struct scsi_device *); | ||
121 | 140 | ||
122 | #endif /* SCSI_TRANSPORT_SAS_H */ | 141 | #endif /* SCSI_TRANSPORT_SAS_H */ |