diff options
-rw-r--r-- | drivers/scsi/scsi_transport_sas.c | 141 | ||||
-rw-r--r-- | include/scsi/scsi_transport_sas.h | 31 |
2 files changed, 166 insertions, 6 deletions
diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index 3eb11a175904..5a70d04352cc 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c | |||
@@ -40,6 +40,7 @@ | |||
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 | #define SAS_END_DEV_ATTRS 3 |
43 | #define SAS_EXPANDER_ATTRS 7 | ||
43 | 44 | ||
44 | struct sas_internal { | 45 | struct sas_internal { |
45 | struct scsi_transport_template t; | 46 | struct scsi_transport_template t; |
@@ -49,10 +50,12 @@ struct sas_internal { | |||
49 | struct class_device_attribute private_phy_attrs[SAS_PORT_ATTRS]; | 50 | struct class_device_attribute private_phy_attrs[SAS_PORT_ATTRS]; |
50 | struct class_device_attribute private_rphy_attrs[SAS_RPORT_ATTRS]; | 51 | struct class_device_attribute private_rphy_attrs[SAS_RPORT_ATTRS]; |
51 | struct class_device_attribute private_end_dev_attrs[SAS_END_DEV_ATTRS]; | 52 | struct class_device_attribute private_end_dev_attrs[SAS_END_DEV_ATTRS]; |
53 | struct class_device_attribute private_expander_attrs[SAS_EXPANDER_ATTRS]; | ||
52 | 54 | ||
53 | struct transport_container phy_attr_cont; | 55 | struct transport_container phy_attr_cont; |
54 | struct transport_container rphy_attr_cont; | 56 | struct transport_container rphy_attr_cont; |
55 | struct transport_container end_dev_attr_cont; | 57 | struct transport_container end_dev_attr_cont; |
58 | struct transport_container expander_attr_cont; | ||
56 | 59 | ||
57 | /* | 60 | /* |
58 | * The array of null terminated pointers to attributes | 61 | * The array of null terminated pointers to attributes |
@@ -62,6 +65,7 @@ struct sas_internal { | |||
62 | struct class_device_attribute *phy_attrs[SAS_PORT_ATTRS + 1]; | 65 | struct class_device_attribute *phy_attrs[SAS_PORT_ATTRS + 1]; |
63 | struct class_device_attribute *rphy_attrs[SAS_RPORT_ATTRS + 1]; | 66 | struct class_device_attribute *rphy_attrs[SAS_RPORT_ATTRS + 1]; |
64 | struct class_device_attribute *end_dev_attrs[SAS_END_DEV_ATTRS + 1]; | 67 | struct class_device_attribute *end_dev_attrs[SAS_END_DEV_ATTRS + 1]; |
68 | struct class_device_attribute *expander_attrs[SAS_EXPANDER_ATTRS + 1]; | ||
65 | }; | 69 | }; |
66 | #define to_sas_internal(tmpl) container_of(tmpl, struct sas_internal, t) | 70 | #define to_sas_internal(tmpl) container_of(tmpl, struct sas_internal, t) |
67 | 71 | ||
@@ -69,6 +73,7 @@ struct sas_host_attrs { | |||
69 | struct list_head rphy_list; | 73 | struct list_head rphy_list; |
70 | struct mutex lock; | 74 | struct mutex lock; |
71 | u32 next_target_id; | 75 | u32 next_target_id; |
76 | u32 next_expander_id; | ||
72 | }; | 77 | }; |
73 | #define to_sas_host_attrs(host) ((struct sas_host_attrs *)(host)->shost_data) | 78 | #define to_sas_host_attrs(host) ((struct sas_host_attrs *)(host)->shost_data) |
74 | 79 | ||
@@ -173,6 +178,7 @@ static int sas_host_setup(struct transport_container *tc, struct device *dev, | |||
173 | INIT_LIST_HEAD(&sas_host->rphy_list); | 178 | INIT_LIST_HEAD(&sas_host->rphy_list); |
174 | mutex_init(&sas_host->lock); | 179 | mutex_init(&sas_host->lock); |
175 | sas_host->next_target_id = 0; | 180 | sas_host->next_target_id = 0; |
181 | sas_host->next_expander_id = 0; | ||
176 | return 0; | 182 | return 0; |
177 | } | 183 | } |
178 | 184 | ||
@@ -407,7 +413,12 @@ struct sas_phy *sas_phy_alloc(struct device *parent, int number) | |||
407 | device_initialize(&phy->dev); | 413 | device_initialize(&phy->dev); |
408 | phy->dev.parent = get_device(parent); | 414 | phy->dev.parent = get_device(parent); |
409 | phy->dev.release = sas_phy_release; | 415 | phy->dev.release = sas_phy_release; |
410 | sprintf(phy->dev.bus_id, "phy-%d:%d", shost->host_no, number); | 416 | if (scsi_is_sas_expander_device(parent)) { |
417 | struct sas_rphy *rphy = dev_to_rphy(parent); | ||
418 | sprintf(phy->dev.bus_id, "phy-%d-%d:%d", shost->host_no, | ||
419 | rphy->scsi_target_id, number); | ||
420 | } else | ||
421 | sprintf(phy->dev.bus_id, "phy-%d:%d", shost->host_no, number); | ||
411 | 422 | ||
412 | transport_setup_device(&phy->dev); | 423 | transport_setup_device(&phy->dev); |
413 | 424 | ||
@@ -635,6 +646,9 @@ int sas_read_port_mode_page(struct scsi_device *sdev) | |||
635 | } | 646 | } |
636 | EXPORT_SYMBOL(sas_read_port_mode_page); | 647 | EXPORT_SYMBOL(sas_read_port_mode_page); |
637 | 648 | ||
649 | static DECLARE_TRANSPORT_CLASS(sas_end_dev_class, | ||
650 | "sas_end_device", NULL, NULL, NULL); | ||
651 | |||
638 | #define sas_end_dev_show_simple(field, name, format_string, cast) \ | 652 | #define sas_end_dev_show_simple(field, name, format_string, cast) \ |
639 | static ssize_t \ | 653 | static ssize_t \ |
640 | show_sas_end_dev_##name(struct class_device *cdev, char *buf) \ | 654 | show_sas_end_dev_##name(struct class_device *cdev, char *buf) \ |
@@ -656,8 +670,33 @@ sas_end_dev_simple_attr(I_T_nexus_loss_timeout, I_T_nexus_loss_timeout, | |||
656 | sas_end_dev_simple_attr(initiator_response_timeout, initiator_response_timeout, | 670 | sas_end_dev_simple_attr(initiator_response_timeout, initiator_response_timeout, |
657 | "%d\n", int); | 671 | "%d\n", int); |
658 | 672 | ||
659 | static DECLARE_TRANSPORT_CLASS(sas_end_dev_class, | 673 | static DECLARE_TRANSPORT_CLASS(sas_expander_class, |
660 | "sas_end_device", NULL, NULL, NULL); | 674 | "sas_expander", NULL, NULL, NULL); |
675 | |||
676 | #define sas_expander_show_simple(field, name, format_string, cast) \ | ||
677 | static ssize_t \ | ||
678 | show_sas_expander_##name(struct class_device *cdev, char *buf) \ | ||
679 | { \ | ||
680 | struct sas_rphy *rphy = transport_class_to_rphy(cdev); \ | ||
681 | struct sas_expander_device *edev = rphy_to_expander_device(rphy); \ | ||
682 | \ | ||
683 | return snprintf(buf, 20, format_string, cast edev->field); \ | ||
684 | } | ||
685 | |||
686 | #define sas_expander_simple_attr(field, name, format_string, type) \ | ||
687 | sas_expander_show_simple(field, name, format_string, (type)) \ | ||
688 | static SAS_CLASS_DEVICE_ATTR(expander, name, S_IRUGO, \ | ||
689 | show_sas_expander_##name, NULL) | ||
690 | |||
691 | sas_expander_simple_attr(vendor_id, vendor_id, "%s\n", char *); | ||
692 | sas_expander_simple_attr(product_id, product_id, "%s\n", char *); | ||
693 | sas_expander_simple_attr(product_rev, product_rev, "%s\n", char *); | ||
694 | sas_expander_simple_attr(component_vendor_id, component_vendor_id, | ||
695 | "%s\n", char *); | ||
696 | sas_expander_simple_attr(component_id, component_id, "%u\n", unsigned int); | ||
697 | sas_expander_simple_attr(component_revision_id, component_revision_id, "%u\n", | ||
698 | unsigned int); | ||
699 | sas_expander_simple_attr(level, level, "%d\n", int); | ||
661 | 700 | ||
662 | static DECLARE_TRANSPORT_CLASS(sas_rphy_class, | 701 | static DECLARE_TRANSPORT_CLASS(sas_rphy_class, |
663 | "sas_rphy", NULL, NULL, NULL); | 702 | "sas_rphy", NULL, NULL, NULL); |
@@ -706,6 +745,32 @@ static int sas_end_dev_match(struct attribute_container *cont, | |||
706 | rphy->contained; | 745 | rphy->contained; |
707 | } | 746 | } |
708 | 747 | ||
748 | static int sas_expander_match(struct attribute_container *cont, | ||
749 | struct device *dev) | ||
750 | { | ||
751 | struct Scsi_Host *shost; | ||
752 | struct sas_internal *i; | ||
753 | struct sas_rphy *rphy; | ||
754 | |||
755 | if (!scsi_is_sas_rphy(dev)) | ||
756 | return 0; | ||
757 | shost = dev_to_shost(dev->parent->parent); | ||
758 | rphy = dev_to_rphy(dev); | ||
759 | |||
760 | if (!shost->transportt) | ||
761 | return 0; | ||
762 | if (shost->transportt->host_attrs.ac.class != | ||
763 | &sas_host_class.class) | ||
764 | return 0; | ||
765 | |||
766 | i = to_sas_internal(shost->transportt); | ||
767 | return &i->expander_attr_cont.ac == cont && | ||
768 | (rphy->identify.device_type == SAS_EDGE_EXPANDER_DEVICE || | ||
769 | rphy->identify.device_type == SAS_FANOUT_EXPANDER_DEVICE) && | ||
770 | /* FIXME: remove contained eventually */ | ||
771 | rphy->contained; | ||
772 | } | ||
773 | |||
709 | static void sas_rphy_release(struct device *dev) | 774 | static void sas_rphy_release(struct device *dev) |
710 | { | 775 | { |
711 | struct sas_rphy *rphy = dev_to_rphy(dev); | 776 | struct sas_rphy *rphy = dev_to_rphy(dev); |
@@ -778,6 +843,46 @@ struct sas_rphy *sas_end_device_alloc(struct sas_phy *parent) | |||
778 | } | 843 | } |
779 | EXPORT_SYMBOL(sas_end_device_alloc); | 844 | EXPORT_SYMBOL(sas_end_device_alloc); |
780 | 845 | ||
846 | /** | ||
847 | * sas_expander_alloc - allocate an rphy for an end device | ||
848 | * | ||
849 | * Allocates an SAS remote PHY structure, connected to @parent. | ||
850 | * | ||
851 | * Returns: | ||
852 | * SAS PHY allocated or %NULL if the allocation failed. | ||
853 | */ | ||
854 | struct sas_rphy *sas_expander_alloc(struct sas_phy *parent, | ||
855 | enum sas_device_type type) | ||
856 | { | ||
857 | struct Scsi_Host *shost = dev_to_shost(&parent->dev); | ||
858 | struct sas_expander_device *rdev; | ||
859 | struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); | ||
860 | |||
861 | BUG_ON(type != SAS_EDGE_EXPANDER_DEVICE && | ||
862 | type != SAS_FANOUT_EXPANDER_DEVICE); | ||
863 | |||
864 | rdev = kzalloc(sizeof(*rdev), GFP_KERNEL); | ||
865 | if (!rdev) { | ||
866 | put_device(&parent->dev); | ||
867 | return NULL; | ||
868 | } | ||
869 | |||
870 | device_initialize(&rdev->rphy.dev); | ||
871 | rdev->rphy.dev.parent = get_device(&parent->dev); | ||
872 | rdev->rphy.dev.release = sas_rphy_release; | ||
873 | mutex_lock(&sas_host->lock); | ||
874 | rdev->rphy.scsi_target_id = sas_host->next_expander_id++; | ||
875 | mutex_unlock(&sas_host->lock); | ||
876 | sprintf(rdev->rphy.dev.bus_id, "expander-%d:%d", | ||
877 | shost->host_no, rdev->rphy.scsi_target_id); | ||
878 | rdev->rphy.identify.device_type = type; | ||
879 | /* FIXME: mark the rphy as being contained in a larger structure */ | ||
880 | rdev->rphy.contained = 1; | ||
881 | transport_setup_device(&rdev->rphy.dev); | ||
882 | |||
883 | return &rdev->rphy; | ||
884 | } | ||
885 | EXPORT_SYMBOL(sas_expander_alloc); | ||
781 | 886 | ||
782 | /** | 887 | /** |
783 | * sas_rphy_add -- add a SAS remote PHY to the device hierachy | 888 | * sas_rphy_add -- add a SAS remote PHY to the device hierachy |
@@ -809,11 +914,10 @@ int sas_rphy_add(struct sas_rphy *rphy) | |||
809 | (identify->target_port_protocols & | 914 | (identify->target_port_protocols & |
810 | (SAS_PROTOCOL_SSP|SAS_PROTOCOL_STP|SAS_PROTOCOL_SATA))) | 915 | (SAS_PROTOCOL_SSP|SAS_PROTOCOL_STP|SAS_PROTOCOL_SATA))) |
811 | rphy->scsi_target_id = sas_host->next_target_id++; | 916 | rphy->scsi_target_id = sas_host->next_target_id++; |
812 | else | ||
813 | rphy->scsi_target_id = -1; | ||
814 | mutex_unlock(&sas_host->lock); | 917 | mutex_unlock(&sas_host->lock); |
815 | 918 | ||
816 | if (rphy->scsi_target_id != -1) { | 919 | if (identify->device_type == SAS_END_DEVICE && |
920 | rphy->scsi_target_id != -1) { | ||
817 | scsi_scan_target(&rphy->dev, parent->port_identifier, | 921 | scsi_scan_target(&rphy->dev, parent->port_identifier, |
818 | rphy->scsi_target_id, ~0, 0); | 922 | rphy->scsi_target_id, ~0, 0); |
819 | } | 923 | } |
@@ -967,6 +1071,9 @@ static int sas_user_scan(struct Scsi_Host *shost, uint channel, | |||
967 | #define SETUP_END_DEV_ATTRIBUTE(field) \ | 1071 | #define SETUP_END_DEV_ATTRIBUTE(field) \ |
968 | SETUP_TEMPLATE(end_dev_attrs, field, S_IRUGO, 1) | 1072 | SETUP_TEMPLATE(end_dev_attrs, field, S_IRUGO, 1) |
969 | 1073 | ||
1074 | #define SETUP_EXPANDER_ATTRIBUTE(field) \ | ||
1075 | SETUP_TEMPLATE(expander_attrs, expander_##field, S_IRUGO, 1) | ||
1076 | |||
970 | /** | 1077 | /** |
971 | * sas_attach_transport -- instantiate SAS transport template | 1078 | * sas_attach_transport -- instantiate SAS transport template |
972 | * @ft: SAS transport class function template | 1079 | * @ft: SAS transport class function template |
@@ -1004,6 +1111,11 @@ sas_attach_transport(struct sas_function_template *ft) | |||
1004 | i->end_dev_attr_cont.ac.match = sas_end_dev_match; | 1111 | i->end_dev_attr_cont.ac.match = sas_end_dev_match; |
1005 | transport_container_register(&i->end_dev_attr_cont); | 1112 | transport_container_register(&i->end_dev_attr_cont); |
1006 | 1113 | ||
1114 | i->expander_attr_cont.ac.class = &sas_expander_class.class; | ||
1115 | i->expander_attr_cont.ac.attrs = &i->expander_attrs[0]; | ||
1116 | i->expander_attr_cont.ac.match = sas_expander_match; | ||
1117 | transport_container_register(&i->expander_attr_cont); | ||
1118 | |||
1007 | i->f = ft; | 1119 | i->f = ft; |
1008 | 1120 | ||
1009 | count = 0; | 1121 | count = 0; |
@@ -1048,6 +1160,16 @@ sas_attach_transport(struct sas_function_template *ft) | |||
1048 | SETUP_END_DEV_ATTRIBUTE(end_dev_initiator_response_timeout); | 1160 | SETUP_END_DEV_ATTRIBUTE(end_dev_initiator_response_timeout); |
1049 | i->end_dev_attrs[count] = NULL; | 1161 | i->end_dev_attrs[count] = NULL; |
1050 | 1162 | ||
1163 | count = 0; | ||
1164 | SETUP_EXPANDER_ATTRIBUTE(vendor_id); | ||
1165 | SETUP_EXPANDER_ATTRIBUTE(product_id); | ||
1166 | SETUP_EXPANDER_ATTRIBUTE(product_rev); | ||
1167 | SETUP_EXPANDER_ATTRIBUTE(component_vendor_id); | ||
1168 | SETUP_EXPANDER_ATTRIBUTE(component_id); | ||
1169 | SETUP_EXPANDER_ATTRIBUTE(component_revision_id); | ||
1170 | SETUP_EXPANDER_ATTRIBUTE(level); | ||
1171 | i->expander_attrs[count] = NULL; | ||
1172 | |||
1051 | return &i->t; | 1173 | return &i->t; |
1052 | } | 1174 | } |
1053 | EXPORT_SYMBOL(sas_attach_transport); | 1175 | EXPORT_SYMBOL(sas_attach_transport); |
@@ -1064,6 +1186,7 @@ void sas_release_transport(struct scsi_transport_template *t) | |||
1064 | transport_container_unregister(&i->phy_attr_cont); | 1186 | transport_container_unregister(&i->phy_attr_cont); |
1065 | transport_container_unregister(&i->rphy_attr_cont); | 1187 | transport_container_unregister(&i->rphy_attr_cont); |
1066 | transport_container_unregister(&i->end_dev_attr_cont); | 1188 | transport_container_unregister(&i->end_dev_attr_cont); |
1189 | transport_container_unregister(&i->expander_attr_cont); | ||
1067 | 1190 | ||
1068 | kfree(i); | 1191 | kfree(i); |
1069 | } | 1192 | } |
@@ -1085,9 +1208,14 @@ static __init int sas_transport_init(void) | |||
1085 | error = transport_class_register(&sas_end_dev_class); | 1208 | error = transport_class_register(&sas_end_dev_class); |
1086 | if (error) | 1209 | if (error) |
1087 | goto out_unregister_rphy; | 1210 | goto out_unregister_rphy; |
1211 | error = transport_class_register(&sas_expander_class); | ||
1212 | if (error) | ||
1213 | goto out_unregister_end_dev; | ||
1088 | 1214 | ||
1089 | return 0; | 1215 | return 0; |
1090 | 1216 | ||
1217 | out_unregister_end_dev: | ||
1218 | transport_class_unregister(&sas_end_dev_class); | ||
1091 | out_unregister_rphy: | 1219 | out_unregister_rphy: |
1092 | transport_class_unregister(&sas_rphy_class); | 1220 | transport_class_unregister(&sas_rphy_class); |
1093 | out_unregister_phy: | 1221 | out_unregister_phy: |
@@ -1105,6 +1233,7 @@ static void __exit sas_transport_exit(void) | |||
1105 | transport_class_unregister(&sas_phy_class); | 1233 | transport_class_unregister(&sas_phy_class); |
1106 | transport_class_unregister(&sas_rphy_class); | 1234 | transport_class_unregister(&sas_rphy_class); |
1107 | transport_class_unregister(&sas_end_dev_class); | 1235 | transport_class_unregister(&sas_end_dev_class); |
1236 | transport_class_unregister(&sas_expander_class); | ||
1108 | } | 1237 | } |
1109 | 1238 | ||
1110 | MODULE_AUTHOR("Christoph Hellwig"); | 1239 | MODULE_AUTHOR("Christoph Hellwig"); |
diff --git a/include/scsi/scsi_transport_sas.h b/include/scsi/scsi_transport_sas.h index 8fded431cf46..2943ccc22a43 100644 --- a/include/scsi/scsi_transport_sas.h +++ b/include/scsi/scsi_transport_sas.h | |||
@@ -108,6 +108,25 @@ struct sas_end_device { | |||
108 | #define rphy_to_end_device(r) \ | 108 | #define rphy_to_end_device(r) \ |
109 | container_of((r), struct sas_end_device, rphy) | 109 | container_of((r), struct sas_end_device, rphy) |
110 | 110 | ||
111 | struct sas_expander_device { | ||
112 | int level; | ||
113 | |||
114 | #define SAS_EXPANDER_VENDOR_ID_LEN 8 | ||
115 | char vendor_id[SAS_EXPANDER_VENDOR_ID_LEN+1]; | ||
116 | #define SAS_EXPANDER_PRODUCT_ID_LEN 16 | ||
117 | char product_id[SAS_EXPANDER_PRODUCT_ID_LEN+1]; | ||
118 | #define SAS_EXPANDER_PRODUCT_REV_LEN 4 | ||
119 | char product_rev[SAS_EXPANDER_PRODUCT_REV_LEN+1]; | ||
120 | #define SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN 8 | ||
121 | char component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN+1]; | ||
122 | u16 component_id; | ||
123 | u8 component_revision_id; | ||
124 | |||
125 | struct sas_rphy rphy; | ||
126 | |||
127 | }; | ||
128 | #define rphy_to_expander_device(r) \ | ||
129 | container_of((r), struct sas_expander_device, rphy) | ||
111 | 130 | ||
112 | /* The functions by which the transport class and the driver communicate */ | 131 | /* The functions by which the transport class and the driver communicate */ |
113 | struct sas_function_template { | 132 | struct sas_function_template { |
@@ -128,6 +147,7 @@ extern int scsi_is_sas_phy(const struct device *); | |||
128 | 147 | ||
129 | extern struct sas_rphy *sas_rphy_alloc(struct sas_phy *); | 148 | extern struct sas_rphy *sas_rphy_alloc(struct sas_phy *); |
130 | extern struct sas_rphy *sas_end_device_alloc(struct sas_phy *); | 149 | extern struct sas_rphy *sas_end_device_alloc(struct sas_phy *); |
150 | extern struct sas_rphy *sas_expander_alloc(struct sas_phy *, enum sas_device_type); | ||
131 | void sas_rphy_free(struct sas_rphy *); | 151 | void sas_rphy_free(struct sas_rphy *); |
132 | extern int sas_rphy_add(struct sas_rphy *); | 152 | extern int sas_rphy_add(struct sas_rphy *); |
133 | extern void sas_rphy_delete(struct sas_rphy *); | 153 | extern void sas_rphy_delete(struct sas_rphy *); |
@@ -138,4 +158,15 @@ sas_attach_transport(struct sas_function_template *); | |||
138 | extern void sas_release_transport(struct scsi_transport_template *); | 158 | extern void sas_release_transport(struct scsi_transport_template *); |
139 | int sas_read_port_mode_page(struct scsi_device *); | 159 | int sas_read_port_mode_page(struct scsi_device *); |
140 | 160 | ||
161 | static inline int | ||
162 | scsi_is_sas_expander_device(struct device *dev) | ||
163 | { | ||
164 | struct sas_rphy *rphy; | ||
165 | if (!scsi_is_sas_rphy(dev)) | ||
166 | return 0; | ||
167 | rphy = dev_to_rphy(dev); | ||
168 | return rphy->identify.device_type == SAS_FANOUT_EXPANDER_DEVICE || | ||
169 | rphy->identify.device_type == SAS_EDGE_EXPANDER_DEVICE; | ||
170 | } | ||
171 | |||
141 | #endif /* SCSI_TRANSPORT_SAS_H */ | 172 | #endif /* SCSI_TRANSPORT_SAS_H */ |