diff options
author | Moore, Eric <Eric.Moore@lsil.com> | 2006-03-14 11:14:24 -0500 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.il.steeleye.com> | 2006-03-14 15:35:17 -0500 |
commit | e6b2d76a49f0ee48527691867d8af2b8f9c10452 (patch) | |
tree | 8f35dda5dcf65ccc740761c96924111baf0a8644 /drivers/message/fusion | |
parent | f44e5461d919a344d44f7ca4f06cf8d169da8454 (diff) |
[SCSI] fusion - expander hotplug suport in mptsas module
This adds support for hot adding and removing
expanders, and its associated attached devices.
When there is a change in topology,
the fusion firmware sends the
MPI_EVENT_SAS_DISCOVERY event to the driver.
The driver will read firmware config pages
to determine what changes took place, and refresh
drivers view of the world stored in ioc->sas_topology.
Here is the details of the action the driver does:
(1) Expander Added : The mptsas_discovery_work
workqueue is called. Config pages read, and
ioc->sas_topology is refreshed. The sas_phy_add()
is called for each phy of the expander. The
expanders attached devices are added via
sas_rphy_add(). Added end devices are handled within
the MPT_ADD_DEVICE logic in mptsas_hotplug_work
workqueue.
(2) Expander Delete : The sas_rphy_delete() will be
called for the top most compenent of the parent that the
expander is attached to. The sas_rphy_delete call
will delete all the children phys, rphys, and end devices.
This is handled from mptsas_discovery_work workqueue.
Signed-off-by: Eric Moore <Eric.Moore@lsil.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/message/fusion')
-rw-r--r-- | drivers/message/fusion/mptbase.h | 4 | ||||
-rw-r--r-- | drivers/message/fusion/mptsas.c | 354 |
2 files changed, 289 insertions, 69 deletions
diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h index 9b58234add36..892af47cb91c 100644 --- a/drivers/message/fusion/mptbase.h +++ b/drivers/message/fusion/mptbase.h | |||
@@ -619,6 +619,10 @@ typedef struct _MPT_ADAPTER | |||
619 | struct net_device *netdev; | 619 | struct net_device *netdev; |
620 | struct list_head sas_topology; | 620 | struct list_head sas_topology; |
621 | struct mutex sas_topology_mutex; | 621 | struct mutex sas_topology_mutex; |
622 | struct mutex sas_discovery_mutex; | ||
623 | u8 sas_discovery_runtime; | ||
624 | u8 sas_discovery_ignore_events; | ||
625 | int sas_index; /* index refrencing */ | ||
622 | MPT_SAS_MGMT sas_mgmt; | 626 | MPT_SAS_MGMT sas_mgmt; |
623 | int num_ports; | 627 | int num_ports; |
624 | struct work_struct mptscsih_persistTask; | 628 | struct work_struct mptscsih_persistTask; |
diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c index 289fcdbe89ba..be4eb8a308b7 100644 --- a/drivers/message/fusion/mptsas.c +++ b/drivers/message/fusion/mptsas.c | |||
@@ -108,6 +108,11 @@ struct mptsas_hotplug_event { | |||
108 | u8 phys_disk_num_valid; | 108 | u8 phys_disk_num_valid; |
109 | }; | 109 | }; |
110 | 110 | ||
111 | struct mptsas_discovery_event { | ||
112 | struct work_struct work; | ||
113 | MPT_ADAPTER *ioc; | ||
114 | }; | ||
115 | |||
111 | /* | 116 | /* |
112 | * SAS topology structures | 117 | * SAS topology structures |
113 | * | 118 | * |
@@ -163,7 +168,6 @@ struct mptsas_enclosure { | |||
163 | u8 sep_channel; /* SEP channel logical channel id */ | 168 | u8 sep_channel; /* SEP channel logical channel id */ |
164 | }; | 169 | }; |
165 | 170 | ||
166 | |||
167 | #ifdef SASDEBUG | 171 | #ifdef SASDEBUG |
168 | static void mptsas_print_phy_data(MPI_SAS_IO_UNIT0_PHY_DATA *phy_data) | 172 | static void mptsas_print_phy_data(MPI_SAS_IO_UNIT0_PHY_DATA *phy_data) |
169 | { | 173 | { |
@@ -273,6 +277,27 @@ static inline MPT_ADAPTER *rphy_to_ioc(struct sas_rphy *rphy) | |||
273 | return ((MPT_SCSI_HOST *)shost->hostdata)->ioc; | 277 | return ((MPT_SCSI_HOST *)shost->hostdata)->ioc; |
274 | } | 278 | } |
275 | 279 | ||
280 | /* | ||
281 | * mptsas_find_portinfo_by_handle | ||
282 | * | ||
283 | * This function should be called with the sas_topology_mutex already held | ||
284 | */ | ||
285 | static struct mptsas_portinfo * | ||
286 | mptsas_find_portinfo_by_handle(MPT_ADAPTER *ioc, u16 handle) | ||
287 | { | ||
288 | struct mptsas_portinfo *port_info, *rc=NULL; | ||
289 | int i; | ||
290 | |||
291 | list_for_each_entry(port_info, &ioc->sas_topology, list) | ||
292 | for (i = 0; i < port_info->num_phys; i++) | ||
293 | if (port_info->phy_info[i].identify.handle == handle) { | ||
294 | rc = port_info; | ||
295 | goto out; | ||
296 | } | ||
297 | out: | ||
298 | return rc; | ||
299 | } | ||
300 | |||
276 | static int | 301 | static int |
277 | mptsas_sas_enclosure_pg0(MPT_ADAPTER *ioc, struct mptsas_enclosure *enclosure, | 302 | mptsas_sas_enclosure_pg0(MPT_ADAPTER *ioc, struct mptsas_enclosure *enclosure, |
278 | u32 form, u32 form_specific) | 303 | u32 form, u32 form_specific) |
@@ -423,33 +448,9 @@ mptsas_slave_destroy(struct scsi_device *sdev) | |||
423 | { | 448 | { |
424 | struct Scsi_Host *host = sdev->host; | 449 | struct Scsi_Host *host = sdev->host; |
425 | MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata; | 450 | MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata; |
426 | struct sas_rphy *rphy; | ||
427 | struct mptsas_portinfo *p; | ||
428 | int i; | ||
429 | VirtDevice *vdev; | 451 | VirtDevice *vdev; |
430 | 452 | ||
431 | /* | 453 | /* |
432 | * Handle hotplug removal case. | ||
433 | * We need to clear out attached data structure. | ||
434 | */ | ||
435 | rphy = dev_to_rphy(sdev->sdev_target->dev.parent); | ||
436 | |||
437 | mutex_lock(&hd->ioc->sas_topology_mutex); | ||
438 | list_for_each_entry(p, &hd->ioc->sas_topology, list) { | ||
439 | for (i = 0; i < p->num_phys; i++) { | ||
440 | if (p->phy_info[i].attached.sas_address == | ||
441 | rphy->identify.sas_address) { | ||
442 | memset(&p->phy_info[i].attached, 0, | ||
443 | sizeof(struct mptsas_devinfo)); | ||
444 | p->phy_info[i].rphy = NULL; | ||
445 | goto out; | ||
446 | } | ||
447 | } | ||
448 | } | ||
449 | |||
450 | out: | ||
451 | mutex_unlock(&hd->ioc->sas_topology_mutex); | ||
452 | /* | ||
453 | * Issue target reset to flush firmware outstanding commands. | 454 | * Issue target reset to flush firmware outstanding commands. |
454 | */ | 455 | */ |
455 | vdev = sdev->hostdata; | 456 | vdev = sdev->hostdata; |
@@ -1044,7 +1045,6 @@ mptsas_sas_expander_pg1(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info, | |||
1044 | phy_info->identify.handle = le16_to_cpu(buffer->OwnerDevHandle); | 1045 | phy_info->identify.handle = le16_to_cpu(buffer->OwnerDevHandle); |
1045 | phy_info->attached.handle = le16_to_cpu(buffer->AttachedDevHandle); | 1046 | phy_info->attached.handle = le16_to_cpu(buffer->AttachedDevHandle); |
1046 | 1047 | ||
1047 | |||
1048 | out_free_consistent: | 1048 | out_free_consistent: |
1049 | pci_free_consistent(ioc->pcidev, hdr.ExtPageLength * 4, | 1049 | pci_free_consistent(ioc->pcidev, hdr.ExtPageLength * 4, |
1050 | buffer, dma_handle); | 1050 | buffer, dma_handle); |
@@ -1134,12 +1134,19 @@ mptsas_parse_device_info(struct sas_identify *identify, | |||
1134 | static int mptsas_probe_one_phy(struct device *dev, | 1134 | static int mptsas_probe_one_phy(struct device *dev, |
1135 | struct mptsas_phyinfo *phy_info, int index, int local) | 1135 | struct mptsas_phyinfo *phy_info, int index, int local) |
1136 | { | 1136 | { |
1137 | MPT_ADAPTER *ioc; | ||
1137 | struct sas_phy *phy; | 1138 | struct sas_phy *phy; |
1138 | int error; | 1139 | int error; |
1139 | 1140 | ||
1140 | phy = sas_phy_alloc(dev, index); | 1141 | if (!dev) |
1141 | if (!phy) | 1142 | return -ENODEV; |
1142 | return -ENOMEM; | 1143 | |
1144 | if (!phy_info->phy) { | ||
1145 | phy = sas_phy_alloc(dev, index); | ||
1146 | if (!phy) | ||
1147 | return -ENOMEM; | ||
1148 | } else | ||
1149 | phy = phy_info->phy; | ||
1143 | 1150 | ||
1144 | phy->port_identifier = phy_info->port_id; | 1151 | phy->port_identifier = phy_info->port_id; |
1145 | mptsas_parse_device_info(&phy->identify, &phy_info->identify); | 1152 | mptsas_parse_device_info(&phy->identify, &phy_info->identify); |
@@ -1225,19 +1232,35 @@ static int mptsas_probe_one_phy(struct device *dev, | |||
1225 | break; | 1232 | break; |
1226 | } | 1233 | } |
1227 | 1234 | ||
1228 | if (local) | 1235 | if (!phy_info->phy) { |
1229 | phy->local_attached = 1; | ||
1230 | 1236 | ||
1231 | error = sas_phy_add(phy); | 1237 | if (local) |
1232 | if (error) { | 1238 | phy->local_attached = 1; |
1233 | sas_phy_free(phy); | 1239 | |
1234 | return error; | 1240 | error = sas_phy_add(phy); |
1241 | if (error) { | ||
1242 | sas_phy_free(phy); | ||
1243 | return error; | ||
1244 | } | ||
1245 | phy_info->phy = phy; | ||
1235 | } | 1246 | } |
1236 | phy_info->phy = phy; | ||
1237 | 1247 | ||
1238 | if (phy_info->attached.handle) { | 1248 | if ((phy_info->attached.handle) && |
1249 | (!phy_info->rphy)) { | ||
1250 | |||
1239 | struct sas_rphy *rphy; | 1251 | struct sas_rphy *rphy; |
1240 | 1252 | ||
1253 | ioc = phy_to_ioc(phy_info->phy); | ||
1254 | |||
1255 | /* | ||
1256 | * Let the hotplug_work thread handle processing | ||
1257 | * the adding/removing of devices that occur | ||
1258 | * after start of day. | ||
1259 | */ | ||
1260 | if (ioc->sas_discovery_runtime && | ||
1261 | mptsas_is_end_device(&phy_info->attached)) | ||
1262 | return 0; | ||
1263 | |||
1241 | rphy = sas_rphy_alloc(phy); | 1264 | rphy = sas_rphy_alloc(phy); |
1242 | if (!rphy) | 1265 | if (!rphy) |
1243 | return 0; /* non-fatal: an rphy can be added later */ | 1266 | return 0; /* non-fatal: an rphy can be added later */ |
@@ -1256,24 +1279,37 @@ static int mptsas_probe_one_phy(struct device *dev, | |||
1256 | } | 1279 | } |
1257 | 1280 | ||
1258 | static int | 1281 | static int |
1259 | mptsas_probe_hba_phys(MPT_ADAPTER *ioc, int *index) | 1282 | mptsas_probe_hba_phys(MPT_ADAPTER *ioc) |
1260 | { | 1283 | { |
1261 | struct mptsas_portinfo *port_info; | 1284 | struct mptsas_portinfo *port_info, *hba; |
1262 | u32 handle = 0xFFFF; | 1285 | u32 handle = 0xFFFF; |
1263 | int error = -ENOMEM, i; | 1286 | int error = -ENOMEM, i; |
1264 | 1287 | ||
1265 | port_info = kzalloc(sizeof(*port_info), GFP_KERNEL); | 1288 | hba = kzalloc(sizeof(*port_info), GFP_KERNEL); |
1266 | if (!port_info) | 1289 | if (! hba) |
1267 | goto out; | 1290 | goto out; |
1268 | 1291 | ||
1269 | error = mptsas_sas_io_unit_pg0(ioc, port_info); | 1292 | error = mptsas_sas_io_unit_pg0(ioc, hba); |
1270 | if (error) | 1293 | if (error) |
1271 | goto out_free_port_info; | 1294 | goto out_free_port_info; |
1272 | 1295 | ||
1273 | ioc->num_ports = port_info->num_phys; | ||
1274 | mutex_lock(&ioc->sas_topology_mutex); | 1296 | mutex_lock(&ioc->sas_topology_mutex); |
1275 | list_add_tail(&port_info->list, &ioc->sas_topology); | 1297 | port_info = mptsas_find_portinfo_by_handle(ioc, hba->handle); |
1298 | if (!port_info) { | ||
1299 | port_info = hba; | ||
1300 | list_add_tail(&port_info->list, &ioc->sas_topology); | ||
1301 | } else { | ||
1302 | port_info->handle = hba->handle; | ||
1303 | for (i = 0; i < hba->num_phys; i++) | ||
1304 | port_info->phy_info[i].negotiated_link_rate = | ||
1305 | hba->phy_info[i].negotiated_link_rate; | ||
1306 | if (hba->phy_info) | ||
1307 | kfree(hba->phy_info); | ||
1308 | kfree(hba); | ||
1309 | hba = NULL; | ||
1310 | } | ||
1276 | mutex_unlock(&ioc->sas_topology_mutex); | 1311 | mutex_unlock(&ioc->sas_topology_mutex); |
1312 | ioc->num_ports = port_info->num_phys; | ||
1277 | 1313 | ||
1278 | for (i = 0; i < port_info->num_phys; i++) { | 1314 | for (i = 0; i < port_info->num_phys; i++) { |
1279 | mptsas_sas_phy_pg0(ioc, &port_info->phy_info[i], | 1315 | mptsas_sas_phy_pg0(ioc, &port_info->phy_info[i], |
@@ -1296,38 +1332,49 @@ mptsas_probe_hba_phys(MPT_ADAPTER *ioc, int *index) | |||
1296 | } | 1332 | } |
1297 | 1333 | ||
1298 | mptsas_probe_one_phy(&ioc->sh->shost_gendev, | 1334 | mptsas_probe_one_phy(&ioc->sh->shost_gendev, |
1299 | &port_info->phy_info[i], *index, 1); | 1335 | &port_info->phy_info[i], ioc->sas_index, 1); |
1300 | (*index)++; | 1336 | ioc->sas_index++; |
1301 | } | 1337 | } |
1302 | 1338 | ||
1303 | return 0; | 1339 | return 0; |
1304 | 1340 | ||
1305 | out_free_port_info: | 1341 | out_free_port_info: |
1306 | kfree(port_info); | 1342 | if (hba) |
1343 | kfree(hba); | ||
1307 | out: | 1344 | out: |
1308 | return error; | 1345 | return error; |
1309 | } | 1346 | } |
1310 | 1347 | ||
1311 | static int | 1348 | static int |
1312 | mptsas_probe_expander_phys(MPT_ADAPTER *ioc, u32 *handle, int *index) | 1349 | mptsas_probe_expander_phys(MPT_ADAPTER *ioc, u32 *handle) |
1313 | { | 1350 | { |
1314 | struct mptsas_portinfo *port_info, *p; | 1351 | struct mptsas_portinfo *port_info, *p, *ex; |
1315 | int error = -ENOMEM, i, j; | 1352 | int error = -ENOMEM, i, j; |
1316 | 1353 | ||
1317 | port_info = kzalloc(sizeof(*port_info), GFP_KERNEL); | 1354 | ex = kzalloc(sizeof(*port_info), GFP_KERNEL); |
1318 | if (!port_info) | 1355 | if (!ex) |
1319 | goto out; | 1356 | goto out; |
1320 | 1357 | ||
1321 | error = mptsas_sas_expander_pg0(ioc, port_info, | 1358 | error = mptsas_sas_expander_pg0(ioc, ex, |
1322 | (MPI_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE << | 1359 | (MPI_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE << |
1323 | MPI_SAS_EXPAND_PGAD_FORM_SHIFT), *handle); | 1360 | MPI_SAS_EXPAND_PGAD_FORM_SHIFT), *handle); |
1324 | if (error) | 1361 | if (error) |
1325 | goto out_free_port_info; | 1362 | goto out_free_port_info; |
1326 | 1363 | ||
1327 | *handle = port_info->handle; | 1364 | *handle = ex->handle; |
1328 | 1365 | ||
1329 | mutex_lock(&ioc->sas_topology_mutex); | 1366 | mutex_lock(&ioc->sas_topology_mutex); |
1330 | list_add_tail(&port_info->list, &ioc->sas_topology); | 1367 | port_info = mptsas_find_portinfo_by_handle(ioc, *handle); |
1368 | if (!port_info) { | ||
1369 | port_info = ex; | ||
1370 | list_add_tail(&port_info->list, &ioc->sas_topology); | ||
1371 | } else { | ||
1372 | port_info->handle = ex->handle; | ||
1373 | if (ex->phy_info) | ||
1374 | kfree(ex->phy_info); | ||
1375 | kfree(ex); | ||
1376 | ex = NULL; | ||
1377 | } | ||
1331 | mutex_unlock(&ioc->sas_topology_mutex); | 1378 | mutex_unlock(&ioc->sas_topology_mutex); |
1332 | 1379 | ||
1333 | for (i = 0; i < port_info->num_phys; i++) { | 1380 | for (i = 0; i < port_info->num_phys; i++) { |
@@ -1374,28 +1421,101 @@ mptsas_probe_expander_phys(MPT_ADAPTER *ioc, u32 *handle, int *index) | |||
1374 | mutex_unlock(&ioc->sas_topology_mutex); | 1421 | mutex_unlock(&ioc->sas_topology_mutex); |
1375 | 1422 | ||
1376 | mptsas_probe_one_phy(parent, &port_info->phy_info[i], | 1423 | mptsas_probe_one_phy(parent, &port_info->phy_info[i], |
1377 | *index, 0); | 1424 | ioc->sas_index, 0); |
1378 | (*index)++; | 1425 | ioc->sas_index++; |
1379 | } | 1426 | } |
1380 | 1427 | ||
1381 | return 0; | 1428 | return 0; |
1382 | 1429 | ||
1383 | out_free_port_info: | 1430 | out_free_port_info: |
1384 | kfree(port_info->phy_info); | 1431 | if (ex) { |
1385 | kfree(port_info); | 1432 | if (ex->phy_info) |
1433 | kfree(ex->phy_info); | ||
1434 | kfree(ex); | ||
1435 | } | ||
1386 | out: | 1436 | out: |
1387 | return error; | 1437 | return error; |
1388 | } | 1438 | } |
1389 | 1439 | ||
1440 | /* | ||
1441 | * mptsas_delete_expander_phys | ||
1442 | * | ||
1443 | * | ||
1444 | * This will traverse topology, and remove expanders | ||
1445 | * that are no longer present | ||
1446 | */ | ||
1447 | static void | ||
1448 | mptsas_delete_expander_phys(MPT_ADAPTER *ioc) | ||
1449 | { | ||
1450 | struct mptsas_portinfo buffer; | ||
1451 | struct mptsas_portinfo *port_info, *n, *parent; | ||
1452 | int i; | ||
1453 | |||
1454 | mutex_lock(&ioc->sas_topology_mutex); | ||
1455 | list_for_each_entry_safe(port_info, n, &ioc->sas_topology, list) { | ||
1456 | |||
1457 | if (port_info->phy_info && | ||
1458 | (!(port_info->phy_info[0].identify.device_info & | ||
1459 | MPI_SAS_DEVICE_INFO_SMP_TARGET))) | ||
1460 | continue; | ||
1461 | |||
1462 | if (mptsas_sas_expander_pg0(ioc, &buffer, | ||
1463 | (MPI_SAS_EXPAND_PGAD_FORM_HANDLE << | ||
1464 | MPI_SAS_EXPAND_PGAD_FORM_SHIFT), port_info->handle)) { | ||
1465 | |||
1466 | /* | ||
1467 | * Obtain the port_info instance to the parent port | ||
1468 | */ | ||
1469 | parent = mptsas_find_portinfo_by_handle(ioc, | ||
1470 | port_info->phy_info[0].identify.handle_parent); | ||
1471 | |||
1472 | if (!parent) | ||
1473 | goto next_port; | ||
1474 | |||
1475 | /* | ||
1476 | * Delete rphys in the parent that point | ||
1477 | * to this expander. The transport layer will | ||
1478 | * cleanup all the children. | ||
1479 | */ | ||
1480 | for (i = 0; i < parent->num_phys; i++) { | ||
1481 | if ((!parent->phy_info[i].rphy) || | ||
1482 | (parent->phy_info[i].attached.sas_address != | ||
1483 | port_info->phy_info[i].identify.sas_address)) | ||
1484 | continue; | ||
1485 | sas_rphy_delete(parent->phy_info[i].rphy); | ||
1486 | memset(&parent->phy_info[i].attached, 0, | ||
1487 | sizeof(struct mptsas_devinfo)); | ||
1488 | parent->phy_info[i].rphy = NULL; | ||
1489 | parent->phy_info[i].starget = NULL; | ||
1490 | } | ||
1491 | next_port: | ||
1492 | list_del(&port_info->list); | ||
1493 | if (port_info->phy_info) | ||
1494 | kfree(port_info->phy_info); | ||
1495 | kfree(port_info); | ||
1496 | } | ||
1497 | /* | ||
1498 | * Free this memory allocated from inside | ||
1499 | * mptsas_sas_expander_pg0 | ||
1500 | */ | ||
1501 | if (buffer.phy_info) | ||
1502 | kfree(buffer.phy_info); | ||
1503 | } | ||
1504 | mutex_unlock(&ioc->sas_topology_mutex); | ||
1505 | } | ||
1506 | |||
1507 | /* | ||
1508 | * Start of day discovery | ||
1509 | */ | ||
1390 | static void | 1510 | static void |
1391 | mptsas_scan_sas_topology(MPT_ADAPTER *ioc) | 1511 | mptsas_scan_sas_topology(MPT_ADAPTER *ioc) |
1392 | { | 1512 | { |
1393 | u32 handle = 0xFFFF; | 1513 | u32 handle = 0xFFFF; |
1394 | int index = 0; | ||
1395 | int i; | 1514 | int i; |
1396 | 1515 | ||
1397 | mptsas_probe_hba_phys(ioc, &index); | 1516 | mutex_lock(&ioc->sas_discovery_mutex); |
1398 | while (!mptsas_probe_expander_phys(ioc, &handle, &index)) | 1517 | mptsas_probe_hba_phys(ioc); |
1518 | while (!mptsas_probe_expander_phys(ioc, &handle)) | ||
1399 | ; | 1519 | ; |
1400 | /* | 1520 | /* |
1401 | Reporting RAID volumes. | 1521 | Reporting RAID volumes. |
@@ -1409,7 +1529,29 @@ mptsas_scan_sas_topology(MPT_ADAPTER *ioc) | |||
1409 | ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID, 0); | 1529 | ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID, 0); |
1410 | } | 1530 | } |
1411 | out: | 1531 | out: |
1412 | return; | 1532 | mutex_unlock(&ioc->sas_discovery_mutex); |
1533 | } | ||
1534 | |||
1535 | /* | ||
1536 | * Work queue thread to handle Runtime discovery | ||
1537 | * Mere purpose is the hot add/delete of expanders | ||
1538 | */ | ||
1539 | static void | ||
1540 | mptscsih_discovery_work(void * arg) | ||
1541 | { | ||
1542 | struct mptsas_discovery_event *ev = arg; | ||
1543 | MPT_ADAPTER *ioc = ev->ioc; | ||
1544 | u32 handle = 0xFFFF; | ||
1545 | |||
1546 | mutex_lock(&ioc->sas_discovery_mutex); | ||
1547 | ioc->sas_discovery_runtime=1; | ||
1548 | mptsas_delete_expander_phys(ioc); | ||
1549 | mptsas_probe_hba_phys(ioc); | ||
1550 | while (!mptsas_probe_expander_phys(ioc, &handle)) | ||
1551 | ; | ||
1552 | kfree(ev); | ||
1553 | ioc->sas_discovery_runtime=0; | ||
1554 | mutex_unlock(&ioc->sas_discovery_mutex); | ||
1413 | } | 1555 | } |
1414 | 1556 | ||
1415 | static struct mptsas_phyinfo * | 1557 | static struct mptsas_phyinfo * |
@@ -1427,10 +1569,8 @@ mptsas_find_phyinfo_by_parent(MPT_ADAPTER *ioc, u16 parent_handle, u8 phy_id) | |||
1427 | (MPI_SAS_DEVICE_PGAD_FORM_HANDLE << | 1569 | (MPI_SAS_DEVICE_PGAD_FORM_HANDLE << |
1428 | MPI_SAS_DEVICE_PGAD_FORM_SHIFT), | 1570 | MPI_SAS_DEVICE_PGAD_FORM_SHIFT), |
1429 | parent_handle); | 1571 | parent_handle); |
1430 | if (error) { | 1572 | if (error) |
1431 | printk("mptsas: failed to retrieve device page\n"); | ||
1432 | return NULL; | 1573 | return NULL; |
1433 | } | ||
1434 | 1574 | ||
1435 | /* | 1575 | /* |
1436 | * The phy_info structures are never deallocated during lifetime of | 1576 | * The phy_info structures are never deallocated during lifetime of |
@@ -1502,6 +1642,10 @@ mptsas_reprobe_target(struct scsi_target *starget, int uld_attach) | |||
1502 | mptsas_reprobe_lun); | 1642 | mptsas_reprobe_lun); |
1503 | } | 1643 | } |
1504 | 1644 | ||
1645 | |||
1646 | /* | ||
1647 | * Work queue thread to handle SAS hotplug events | ||
1648 | */ | ||
1505 | static void | 1649 | static void |
1506 | mptsas_hotplug_work(void *arg) | 1650 | mptsas_hotplug_work(void *arg) |
1507 | { | 1651 | { |
@@ -1514,10 +1658,13 @@ mptsas_hotplug_work(void *arg) | |||
1514 | struct mptsas_devinfo sas_device; | 1658 | struct mptsas_devinfo sas_device; |
1515 | VirtTarget *vtarget; | 1659 | VirtTarget *vtarget; |
1516 | 1660 | ||
1661 | mutex_lock(&ioc->sas_discovery_mutex); | ||
1662 | |||
1517 | switch (ev->event_type) { | 1663 | switch (ev->event_type) { |
1518 | case MPTSAS_DEL_DEVICE: | 1664 | case MPTSAS_DEL_DEVICE: |
1519 | 1665 | ||
1520 | phy_info = mptsas_find_phyinfo_by_target(ioc, ev->id); | 1666 | phy_info = mptsas_find_phyinfo_by_target(ioc, ev->id); |
1667 | |||
1521 | /* | 1668 | /* |
1522 | * Sanity checks, for non-existing phys and remote rphys. | 1669 | * Sanity checks, for non-existing phys and remote rphys. |
1523 | */ | 1670 | */ |
@@ -1569,8 +1716,36 @@ mptsas_hotplug_work(void *arg) | |||
1569 | 1716 | ||
1570 | phy_info = mptsas_find_phyinfo_by_parent(ioc, | 1717 | phy_info = mptsas_find_phyinfo_by_parent(ioc, |
1571 | sas_device.handle_parent, sas_device.phy_id); | 1718 | sas_device.handle_parent, sas_device.phy_id); |
1572 | if (!phy_info) | 1719 | |
1573 | break; | 1720 | if (!phy_info) { |
1721 | u32 handle = 0xFFFF; | ||
1722 | |||
1723 | /* | ||
1724 | * Its possible when an expander has been hot added | ||
1725 | * containing attached devices, the sas firmware | ||
1726 | * may send a RC_ADDED event prior to the | ||
1727 | * DISCOVERY STOP event. If that occurs, our | ||
1728 | * view of the topology in the driver in respect to this | ||
1729 | * expander might of not been setup, and we hit this | ||
1730 | * condition. | ||
1731 | * Therefore, this code kicks off discovery to | ||
1732 | * refresh the data. | ||
1733 | * Then again, we check whether the parent phy has | ||
1734 | * been created. | ||
1735 | */ | ||
1736 | ioc->sas_discovery_runtime=1; | ||
1737 | mptsas_delete_expander_phys(ioc); | ||
1738 | mptsas_probe_hba_phys(ioc); | ||
1739 | while (!mptsas_probe_expander_phys(ioc, &handle)) | ||
1740 | ; | ||
1741 | ioc->sas_discovery_runtime=0; | ||
1742 | |||
1743 | phy_info = mptsas_find_phyinfo_by_parent(ioc, | ||
1744 | sas_device.handle_parent, sas_device.phy_id); | ||
1745 | if (!phy_info) | ||
1746 | break; | ||
1747 | } | ||
1748 | |||
1574 | if (phy_info->starget) { | 1749 | if (phy_info->starget) { |
1575 | vtarget = phy_info->starget->hostdata; | 1750 | vtarget = phy_info->starget->hostdata; |
1576 | 1751 | ||
@@ -1604,7 +1779,6 @@ mptsas_hotplug_work(void *arg) | |||
1604 | "attaching %s device, channel %d, id %d, phy %d\n", | 1779 | "attaching %s device, channel %d, id %d, phy %d\n", |
1605 | ioc->name, ds, ev->channel, ev->id, ev->phy_id); | 1780 | ioc->name, ds, ev->channel, ev->id, ev->phy_id); |
1606 | 1781 | ||
1607 | |||
1608 | rphy = sas_rphy_alloc(phy_info->phy); | 1782 | rphy = sas_rphy_alloc(phy_info->phy); |
1609 | if (!rphy) | 1783 | if (!rphy) |
1610 | break; /* non-fatal: an rphy can be added later */ | 1784 | break; /* non-fatal: an rphy can be added later */ |
@@ -1654,6 +1828,7 @@ mptsas_hotplug_work(void *arg) | |||
1654 | } | 1828 | } |
1655 | 1829 | ||
1656 | kfree(ev); | 1830 | kfree(ev); |
1831 | mutex_unlock(&ioc->sas_discovery_mutex); | ||
1657 | } | 1832 | } |
1658 | 1833 | ||
1659 | static void | 1834 | static void |
@@ -1767,6 +1942,32 @@ mptscsih_send_raid_event(MPT_ADAPTER *ioc, | |||
1767 | schedule_work(&ev->work); | 1942 | schedule_work(&ev->work); |
1768 | } | 1943 | } |
1769 | 1944 | ||
1945 | static void | ||
1946 | mptscsih_send_discovery(MPT_ADAPTER *ioc, | ||
1947 | EVENT_DATA_SAS_DISCOVERY *discovery_data) | ||
1948 | { | ||
1949 | struct mptsas_discovery_event *ev; | ||
1950 | |||
1951 | /* | ||
1952 | * DiscoveryStatus | ||
1953 | * | ||
1954 | * This flag will be non-zero when firmware | ||
1955 | * kicks off discovery, and return to zero | ||
1956 | * once its completed. | ||
1957 | */ | ||
1958 | if (discovery_data->DiscoveryStatus) | ||
1959 | return; | ||
1960 | |||
1961 | ev = kmalloc(sizeof(*ev), GFP_ATOMIC); | ||
1962 | if (!ev) | ||
1963 | return; | ||
1964 | memset(ev,0,sizeof(struct mptsas_discovery_event)); | ||
1965 | INIT_WORK(&ev->work, mptscsih_discovery_work, ev); | ||
1966 | ev->ioc = ioc; | ||
1967 | schedule_work(&ev->work); | ||
1968 | }; | ||
1969 | |||
1970 | |||
1770 | static int | 1971 | static int |
1771 | mptsas_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *reply) | 1972 | mptsas_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *reply) |
1772 | { | 1973 | { |
@@ -1776,6 +1977,17 @@ mptsas_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *reply) | |||
1776 | if (!ioc->sh) | 1977 | if (!ioc->sh) |
1777 | goto out; | 1978 | goto out; |
1778 | 1979 | ||
1980 | /* | ||
1981 | * sas_discovery_ignore_events | ||
1982 | * | ||
1983 | * This flag is to prevent anymore processing of | ||
1984 | * sas events once mptsas_remove function is called. | ||
1985 | */ | ||
1986 | if (ioc->sas_discovery_ignore_events) { | ||
1987 | rc = mptscsih_event_process(ioc, reply); | ||
1988 | goto out; | ||
1989 | } | ||
1990 | |||
1779 | switch (event) { | 1991 | switch (event) { |
1780 | case MPI_EVENT_SAS_DEVICE_STATUS_CHANGE: | 1992 | case MPI_EVENT_SAS_DEVICE_STATUS_CHANGE: |
1781 | mptscsih_send_sas_event(ioc, | 1993 | mptscsih_send_sas_event(ioc, |
@@ -1792,6 +2004,9 @@ mptsas_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *reply) | |||
1792 | schedule_work(&ioc->mptscsih_persistTask); | 2004 | schedule_work(&ioc->mptscsih_persistTask); |
1793 | break; | 2005 | break; |
1794 | case MPI_EVENT_SAS_DISCOVERY: | 2006 | case MPI_EVENT_SAS_DISCOVERY: |
2007 | mptscsih_send_discovery(ioc, | ||
2008 | (EVENT_DATA_SAS_DISCOVERY *)reply->Data); | ||
2009 | break; | ||
1795 | default: | 2010 | default: |
1796 | rc = mptscsih_event_process(ioc, reply); | 2011 | rc = mptscsih_event_process(ioc, reply); |
1797 | break; | 2012 | break; |
@@ -1893,7 +2108,7 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id) | |||
1893 | 2108 | ||
1894 | INIT_LIST_HEAD(&ioc->sas_topology); | 2109 | INIT_LIST_HEAD(&ioc->sas_topology); |
1895 | mutex_init(&ioc->sas_topology_mutex); | 2110 | mutex_init(&ioc->sas_topology_mutex); |
1896 | 2111 | mutex_init(&ioc->sas_discovery_mutex); | |
1897 | mutex_init(&ioc->sas_mgmt.mutex); | 2112 | mutex_init(&ioc->sas_mgmt.mutex); |
1898 | init_completion(&ioc->sas_mgmt.done); | 2113 | init_completion(&ioc->sas_mgmt.done); |
1899 | 2114 | ||
@@ -2019,6 +2234,7 @@ static void __devexit mptsas_remove(struct pci_dev *pdev) | |||
2019 | MPT_ADAPTER *ioc = pci_get_drvdata(pdev); | 2234 | MPT_ADAPTER *ioc = pci_get_drvdata(pdev); |
2020 | struct mptsas_portinfo *p, *n; | 2235 | struct mptsas_portinfo *p, *n; |
2021 | 2236 | ||
2237 | ioc->sas_discovery_ignore_events=1; | ||
2022 | sas_remove_host(ioc->sh); | 2238 | sas_remove_host(ioc->sh); |
2023 | 2239 | ||
2024 | mutex_lock(&ioc->sas_topology_mutex); | 2240 | mutex_lock(&ioc->sas_topology_mutex); |