diff options
author | James Smart <James.Smart@Emulex.Com> | 2006-04-10 10:14:05 -0400 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.il.steeleye.com> | 2006-04-13 14:25:16 -0400 |
commit | aedf349773e5877d716a89368d512b9baa3e8c7b (patch) | |
tree | acc08d181b97c434c70c7ad984e262813067ba89 /drivers | |
parent | b0d2364887e94c880680f1e17943cd660bcf8979 (diff) |
[SCSI] FC transport: fixes for workq deadlocks
As previously reported via Michael Reed, the FC transport took a hit
in 2.6.15 (perhaps a little earlier) when we solved a recursion error.
There are 2 deadlocks occurring:
- With scan and the delete items sharing the same workq, flushing the
workq for the delete code was getting it stalled behind a very long
running scan code path.
- There's a deadlock where scsi_remove_target() has to sit behind
scsi_scan_target() due to contention over the scan_lock().
This patch resolves the 1st deadlock and significantly reduces the
odds of the second. So far, we have only replicated the 2nd deadlock
on a highly-parallel SMP system. More on the 2nd deadlock in a following
email.
This patch reworks the transport to:
- Only use the scsi host workq for scanning
- Use 2 other workq's internally. One for deletions, the other for
scheduled deletions. Originally, we tried this with a single workq,
but the occassional flushes of the scheduled queues was hitting the
second deadlock with a slightly higher frequency. In the future, we'll
look at the LLDD's and the transport to see if we can get rid of this
extra overhead.
- When moving to the other workq's we tightened up some object states
and some lock handling.
- Properly syncs adds/deletes
- minor code cleanups
- directly reference fc_host_attrs, rather than through attribute
macros
- flush the right workq on delayed work cancel failures.
Large kudos to Michael Reed who has been working this issue for the last
month.
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/scsi/scsi_transport_fc.c | 466 |
1 files changed, 311 insertions, 155 deletions
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 8db656214b5c..95c5478dcdfd 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c | |||
@@ -34,6 +34,8 @@ | |||
34 | #include <scsi/scsi_cmnd.h> | 34 | #include <scsi/scsi_cmnd.h> |
35 | #include "scsi_priv.h" | 35 | #include "scsi_priv.h" |
36 | 36 | ||
37 | static int fc_queue_work(struct Scsi_Host *, struct work_struct *); | ||
38 | |||
37 | /* | 39 | /* |
38 | * Redefine so that we can have same named attributes in the | 40 | * Redefine so that we can have same named attributes in the |
39 | * sdev/starget/host objects. | 41 | * sdev/starget/host objects. |
@@ -213,10 +215,8 @@ fc_bitfield_name_search(remote_port_roles, fc_remote_port_role_names) | |||
213 | #define FC_MGMTSRVR_PORTID 0x00000a | 215 | #define FC_MGMTSRVR_PORTID 0x00000a |
214 | 216 | ||
215 | 217 | ||
216 | static void fc_shost_remove_rports(void *data); | ||
217 | static void fc_timeout_deleted_rport(void *data); | 218 | static void fc_timeout_deleted_rport(void *data); |
218 | static void fc_scsi_scan_rport(void *data); | 219 | static void fc_scsi_scan_rport(void *data); |
219 | static void fc_rport_terminate(struct fc_rport *rport); | ||
220 | 220 | ||
221 | /* | 221 | /* |
222 | * Attribute counts pre object type... | 222 | * Attribute counts pre object type... |
@@ -288,42 +288,58 @@ static int fc_host_setup(struct transport_container *tc, struct device *dev, | |||
288 | struct class_device *cdev) | 288 | struct class_device *cdev) |
289 | { | 289 | { |
290 | struct Scsi_Host *shost = dev_to_shost(dev); | 290 | struct Scsi_Host *shost = dev_to_shost(dev); |
291 | struct fc_host_attrs *fc_host = shost_to_fc_host(shost); | ||
291 | 292 | ||
292 | /* | 293 | /* |
293 | * Set default values easily detected by the midlayer as | 294 | * Set default values easily detected by the midlayer as |
294 | * failure cases. The scsi lldd is responsible for initializing | 295 | * failure cases. The scsi lldd is responsible for initializing |
295 | * all transport attributes to valid values per host. | 296 | * all transport attributes to valid values per host. |
296 | */ | 297 | */ |
297 | fc_host_node_name(shost) = -1; | 298 | fc_host->node_name = -1; |
298 | fc_host_port_name(shost) = -1; | 299 | fc_host->port_name = -1; |
299 | fc_host_permanent_port_name(shost) = -1; | 300 | fc_host->permanent_port_name = -1; |
300 | fc_host_supported_classes(shost) = FC_COS_UNSPECIFIED; | 301 | fc_host->supported_classes = FC_COS_UNSPECIFIED; |
301 | memset(fc_host_supported_fc4s(shost), 0, | 302 | memset(fc_host->supported_fc4s, 0, |
302 | sizeof(fc_host_supported_fc4s(shost))); | 303 | sizeof(fc_host->supported_fc4s)); |
303 | memset(fc_host_symbolic_name(shost), 0, | 304 | memset(fc_host->symbolic_name, 0, |
304 | sizeof(fc_host_symbolic_name(shost))); | 305 | sizeof(fc_host->symbolic_name)); |
305 | fc_host_supported_speeds(shost) = FC_PORTSPEED_UNKNOWN; | 306 | fc_host->supported_speeds = FC_PORTSPEED_UNKNOWN; |
306 | fc_host_maxframe_size(shost) = -1; | 307 | fc_host->maxframe_size = -1; |
307 | memset(fc_host_serial_number(shost), 0, | 308 | memset(fc_host->serial_number, 0, |
308 | sizeof(fc_host_serial_number(shost))); | 309 | sizeof(fc_host->serial_number)); |
309 | 310 | ||
310 | fc_host_port_id(shost) = -1; | 311 | fc_host->port_id = -1; |
311 | fc_host_port_type(shost) = FC_PORTTYPE_UNKNOWN; | 312 | fc_host->port_type = FC_PORTTYPE_UNKNOWN; |
312 | fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN; | 313 | fc_host->port_state = FC_PORTSTATE_UNKNOWN; |
313 | memset(fc_host_active_fc4s(shost), 0, | 314 | memset(fc_host->active_fc4s, 0, |
314 | sizeof(fc_host_active_fc4s(shost))); | 315 | sizeof(fc_host->active_fc4s)); |
315 | fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN; | 316 | fc_host->speed = FC_PORTSPEED_UNKNOWN; |
316 | fc_host_fabric_name(shost) = -1; | 317 | fc_host->fabric_name = -1; |
317 | 318 | ||
318 | fc_host_tgtid_bind_type(shost) = FC_TGTID_BIND_BY_WWPN; | 319 | fc_host->tgtid_bind_type = FC_TGTID_BIND_BY_WWPN; |
319 | 320 | ||
320 | INIT_LIST_HEAD(&fc_host_rports(shost)); | 321 | INIT_LIST_HEAD(&fc_host->rports); |
321 | INIT_LIST_HEAD(&fc_host_rport_bindings(shost)); | 322 | INIT_LIST_HEAD(&fc_host->rport_bindings); |
322 | fc_host_next_rport_number(shost) = 0; | 323 | fc_host->next_rport_number = 0; |
323 | fc_host_next_target_id(shost) = 0; | 324 | fc_host->next_target_id = 0; |
324 | 325 | ||
325 | fc_host_flags(shost) = 0; | 326 | snprintf(fc_host->work_q_name, KOBJ_NAME_LEN, "fc_wq_%d", |
326 | INIT_WORK(&fc_host_rport_del_work(shost), fc_shost_remove_rports, shost); | 327 | shost->host_no); |
328 | fc_host->work_q = create_singlethread_workqueue( | ||
329 | fc_host->work_q_name); | ||
330 | if (!fc_host->work_q) | ||
331 | return -ENOMEM; | ||
332 | |||
333 | snprintf(fc_host->devloss_work_q_name, KOBJ_NAME_LEN, "fc_dl_%d", | ||
334 | shost->host_no); | ||
335 | fc_host->devloss_work_q = create_singlethread_workqueue( | ||
336 | fc_host->devloss_work_q_name); | ||
337 | if (!fc_host->devloss_work_q) { | ||
338 | destroy_workqueue(fc_host->work_q); | ||
339 | fc_host->work_q = NULL; | ||
340 | return -ENOMEM; | ||
341 | } | ||
342 | |||
327 | return 0; | 343 | return 0; |
328 | } | 344 | } |
329 | 345 | ||
@@ -879,9 +895,9 @@ store_fc_private_host_tgtid_bind_type(struct class_device *cdev, | |||
879 | while (!list_empty(&fc_host_rport_bindings(shost))) { | 895 | while (!list_empty(&fc_host_rport_bindings(shost))) { |
880 | get_list_head_entry(rport, | 896 | get_list_head_entry(rport, |
881 | &fc_host_rport_bindings(shost), peers); | 897 | &fc_host_rport_bindings(shost), peers); |
882 | spin_unlock_irqrestore(shost->host_lock, flags); | 898 | list_del(&rport->peers); |
883 | fc_rport_terminate(rport); | 899 | rport->port_state = FC_PORTSTATE_DELETED; |
884 | spin_lock_irqsave(shost->host_lock, flags); | 900 | fc_queue_work(shost, &rport->rport_delete_work); |
885 | } | 901 | } |
886 | spin_unlock_irqrestore(shost->host_lock, flags); | 902 | spin_unlock_irqrestore(shost->host_lock, flags); |
887 | } | 903 | } |
@@ -1262,6 +1278,90 @@ void fc_release_transport(struct scsi_transport_template *t) | |||
1262 | } | 1278 | } |
1263 | EXPORT_SYMBOL(fc_release_transport); | 1279 | EXPORT_SYMBOL(fc_release_transport); |
1264 | 1280 | ||
1281 | /** | ||
1282 | * fc_queue_work - Queue work to the fc_host workqueue. | ||
1283 | * @shost: Pointer to Scsi_Host bound to fc_host. | ||
1284 | * @work: Work to queue for execution. | ||
1285 | * | ||
1286 | * Return value: | ||
1287 | * 0 on success / != 0 for error | ||
1288 | **/ | ||
1289 | static int | ||
1290 | fc_queue_work(struct Scsi_Host *shost, struct work_struct *work) | ||
1291 | { | ||
1292 | if (unlikely(!fc_host_work_q(shost))) { | ||
1293 | printk(KERN_ERR | ||
1294 | "ERROR: FC host '%s' attempted to queue work, " | ||
1295 | "when no workqueue created.\n", shost->hostt->name); | ||
1296 | dump_stack(); | ||
1297 | |||
1298 | return -EINVAL; | ||
1299 | } | ||
1300 | |||
1301 | return queue_work(fc_host_work_q(shost), work); | ||
1302 | } | ||
1303 | |||
1304 | /** | ||
1305 | * fc_flush_work - Flush a fc_host's workqueue. | ||
1306 | * @shost: Pointer to Scsi_Host bound to fc_host. | ||
1307 | **/ | ||
1308 | static void | ||
1309 | fc_flush_work(struct Scsi_Host *shost) | ||
1310 | { | ||
1311 | if (!fc_host_work_q(shost)) { | ||
1312 | printk(KERN_ERR | ||
1313 | "ERROR: FC host '%s' attempted to flush work, " | ||
1314 | "when no workqueue created.\n", shost->hostt->name); | ||
1315 | dump_stack(); | ||
1316 | return; | ||
1317 | } | ||
1318 | |||
1319 | flush_workqueue(fc_host_work_q(shost)); | ||
1320 | } | ||
1321 | |||
1322 | /** | ||
1323 | * fc_queue_devloss_work - Schedule work for the fc_host devloss workqueue. | ||
1324 | * @shost: Pointer to Scsi_Host bound to fc_host. | ||
1325 | * @work: Work to queue for execution. | ||
1326 | * @delay: jiffies to delay the work queuing | ||
1327 | * | ||
1328 | * Return value: | ||
1329 | * 0 on success / != 0 for error | ||
1330 | **/ | ||
1331 | static int | ||
1332 | fc_queue_devloss_work(struct Scsi_Host *shost, struct work_struct *work, | ||
1333 | unsigned long delay) | ||
1334 | { | ||
1335 | if (unlikely(!fc_host_devloss_work_q(shost))) { | ||
1336 | printk(KERN_ERR | ||
1337 | "ERROR: FC host '%s' attempted to queue work, " | ||
1338 | "when no workqueue created.\n", shost->hostt->name); | ||
1339 | dump_stack(); | ||
1340 | |||
1341 | return -EINVAL; | ||
1342 | } | ||
1343 | |||
1344 | return queue_delayed_work(fc_host_devloss_work_q(shost), work, delay); | ||
1345 | } | ||
1346 | |||
1347 | /** | ||
1348 | * fc_flush_devloss - Flush a fc_host's devloss workqueue. | ||
1349 | * @shost: Pointer to Scsi_Host bound to fc_host. | ||
1350 | **/ | ||
1351 | static void | ||
1352 | fc_flush_devloss(struct Scsi_Host *shost) | ||
1353 | { | ||
1354 | if (!fc_host_devloss_work_q(shost)) { | ||
1355 | printk(KERN_ERR | ||
1356 | "ERROR: FC host '%s' attempted to flush work, " | ||
1357 | "when no workqueue created.\n", shost->hostt->name); | ||
1358 | dump_stack(); | ||
1359 | return; | ||
1360 | } | ||
1361 | |||
1362 | flush_workqueue(fc_host_devloss_work_q(shost)); | ||
1363 | } | ||
1364 | |||
1265 | 1365 | ||
1266 | /** | 1366 | /** |
1267 | * fc_remove_host - called to terminate any fc_transport-related elements | 1367 | * fc_remove_host - called to terminate any fc_transport-related elements |
@@ -1283,36 +1383,103 @@ void | |||
1283 | fc_remove_host(struct Scsi_Host *shost) | 1383 | fc_remove_host(struct Scsi_Host *shost) |
1284 | { | 1384 | { |
1285 | struct fc_rport *rport, *next_rport; | 1385 | struct fc_rport *rport, *next_rport; |
1386 | struct workqueue_struct *work_q; | ||
1387 | struct fc_host_attrs *fc_host = shost_to_fc_host(shost); | ||
1286 | 1388 | ||
1287 | /* Remove any remote ports */ | 1389 | /* Remove any remote ports */ |
1288 | list_for_each_entry_safe(rport, next_rport, | 1390 | list_for_each_entry_safe(rport, next_rport, |
1289 | &fc_host_rports(shost), peers) | 1391 | &fc_host->rports, peers) { |
1290 | fc_rport_terminate(rport); | 1392 | list_del(&rport->peers); |
1393 | rport->port_state = FC_PORTSTATE_DELETED; | ||
1394 | fc_queue_work(shost, &rport->rport_delete_work); | ||
1395 | } | ||
1396 | |||
1291 | list_for_each_entry_safe(rport, next_rport, | 1397 | list_for_each_entry_safe(rport, next_rport, |
1292 | &fc_host_rport_bindings(shost), peers) | 1398 | &fc_host->rport_bindings, peers) { |
1293 | fc_rport_terminate(rport); | 1399 | list_del(&rport->peers); |
1400 | rport->port_state = FC_PORTSTATE_DELETED; | ||
1401 | fc_queue_work(shost, &rport->rport_delete_work); | ||
1402 | } | ||
1403 | |||
1404 | /* flush all scan work items */ | ||
1405 | scsi_flush_work(shost); | ||
1406 | |||
1407 | /* flush all stgt delete, and rport delete work items, then kill it */ | ||
1408 | if (fc_host->work_q) { | ||
1409 | work_q = fc_host->work_q; | ||
1410 | fc_host->work_q = NULL; | ||
1411 | destroy_workqueue(work_q); | ||
1412 | } | ||
1413 | |||
1414 | /* flush all devloss work items, then kill it */ | ||
1415 | if (fc_host->devloss_work_q) { | ||
1416 | work_q = fc_host->devloss_work_q; | ||
1417 | fc_host->devloss_work_q = NULL; | ||
1418 | destroy_workqueue(work_q); | ||
1419 | } | ||
1294 | } | 1420 | } |
1295 | EXPORT_SYMBOL(fc_remove_host); | 1421 | EXPORT_SYMBOL(fc_remove_host); |
1296 | 1422 | ||
1297 | /* | 1423 | |
1298 | * fc_rport_tgt_remove - Removes the scsi target on the remote port | 1424 | /** |
1299 | * @rport: The remote port to be operated on | 1425 | * fc_starget_delete - called to delete the scsi decendents of an rport |
1300 | */ | 1426 | * (target and all sdevs) |
1427 | * | ||
1428 | * @data: remote port to be operated on. | ||
1429 | **/ | ||
1301 | static void | 1430 | static void |
1302 | fc_rport_tgt_remove(struct fc_rport *rport) | 1431 | fc_starget_delete(void *data) |
1303 | { | 1432 | { |
1433 | struct fc_rport *rport = (struct fc_rport *)data; | ||
1304 | struct Scsi_Host *shost = rport_to_shost(rport); | 1434 | struct Scsi_Host *shost = rport_to_shost(rport); |
1435 | unsigned long flags; | ||
1305 | 1436 | ||
1306 | scsi_target_unblock(&rport->dev); | 1437 | scsi_target_unblock(&rport->dev); |
1307 | 1438 | ||
1308 | /* Stop anything on the workq */ | 1439 | spin_lock_irqsave(shost->host_lock, flags); |
1309 | if (!cancel_delayed_work(&rport->dev_loss_work)) | 1440 | if (rport->flags & FC_RPORT_DEVLOSS_PENDING) { |
1310 | flush_scheduled_work(); | 1441 | spin_unlock_irqrestore(shost->host_lock, flags); |
1311 | scsi_flush_work(shost); | 1442 | if (!cancel_delayed_work(&rport->dev_loss_work)) |
1443 | fc_flush_devloss(shost); | ||
1444 | spin_lock_irqsave(shost->host_lock, flags); | ||
1445 | rport->flags &= ~FC_RPORT_DEVLOSS_PENDING; | ||
1446 | } | ||
1447 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1312 | 1448 | ||
1313 | scsi_remove_target(&rport->dev); | 1449 | scsi_remove_target(&rport->dev); |
1314 | } | 1450 | } |
1315 | 1451 | ||
1452 | |||
1453 | /** | ||
1454 | * fc_rport_final_delete - finish rport termination and delete it. | ||
1455 | * | ||
1456 | * @data: remote port to be deleted. | ||
1457 | **/ | ||
1458 | static void | ||
1459 | fc_rport_final_delete(void *data) | ||
1460 | { | ||
1461 | struct fc_rport *rport = (struct fc_rport *)data; | ||
1462 | struct device *dev = &rport->dev; | ||
1463 | struct Scsi_Host *shost = rport_to_shost(rport); | ||
1464 | |||
1465 | /* Delete SCSI target and sdevs */ | ||
1466 | if (rport->scsi_target_id != -1) | ||
1467 | fc_starget_delete(data); | ||
1468 | |||
1469 | /* | ||
1470 | * if a scan is pending, flush the SCSI Host work_q so that | ||
1471 | * that we can reclaim the rport scan work element. | ||
1472 | */ | ||
1473 | if (rport->flags & FC_RPORT_SCAN_PENDING) | ||
1474 | scsi_flush_work(shost); | ||
1475 | |||
1476 | transport_remove_device(dev); | ||
1477 | device_del(dev); | ||
1478 | transport_destroy_device(dev); | ||
1479 | put_device(&shost->shost_gendev); | ||
1480 | } | ||
1481 | |||
1482 | |||
1316 | /** | 1483 | /** |
1317 | * fc_rport_create - allocates and creates a remote FC port. | 1484 | * fc_rport_create - allocates and creates a remote FC port. |
1318 | * @shost: scsi host the remote port is connected to. | 1485 | * @shost: scsi host the remote port is connected to. |
@@ -1330,8 +1497,7 @@ struct fc_rport * | |||
1330 | fc_rport_create(struct Scsi_Host *shost, int channel, | 1497 | fc_rport_create(struct Scsi_Host *shost, int channel, |
1331 | struct fc_rport_identifiers *ids) | 1498 | struct fc_rport_identifiers *ids) |
1332 | { | 1499 | { |
1333 | struct fc_host_attrs *fc_host = | 1500 | struct fc_host_attrs *fc_host = shost_to_fc_host(shost); |
1334 | (struct fc_host_attrs *)shost->shost_data; | ||
1335 | struct fc_internal *fci = to_fc_internal(shost->transportt); | 1501 | struct fc_internal *fci = to_fc_internal(shost->transportt); |
1336 | struct fc_rport *rport; | 1502 | struct fc_rport *rport; |
1337 | struct device *dev; | 1503 | struct device *dev; |
@@ -1360,6 +1526,8 @@ fc_rport_create(struct Scsi_Host *shost, int channel, | |||
1360 | 1526 | ||
1361 | INIT_WORK(&rport->dev_loss_work, fc_timeout_deleted_rport, rport); | 1527 | INIT_WORK(&rport->dev_loss_work, fc_timeout_deleted_rport, rport); |
1362 | INIT_WORK(&rport->scan_work, fc_scsi_scan_rport, rport); | 1528 | INIT_WORK(&rport->scan_work, fc_scsi_scan_rport, rport); |
1529 | INIT_WORK(&rport->stgt_delete_work, fc_starget_delete, rport); | ||
1530 | INIT_WORK(&rport->rport_delete_work, fc_rport_final_delete, rport); | ||
1363 | 1531 | ||
1364 | spin_lock_irqsave(shost->host_lock, flags); | 1532 | spin_lock_irqsave(shost->host_lock, flags); |
1365 | 1533 | ||
@@ -1368,7 +1536,7 @@ fc_rport_create(struct Scsi_Host *shost, int channel, | |||
1368 | rport->scsi_target_id = fc_host->next_target_id++; | 1536 | rport->scsi_target_id = fc_host->next_target_id++; |
1369 | else | 1537 | else |
1370 | rport->scsi_target_id = -1; | 1538 | rport->scsi_target_id = -1; |
1371 | list_add_tail(&rport->peers, &fc_host_rports(shost)); | 1539 | list_add_tail(&rport->peers, &fc_host->rports); |
1372 | get_device(&shost->shost_gendev); | 1540 | get_device(&shost->shost_gendev); |
1373 | 1541 | ||
1374 | spin_unlock_irqrestore(shost->host_lock, flags); | 1542 | spin_unlock_irqrestore(shost->host_lock, flags); |
@@ -1389,9 +1557,11 @@ fc_rport_create(struct Scsi_Host *shost, int channel, | |||
1389 | transport_add_device(dev); | 1557 | transport_add_device(dev); |
1390 | transport_configure_device(dev); | 1558 | transport_configure_device(dev); |
1391 | 1559 | ||
1392 | if (rport->roles & FC_RPORT_ROLE_FCP_TARGET) | 1560 | if (rport->roles & FC_RPORT_ROLE_FCP_TARGET) { |
1393 | /* initiate a scan of the target */ | 1561 | /* initiate a scan of the target */ |
1562 | rport->flags |= FC_RPORT_SCAN_PENDING; | ||
1394 | scsi_queue_work(shost, &rport->scan_work); | 1563 | scsi_queue_work(shost, &rport->scan_work); |
1564 | } | ||
1395 | 1565 | ||
1396 | return rport; | 1566 | return rport; |
1397 | 1567 | ||
@@ -1451,10 +1621,14 @@ fc_remote_port_add(struct Scsi_Host *shost, int channel, | |||
1451 | struct fc_rport_identifiers *ids) | 1621 | struct fc_rport_identifiers *ids) |
1452 | { | 1622 | { |
1453 | struct fc_internal *fci = to_fc_internal(shost->transportt); | 1623 | struct fc_internal *fci = to_fc_internal(shost->transportt); |
1624 | struct fc_host_attrs *fc_host = shost_to_fc_host(shost); | ||
1454 | struct fc_rport *rport; | 1625 | struct fc_rport *rport; |
1455 | unsigned long flags; | 1626 | unsigned long flags; |
1456 | int match = 0; | 1627 | int match = 0; |
1457 | 1628 | ||
1629 | /* ensure any stgt delete functions are done */ | ||
1630 | fc_flush_work(shost); | ||
1631 | |||
1458 | /* | 1632 | /* |
1459 | * Search the list of "active" rports, for an rport that has been | 1633 | * Search the list of "active" rports, for an rport that has been |
1460 | * deleted, but we've held off the real delete while the target | 1634 | * deleted, but we've held off the real delete while the target |
@@ -1462,12 +1636,12 @@ fc_remote_port_add(struct Scsi_Host *shost, int channel, | |||
1462 | */ | 1636 | */ |
1463 | spin_lock_irqsave(shost->host_lock, flags); | 1637 | spin_lock_irqsave(shost->host_lock, flags); |
1464 | 1638 | ||
1465 | list_for_each_entry(rport, &fc_host_rports(shost), peers) { | 1639 | list_for_each_entry(rport, &fc_host->rports, peers) { |
1466 | 1640 | ||
1467 | if ((rport->port_state == FC_PORTSTATE_BLOCKED) && | 1641 | if ((rport->port_state == FC_PORTSTATE_BLOCKED) && |
1468 | (rport->channel == channel)) { | 1642 | (rport->channel == channel)) { |
1469 | 1643 | ||
1470 | switch (fc_host_tgtid_bind_type(shost)) { | 1644 | switch (fc_host->tgtid_bind_type) { |
1471 | case FC_TGTID_BIND_BY_WWPN: | 1645 | case FC_TGTID_BIND_BY_WWPN: |
1472 | case FC_TGTID_BIND_NONE: | 1646 | case FC_TGTID_BIND_NONE: |
1473 | if (rport->port_name == ids->port_name) | 1647 | if (rport->port_name == ids->port_name) |
@@ -1521,27 +1695,34 @@ fc_remote_port_add(struct Scsi_Host *shost, int channel, | |||
1521 | * transaction. | 1695 | * transaction. |
1522 | */ | 1696 | */ |
1523 | if (!cancel_delayed_work(work)) | 1697 | if (!cancel_delayed_work(work)) |
1524 | flush_scheduled_work(); | 1698 | fc_flush_devloss(shost); |
1699 | |||
1700 | spin_lock_irqsave(shost->host_lock, flags); | ||
1701 | |||
1702 | rport->flags &= ~FC_RPORT_DEVLOSS_PENDING; | ||
1525 | 1703 | ||
1526 | /* initiate a scan of the target */ | 1704 | /* initiate a scan of the target */ |
1705 | rport->flags |= FC_RPORT_SCAN_PENDING; | ||
1527 | scsi_queue_work(shost, &rport->scan_work); | 1706 | scsi_queue_work(shost, &rport->scan_work); |
1528 | 1707 | ||
1708 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1709 | |||
1529 | return rport; | 1710 | return rport; |
1530 | } | 1711 | } |
1531 | } | 1712 | } |
1532 | } | 1713 | } |
1533 | 1714 | ||
1534 | /* Search the bindings array */ | 1715 | /* Search the bindings array */ |
1535 | if (fc_host_tgtid_bind_type(shost) != FC_TGTID_BIND_NONE) { | 1716 | if (fc_host->tgtid_bind_type != FC_TGTID_BIND_NONE) { |
1536 | 1717 | ||
1537 | /* search for a matching consistent binding */ | 1718 | /* search for a matching consistent binding */ |
1538 | 1719 | ||
1539 | list_for_each_entry(rport, &fc_host_rport_bindings(shost), | 1720 | list_for_each_entry(rport, &fc_host->rport_bindings, |
1540 | peers) { | 1721 | peers) { |
1541 | if (rport->channel != channel) | 1722 | if (rport->channel != channel) |
1542 | continue; | 1723 | continue; |
1543 | 1724 | ||
1544 | switch (fc_host_tgtid_bind_type(shost)) { | 1725 | switch (fc_host->tgtid_bind_type) { |
1545 | case FC_TGTID_BIND_BY_WWPN: | 1726 | case FC_TGTID_BIND_BY_WWPN: |
1546 | if (rport->port_name == ids->port_name) | 1727 | if (rport->port_name == ids->port_name) |
1547 | match = 1; | 1728 | match = 1; |
@@ -1559,8 +1740,7 @@ fc_remote_port_add(struct Scsi_Host *shost, int channel, | |||
1559 | } | 1740 | } |
1560 | 1741 | ||
1561 | if (match) { | 1742 | if (match) { |
1562 | list_move_tail(&rport->peers, | 1743 | list_move_tail(&rport->peers, &fc_host->rports); |
1563 | &fc_host_rports(shost)); | ||
1564 | break; | 1744 | break; |
1565 | } | 1745 | } |
1566 | } | 1746 | } |
@@ -1574,15 +1754,17 @@ fc_remote_port_add(struct Scsi_Host *shost, int channel, | |||
1574 | rport->roles = ids->roles; | 1754 | rport->roles = ids->roles; |
1575 | rport->port_state = FC_PORTSTATE_ONLINE; | 1755 | rport->port_state = FC_PORTSTATE_ONLINE; |
1576 | 1756 | ||
1577 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1578 | |||
1579 | if (fci->f->dd_fcrport_size) | 1757 | if (fci->f->dd_fcrport_size) |
1580 | memset(rport->dd_data, 0, | 1758 | memset(rport->dd_data, 0, |
1581 | fci->f->dd_fcrport_size); | 1759 | fci->f->dd_fcrport_size); |
1582 | 1760 | ||
1583 | if (rport->roles & FC_RPORT_ROLE_FCP_TARGET) | 1761 | if (rport->roles & FC_RPORT_ROLE_FCP_TARGET) { |
1584 | /* initiate a scan of the target */ | 1762 | /* initiate a scan of the target */ |
1763 | rport->flags |= FC_RPORT_SCAN_PENDING; | ||
1585 | scsi_queue_work(shost, &rport->scan_work); | 1764 | scsi_queue_work(shost, &rport->scan_work); |
1765 | } | ||
1766 | |||
1767 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1586 | 1768 | ||
1587 | return rport; | 1769 | return rport; |
1588 | } | 1770 | } |
@@ -1597,30 +1779,6 @@ fc_remote_port_add(struct Scsi_Host *shost, int channel, | |||
1597 | } | 1779 | } |
1598 | EXPORT_SYMBOL(fc_remote_port_add); | 1780 | EXPORT_SYMBOL(fc_remote_port_add); |
1599 | 1781 | ||
1600 | /* | ||
1601 | * fc_rport_terminate - this routine tears down and deallocates a remote port. | ||
1602 | * @rport: The remote port to be terminated | ||
1603 | * | ||
1604 | * Notes: | ||
1605 | * This routine assumes no locks are held on entry. | ||
1606 | */ | ||
1607 | static void | ||
1608 | fc_rport_terminate(struct fc_rport *rport) | ||
1609 | { | ||
1610 | struct Scsi_Host *shost = rport_to_shost(rport); | ||
1611 | struct device *dev = &rport->dev; | ||
1612 | unsigned long flags; | ||
1613 | |||
1614 | fc_rport_tgt_remove(rport); | ||
1615 | |||
1616 | transport_remove_device(dev); | ||
1617 | device_del(dev); | ||
1618 | transport_destroy_device(dev); | ||
1619 | spin_lock_irqsave(shost->host_lock, flags); | ||
1620 | list_del(&rport->peers); | ||
1621 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1622 | put_device(&shost->shost_gendev); | ||
1623 | } | ||
1624 | 1782 | ||
1625 | /** | 1783 | /** |
1626 | * fc_remote_port_delete - notifies the fc transport that a remote | 1784 | * fc_remote_port_delete - notifies the fc transport that a remote |
@@ -1675,20 +1833,39 @@ fc_rport_terminate(struct fc_rport *rport) | |||
1675 | void | 1833 | void |
1676 | fc_remote_port_delete(struct fc_rport *rport) | 1834 | fc_remote_port_delete(struct fc_rport *rport) |
1677 | { | 1835 | { |
1836 | struct Scsi_Host *shost = rport_to_shost(rport); | ||
1678 | int timeout = rport->dev_loss_tmo; | 1837 | int timeout = rport->dev_loss_tmo; |
1838 | unsigned long flags; | ||
1839 | |||
1840 | /* | ||
1841 | * No need to flush the fc_host work_q's, as all adds are synchronous. | ||
1842 | * | ||
1843 | * We do need to reclaim the rport scan work element, so eventually | ||
1844 | * (in fc_rport_final_delete()) we'll flush the scsi host work_q if | ||
1845 | * there's still a scan pending. | ||
1846 | */ | ||
1847 | |||
1848 | spin_lock_irqsave(shost->host_lock, flags); | ||
1679 | 1849 | ||
1680 | /* If no scsi target id mapping, delete it */ | 1850 | /* If no scsi target id mapping, delete it */ |
1681 | if (rport->scsi_target_id == -1) { | 1851 | if (rport->scsi_target_id == -1) { |
1682 | fc_rport_terminate(rport); | 1852 | list_del(&rport->peers); |
1853 | rport->port_state = FC_PORTSTATE_DELETED; | ||
1854 | fc_queue_work(shost, &rport->rport_delete_work); | ||
1855 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1683 | return; | 1856 | return; |
1684 | } | 1857 | } |
1685 | 1858 | ||
1859 | rport->port_state = FC_PORTSTATE_BLOCKED; | ||
1860 | |||
1861 | rport->flags |= FC_RPORT_DEVLOSS_PENDING; | ||
1862 | |||
1863 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1864 | |||
1686 | scsi_target_block(&rport->dev); | 1865 | scsi_target_block(&rport->dev); |
1687 | 1866 | ||
1688 | /* cap the length the devices can be blocked until they are deleted */ | 1867 | /* cap the length the devices can be blocked until they are deleted */ |
1689 | schedule_delayed_work(&rport->dev_loss_work, timeout * HZ); | 1868 | fc_queue_devloss_work(shost, &rport->dev_loss_work, timeout * HZ); |
1690 | |||
1691 | rport->port_state = FC_PORTSTATE_BLOCKED; | ||
1692 | } | 1869 | } |
1693 | EXPORT_SYMBOL(fc_remote_port_delete); | 1870 | EXPORT_SYMBOL(fc_remote_port_delete); |
1694 | 1871 | ||
@@ -1716,8 +1893,7 @@ void | |||
1716 | fc_remote_port_rolechg(struct fc_rport *rport, u32 roles) | 1893 | fc_remote_port_rolechg(struct fc_rport *rport, u32 roles) |
1717 | { | 1894 | { |
1718 | struct Scsi_Host *shost = rport_to_shost(rport); | 1895 | struct Scsi_Host *shost = rport_to_shost(rport); |
1719 | struct fc_host_attrs *fc_host = | 1896 | struct fc_host_attrs *fc_host = shost_to_fc_host(shost); |
1720 | (struct fc_host_attrs *)shost->shost_data; | ||
1721 | unsigned long flags; | 1897 | unsigned long flags; |
1722 | int create = 0; | 1898 | int create = 0; |
1723 | 1899 | ||
@@ -1729,10 +1905,11 @@ fc_remote_port_rolechg(struct fc_rport *rport, u32 roles) | |||
1729 | } else if (!(rport->roles & FC_RPORT_ROLE_FCP_TARGET)) | 1905 | } else if (!(rport->roles & FC_RPORT_ROLE_FCP_TARGET)) |
1730 | create = 1; | 1906 | create = 1; |
1731 | } | 1907 | } |
1732 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1733 | 1908 | ||
1734 | rport->roles = roles; | 1909 | rport->roles = roles; |
1735 | 1910 | ||
1911 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1912 | |||
1736 | if (create) { | 1913 | if (create) { |
1737 | /* | 1914 | /* |
1738 | * There may have been a delete timer running on the | 1915 | * There may have been a delete timer running on the |
@@ -1747,10 +1924,20 @@ fc_remote_port_rolechg(struct fc_rport *rport, u32 roles) | |||
1747 | * transaction. | 1924 | * transaction. |
1748 | */ | 1925 | */ |
1749 | if (!cancel_delayed_work(&rport->dev_loss_work)) | 1926 | if (!cancel_delayed_work(&rport->dev_loss_work)) |
1750 | flush_scheduled_work(); | 1927 | fc_flush_devloss(shost); |
1928 | |||
1929 | spin_lock_irqsave(shost->host_lock, flags); | ||
1930 | rport->flags &= ~FC_RPORT_DEVLOSS_PENDING; | ||
1931 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1932 | |||
1933 | /* ensure any stgt delete functions are done */ | ||
1934 | fc_flush_work(shost); | ||
1751 | 1935 | ||
1752 | /* initiate a scan of the target */ | 1936 | /* initiate a scan of the target */ |
1937 | spin_lock_irqsave(shost->host_lock, flags); | ||
1938 | rport->flags |= FC_RPORT_SCAN_PENDING; | ||
1753 | scsi_queue_work(shost, &rport->scan_work); | 1939 | scsi_queue_work(shost, &rport->scan_work); |
1940 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1754 | } | 1941 | } |
1755 | } | 1942 | } |
1756 | EXPORT_SYMBOL(fc_remote_port_rolechg); | 1943 | EXPORT_SYMBOL(fc_remote_port_rolechg); |
@@ -1767,22 +1954,24 @@ fc_timeout_deleted_rport(void *data) | |||
1767 | { | 1954 | { |
1768 | struct fc_rport *rport = (struct fc_rport *)data; | 1955 | struct fc_rport *rport = (struct fc_rport *)data; |
1769 | struct Scsi_Host *shost = rport_to_shost(rport); | 1956 | struct Scsi_Host *shost = rport_to_shost(rport); |
1957 | struct fc_host_attrs *fc_host = shost_to_fc_host(shost); | ||
1770 | unsigned long flags; | 1958 | unsigned long flags; |
1771 | 1959 | ||
1772 | spin_lock_irqsave(shost->host_lock, flags); | 1960 | spin_lock_irqsave(shost->host_lock, flags); |
1773 | 1961 | ||
1962 | rport->flags &= ~FC_RPORT_DEVLOSS_PENDING; | ||
1963 | |||
1774 | /* | 1964 | /* |
1775 | * If the port is ONLINE, then it came back, but was no longer an | 1965 | * If the port is ONLINE, then it came back. Validate it's still an |
1776 | * FCP target. Thus we need to tear down the scsi_target on it. | 1966 | * FCP target. If not, tear down the scsi_target on it. |
1777 | */ | 1967 | */ |
1778 | if (rport->port_state == FC_PORTSTATE_ONLINE) { | 1968 | if ((rport->port_state == FC_PORTSTATE_ONLINE) && |
1779 | spin_unlock_irqrestore(shost->host_lock, flags); | 1969 | !(rport->roles & FC_RPORT_ROLE_FCP_TARGET)) { |
1780 | |||
1781 | dev_printk(KERN_ERR, &rport->dev, | 1970 | dev_printk(KERN_ERR, &rport->dev, |
1782 | "blocked FC remote port time out: removing target\n"); | 1971 | "blocked FC remote port time out: no longer" |
1783 | 1972 | " a FCP target, removing starget\n"); | |
1784 | fc_rport_tgt_remove(rport); | 1973 | fc_queue_work(shost, &rport->stgt_delete_work); |
1785 | 1974 | spin_unlock_irqrestore(shost->host_lock, flags); | |
1786 | return; | 1975 | return; |
1787 | } | 1976 | } |
1788 | 1977 | ||
@@ -1793,11 +1982,13 @@ fc_timeout_deleted_rport(void *data) | |||
1793 | return; | 1982 | return; |
1794 | } | 1983 | } |
1795 | 1984 | ||
1796 | if (fc_host_tgtid_bind_type(shost) == FC_TGTID_BIND_NONE) { | 1985 | if (fc_host->tgtid_bind_type == FC_TGTID_BIND_NONE) { |
1797 | spin_unlock_irqrestore(shost->host_lock, flags); | 1986 | list_del(&rport->peers); |
1987 | rport->port_state = FC_PORTSTATE_DELETED; | ||
1798 | dev_printk(KERN_ERR, &rport->dev, | 1988 | dev_printk(KERN_ERR, &rport->dev, |
1799 | "blocked FC remote port time out: removing target\n"); | 1989 | "blocked FC remote port time out: removing target\n"); |
1800 | fc_rport_terminate(rport); | 1990 | fc_queue_work(shost, &rport->rport_delete_work); |
1991 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1801 | return; | 1992 | return; |
1802 | } | 1993 | } |
1803 | 1994 | ||
@@ -1805,7 +1996,7 @@ fc_timeout_deleted_rport(void *data) | |||
1805 | "blocked FC remote port time out: removing target and " | 1996 | "blocked FC remote port time out: removing target and " |
1806 | "saving binding\n"); | 1997 | "saving binding\n"); |
1807 | 1998 | ||
1808 | list_move_tail(&rport->peers, &fc_host_rport_bindings(shost)); | 1999 | list_move_tail(&rport->peers, &fc_host->rport_bindings); |
1809 | 2000 | ||
1810 | /* | 2001 | /* |
1811 | * Note: We do not remove or clear the hostdata area. This allows | 2002 | * Note: We do not remove or clear the hostdata area. This allows |
@@ -1819,10 +2010,10 @@ fc_timeout_deleted_rport(void *data) | |||
1819 | rport->maxframe_size = -1; | 2010 | rport->maxframe_size = -1; |
1820 | rport->supported_classes = FC_COS_UNSPECIFIED; | 2011 | rport->supported_classes = FC_COS_UNSPECIFIED; |
1821 | rport->roles = FC_RPORT_ROLE_UNKNOWN; | 2012 | rport->roles = FC_RPORT_ROLE_UNKNOWN; |
1822 | rport->port_state = FC_PORTSTATE_DELETED; | 2013 | rport->port_state = FC_PORTSTATE_NOTPRESENT; |
1823 | 2014 | ||
1824 | /* remove the identifiers that aren't used in the consisting binding */ | 2015 | /* remove the identifiers that aren't used in the consisting binding */ |
1825 | switch (fc_host_tgtid_bind_type(shost)) { | 2016 | switch (fc_host->tgtid_bind_type) { |
1826 | case FC_TGTID_BIND_BY_WWPN: | 2017 | case FC_TGTID_BIND_BY_WWPN: |
1827 | rport->node_name = -1; | 2018 | rport->node_name = -1; |
1828 | rport->port_id = -1; | 2019 | rport->port_id = -1; |
@@ -1843,17 +2034,8 @@ fc_timeout_deleted_rport(void *data) | |||
1843 | * As this only occurs if the remote port (scsi target) | 2034 | * As this only occurs if the remote port (scsi target) |
1844 | * went away and didn't come back - we'll remove | 2035 | * went away and didn't come back - we'll remove |
1845 | * all attached scsi devices. | 2036 | * all attached scsi devices. |
1846 | * | ||
1847 | * We'll schedule the shost work item to perform the actual removal | ||
1848 | * to avoid recursion in the different flush calls if we perform | ||
1849 | * the removal in each target - and there are lots of targets | ||
1850 | * whose timeouts fire at the same time. | ||
1851 | */ | 2037 | */ |
1852 | 2038 | fc_queue_work(shost, &rport->stgt_delete_work); | |
1853 | if ( !(fc_host_flags(shost) & FC_SHOST_RPORT_DEL_SCHEDULED)) { | ||
1854 | fc_host_flags(shost) |= FC_SHOST_RPORT_DEL_SCHEDULED; | ||
1855 | scsi_queue_work(shost, &fc_host_rport_del_work(shost)); | ||
1856 | } | ||
1857 | 2039 | ||
1858 | spin_unlock_irqrestore(shost->host_lock, flags); | 2040 | spin_unlock_irqrestore(shost->host_lock, flags); |
1859 | } | 2041 | } |
@@ -1870,44 +2052,18 @@ static void | |||
1870 | fc_scsi_scan_rport(void *data) | 2052 | fc_scsi_scan_rport(void *data) |
1871 | { | 2053 | { |
1872 | struct fc_rport *rport = (struct fc_rport *)data; | 2054 | struct fc_rport *rport = (struct fc_rport *)data; |
1873 | 2055 | struct Scsi_Host *shost = rport_to_shost(rport); | |
1874 | scsi_target_unblock(&rport->dev); | ||
1875 | scsi_scan_target(&rport->dev, rport->channel, rport->scsi_target_id, | ||
1876 | SCAN_WILD_CARD, 1); | ||
1877 | } | ||
1878 | |||
1879 | |||
1880 | /** | ||
1881 | * fc_shost_remove_rports - called to remove all rports that are marked | ||
1882 | * as in a deleted (not connected) state. | ||
1883 | * | ||
1884 | * @data: shost whose rports are to be looked at | ||
1885 | **/ | ||
1886 | static void | ||
1887 | fc_shost_remove_rports(void *data) | ||
1888 | { | ||
1889 | struct Scsi_Host *shost = (struct Scsi_Host *)data; | ||
1890 | struct fc_rport *rport, *next_rport; | ||
1891 | unsigned long flags; | 2056 | unsigned long flags; |
1892 | 2057 | ||
1893 | spin_lock_irqsave(shost->host_lock, flags); | 2058 | if ((rport->port_state == FC_PORTSTATE_ONLINE) && |
1894 | while (fc_host_flags(shost) & FC_SHOST_RPORT_DEL_SCHEDULED) { | 2059 | (rport->roles & FC_RPORT_ROLE_FCP_TARGET)) { |
1895 | 2060 | scsi_target_unblock(&rport->dev); | |
1896 | fc_host_flags(shost) &= ~FC_SHOST_RPORT_DEL_SCHEDULED; | 2061 | scsi_scan_target(&rport->dev, rport->channel, |
1897 | 2062 | rport->scsi_target_id, SCAN_WILD_CARD, 1); | |
1898 | restart_search: | ||
1899 | list_for_each_entry_safe(rport, next_rport, | ||
1900 | &fc_host_rport_bindings(shost), peers) { | ||
1901 | if (rport->port_state == FC_PORTSTATE_DELETED) { | ||
1902 | rport->port_state = FC_PORTSTATE_NOTPRESENT; | ||
1903 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1904 | fc_rport_tgt_remove(rport); | ||
1905 | spin_lock_irqsave(shost->host_lock, flags); | ||
1906 | goto restart_search; | ||
1907 | } | ||
1908 | } | ||
1909 | |||
1910 | } | 2063 | } |
2064 | |||
2065 | spin_lock_irqsave(shost->host_lock, flags); | ||
2066 | rport->flags &= ~FC_RPORT_SCAN_PENDING; | ||
1911 | spin_unlock_irqrestore(shost->host_lock, flags); | 2067 | spin_unlock_irqrestore(shost->host_lock, flags); |
1912 | } | 2068 | } |
1913 | 2069 | ||