aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi
diff options
context:
space:
mode:
authorBhanu Prakash Gollapudi <bprakash@broadcom.com>2011-05-27 14:47:27 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-10-03 14:41:05 -0400
commit49aa932d3fe9c6b175d30d9e9d575f532f650cfc (patch)
tree603b42a58f70db9c7444ee752c5ee06f8222226e /drivers/scsi
parente888ec89ef2ac191e71c79382ba26e0290f7d2ca (diff)
bnx2fc: Fix kernel panic when deleting NPIV ports
commit d36b3279e157641c345b12eddb3db78fb42da80f upstream. 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> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/scsi')
-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 0a404bfb44f..856fcbfbb7e 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 ab255fbc7f3..bdf62a5fc19 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
@@ -1225,6 +1225,7 @@ static int bnx2fc_interface_setup(struct bnx2fc_hba *hba,
1225 hba->ctlr.get_src_addr = bnx2fc_get_src_mac; 1225 hba->ctlr.get_src_addr = bnx2fc_get_src_mac;
1226 set_bit(BNX2FC_CTLR_INIT_DONE, &hba->init_done); 1226 set_bit(BNX2FC_CTLR_INIT_DONE, &hba->init_done);
1227 1227
1228 INIT_LIST_HEAD(&hba->vports);
1228 rc = bnx2fc_netdev_setup(hba); 1229 rc = bnx2fc_netdev_setup(hba);
1229 if (rc) 1230 if (rc)
1230 goto setup_err; 1231 goto setup_err;
@@ -1261,8 +1262,15 @@ static struct fc_lport *bnx2fc_if_create(struct bnx2fc_hba *hba,
1261 struct fcoe_port *port; 1262 struct fcoe_port *port;
1262 struct Scsi_Host *shost; 1263 struct Scsi_Host *shost;
1263 struct fc_vport *vport = dev_to_vport(parent); 1264 struct fc_vport *vport = dev_to_vport(parent);
1265 struct bnx2fc_lport *blport;
1264 int rc = 0; 1266 int rc = 0;
1265 1267
1268 blport = kzalloc(sizeof(struct bnx2fc_lport), GFP_KERNEL);
1269 if (!blport) {
1270 BNX2FC_HBA_DBG(hba->ctlr.lp, "Unable to alloc bnx2fc_lport\n");
1271 return NULL;
1272 }
1273
1266 /* Allocate Scsi_Host structure */ 1274 /* Allocate Scsi_Host structure */
1267 if (!npiv) 1275 if (!npiv)
1268 lport = libfc_host_alloc(&bnx2fc_shost_template, sizeof(*port)); 1276 lport = libfc_host_alloc(&bnx2fc_shost_template, sizeof(*port));
@@ -1271,7 +1279,7 @@ static struct fc_lport *bnx2fc_if_create(struct bnx2fc_hba *hba,
1271 1279
1272 if (!lport) { 1280 if (!lport) {
1273 printk(KERN_ERR PFX "could not allocate scsi host structure\n"); 1281 printk(KERN_ERR PFX "could not allocate scsi host structure\n");
1274 return NULL; 1282 goto free_blport;
1275 } 1283 }
1276 shost = lport->host; 1284 shost = lport->host;
1277 port = lport_priv(lport); 1285 port = lport_priv(lport);
@@ -1327,12 +1335,20 @@ static struct fc_lport *bnx2fc_if_create(struct bnx2fc_hba *hba,
1327 } 1335 }
1328 1336
1329 bnx2fc_interface_get(hba); 1337 bnx2fc_interface_get(hba);
1338
1339 spin_lock_bh(&hba->hba_lock);
1340 blport->lport = lport;
1341 list_add_tail(&blport->list, &hba->vports);
1342 spin_unlock_bh(&hba->hba_lock);
1343
1330 return lport; 1344 return lport;
1331 1345
1332shost_err: 1346shost_err:
1333 scsi_remove_host(shost); 1347 scsi_remove_host(shost);
1334lp_config_err: 1348lp_config_err:
1335 scsi_host_put(lport->host); 1349 scsi_host_put(lport->host);
1350free_blport:
1351 kfree(blport);
1336 return NULL; 1352 return NULL;
1337} 1353}
1338 1354
@@ -1348,6 +1364,7 @@ static void bnx2fc_if_destroy(struct fc_lport *lport)
1348{ 1364{
1349 struct fcoe_port *port = lport_priv(lport); 1365 struct fcoe_port *port = lport_priv(lport);
1350 struct bnx2fc_hba *hba = port->priv; 1366 struct bnx2fc_hba *hba = port->priv;
1367 struct bnx2fc_lport *blport, *tmp;
1351 1368
1352 BNX2FC_HBA_DBG(hba->ctlr.lp, "ENTERED bnx2fc_if_destroy\n"); 1369 BNX2FC_HBA_DBG(hba->ctlr.lp, "ENTERED bnx2fc_if_destroy\n");
1353 /* Stop the transmit retry timer */ 1370 /* Stop the transmit retry timer */
@@ -1372,6 +1389,15 @@ static void bnx2fc_if_destroy(struct fc_lport *lport)
1372 /* Free memory used by statistical counters */ 1389 /* Free memory used by statistical counters */
1373 fc_lport_free_stats(lport); 1390 fc_lport_free_stats(lport);
1374 1391
1392 spin_lock_bh(&hba->hba_lock);
1393 list_for_each_entry_safe(blport, tmp, &hba->vports, list) {
1394 if (blport->lport == lport) {
1395 list_del(&blport->list);
1396 kfree(blport);
1397 }
1398 }
1399 spin_unlock_bh(&hba->hba_lock);
1400
1375 /* Release Scsi_Host */ 1401 /* Release Scsi_Host */
1376 scsi_host_put(lport->host); 1402 scsi_host_put(lport->host);
1377 1403
diff --git a/drivers/scsi/bnx2fc/bnx2fc_hwi.c b/drivers/scsi/bnx2fc/bnx2fc_hwi.c
index f756d5f85c7..78baa46c39c 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);