@@ -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 */
385387static 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 */
593596static 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 );
808873out :
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 */
868935static 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 ;
15431603rel :
@@ -1576,8 +1636,6 @@ static void fc_exch_recv_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
15761636 */
15771637static 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 */
17721826static 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/**
0 commit comments