Skip to content

Commit 7030fd6

Browse files
bvanasscheRobert Love
authored and
Robert Love
committed
libfc: Do not invoke the response handler after fc_exch_done()
While the FCoE initiator driver invokes fc_exch_done() from inside the libfc response handler, FCoE target drivers typically invoke fc_exch_done() from outside the libfc response handler. The object fc_exch.arg points at may disappear as soon as fc_exch_done() has finished. So it's important not to invoke the response handler function after fc_exch_done() has finished. Modify libfc such that this guarantee is provided if fc_exch_done() is invoked from outside a response handler. This patch fixes a sporadic crash in FCoE target implementations after a command has been aborted. Signed-off-by: Bart Van Assche <[email protected]> Cc: Neil Horman <[email protected]> Signed-off-by: Robert Love <[email protected]>
1 parent f95b35c commit 7030fd6

File tree

2 files changed

+101
-39
lines changed

2 files changed

+101
-39
lines changed

drivers/scsi/libfc/fc_exch.c

Lines changed: 92 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,8 @@ static void fc_exch_timer_set(struct fc_exch *ep, unsigned int timer_msec)
381381
/**
382382
* fc_exch_done_locked() - Complete an exchange with the exchange lock held
383383
* @ep: The exchange that is complete
384+
*
385+
* Note: May sleep if invoked from outside a response handler.
384386
*/
385387
static int fc_exch_done_locked(struct fc_exch *ep)
386388
{
@@ -392,7 +394,6 @@ static int fc_exch_done_locked(struct fc_exch *ep)
392394
* ep, and in that case we only clear the resp and set it as
393395
* complete, so it can be reused by the timer to send the rrq.
394396
*/
395-
ep->resp = NULL;
396397
if (ep->state & FC_EX_DONE)
397398
return rc;
398399
ep->esb_stat |= ESB_ST_COMPLETE;
@@ -589,15 +590,27 @@ static struct fc_seq *fc_seq_start_next(struct fc_seq *sp)
589590

590591
/*
591592
* Set the response handler for the exchange associated with a sequence.
593+
*
594+
* Note: May sleep if invoked from outside a response handler.
592595
*/
593596
static void fc_seq_set_resp(struct fc_seq *sp,
594597
void (*resp)(struct fc_seq *, struct fc_frame *,
595598
void *),
596599
void *arg)
597600
{
598601
struct fc_exch *ep = fc_seq_exch(sp);
602+
DEFINE_WAIT(wait);
599603

600604
spin_lock_bh(&ep->ex_lock);
605+
while (ep->resp_active && ep->resp_task != current) {
606+
prepare_to_wait(&ep->resp_wq, &wait, TASK_UNINTERRUPTIBLE);
607+
spin_unlock_bh(&ep->ex_lock);
608+
609+
schedule();
610+
611+
spin_lock_bh(&ep->ex_lock);
612+
}
613+
finish_wait(&ep->resp_wq, &wait);
601614
ep->resp = resp;
602615
ep->arg = arg;
603616
spin_unlock_bh(&ep->ex_lock);
@@ -680,6 +693,61 @@ static int fc_seq_exch_abort(const struct fc_seq *req_sp,
680693
return error;
681694
}
682695

696+
/**
697+
* fc_invoke_resp() - invoke ep->resp()
698+
*
699+
* Notes:
700+
* It is assumed that after initialization finished (this means the
701+
* first unlock of ex_lock after fc_exch_alloc()) ep->resp and ep->arg are
702+
* modified only via fc_seq_set_resp(). This guarantees that none of these
703+
* two variables changes if ep->resp_active > 0.
704+
*
705+
* If an fc_seq_set_resp() call is busy modifying ep->resp and ep->arg when
706+
* this function is invoked, the first spin_lock_bh() call in this function
707+
* will wait until fc_seq_set_resp() has finished modifying these variables.
708+
*
709+
* Since fc_exch_done() invokes fc_seq_set_resp() it is guaranteed that that
710+
* ep->resp() won't be invoked after fc_exch_done() has returned.
711+
*
712+
* The response handler itself may invoke fc_exch_done(), which will clear the
713+
* ep->resp pointer.
714+
*
715+
* Return value:
716+
* Returns true if and only if ep->resp has been invoked.
717+
*/
718+
static bool fc_invoke_resp(struct fc_exch *ep, struct fc_seq *sp,
719+
struct fc_frame *fp)
720+
{
721+
void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg);
722+
void *arg;
723+
bool res = false;
724+
725+
spin_lock_bh(&ep->ex_lock);
726+
ep->resp_active++;
727+
if (ep->resp_task != current)
728+
ep->resp_task = !ep->resp_task ? current : NULL;
729+
resp = ep->resp;
730+
arg = ep->arg;
731+
spin_unlock_bh(&ep->ex_lock);
732+
733+
if (resp) {
734+
resp(sp, fp, arg);
735+
res = true;
736+
} else if (!IS_ERR(fp)) {
737+
fc_frame_free(fp);
738+
}
739+
740+
spin_lock_bh(&ep->ex_lock);
741+
if (--ep->resp_active == 0)
742+
ep->resp_task = NULL;
743+
spin_unlock_bh(&ep->ex_lock);
744+
745+
if (ep->resp_active == 0)
746+
wake_up(&ep->resp_wq);
747+
748+
return res;
749+
}
750+
683751
/**
684752
* fc_exch_timeout() - Handle exchange timer expiration
685753
* @work: The work_struct identifying the exchange that timed out
@@ -689,8 +757,6 @@ static void fc_exch_timeout(struct work_struct *work)
689757
struct fc_exch *ep = container_of(work, struct fc_exch,
690758
timeout_work.work);
691759
struct fc_seq *sp = &ep->seq;
692-
void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg);
693-
void *arg;
694760
u32 e_stat;
695761
int rc = 1;
696762

@@ -708,16 +774,13 @@ static void fc_exch_timeout(struct work_struct *work)
708774
fc_exch_rrq(ep);
709775
goto done;
710776
} else {
711-
resp = ep->resp;
712-
arg = ep->arg;
713-
ep->resp = NULL;
714777
if (e_stat & ESB_ST_ABNORMAL)
715778
rc = fc_exch_done_locked(ep);
716779
spin_unlock_bh(&ep->ex_lock);
717780
if (!rc)
718781
fc_exch_delete(ep);
719-
if (resp)
720-
resp(sp, ERR_PTR(-FC_EX_TIMEOUT), arg);
782+
fc_invoke_resp(ep, sp, ERR_PTR(-FC_EX_TIMEOUT));
783+
fc_seq_set_resp(sp, NULL, ep->arg);
721784
fc_seq_exch_abort(sp, 2 * ep->r_a_tov);
722785
goto done;
723786
}
@@ -804,6 +867,8 @@ static struct fc_exch *fc_exch_em_alloc(struct fc_lport *lport,
804867
ep->f_ctl = FC_FC_FIRST_SEQ; /* next seq is first seq */
805868
ep->rxid = FC_XID_UNKNOWN;
806869
ep->class = mp->class;
870+
ep->resp_active = 0;
871+
init_waitqueue_head(&ep->resp_wq);
807872
INIT_DELAYED_WORK(&ep->timeout_work, fc_exch_timeout);
808873
out:
809874
return ep;
@@ -864,6 +929,8 @@ static struct fc_exch *fc_exch_find(struct fc_exch_mgr *mp, u16 xid)
864929
* fc_exch_done() - Indicate that an exchange/sequence tuple is complete and
865930
* the memory allocated for the related objects may be freed.
866931
* @sp: The sequence that has completed
932+
*
933+
* Note: May sleep if invoked from outside a response handler.
867934
*/
868935
static void fc_exch_done(struct fc_seq *sp)
869936
{
@@ -873,6 +940,8 @@ static void fc_exch_done(struct fc_seq *sp)
873940
spin_lock_bh(&ep->ex_lock);
874941
rc = fc_exch_done_locked(ep);
875942
spin_unlock_bh(&ep->ex_lock);
943+
944+
fc_seq_set_resp(sp, NULL, ep->arg);
876945
if (!rc)
877946
fc_exch_delete(ep);
878947
}
@@ -1436,9 +1505,7 @@ static void fc_exch_recv_req(struct fc_lport *lport, struct fc_exch_mgr *mp,
14361505
* If new exch resp handler is valid then call that
14371506
* first.
14381507
*/
1439-
if (ep->resp)
1440-
ep->resp(sp, fp, ep->arg);
1441-
else
1508+
if (!fc_invoke_resp(ep, sp, fp))
14421509
lport->tt.lport_recv(lport, fp);
14431510
fc_exch_release(ep); /* release from lookup */
14441511
} else {
@@ -1462,8 +1529,6 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
14621529
struct fc_exch *ep;
14631530
enum fc_sof sof;
14641531
u32 f_ctl;
1465-
void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg);
1466-
void *ex_resp_arg;
14671532
int rc;
14681533

14691534
ep = fc_exch_find(mp, ntohs(fh->fh_ox_id));
@@ -1506,14 +1571,11 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
15061571

15071572
if (fc_sof_needs_ack(sof))
15081573
fc_seq_send_ack(sp, fp);
1509-
resp = ep->resp;
1510-
ex_resp_arg = ep->arg;
15111574

15121575
if (fh->fh_type != FC_TYPE_FCP && fr_eof(fp) == FC_EOF_T &&
15131576
(f_ctl & (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) ==
15141577
(FC_FC_LAST_SEQ | FC_FC_END_SEQ)) {
15151578
spin_lock_bh(&ep->ex_lock);
1516-
resp = ep->resp;
15171579
rc = fc_exch_done_locked(ep);
15181580
WARN_ON(fc_seq_exch(sp) != ep);
15191581
spin_unlock_bh(&ep->ex_lock);
@@ -1534,10 +1596,8 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
15341596
* If new exch resp handler is valid then call that
15351597
* first.
15361598
*/
1537-
if (resp)
1538-
resp(sp, fp, ex_resp_arg);
1539-
else
1540-
fc_frame_free(fp);
1599+
fc_invoke_resp(ep, sp, fp);
1600+
15411601
fc_exch_release(ep);
15421602
return;
15431603
rel:
@@ -1576,8 +1636,6 @@ static void fc_exch_recv_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
15761636
*/
15771637
static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)
15781638
{
1579-
void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg);
1580-
void *ex_resp_arg;
15811639
struct fc_frame_header *fh;
15821640
struct fc_ba_acc *ap;
15831641
struct fc_seq *sp;
@@ -1622,9 +1680,6 @@ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)
16221680
break;
16231681
}
16241682

1625-
resp = ep->resp;
1626-
ex_resp_arg = ep->arg;
1627-
16281683
/* do we need to do some other checks here. Can we reuse more of
16291684
* fc_exch_recv_seq_resp
16301685
*/
@@ -1636,17 +1691,14 @@ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)
16361691
ntoh24(fh->fh_f_ctl) & FC_FC_LAST_SEQ)
16371692
rc = fc_exch_done_locked(ep);
16381693
spin_unlock_bh(&ep->ex_lock);
1694+
1695+
fc_exch_hold(ep);
16391696
if (!rc)
16401697
fc_exch_delete(ep);
1641-
1642-
if (resp)
1643-
resp(sp, fp, ex_resp_arg);
1644-
else
1645-
fc_frame_free(fp);
1646-
1698+
fc_invoke_resp(ep, sp, fp);
16471699
if (has_rec)
16481700
fc_exch_timer_set(ep, ep->r_a_tov);
1649-
1701+
fc_exch_release(ep);
16501702
}
16511703

16521704
/**
@@ -1768,32 +1820,33 @@ static void fc_seq_ls_rjt(struct fc_frame *rx_fp, enum fc_els_rjt_reason reason,
17681820
/**
17691821
* fc_exch_reset() - Reset an exchange
17701822
* @ep: The exchange to be reset
1823+
*
1824+
* Note: May sleep if invoked from outside a response handler.
17711825
*/
17721826
static void fc_exch_reset(struct fc_exch *ep)
17731827
{
17741828
struct fc_seq *sp;
1775-
void (*resp)(struct fc_seq *, struct fc_frame *, void *);
1776-
void *arg;
17771829
int rc = 1;
17781830

17791831
spin_lock_bh(&ep->ex_lock);
17801832
fc_exch_abort_locked(ep, 0);
17811833
ep->state |= FC_EX_RST_CLEANUP;
17821834
fc_exch_timer_cancel(ep);
1783-
resp = ep->resp;
1784-
ep->resp = NULL;
17851835
if (ep->esb_stat & ESB_ST_REC_QUAL)
17861836
atomic_dec(&ep->ex_refcnt); /* drop hold for rec_qual */
17871837
ep->esb_stat &= ~ESB_ST_REC_QUAL;
1788-
arg = ep->arg;
17891838
sp = &ep->seq;
17901839
rc = fc_exch_done_locked(ep);
17911840
spin_unlock_bh(&ep->ex_lock);
1841+
1842+
fc_exch_hold(ep);
1843+
17921844
if (!rc)
17931845
fc_exch_delete(ep);
17941846

1795-
if (resp)
1796-
resp(sp, ERR_PTR(-FC_EX_CLOSED), arg);
1847+
fc_invoke_resp(ep, sp, ERR_PTR(-FC_EX_CLOSED));
1848+
fc_seq_set_resp(sp, NULL, ep->arg);
1849+
fc_exch_release(ep);
17971850
}
17981851

17991852
/**

include/scsi/libfc.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,12 @@ struct fc_seq {
410410
* @fh_type: The frame type
411411
* @class: The class of service
412412
* @seq: The sequence in use on this exchange
413+
* @resp_active: Number of tasks that are concurrently executing @resp().
414+
* @resp_task: If @resp_active > 0, either the task executing @resp(), the
415+
* task that has been interrupted to execute the soft-IRQ
416+
* executing @resp() or NULL if more than one task is executing
417+
* @resp concurrently.
418+
* @resp_wq: Waitqueue for the tasks waiting on @resp_active.
413419
* @resp: Callback for responses on this exchange
414420
* @destructor: Called when destroying the exchange
415421
* @arg: Passed as a void pointer to the resp() callback
@@ -441,6 +447,9 @@ struct fc_exch {
441447
u32 r_a_tov;
442448
u32 f_ctl;
443449
struct fc_seq seq;
450+
int resp_active;
451+
struct task_struct *resp_task;
452+
wait_queue_head_t resp_wq;
444453
void (*resp)(struct fc_seq *, struct fc_frame *, void *);
445454
void *arg;
446455
void (*destructor)(struct fc_seq *, void *);

0 commit comments

Comments
 (0)