diff options
author | Christoph Hellwig <hch@lst.de> | 2006-01-13 12:04:41 -0500 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.(none)> | 2006-01-14 11:54:56 -0500 |
commit | 9a28f49adbe4955af8a1306fd36ddae15136dde7 (patch) | |
tree | ff161da935cf2a14408b9040f685b9e9a530fd82 /drivers/message/fusion/mptsas.c | |
parent | 9638d89a75776abc614c29cdeece0cc874ea2a4c (diff) |
[SCSI] mptsas: support basic hotplug
Adds hotplug support for SAS end devices. Unfortunately the fusion
firmware doesn't generate similar events for expanders addition/removal
so we can't support them yet. Eric has an idea about a clever scheme to
find out about expander changes so that'll be added later on.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/message/fusion/mptsas.c')
-rw-r--r-- | drivers/message/fusion/mptsas.c | 338 |
1 files changed, 307 insertions, 31 deletions
diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c index 17e9757e728b..b2c682fe634f 100644 --- a/drivers/message/fusion/mptsas.c +++ b/drivers/message/fusion/mptsas.c | |||
@@ -5,7 +5,7 @@ | |||
5 | * | 5 | * |
6 | * Copyright (c) 1999-2005 LSI Logic Corporation | 6 | * Copyright (c) 1999-2005 LSI Logic Corporation |
7 | * (mailto:mpt_linux_developer@lsil.com) | 7 | * (mailto:mpt_linux_developer@lsil.com) |
8 | * Copyright (c) 2005 Dell | 8 | * Copyright (c) 2005-2006 Dell |
9 | */ | 9 | */ |
10 | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 10 | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ |
11 | /* | 11 | /* |
@@ -86,6 +86,24 @@ static int mptsasInternalCtx = -1; /* Used only for internal commands */ | |||
86 | static int mptsasMgmtCtx = -1; | 86 | static int mptsasMgmtCtx = -1; |
87 | 87 | ||
88 | 88 | ||
89 | enum mptsas_hotplug_action { | ||
90 | MPTSAS_ADD_DEVICE, | ||
91 | MPTSAS_DEL_DEVICE, | ||
92 | }; | ||
93 | |||
94 | struct mptsas_hotplug_event { | ||
95 | struct work_struct work; | ||
96 | MPT_ADAPTER *ioc; | ||
97 | enum mptsas_hotplug_action event_type; | ||
98 | u64 sas_address; | ||
99 | u32 channel; | ||
100 | u32 id; | ||
101 | u32 device_info; | ||
102 | u16 handle; | ||
103 | u16 parent_handle; | ||
104 | u8 phy_id; | ||
105 | }; | ||
106 | |||
89 | /* | 107 | /* |
90 | * SAS topology structures | 108 | * SAS topology structures |
91 | * | 109 | * |
@@ -99,8 +117,8 @@ struct mptsas_devinfo { | |||
99 | u8 phy_id; /* phy number of parent device */ | 117 | u8 phy_id; /* phy number of parent device */ |
100 | u8 port_id; /* sas physical port this device | 118 | u8 port_id; /* sas physical port this device |
101 | is assoc'd with */ | 119 | is assoc'd with */ |
102 | u8 target; /* logical target id of this device */ | 120 | u8 id; /* logical target id of this device */ |
103 | u8 bus; /* logical bus number of this device */ | 121 | u8 channel; /* logical bus number of this device */ |
104 | u64 sas_address; /* WWN of this device, | 122 | u64 sas_address; /* WWN of this device, |
105 | SATA is assigned by HBA,expander */ | 123 | SATA is assigned by HBA,expander */ |
106 | u32 device_info; /* bitfield detailed info about this device */ | 124 | u32 device_info; /* bitfield detailed info about this device */ |
@@ -114,6 +132,7 @@ struct mptsas_phyinfo { | |||
114 | u8 programmed_link_rate; /* programmed max/min phy link rate */ | 132 | u8 programmed_link_rate; /* programmed max/min phy link rate */ |
115 | struct mptsas_devinfo identify; /* point to phy device info */ | 133 | struct mptsas_devinfo identify; /* point to phy device info */ |
116 | struct mptsas_devinfo attached; /* point to attached device info */ | 134 | struct mptsas_devinfo attached; /* point to attached device info */ |
135 | struct sas_phy *phy; | ||
117 | struct sas_rphy *rphy; | 136 | struct sas_rphy *rphy; |
118 | }; | 137 | }; |
119 | 138 | ||
@@ -257,24 +276,27 @@ mptsas_slave_alloc(struct scsi_device *sdev) | |||
257 | } | 276 | } |
258 | 277 | ||
259 | rphy = dev_to_rphy(sdev->sdev_target->dev.parent); | 278 | rphy = dev_to_rphy(sdev->sdev_target->dev.parent); |
279 | mutex_lock(&hd->ioc->sas_topology_mutex); | ||
260 | list_for_each_entry(p, &hd->ioc->sas_topology, list) { | 280 | list_for_each_entry(p, &hd->ioc->sas_topology, list) { |
261 | for (i = 0; i < p->num_phys; i++) { | 281 | for (i = 0; i < p->num_phys; i++) { |
262 | if (p->phy_info[i].attached.sas_address == | 282 | if (p->phy_info[i].attached.sas_address == |
263 | rphy->identify.sas_address) { | 283 | rphy->identify.sas_address) { |
264 | vdev->target_id = | 284 | vdev->target_id = |
265 | p->phy_info[i].attached.target; | 285 | p->phy_info[i].attached.id; |
266 | vdev->bus_id = p->phy_info[i].attached.bus; | 286 | vdev->bus_id = p->phy_info[i].attached.channel; |
267 | vdev->lun = sdev->lun; | 287 | vdev->lun = sdev->lun; |
268 | goto out; | 288 | goto out; |
269 | } | 289 | } |
270 | } | 290 | } |
271 | } | 291 | } |
292 | mutex_unlock(&hd->ioc->sas_topology_mutex); | ||
272 | 293 | ||
273 | printk("No matching SAS device found!!\n"); | 294 | printk("No matching SAS device found!!\n"); |
274 | kfree(vdev); | 295 | kfree(vdev); |
275 | return -ENODEV; | 296 | return -ENODEV; |
276 | 297 | ||
277 | out: | 298 | out: |
299 | mutex_unlock(&hd->ioc->sas_topology_mutex); | ||
278 | vtarget->ioc_id = vdev->ioc_id; | 300 | vtarget->ioc_id = vdev->ioc_id; |
279 | vtarget->target_id = vdev->target_id; | 301 | vtarget->target_id = vdev->target_id; |
280 | vtarget->bus_id = vdev->bus_id; | 302 | vtarget->bus_id = vdev->bus_id; |
@@ -282,6 +304,42 @@ mptsas_slave_alloc(struct scsi_device *sdev) | |||
282 | return 0; | 304 | return 0; |
283 | } | 305 | } |
284 | 306 | ||
307 | static void | ||
308 | mptsas_slave_destroy(struct scsi_device *sdev) | ||
309 | { | ||
310 | struct Scsi_Host *host = sdev->host; | ||
311 | MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata; | ||
312 | struct sas_rphy *rphy; | ||
313 | struct mptsas_portinfo *p; | ||
314 | int i; | ||
315 | |||
316 | /* | ||
317 | * Handle hotplug removal case. | ||
318 | * We need to clear out attached data structure. | ||
319 | */ | ||
320 | rphy = dev_to_rphy(sdev->sdev_target->dev.parent); | ||
321 | |||
322 | mutex_lock(&hd->ioc->sas_topology_mutex); | ||
323 | list_for_each_entry(p, &hd->ioc->sas_topology, list) { | ||
324 | for (i = 0; i < p->num_phys; i++) { | ||
325 | if (p->phy_info[i].attached.sas_address == | ||
326 | rphy->identify.sas_address) { | ||
327 | memset(&p->phy_info[i].attached, 0, | ||
328 | sizeof(struct mptsas_devinfo)); | ||
329 | p->phy_info[i].rphy = NULL; | ||
330 | goto out; | ||
331 | } | ||
332 | } | ||
333 | } | ||
334 | |||
335 | out: | ||
336 | mutex_unlock(&hd->ioc->sas_topology_mutex); | ||
337 | /* | ||
338 | * TODO: Issue target reset to flush firmware outstanding commands. | ||
339 | */ | ||
340 | mptscsih_slave_destroy(sdev); | ||
341 | } | ||
342 | |||
285 | static struct scsi_host_template mptsas_driver_template = { | 343 | static struct scsi_host_template mptsas_driver_template = { |
286 | .module = THIS_MODULE, | 344 | .module = THIS_MODULE, |
287 | .proc_name = "mptsas", | 345 | .proc_name = "mptsas", |
@@ -293,7 +351,7 @@ static struct scsi_host_template mptsas_driver_template = { | |||
293 | .slave_alloc = mptsas_slave_alloc, | 351 | .slave_alloc = mptsas_slave_alloc, |
294 | .slave_configure = mptscsih_slave_configure, | 352 | .slave_configure = mptscsih_slave_configure, |
295 | .target_destroy = mptscsih_target_destroy, | 353 | .target_destroy = mptscsih_target_destroy, |
296 | .slave_destroy = mptscsih_slave_destroy, | 354 | .slave_destroy = mptsas_slave_destroy, |
297 | .change_queue_depth = mptscsih_change_queue_depth, | 355 | .change_queue_depth = mptscsih_change_queue_depth, |
298 | .eh_abort_handler = mptscsih_abort, | 356 | .eh_abort_handler = mptscsih_abort, |
299 | .eh_device_reset_handler = mptscsih_dev_reset, | 357 | .eh_device_reset_handler = mptscsih_dev_reset, |
@@ -649,8 +707,8 @@ mptsas_sas_device_pg0(MPT_ADAPTER *ioc, struct mptsas_devinfo *device_info, | |||
649 | device_info->handle = le16_to_cpu(buffer->DevHandle); | 707 | device_info->handle = le16_to_cpu(buffer->DevHandle); |
650 | device_info->phy_id = buffer->PhyNum; | 708 | device_info->phy_id = buffer->PhyNum; |
651 | device_info->port_id = buffer->PhysicalPort; | 709 | device_info->port_id = buffer->PhysicalPort; |
652 | device_info->target = buffer->TargetID; | 710 | device_info->id = buffer->TargetID; |
653 | device_info->bus = buffer->Bus; | 711 | device_info->channel = buffer->Bus; |
654 | memcpy(&sas_address, &buffer->SASAddress, sizeof(__le64)); | 712 | memcpy(&sas_address, &buffer->SASAddress, sizeof(__le64)); |
655 | device_info->sas_address = le64_to_cpu(sas_address); | 713 | device_info->sas_address = le64_to_cpu(sas_address); |
656 | device_info->device_info = | 714 | device_info->device_info = |
@@ -858,36 +916,36 @@ mptsas_parse_device_info(struct sas_identify *identify, | |||
858 | static int mptsas_probe_one_phy(struct device *dev, | 916 | static int mptsas_probe_one_phy(struct device *dev, |
859 | struct mptsas_phyinfo *phy_info, int index, int local) | 917 | struct mptsas_phyinfo *phy_info, int index, int local) |
860 | { | 918 | { |
861 | struct sas_phy *port; | 919 | struct sas_phy *phy; |
862 | int error; | 920 | int error; |
863 | 921 | ||
864 | port = sas_phy_alloc(dev, index); | 922 | phy = sas_phy_alloc(dev, index); |
865 | if (!port) | 923 | if (!phy) |
866 | return -ENOMEM; | 924 | return -ENOMEM; |
867 | 925 | ||
868 | port->port_identifier = phy_info->port_id; | 926 | phy->port_identifier = phy_info->port_id; |
869 | mptsas_parse_device_info(&port->identify, &phy_info->identify); | 927 | mptsas_parse_device_info(&phy->identify, &phy_info->identify); |
870 | 928 | ||
871 | /* | 929 | /* |
872 | * Set Negotiated link rate. | 930 | * Set Negotiated link rate. |
873 | */ | 931 | */ |
874 | switch (phy_info->negotiated_link_rate) { | 932 | switch (phy_info->negotiated_link_rate) { |
875 | case MPI_SAS_IOUNIT0_RATE_PHY_DISABLED: | 933 | case MPI_SAS_IOUNIT0_RATE_PHY_DISABLED: |
876 | port->negotiated_linkrate = SAS_PHY_DISABLED; | 934 | phy->negotiated_linkrate = SAS_PHY_DISABLED; |
877 | break; | 935 | break; |
878 | case MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION: | 936 | case MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION: |
879 | port->negotiated_linkrate = SAS_LINK_RATE_FAILED; | 937 | phy->negotiated_linkrate = SAS_LINK_RATE_FAILED; |
880 | break; | 938 | break; |
881 | case MPI_SAS_IOUNIT0_RATE_1_5: | 939 | case MPI_SAS_IOUNIT0_RATE_1_5: |
882 | port->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS; | 940 | phy->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS; |
883 | break; | 941 | break; |
884 | case MPI_SAS_IOUNIT0_RATE_3_0: | 942 | case MPI_SAS_IOUNIT0_RATE_3_0: |
885 | port->negotiated_linkrate = SAS_LINK_RATE_3_0_GBPS; | 943 | phy->negotiated_linkrate = SAS_LINK_RATE_3_0_GBPS; |
886 | break; | 944 | break; |
887 | case MPI_SAS_IOUNIT0_RATE_SATA_OOB_COMPLETE: | 945 | case MPI_SAS_IOUNIT0_RATE_SATA_OOB_COMPLETE: |
888 | case MPI_SAS_IOUNIT0_RATE_UNKNOWN: | 946 | case MPI_SAS_IOUNIT0_RATE_UNKNOWN: |
889 | default: | 947 | default: |
890 | port->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN; | 948 | phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN; |
891 | break; | 949 | break; |
892 | } | 950 | } |
893 | 951 | ||
@@ -896,10 +954,10 @@ static int mptsas_probe_one_phy(struct device *dev, | |||
896 | */ | 954 | */ |
897 | switch (phy_info->hw_link_rate & MPI_SAS_PHY0_PRATE_MAX_RATE_MASK) { | 955 | switch (phy_info->hw_link_rate & MPI_SAS_PHY0_PRATE_MAX_RATE_MASK) { |
898 | case MPI_SAS_PHY0_HWRATE_MAX_RATE_1_5: | 956 | case MPI_SAS_PHY0_HWRATE_MAX_RATE_1_5: |
899 | port->maximum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS; | 957 | phy->maximum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS; |
900 | break; | 958 | break; |
901 | case MPI_SAS_PHY0_PRATE_MAX_RATE_3_0: | 959 | case MPI_SAS_PHY0_PRATE_MAX_RATE_3_0: |
902 | port->maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS; | 960 | phy->maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS; |
903 | break; | 961 | break; |
904 | default: | 962 | default: |
905 | break; | 963 | break; |
@@ -911,10 +969,10 @@ static int mptsas_probe_one_phy(struct device *dev, | |||
911 | switch (phy_info->programmed_link_rate & | 969 | switch (phy_info->programmed_link_rate & |
912 | MPI_SAS_PHY0_PRATE_MAX_RATE_MASK) { | 970 | MPI_SAS_PHY0_PRATE_MAX_RATE_MASK) { |
913 | case MPI_SAS_PHY0_PRATE_MAX_RATE_1_5: | 971 | case MPI_SAS_PHY0_PRATE_MAX_RATE_1_5: |
914 | port->maximum_linkrate = SAS_LINK_RATE_1_5_GBPS; | 972 | phy->maximum_linkrate = SAS_LINK_RATE_1_5_GBPS; |
915 | break; | 973 | break; |
916 | case MPI_SAS_PHY0_PRATE_MAX_RATE_3_0: | 974 | case MPI_SAS_PHY0_PRATE_MAX_RATE_3_0: |
917 | port->maximum_linkrate = SAS_LINK_RATE_3_0_GBPS; | 975 | phy->maximum_linkrate = SAS_LINK_RATE_3_0_GBPS; |
918 | break; | 976 | break; |
919 | default: | 977 | default: |
920 | break; | 978 | break; |
@@ -925,10 +983,10 @@ static int mptsas_probe_one_phy(struct device *dev, | |||
925 | */ | 983 | */ |
926 | switch (phy_info->hw_link_rate & MPI_SAS_PHY0_HWRATE_MIN_RATE_MASK) { | 984 | switch (phy_info->hw_link_rate & MPI_SAS_PHY0_HWRATE_MIN_RATE_MASK) { |
927 | case MPI_SAS_PHY0_HWRATE_MIN_RATE_1_5: | 985 | case MPI_SAS_PHY0_HWRATE_MIN_RATE_1_5: |
928 | port->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS; | 986 | phy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS; |
929 | break; | 987 | break; |
930 | case MPI_SAS_PHY0_PRATE_MIN_RATE_3_0: | 988 | case MPI_SAS_PHY0_PRATE_MIN_RATE_3_0: |
931 | port->minimum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS; | 989 | phy->minimum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS; |
932 | break; | 990 | break; |
933 | default: | 991 | default: |
934 | break; | 992 | break; |
@@ -940,28 +998,29 @@ static int mptsas_probe_one_phy(struct device *dev, | |||
940 | switch (phy_info->programmed_link_rate & | 998 | switch (phy_info->programmed_link_rate & |
941 | MPI_SAS_PHY0_PRATE_MIN_RATE_MASK) { | 999 | MPI_SAS_PHY0_PRATE_MIN_RATE_MASK) { |
942 | case MPI_SAS_PHY0_PRATE_MIN_RATE_1_5: | 1000 | case MPI_SAS_PHY0_PRATE_MIN_RATE_1_5: |
943 | port->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS; | 1001 | phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS; |
944 | break; | 1002 | break; |
945 | case MPI_SAS_PHY0_PRATE_MIN_RATE_3_0: | 1003 | case MPI_SAS_PHY0_PRATE_MIN_RATE_3_0: |
946 | port->minimum_linkrate = SAS_LINK_RATE_3_0_GBPS; | 1004 | phy->minimum_linkrate = SAS_LINK_RATE_3_0_GBPS; |
947 | break; | 1005 | break; |
948 | default: | 1006 | default: |
949 | break; | 1007 | break; |
950 | } | 1008 | } |
951 | 1009 | ||
952 | if (local) | 1010 | if (local) |
953 | port->local_attached = 1; | 1011 | phy->local_attached = 1; |
954 | 1012 | ||
955 | error = sas_phy_add(port); | 1013 | error = sas_phy_add(phy); |
956 | if (error) { | 1014 | if (error) { |
957 | sas_phy_free(port); | 1015 | sas_phy_free(phy); |
958 | return error; | 1016 | return error; |
959 | } | 1017 | } |
1018 | phy_info->phy = phy; | ||
960 | 1019 | ||
961 | if (phy_info->attached.handle) { | 1020 | if (phy_info->attached.handle) { |
962 | struct sas_rphy *rphy; | 1021 | struct sas_rphy *rphy; |
963 | 1022 | ||
964 | rphy = sas_rphy_alloc(port); | 1023 | rphy = sas_rphy_alloc(phy); |
965 | if (!rphy) | 1024 | if (!rphy) |
966 | return 0; /* non-fatal: an rphy can be added later */ | 1025 | return 0; /* non-fatal: an rphy can be added later */ |
967 | 1026 | ||
@@ -994,7 +1053,10 @@ mptsas_probe_hba_phys(MPT_ADAPTER *ioc, int *index) | |||
994 | if (error) | 1053 | if (error) |
995 | goto out_free_port_info; | 1054 | goto out_free_port_info; |
996 | 1055 | ||
1056 | mutex_lock(&ioc->sas_topology_mutex); | ||
997 | list_add_tail(&port_info->list, &ioc->sas_topology); | 1057 | list_add_tail(&port_info->list, &ioc->sas_topology); |
1058 | mutex_unlock(&ioc->sas_topology_mutex); | ||
1059 | |||
998 | for (i = 0; i < port_info->num_phys; i++) { | 1060 | for (i = 0; i < port_info->num_phys; i++) { |
999 | mptsas_sas_phy_pg0(ioc, &port_info->phy_info[i], | 1061 | mptsas_sas_phy_pg0(ioc, &port_info->phy_info[i], |
1000 | (MPI_SAS_PHY_PGAD_FORM_PHY_NUMBER << | 1062 | (MPI_SAS_PHY_PGAD_FORM_PHY_NUMBER << |
@@ -1047,7 +1109,10 @@ mptsas_probe_expander_phys(MPT_ADAPTER *ioc, u32 *handle, int *index) | |||
1047 | 1109 | ||
1048 | *handle = port_info->handle; | 1110 | *handle = port_info->handle; |
1049 | 1111 | ||
1112 | mutex_lock(&ioc->sas_topology_mutex); | ||
1050 | list_add_tail(&port_info->list, &ioc->sas_topology); | 1113 | list_add_tail(&port_info->list, &ioc->sas_topology); |
1114 | mutex_unlock(&ioc->sas_topology_mutex); | ||
1115 | |||
1051 | for (i = 0; i < port_info->num_phys; i++) { | 1116 | for (i = 0; i < port_info->num_phys; i++) { |
1052 | struct device *parent; | 1117 | struct device *parent; |
1053 | 1118 | ||
@@ -1079,6 +1144,7 @@ mptsas_probe_expander_phys(MPT_ADAPTER *ioc, u32 *handle, int *index) | |||
1079 | * HBA phys. | 1144 | * HBA phys. |
1080 | */ | 1145 | */ |
1081 | parent = &ioc->sh->shost_gendev; | 1146 | parent = &ioc->sh->shost_gendev; |
1147 | mutex_lock(&ioc->sas_topology_mutex); | ||
1082 | list_for_each_entry(p, &ioc->sas_topology, list) { | 1148 | list_for_each_entry(p, &ioc->sas_topology, list) { |
1083 | for (j = 0; j < p->num_phys; j++) { | 1149 | for (j = 0; j < p->num_phys; j++) { |
1084 | if (port_info->phy_info[i].identify.handle == | 1150 | if (port_info->phy_info[i].identify.handle == |
@@ -1086,6 +1152,7 @@ mptsas_probe_expander_phys(MPT_ADAPTER *ioc, u32 *handle, int *index) | |||
1086 | parent = &p->phy_info[j].rphy->dev; | 1152 | parent = &p->phy_info[j].rphy->dev; |
1087 | } | 1153 | } |
1088 | } | 1154 | } |
1155 | mutex_unlock(&ioc->sas_topology_mutex); | ||
1089 | 1156 | ||
1090 | mptsas_probe_one_phy(parent, &port_info->phy_info[i], | 1157 | mptsas_probe_one_phy(parent, &port_info->phy_info[i], |
1091 | *index, 0); | 1158 | *index, 0); |
@@ -1111,6 +1178,211 @@ mptsas_scan_sas_topology(MPT_ADAPTER *ioc) | |||
1111 | ; | 1178 | ; |
1112 | } | 1179 | } |
1113 | 1180 | ||
1181 | static struct mptsas_phyinfo * | ||
1182 | mptsas_find_phyinfo_by_parent(MPT_ADAPTER *ioc, u16 parent_handle, u8 phy_id) | ||
1183 | { | ||
1184 | struct mptsas_portinfo *port_info; | ||
1185 | struct mptsas_devinfo device_info; | ||
1186 | struct mptsas_phyinfo *phy_info = NULL; | ||
1187 | int i, error; | ||
1188 | |||
1189 | /* | ||
1190 | * Retrieve the parent sas_address | ||
1191 | */ | ||
1192 | error = mptsas_sas_device_pg0(ioc, &device_info, | ||
1193 | (MPI_SAS_DEVICE_PGAD_FORM_HANDLE << | ||
1194 | MPI_SAS_DEVICE_PGAD_FORM_SHIFT), | ||
1195 | parent_handle); | ||
1196 | if (error) { | ||
1197 | printk("mptsas: failed to retrieve device page\n"); | ||
1198 | return NULL; | ||
1199 | } | ||
1200 | |||
1201 | /* | ||
1202 | * The phy_info structures are never deallocated during lifetime of | ||
1203 | * a host, so the code below is safe without additional refcounting. | ||
1204 | */ | ||
1205 | mutex_lock(&ioc->sas_topology_mutex); | ||
1206 | list_for_each_entry(port_info, &ioc->sas_topology, list) { | ||
1207 | for (i = 0; i < port_info->num_phys; i++) { | ||
1208 | if (port_info->phy_info[i].identify.sas_address == | ||
1209 | device_info.sas_address && | ||
1210 | port_info->phy_info[i].phy_id == phy_id) { | ||
1211 | phy_info = &port_info->phy_info[i]; | ||
1212 | break; | ||
1213 | } | ||
1214 | } | ||
1215 | } | ||
1216 | mutex_unlock(&ioc->sas_topology_mutex); | ||
1217 | |||
1218 | return phy_info; | ||
1219 | } | ||
1220 | |||
1221 | static struct mptsas_phyinfo * | ||
1222 | mptsas_find_phyinfo_by_handle(MPT_ADAPTER *ioc, u16 handle) | ||
1223 | { | ||
1224 | struct mptsas_portinfo *port_info; | ||
1225 | struct mptsas_phyinfo *phy_info = NULL; | ||
1226 | int i; | ||
1227 | |||
1228 | /* | ||
1229 | * The phy_info structures are never deallocated during lifetime of | ||
1230 | * a host, so the code below is safe without additional refcounting. | ||
1231 | */ | ||
1232 | mutex_lock(&ioc->sas_topology_mutex); | ||
1233 | list_for_each_entry(port_info, &ioc->sas_topology, list) { | ||
1234 | for (i = 0; i < port_info->num_phys; i++) { | ||
1235 | if (port_info->phy_info[i].attached.handle == handle) { | ||
1236 | phy_info = &port_info->phy_info[i]; | ||
1237 | break; | ||
1238 | } | ||
1239 | } | ||
1240 | } | ||
1241 | mutex_unlock(&ioc->sas_topology_mutex); | ||
1242 | |||
1243 | return phy_info; | ||
1244 | } | ||
1245 | |||
1246 | static void | ||
1247 | mptsas_hotplug_work(void *arg) | ||
1248 | { | ||
1249 | struct mptsas_hotplug_event *ev = arg; | ||
1250 | MPT_ADAPTER *ioc = ev->ioc; | ||
1251 | struct mptsas_phyinfo *phy_info; | ||
1252 | struct sas_rphy *rphy; | ||
1253 | char *ds = NULL; | ||
1254 | |||
1255 | if (ev->device_info & MPI_SAS_DEVICE_INFO_SSP_TARGET) | ||
1256 | ds = "ssp"; | ||
1257 | if (ev->device_info & MPI_SAS_DEVICE_INFO_STP_TARGET) | ||
1258 | ds = "stp"; | ||
1259 | if (ev->device_info & MPI_SAS_DEVICE_INFO_SATA_DEVICE) | ||
1260 | ds = "sata"; | ||
1261 | |||
1262 | switch (ev->event_type) { | ||
1263 | case MPTSAS_DEL_DEVICE: | ||
1264 | printk(MYIOC_s_INFO_FMT | ||
1265 | "removing %s device, channel %d, id %d, phy %d\n", | ||
1266 | ioc->name, ds, ev->channel, ev->id, ev->phy_id); | ||
1267 | |||
1268 | phy_info = mptsas_find_phyinfo_by_handle(ioc, ev->handle); | ||
1269 | if (!phy_info) { | ||
1270 | printk("mptsas: remove event for non-existant PHY.\n"); | ||
1271 | break; | ||
1272 | } | ||
1273 | |||
1274 | if (phy_info->rphy) { | ||
1275 | sas_rphy_delete(phy_info->rphy); | ||
1276 | phy_info->rphy = NULL; | ||
1277 | } | ||
1278 | break; | ||
1279 | case MPTSAS_ADD_DEVICE: | ||
1280 | printk(MYIOC_s_INFO_FMT | ||
1281 | "attaching %s device, channel %d, id %d, phy %d\n", | ||
1282 | ioc->name, ds, ev->channel, ev->id, ev->phy_id); | ||
1283 | |||
1284 | phy_info = mptsas_find_phyinfo_by_parent(ioc, | ||
1285 | ev->parent_handle, ev->phy_id); | ||
1286 | if (!phy_info) { | ||
1287 | printk("mptsas: add event for non-existant PHY.\n"); | ||
1288 | break; | ||
1289 | } | ||
1290 | |||
1291 | if (phy_info->rphy) { | ||
1292 | printk("mptsas: trying to add existing device.\n"); | ||
1293 | break; | ||
1294 | } | ||
1295 | |||
1296 | /* fill attached info */ | ||
1297 | phy_info->attached.handle = ev->handle; | ||
1298 | phy_info->attached.phy_id = ev->phy_id; | ||
1299 | phy_info->attached.port_id = phy_info->identify.port_id; | ||
1300 | phy_info->attached.id = ev->id; | ||
1301 | phy_info->attached.channel = ev->channel; | ||
1302 | phy_info->attached.sas_address = ev->sas_address; | ||
1303 | phy_info->attached.device_info = ev->device_info; | ||
1304 | |||
1305 | rphy = sas_rphy_alloc(phy_info->phy); | ||
1306 | if (!rphy) | ||
1307 | break; /* non-fatal: an rphy can be added later */ | ||
1308 | |||
1309 | mptsas_parse_device_info(&rphy->identify, &phy_info->attached); | ||
1310 | if (sas_rphy_add(rphy)) { | ||
1311 | sas_rphy_free(rphy); | ||
1312 | break; | ||
1313 | } | ||
1314 | |||
1315 | phy_info->rphy = rphy; | ||
1316 | break; | ||
1317 | } | ||
1318 | |||
1319 | kfree(ev); | ||
1320 | } | ||
1321 | |||
1322 | static void | ||
1323 | mptscsih_send_sas_event(MPT_ADAPTER *ioc, | ||
1324 | EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *sas_event_data) | ||
1325 | { | ||
1326 | struct mptsas_hotplug_event *ev; | ||
1327 | u32 device_info = le32_to_cpu(sas_event_data->DeviceInfo); | ||
1328 | __le64 sas_address; | ||
1329 | |||
1330 | if ((device_info & | ||
1331 | (MPI_SAS_DEVICE_INFO_SSP_TARGET | | ||
1332 | MPI_SAS_DEVICE_INFO_STP_TARGET | | ||
1333 | MPI_SAS_DEVICE_INFO_SATA_DEVICE )) == 0) | ||
1334 | return; | ||
1335 | |||
1336 | if ((sas_event_data->ReasonCode & | ||
1337 | (MPI_EVENT_SAS_DEV_STAT_RC_ADDED | | ||
1338 | MPI_EVENT_SAS_DEV_STAT_RC_NOT_RESPONDING)) == 0) | ||
1339 | return; | ||
1340 | |||
1341 | ev = kmalloc(sizeof(*ev), GFP_ATOMIC); | ||
1342 | if (!ev) { | ||
1343 | printk(KERN_WARNING "mptsas: lost hotplug event\n"); | ||
1344 | return; | ||
1345 | } | ||
1346 | |||
1347 | |||
1348 | INIT_WORK(&ev->work, mptsas_hotplug_work, ev); | ||
1349 | ev->ioc = ioc; | ||
1350 | ev->handle = le16_to_cpu(sas_event_data->DevHandle); | ||
1351 | ev->parent_handle = le16_to_cpu(sas_event_data->ParentDevHandle); | ||
1352 | ev->channel = sas_event_data->Bus; | ||
1353 | ev->id = sas_event_data->TargetID; | ||
1354 | ev->phy_id = sas_event_data->PhyNum; | ||
1355 | memcpy(&sas_address, &sas_event_data->SASAddress, sizeof(__le64)); | ||
1356 | ev->sas_address = le64_to_cpu(sas_address); | ||
1357 | ev->device_info = device_info; | ||
1358 | |||
1359 | if (sas_event_data->ReasonCode & MPI_EVENT_SAS_DEV_STAT_RC_ADDED) | ||
1360 | ev->event_type = MPTSAS_ADD_DEVICE; | ||
1361 | else | ||
1362 | ev->event_type = MPTSAS_DEL_DEVICE; | ||
1363 | |||
1364 | schedule_work(&ev->work); | ||
1365 | } | ||
1366 | |||
1367 | static int | ||
1368 | mptsas_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *reply) | ||
1369 | { | ||
1370 | u8 event = le32_to_cpu(reply->Event) & 0xFF; | ||
1371 | |||
1372 | if (!ioc->sh) | ||
1373 | return 1; | ||
1374 | |||
1375 | switch (event) { | ||
1376 | case MPI_EVENT_SAS_DEVICE_STATUS_CHANGE: | ||
1377 | mptscsih_send_sas_event(ioc, | ||
1378 | (EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *)reply->Data); | ||
1379 | return 1; /* currently means nothing really */ | ||
1380 | |||
1381 | default: | ||
1382 | return mptscsih_event_process(ioc, reply); | ||
1383 | } | ||
1384 | } | ||
1385 | |||
1114 | static int | 1386 | static int |
1115 | mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id) | 1387 | mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id) |
1116 | { | 1388 | { |
@@ -1203,6 +1475,8 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id) | |||
1203 | sh->unique_id = ioc->id; | 1475 | sh->unique_id = ioc->id; |
1204 | 1476 | ||
1205 | INIT_LIST_HEAD(&ioc->sas_topology); | 1477 | INIT_LIST_HEAD(&ioc->sas_topology); |
1478 | mutex_init(&ioc->sas_topology_mutex); | ||
1479 | |||
1206 | init_MUTEX(&ioc->sas_mgmt.mutex); | 1480 | init_MUTEX(&ioc->sas_mgmt.mutex); |
1207 | init_completion(&ioc->sas_mgmt.done); | 1481 | init_completion(&ioc->sas_mgmt.done); |
1208 | 1482 | ||
@@ -1339,10 +1613,12 @@ static void __devexit mptsas_remove(struct pci_dev *pdev) | |||
1339 | 1613 | ||
1340 | sas_remove_host(ioc->sh); | 1614 | sas_remove_host(ioc->sh); |
1341 | 1615 | ||
1616 | mutex_lock(&ioc->sas_topology_mutex); | ||
1342 | list_for_each_entry_safe(p, n, &ioc->sas_topology, list) { | 1617 | list_for_each_entry_safe(p, n, &ioc->sas_topology, list) { |
1343 | list_del(&p->list); | 1618 | list_del(&p->list); |
1344 | kfree(p); | 1619 | kfree(p); |
1345 | } | 1620 | } |
1621 | mutex_unlock(&ioc->sas_topology_mutex); | ||
1346 | 1622 | ||
1347 | mptscsih_remove(pdev); | 1623 | mptscsih_remove(pdev); |
1348 | } | 1624 | } |
@@ -1393,7 +1669,7 @@ mptsas_init(void) | |||
1393 | mpt_register(mptscsih_scandv_complete, MPTSAS_DRIVER); | 1669 | mpt_register(mptscsih_scandv_complete, MPTSAS_DRIVER); |
1394 | mptsasMgmtCtx = mpt_register(mptsas_mgmt_done, MPTSAS_DRIVER); | 1670 | mptsasMgmtCtx = mpt_register(mptsas_mgmt_done, MPTSAS_DRIVER); |
1395 | 1671 | ||
1396 | if (mpt_event_register(mptsasDoneCtx, mptscsih_event_process) == 0) { | 1672 | if (mpt_event_register(mptsasDoneCtx, mptsas_event_process) == 0) { |
1397 | devtprintk((KERN_INFO MYNAM | 1673 | devtprintk((KERN_INFO MYNAM |
1398 | ": Registered for IOC event notifications\n")); | 1674 | ": Registered for IOC event notifications\n")); |
1399 | } | 1675 | } |