aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBhanu Prakash Gollapudi <bprakash@broadcom.com>2011-05-27 14:47:27 -0400
committerJames Bottomley <JBottomley@Parallels.com>2011-06-29 12:02:09 -0400
commitd36b3279e157641c345b12eddb3db78fb42da80f (patch)
treea4b1a9a40930728bde81b569f67f2f708384b25d
parentb5a95fe7ef464a67fab6ff870aa740739e788f90 (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>
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc.h8
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_fcoe.c28
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_hwi.c24
3 files changed, 57 insertions, 3 deletions
diff --git a/drivers/scsi/bnx2fc/bnx2fc.h b/drivers/scsi/bnx2fc/bnx2fc.h
index 0a404bfb44fe..856fcbfbb7e9 100644
--- a/drivers/scsi/bnx2fc/bnx2fc.h
+++ b/drivers/scsi/bnx2fc/bnx2fc.h
@@ -152,7 +152,6 @@ struct bnx2fc_percpu_s {
152 spinlock_t fp_work_lock; 152 spinlock_t fp_work_lock;
153}; 153};
154 154
155
156struct bnx2fc_hba { 155struct bnx2fc_hba {
157 struct list_head link; 156 struct list_head link;
158 struct cnic_dev *cnic; 157 struct cnic_dev *cnic;
@@ -179,6 +178,7 @@ struct bnx2fc_hba {
179 #define BNX2FC_CTLR_INIT_DONE 1 178 #define BNX2FC_CTLR_INIT_DONE 1
180 #define BNX2FC_CREATE_DONE 2 179 #define BNX2FC_CREATE_DONE 2
181 struct fcoe_ctlr ctlr; 180 struct fcoe_ctlr ctlr;
181 struct list_head vports;
182 u8 vlan_enabled; 182 u8 vlan_enabled;
183 int vlan_id; 183 int vlan_id;
184 u32 next_conn_id; 184 u32 next_conn_id;
@@ -232,6 +232,11 @@ struct bnx2fc_hba {
232 232
233#define bnx2fc_from_ctlr(fip) container_of(fip, struct bnx2fc_hba, ctlr) 233#define bnx2fc_from_ctlr(fip) container_of(fip, struct bnx2fc_hba, ctlr)
234 234
235struct bnx2fc_lport {
236 struct list_head list;
237 struct fc_lport *lport;
238};
239
235struct bnx2fc_cmd_mgr { 240struct bnx2fc_cmd_mgr {
236 struct bnx2fc_hba *hba; 241 struct bnx2fc_hba *hba;
237 u16 next_idx; 242 u16 next_idx;
@@ -423,6 +428,7 @@ struct bnx2fc_work {
423struct bnx2fc_unsol_els { 428struct bnx2fc_unsol_els {
424 struct fc_lport *lport; 429 struct fc_lport *lport;
425 struct fc_frame *fp; 430 struct fc_frame *fp;
431 struct bnx2fc_hba *hba;
426 struct work_struct unsol_els_work; 432 struct work_struct unsol_els_work;
427}; 433};
428 434
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
1335shost_err: 1349shost_err:
1336 scsi_remove_host(shost); 1350 scsi_remove_host(shost);
1337lp_config_err: 1351lp_config_err:
1338 scsi_host_put(lport->host); 1352 scsi_host_put(lport->host);
1353free_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
diff --git a/drivers/scsi/bnx2fc/bnx2fc_hwi.c b/drivers/scsi/bnx2fc/bnx2fc_hwi.c
index f756d5f85c7a..78baa46c39cd 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_hwi.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_hwi.c
@@ -480,16 +480,36 @@ int bnx2fc_send_session_destroy_req(struct bnx2fc_hba *hba,
480 return rc; 480 return rc;
481} 481}
482 482
483static bool is_valid_lport(struct bnx2fc_hba *hba, struct fc_lport *lport)
484{
485 struct bnx2fc_lport *blport;
486
487 spin_lock_bh(&hba->hba_lock);
488 list_for_each_entry(blport, &hba->vports, list) {
489 if (blport->lport == lport) {
490 spin_unlock_bh(&hba->hba_lock);
491 return true;
492 }
493 }
494 spin_unlock_bh(&hba->hba_lock);
495 return false;
496
497}
498
499
483static void bnx2fc_unsol_els_work(struct work_struct *work) 500static void bnx2fc_unsol_els_work(struct work_struct *work)
484{ 501{
485 struct bnx2fc_unsol_els *unsol_els; 502 struct bnx2fc_unsol_els *unsol_els;
486 struct fc_lport *lport; 503 struct fc_lport *lport;
504 struct bnx2fc_hba *hba;
487 struct fc_frame *fp; 505 struct fc_frame *fp;
488 506
489 unsol_els = container_of(work, struct bnx2fc_unsol_els, unsol_els_work); 507 unsol_els = container_of(work, struct bnx2fc_unsol_els, unsol_els_work);
490 lport = unsol_els->lport; 508 lport = unsol_els->lport;
491 fp = unsol_els->fp; 509 fp = unsol_els->fp;
492 fc_exch_recv(lport, fp); 510 hba = unsol_els->hba;
511 if (is_valid_lport(hba, lport))
512 fc_exch_recv(lport, fp);
493 kfree(unsol_els); 513 kfree(unsol_els);
494} 514}
495 515
@@ -499,6 +519,7 @@ void bnx2fc_process_l2_frame_compl(struct bnx2fc_rport *tgt,
499{ 519{
500 struct fcoe_port *port = tgt->port; 520 struct fcoe_port *port = tgt->port;
501 struct fc_lport *lport = port->lport; 521 struct fc_lport *lport = port->lport;
522 struct bnx2fc_hba *hba = port->priv;
502 struct bnx2fc_unsol_els *unsol_els; 523 struct bnx2fc_unsol_els *unsol_els;
503 struct fc_frame_header *fh; 524 struct fc_frame_header *fh;
504 struct fc_frame *fp; 525 struct fc_frame *fp;
@@ -559,6 +580,7 @@ void bnx2fc_process_l2_frame_compl(struct bnx2fc_rport *tgt,
559 fr_eof(fp) = FC_EOF_T; 580 fr_eof(fp) = FC_EOF_T;
560 fr_crc(fp) = cpu_to_le32(~crc); 581 fr_crc(fp) = cpu_to_le32(~crc);
561 unsol_els->lport = lport; 582 unsol_els->lport = lport;
583 unsol_els->hba = hba;
562 unsol_els->fp = fp; 584 unsol_els->fp = fp;
563 INIT_WORK(&unsol_els->unsol_els_work, bnx2fc_unsol_els_work); 585 INIT_WORK(&unsol_els->unsol_els_work, bnx2fc_unsol_els_work);
564 queue_work(bnx2fc_wq, &unsol_els->unsol_els_work); 586 queue_work(bnx2fc_wq, &unsol_els->unsol_els_work);