diff options
author | Bhanu Prakash Gollapudi <bprakash@broadcom.com> | 2011-05-27 14:47:27 -0400 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2011-06-29 12:02:09 -0400 |
commit | d36b3279e157641c345b12eddb3db78fb42da80f (patch) | |
tree | a4b1a9a40930728bde81b569f67f2f708384b25d /drivers/scsi/bnx2fc/bnx2fc_fcoe.c | |
parent | b5a95fe7ef464a67fab6ff870aa740739e788f90 (diff) |
[SCSI] bnx2fc: Fix kernel panic when deleting NPIV ports
Deleting NPIV port causes a kernel panic when the NPIV port is in the same zone
as the physical port and shares the same LUN. This happens due to the fact that
vport destroy and unsolicited ELS are scheduled to run on the same workqueue,
and vport destroy destroys the lport and the unsolicited ELS tries to access
the invalid lport. This patch fixes this issue by maintaining a list of valid
lports and verifying if the lport is valid or not before accessing it.
Signed-off-by: Bhanu Prakash Gollapudi <bprakash@broadcom.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi/bnx2fc/bnx2fc_fcoe.c')
-rw-r--r-- | drivers/scsi/bnx2fc/bnx2fc_fcoe.c | 28 |
1 files changed, 27 insertions, 1 deletions
diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index 8f894e410b5d..5c8c59e5bade 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c | |||
@@ -1228,6 +1228,7 @@ static int bnx2fc_interface_setup(struct bnx2fc_hba *hba, | |||
1228 | hba->ctlr.get_src_addr = bnx2fc_get_src_mac; | 1228 | hba->ctlr.get_src_addr = bnx2fc_get_src_mac; |
1229 | set_bit(BNX2FC_CTLR_INIT_DONE, &hba->init_done); | 1229 | set_bit(BNX2FC_CTLR_INIT_DONE, &hba->init_done); |
1230 | 1230 | ||
1231 | INIT_LIST_HEAD(&hba->vports); | ||
1231 | rc = bnx2fc_netdev_setup(hba); | 1232 | rc = bnx2fc_netdev_setup(hba); |
1232 | if (rc) | 1233 | if (rc) |
1233 | goto setup_err; | 1234 | goto setup_err; |
@@ -1264,8 +1265,15 @@ static struct fc_lport *bnx2fc_if_create(struct bnx2fc_hba *hba, | |||
1264 | struct fcoe_port *port; | 1265 | struct fcoe_port *port; |
1265 | struct Scsi_Host *shost; | 1266 | struct Scsi_Host *shost; |
1266 | struct fc_vport *vport = dev_to_vport(parent); | 1267 | struct fc_vport *vport = dev_to_vport(parent); |
1268 | struct bnx2fc_lport *blport; | ||
1267 | int rc = 0; | 1269 | int rc = 0; |
1268 | 1270 | ||
1271 | blport = kzalloc(sizeof(struct bnx2fc_lport), GFP_KERNEL); | ||
1272 | if (!blport) { | ||
1273 | BNX2FC_HBA_DBG(hba->ctlr.lp, "Unable to alloc bnx2fc_lport\n"); | ||
1274 | return NULL; | ||
1275 | } | ||
1276 | |||
1269 | /* Allocate Scsi_Host structure */ | 1277 | /* Allocate Scsi_Host structure */ |
1270 | if (!npiv) | 1278 | if (!npiv) |
1271 | lport = libfc_host_alloc(&bnx2fc_shost_template, sizeof(*port)); | 1279 | lport = libfc_host_alloc(&bnx2fc_shost_template, sizeof(*port)); |
@@ -1274,7 +1282,7 @@ static struct fc_lport *bnx2fc_if_create(struct bnx2fc_hba *hba, | |||
1274 | 1282 | ||
1275 | if (!lport) { | 1283 | if (!lport) { |
1276 | printk(KERN_ERR PFX "could not allocate scsi host structure\n"); | 1284 | printk(KERN_ERR PFX "could not allocate scsi host structure\n"); |
1277 | return NULL; | 1285 | goto free_blport; |
1278 | } | 1286 | } |
1279 | shost = lport->host; | 1287 | shost = lport->host; |
1280 | port = lport_priv(lport); | 1288 | port = lport_priv(lport); |
@@ -1330,12 +1338,20 @@ static struct fc_lport *bnx2fc_if_create(struct bnx2fc_hba *hba, | |||
1330 | } | 1338 | } |
1331 | 1339 | ||
1332 | bnx2fc_interface_get(hba); | 1340 | bnx2fc_interface_get(hba); |
1341 | |||
1342 | spin_lock_bh(&hba->hba_lock); | ||
1343 | blport->lport = lport; | ||
1344 | list_add_tail(&blport->list, &hba->vports); | ||
1345 | spin_unlock_bh(&hba->hba_lock); | ||
1346 | |||
1333 | return lport; | 1347 | return lport; |
1334 | 1348 | ||
1335 | shost_err: | 1349 | shost_err: |
1336 | scsi_remove_host(shost); | 1350 | scsi_remove_host(shost); |
1337 | lp_config_err: | 1351 | lp_config_err: |
1338 | scsi_host_put(lport->host); | 1352 | scsi_host_put(lport->host); |
1353 | free_blport: | ||
1354 | kfree(blport); | ||
1339 | return NULL; | 1355 | return NULL; |
1340 | } | 1356 | } |
1341 | 1357 | ||
@@ -1351,6 +1367,7 @@ static void bnx2fc_if_destroy(struct fc_lport *lport) | |||
1351 | { | 1367 | { |
1352 | struct fcoe_port *port = lport_priv(lport); | 1368 | struct fcoe_port *port = lport_priv(lport); |
1353 | struct bnx2fc_hba *hba = port->priv; | 1369 | struct bnx2fc_hba *hba = port->priv; |
1370 | struct bnx2fc_lport *blport, *tmp; | ||
1354 | 1371 | ||
1355 | BNX2FC_HBA_DBG(hba->ctlr.lp, "ENTERED bnx2fc_if_destroy\n"); | 1372 | BNX2FC_HBA_DBG(hba->ctlr.lp, "ENTERED bnx2fc_if_destroy\n"); |
1356 | /* Stop the transmit retry timer */ | 1373 | /* Stop the transmit retry timer */ |
@@ -1375,6 +1392,15 @@ static void bnx2fc_if_destroy(struct fc_lport *lport) | |||
1375 | /* Free memory used by statistical counters */ | 1392 | /* Free memory used by statistical counters */ |
1376 | fc_lport_free_stats(lport); | 1393 | fc_lport_free_stats(lport); |
1377 | 1394 | ||
1395 | spin_lock_bh(&hba->hba_lock); | ||
1396 | list_for_each_entry_safe(blport, tmp, &hba->vports, list) { | ||
1397 | if (blport->lport == lport) { | ||
1398 | list_del(&blport->list); | ||
1399 | kfree(blport); | ||
1400 | } | ||
1401 | } | ||
1402 | spin_unlock_bh(&hba->hba_lock); | ||
1403 | |||
1378 | /* Release Scsi_Host */ | 1404 | /* Release Scsi_Host */ |
1379 | scsi_host_put(lport->host); | 1405 | scsi_host_put(lport->host); |
1380 | 1406 | ||