diff options
author | Neil Horman <nhorman@tuxdriver.com> | 2013-05-03 15:34:15 -0400 |
---|---|---|
committer | Robert Love <robert.w.love@intel.com> | 2013-05-10 13:19:23 -0400 |
commit | fb00cc2353ca22b3278f72d73e65a33486d1dbc7 (patch) | |
tree | 2a9d5adb4e83604d9e1ef5a343b0cfa7d64fd5aa | |
parent | 732bdb9d141879b1b5b357f934553fe827c1f46b (diff) |
libfc: extend ex_lock to protect all of fc_seq_send
This warning was reported recently:
WARNING: at drivers/scsi/libfc/fc_exch.c:478 fc_seq_send+0x14f/0x160 [libfc]()
(Not tainted)
Hardware name: ProLiant DL120 G7
Modules linked in: tcm_fc target_core_iblock target_core_file target_core_pscsi
target_core_mod configfs dm_round_robin dm_multipath 8021q garp stp llc bnx2fc
cnic uio fcoe libfcoe libfc scsi_transport_fc scsi_tgt autofs4 sunrpc
pcc_cpufreq ipv6 hpilo hpwdt e1000e microcode iTCO_wdt iTCO_vendor_support
serio_raw shpchp ixgbe dca mdio sg ext4 mbcache jbd2 sd_mod crc_t10dif pata_acpi
ata_generic ata_piix hpsa dm_mirror dm_region_hash dm_log dm_mod [last unloaded:
scsi_wait_scan]
Pid: 5464, comm: target_completi Not tainted 2.6.32-272.el6.x86_64 #1
Call Trace:
[<ffffffff8106b747>] ? warn_slowpath_common+0x87/0xc0
[<ffffffff8106b79a>] ? warn_slowpath_null+0x1a/0x20
[<ffffffffa025f7df>] ? fc_seq_send+0x14f/0x160 [libfc]
[<ffffffffa035cbce>] ? ft_queue_status+0x16e/0x210 [tcm_fc]
[<ffffffffa030a660>] ? target_complete_ok_work+0x0/0x4b0 [target_core_mod]
[<ffffffffa030a766>] ? target_complete_ok_work+0x106/0x4b0 [target_core_mod]
[<ffffffffa030a660>] ? target_complete_ok_work+0x0/0x4b0 [target_core_mod]
[<ffffffff8108c760>] ? worker_thread+0x170/0x2a0
[<ffffffff810920d0>] ? autoremove_wake_function+0x0/0x40
[<ffffffff8108c5f0>] ? worker_thread+0x0/0x2a0
[<ffffffff81091d66>] ? kthread+0x96/0xa0
[<ffffffff8100c14a>] ? child_rip+0xa/0x20
[<ffffffff81091cd0>] ? kthread+0x0/0xa0
[<ffffffff8100c140>] ? child_rip+0x0/0x20
It occurs because fc_seq_send can have multiple contexts executing within it at
the same time, and fc_seq_send doesn't consistently use the ep->ex_lock that
protects this structure. Because of that, its possible for one context to clear
the INIT bit in the ep->esb_state field while another checks it, leading to the
above stack trace generated by the WARN_ON in the function.
We should probably undertake the effort to convert access to the fc_exch
structures to use rcu, but that a larger work item. To just fix this specific
issue, we can just extend the ex_lock protection through the entire fc_seq_send
path
Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
Reported-by: Gris Ge <fge@redhat.com>
CC: Robert Love <robert.w.love@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
-rw-r--r-- | drivers/scsi/libfc/fc_exch.c | 37 |
1 files changed, 24 insertions, 13 deletions
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index c772d8d27159..8b928c67e4b9 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c | |||
@@ -463,13 +463,7 @@ static void fc_exch_delete(struct fc_exch *ep) | |||
463 | fc_exch_release(ep); /* drop hold for exch in mp */ | 463 | fc_exch_release(ep); /* drop hold for exch in mp */ |
464 | } | 464 | } |
465 | 465 | ||
466 | /** | 466 | static int fc_seq_send_locked(struct fc_lport *lport, struct fc_seq *sp, |
467 | * fc_seq_send() - Send a frame using existing sequence/exchange pair | ||
468 | * @lport: The local port that the exchange will be sent on | ||
469 | * @sp: The sequence to be sent | ||
470 | * @fp: The frame to be sent on the exchange | ||
471 | */ | ||
472 | static int fc_seq_send(struct fc_lport *lport, struct fc_seq *sp, | ||
473 | struct fc_frame *fp) | 467 | struct fc_frame *fp) |
474 | { | 468 | { |
475 | struct fc_exch *ep; | 469 | struct fc_exch *ep; |
@@ -479,7 +473,7 @@ static int fc_seq_send(struct fc_lport *lport, struct fc_seq *sp, | |||
479 | u8 fh_type = fh->fh_type; | 473 | u8 fh_type = fh->fh_type; |
480 | 474 | ||
481 | ep = fc_seq_exch(sp); | 475 | ep = fc_seq_exch(sp); |
482 | WARN_ON((ep->esb_stat & ESB_ST_SEQ_INIT) != ESB_ST_SEQ_INIT); | 476 | WARN_ON(!(ep->esb_stat & ESB_ST_SEQ_INIT)); |
483 | 477 | ||
484 | f_ctl = ntoh24(fh->fh_f_ctl); | 478 | f_ctl = ntoh24(fh->fh_f_ctl); |
485 | fc_exch_setup_hdr(ep, fp, f_ctl); | 479 | fc_exch_setup_hdr(ep, fp, f_ctl); |
@@ -502,17 +496,34 @@ static int fc_seq_send(struct fc_lport *lport, struct fc_seq *sp, | |||
502 | error = lport->tt.frame_send(lport, fp); | 496 | error = lport->tt.frame_send(lport, fp); |
503 | 497 | ||
504 | if (fh_type == FC_TYPE_BLS) | 498 | if (fh_type == FC_TYPE_BLS) |
505 | return error; | 499 | goto out; |
506 | 500 | ||
507 | /* | 501 | /* |
508 | * Update the exchange and sequence flags, | 502 | * Update the exchange and sequence flags, |
509 | * assuming all frames for the sequence have been sent. | 503 | * assuming all frames for the sequence have been sent. |
510 | * We can only be called to send once for each sequence. | 504 | * We can only be called to send once for each sequence. |
511 | */ | 505 | */ |
512 | spin_lock_bh(&ep->ex_lock); | ||
513 | ep->f_ctl = f_ctl & ~FC_FC_FIRST_SEQ; /* not first seq */ | 506 | ep->f_ctl = f_ctl & ~FC_FC_FIRST_SEQ; /* not first seq */ |
514 | if (f_ctl & FC_FC_SEQ_INIT) | 507 | if (f_ctl & FC_FC_SEQ_INIT) |
515 | ep->esb_stat &= ~ESB_ST_SEQ_INIT; | 508 | ep->esb_stat &= ~ESB_ST_SEQ_INIT; |
509 | out: | ||
510 | return error; | ||
511 | } | ||
512 | |||
513 | /** | ||
514 | * fc_seq_send() - Send a frame using existing sequence/exchange pair | ||
515 | * @lport: The local port that the exchange will be sent on | ||
516 | * @sp: The sequence to be sent | ||
517 | * @fp: The frame to be sent on the exchange | ||
518 | */ | ||
519 | static int fc_seq_send(struct fc_lport *lport, struct fc_seq *sp, | ||
520 | struct fc_frame *fp) | ||
521 | { | ||
522 | struct fc_exch *ep; | ||
523 | int error; | ||
524 | ep = fc_seq_exch(sp); | ||
525 | spin_lock_bh(&ep->ex_lock); | ||
526 | error = fc_seq_send_locked(lport, sp, fp); | ||
516 | spin_unlock_bh(&ep->ex_lock); | 527 | spin_unlock_bh(&ep->ex_lock); |
517 | return error; | 528 | return error; |
518 | } | 529 | } |
@@ -629,7 +640,7 @@ static int fc_exch_abort_locked(struct fc_exch *ep, | |||
629 | if (fp) { | 640 | if (fp) { |
630 | fc_fill_fc_hdr(fp, FC_RCTL_BA_ABTS, ep->did, ep->sid, | 641 | fc_fill_fc_hdr(fp, FC_RCTL_BA_ABTS, ep->did, ep->sid, |
631 | FC_TYPE_BLS, FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); | 642 | FC_TYPE_BLS, FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); |
632 | error = fc_seq_send(ep->lp, sp, fp); | 643 | error = fc_seq_send_locked(ep->lp, sp, fp); |
633 | } else | 644 | } else |
634 | error = -ENOBUFS; | 645 | error = -ENOBUFS; |
635 | return error; | 646 | return error; |
@@ -1132,7 +1143,7 @@ static void fc_seq_send_last(struct fc_seq *sp, struct fc_frame *fp, | |||
1132 | f_ctl = FC_FC_LAST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT; | 1143 | f_ctl = FC_FC_LAST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT; |
1133 | f_ctl |= ep->f_ctl; | 1144 | f_ctl |= ep->f_ctl; |
1134 | fc_fill_fc_hdr(fp, rctl, ep->did, ep->sid, fh_type, f_ctl, 0); | 1145 | fc_fill_fc_hdr(fp, rctl, ep->did, ep->sid, fh_type, f_ctl, 0); |
1135 | fc_seq_send(ep->lp, sp, fp); | 1146 | fc_seq_send_locked(ep->lp, sp, fp); |
1136 | } | 1147 | } |
1137 | 1148 | ||
1138 | /** | 1149 | /** |
@@ -1307,8 +1318,8 @@ static void fc_exch_recv_abts(struct fc_exch *ep, struct fc_frame *rx_fp) | |||
1307 | ap->ba_low_seq_cnt = htons(sp->cnt); | 1318 | ap->ba_low_seq_cnt = htons(sp->cnt); |
1308 | } | 1319 | } |
1309 | sp = fc_seq_start_next_locked(sp); | 1320 | sp = fc_seq_start_next_locked(sp); |
1310 | spin_unlock_bh(&ep->ex_lock); | ||
1311 | fc_seq_send_last(sp, fp, FC_RCTL_BA_ACC, FC_TYPE_BLS); | 1321 | fc_seq_send_last(sp, fp, FC_RCTL_BA_ACC, FC_TYPE_BLS); |
1322 | spin_unlock_bh(&ep->ex_lock); | ||
1312 | fc_frame_free(rx_fp); | 1323 | fc_frame_free(rx_fp); |
1313 | return; | 1324 | return; |
1314 | 1325 | ||