diff options
author | Bart Van Assche <bvanassche@acm.org> | 2013-08-17 16:34:43 -0400 |
---|---|---|
committer | Robert Love <robert.w.love@intel.com> | 2013-09-04 16:45:22 -0400 |
commit | 7030fd626129ec4d616784516a462d317c251d39 (patch) | |
tree | cd9a65fa2d45e6397ba1620846dc4c9359638df4 /drivers/scsi/libfc | |
parent | f95b35cfcacadac16dbc5477fd22b0786256a3d1 (diff) |
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 <bvanassche@acm.org>
Cc: Neil Horman <nhorman@tuxdriver.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
Diffstat (limited to 'drivers/scsi/libfc')
-rw-r--r-- | drivers/scsi/libfc/fc_exch.c | 131 |
1 files changed, 92 insertions, 39 deletions
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index 47ebc7b1e143..1b3a09473452 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c | |||
@@ -381,6 +381,8 @@ static void fc_exch_timer_set(struct fc_exch *ep, unsigned int timer_msec) | |||
381 | /** | 381 | /** |
382 | * fc_exch_done_locked() - Complete an exchange with the exchange lock held | 382 | * fc_exch_done_locked() - Complete an exchange with the exchange lock held |
383 | * @ep: The exchange that is complete | 383 | * @ep: The exchange that is complete |
384 | * | ||
385 | * Note: May sleep if invoked from outside a response handler. | ||
384 | */ | 386 | */ |
385 | static int fc_exch_done_locked(struct fc_exch *ep) | 387 | static int fc_exch_done_locked(struct fc_exch *ep) |
386 | { | 388 | { |
@@ -392,7 +394,6 @@ static int fc_exch_done_locked(struct fc_exch *ep) | |||
392 | * ep, and in that case we only clear the resp and set it as | 394 | * ep, and in that case we only clear the resp and set it as |
393 | * complete, so it can be reused by the timer to send the rrq. | 395 | * complete, so it can be reused by the timer to send the rrq. |
394 | */ | 396 | */ |
395 | ep->resp = NULL; | ||
396 | if (ep->state & FC_EX_DONE) | 397 | if (ep->state & FC_EX_DONE) |
397 | return rc; | 398 | return rc; |
398 | ep->esb_stat |= ESB_ST_COMPLETE; | 399 | ep->esb_stat |= ESB_ST_COMPLETE; |
@@ -589,6 +590,8 @@ static struct fc_seq *fc_seq_start_next(struct fc_seq *sp) | |||
589 | 590 | ||
590 | /* | 591 | /* |
591 | * Set the response handler for the exchange associated with a sequence. | 592 | * Set the response handler for the exchange associated with a sequence. |
593 | * | ||
594 | * Note: May sleep if invoked from outside a response handler. | ||
592 | */ | 595 | */ |
593 | static void fc_seq_set_resp(struct fc_seq *sp, | 596 | static void fc_seq_set_resp(struct fc_seq *sp, |
594 | void (*resp)(struct fc_seq *, struct fc_frame *, | 597 | void (*resp)(struct fc_seq *, struct fc_frame *, |
@@ -596,8 +599,18 @@ static void fc_seq_set_resp(struct fc_seq *sp, | |||
596 | void *arg) | 599 | void *arg) |
597 | { | 600 | { |
598 | struct fc_exch *ep = fc_seq_exch(sp); | 601 | struct fc_exch *ep = fc_seq_exch(sp); |
602 | DEFINE_WAIT(wait); | ||
599 | 603 | ||
600 | spin_lock_bh(&ep->ex_lock); | 604 | 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); | ||
601 | ep->resp = resp; | 614 | ep->resp = resp; |
602 | ep->arg = arg; | 615 | ep->arg = arg; |
603 | spin_unlock_bh(&ep->ex_lock); | 616 | spin_unlock_bh(&ep->ex_lock); |
@@ -681,6 +694,61 @@ static int fc_seq_exch_abort(const struct fc_seq *req_sp, | |||
681 | } | 694 | } |
682 | 695 | ||
683 | /** | 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 | |||
751 | /** | ||
684 | * fc_exch_timeout() - Handle exchange timer expiration | 752 | * fc_exch_timeout() - Handle exchange timer expiration |
685 | * @work: The work_struct identifying the exchange that timed out | 753 | * @work: The work_struct identifying the exchange that timed out |
686 | */ | 754 | */ |
@@ -689,8 +757,6 @@ static void fc_exch_timeout(struct work_struct *work) | |||
689 | struct fc_exch *ep = container_of(work, struct fc_exch, | 757 | struct fc_exch *ep = container_of(work, struct fc_exch, |
690 | timeout_work.work); | 758 | timeout_work.work); |
691 | struct fc_seq *sp = &ep->seq; | 759 | struct fc_seq *sp = &ep->seq; |
692 | void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg); | ||
693 | void *arg; | ||
694 | u32 e_stat; | 760 | u32 e_stat; |
695 | int rc = 1; | 761 | int rc = 1; |
696 | 762 | ||
@@ -708,16 +774,13 @@ static void fc_exch_timeout(struct work_struct *work) | |||
708 | fc_exch_rrq(ep); | 774 | fc_exch_rrq(ep); |
709 | goto done; | 775 | goto done; |
710 | } else { | 776 | } else { |
711 | resp = ep->resp; | ||
712 | arg = ep->arg; | ||
713 | ep->resp = NULL; | ||
714 | if (e_stat & ESB_ST_ABNORMAL) | 777 | if (e_stat & ESB_ST_ABNORMAL) |
715 | rc = fc_exch_done_locked(ep); | 778 | rc = fc_exch_done_locked(ep); |
716 | spin_unlock_bh(&ep->ex_lock); | 779 | spin_unlock_bh(&ep->ex_lock); |
717 | if (!rc) | 780 | if (!rc) |
718 | fc_exch_delete(ep); | 781 | fc_exch_delete(ep); |
719 | if (resp) | 782 | fc_invoke_resp(ep, sp, ERR_PTR(-FC_EX_TIMEOUT)); |
720 | resp(sp, ERR_PTR(-FC_EX_TIMEOUT), arg); | 783 | fc_seq_set_resp(sp, NULL, ep->arg); |
721 | fc_seq_exch_abort(sp, 2 * ep->r_a_tov); | 784 | fc_seq_exch_abort(sp, 2 * ep->r_a_tov); |
722 | goto done; | 785 | goto done; |
723 | } | 786 | } |
@@ -804,6 +867,8 @@ hit: | |||
804 | ep->f_ctl = FC_FC_FIRST_SEQ; /* next seq is first seq */ | 867 | ep->f_ctl = FC_FC_FIRST_SEQ; /* next seq is first seq */ |
805 | ep->rxid = FC_XID_UNKNOWN; | 868 | ep->rxid = FC_XID_UNKNOWN; |
806 | ep->class = mp->class; | 869 | ep->class = mp->class; |
870 | ep->resp_active = 0; | ||
871 | init_waitqueue_head(&ep->resp_wq); | ||
807 | INIT_DELAYED_WORK(&ep->timeout_work, fc_exch_timeout); | 872 | INIT_DELAYED_WORK(&ep->timeout_work, fc_exch_timeout); |
808 | out: | 873 | out: |
809 | return ep; | 874 | return ep; |
@@ -864,6 +929,8 @@ static struct fc_exch *fc_exch_find(struct fc_exch_mgr *mp, u16 xid) | |||
864 | * fc_exch_done() - Indicate that an exchange/sequence tuple is complete and | 929 | * fc_exch_done() - Indicate that an exchange/sequence tuple is complete and |
865 | * the memory allocated for the related objects may be freed. | 930 | * the memory allocated for the related objects may be freed. |
866 | * @sp: The sequence that has completed | 931 | * @sp: The sequence that has completed |
932 | * | ||
933 | * Note: May sleep if invoked from outside a response handler. | ||
867 | */ | 934 | */ |
868 | static void fc_exch_done(struct fc_seq *sp) | 935 | static void fc_exch_done(struct fc_seq *sp) |
869 | { | 936 | { |
@@ -873,6 +940,8 @@ static void fc_exch_done(struct fc_seq *sp) | |||
873 | spin_lock_bh(&ep->ex_lock); | 940 | spin_lock_bh(&ep->ex_lock); |
874 | rc = fc_exch_done_locked(ep); | 941 | rc = fc_exch_done_locked(ep); |
875 | spin_unlock_bh(&ep->ex_lock); | 942 | spin_unlock_bh(&ep->ex_lock); |
943 | |||
944 | fc_seq_set_resp(sp, NULL, ep->arg); | ||
876 | if (!rc) | 945 | if (!rc) |
877 | fc_exch_delete(ep); | 946 | fc_exch_delete(ep); |
878 | } | 947 | } |
@@ -1436,9 +1505,7 @@ static void fc_exch_recv_req(struct fc_lport *lport, struct fc_exch_mgr *mp, | |||
1436 | * If new exch resp handler is valid then call that | 1505 | * If new exch resp handler is valid then call that |
1437 | * first. | 1506 | * first. |
1438 | */ | 1507 | */ |
1439 | if (ep->resp) | 1508 | if (!fc_invoke_resp(ep, sp, fp)) |
1440 | ep->resp(sp, fp, ep->arg); | ||
1441 | else | ||
1442 | lport->tt.lport_recv(lport, fp); | 1509 | lport->tt.lport_recv(lport, fp); |
1443 | fc_exch_release(ep); /* release from lookup */ | 1510 | fc_exch_release(ep); /* release from lookup */ |
1444 | } else { | 1511 | } else { |
@@ -1462,8 +1529,6 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp) | |||
1462 | struct fc_exch *ep; | 1529 | struct fc_exch *ep; |
1463 | enum fc_sof sof; | 1530 | enum fc_sof sof; |
1464 | u32 f_ctl; | 1531 | u32 f_ctl; |
1465 | void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg); | ||
1466 | void *ex_resp_arg; | ||
1467 | int rc; | 1532 | int rc; |
1468 | 1533 | ||
1469 | ep = fc_exch_find(mp, ntohs(fh->fh_ox_id)); | 1534 | 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) | |||
1506 | 1571 | ||
1507 | if (fc_sof_needs_ack(sof)) | 1572 | if (fc_sof_needs_ack(sof)) |
1508 | fc_seq_send_ack(sp, fp); | 1573 | fc_seq_send_ack(sp, fp); |
1509 | resp = ep->resp; | ||
1510 | ex_resp_arg = ep->arg; | ||
1511 | 1574 | ||
1512 | if (fh->fh_type != FC_TYPE_FCP && fr_eof(fp) == FC_EOF_T && | 1575 | if (fh->fh_type != FC_TYPE_FCP && fr_eof(fp) == FC_EOF_T && |
1513 | (f_ctl & (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) == | 1576 | (f_ctl & (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) == |
1514 | (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) { | 1577 | (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) { |
1515 | spin_lock_bh(&ep->ex_lock); | 1578 | spin_lock_bh(&ep->ex_lock); |
1516 | resp = ep->resp; | ||
1517 | rc = fc_exch_done_locked(ep); | 1579 | rc = fc_exch_done_locked(ep); |
1518 | WARN_ON(fc_seq_exch(sp) != ep); | 1580 | WARN_ON(fc_seq_exch(sp) != ep); |
1519 | spin_unlock_bh(&ep->ex_lock); | 1581 | 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) | |||
1534 | * If new exch resp handler is valid then call that | 1596 | * If new exch resp handler is valid then call that |
1535 | * first. | 1597 | * first. |
1536 | */ | 1598 | */ |
1537 | if (resp) | 1599 | fc_invoke_resp(ep, sp, fp); |
1538 | resp(sp, fp, ex_resp_arg); | 1600 | |
1539 | else | ||
1540 | fc_frame_free(fp); | ||
1541 | fc_exch_release(ep); | 1601 | fc_exch_release(ep); |
1542 | return; | 1602 | return; |
1543 | rel: | 1603 | rel: |
@@ -1576,8 +1636,6 @@ static void fc_exch_recv_resp(struct fc_exch_mgr *mp, struct fc_frame *fp) | |||
1576 | */ | 1636 | */ |
1577 | static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp) | 1637 | static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp) |
1578 | { | 1638 | { |
1579 | void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg); | ||
1580 | void *ex_resp_arg; | ||
1581 | struct fc_frame_header *fh; | 1639 | struct fc_frame_header *fh; |
1582 | struct fc_ba_acc *ap; | 1640 | struct fc_ba_acc *ap; |
1583 | struct fc_seq *sp; | 1641 | struct fc_seq *sp; |
@@ -1622,9 +1680,6 @@ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp) | |||
1622 | break; | 1680 | break; |
1623 | } | 1681 | } |
1624 | 1682 | ||
1625 | resp = ep->resp; | ||
1626 | ex_resp_arg = ep->arg; | ||
1627 | |||
1628 | /* do we need to do some other checks here. Can we reuse more of | 1683 | /* do we need to do some other checks here. Can we reuse more of |
1629 | * fc_exch_recv_seq_resp | 1684 | * fc_exch_recv_seq_resp |
1630 | */ | 1685 | */ |
@@ -1636,17 +1691,14 @@ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp) | |||
1636 | ntoh24(fh->fh_f_ctl) & FC_FC_LAST_SEQ) | 1691 | ntoh24(fh->fh_f_ctl) & FC_FC_LAST_SEQ) |
1637 | rc = fc_exch_done_locked(ep); | 1692 | rc = fc_exch_done_locked(ep); |
1638 | spin_unlock_bh(&ep->ex_lock); | 1693 | spin_unlock_bh(&ep->ex_lock); |
1694 | |||
1695 | fc_exch_hold(ep); | ||
1639 | if (!rc) | 1696 | if (!rc) |
1640 | fc_exch_delete(ep); | 1697 | fc_exch_delete(ep); |
1641 | 1698 | fc_invoke_resp(ep, sp, fp); | |
1642 | if (resp) | ||
1643 | resp(sp, fp, ex_resp_arg); | ||
1644 | else | ||
1645 | fc_frame_free(fp); | ||
1646 | |||
1647 | if (has_rec) | 1699 | if (has_rec) |
1648 | fc_exch_timer_set(ep, ep->r_a_tov); | 1700 | fc_exch_timer_set(ep, ep->r_a_tov); |
1649 | 1701 | fc_exch_release(ep); | |
1650 | } | 1702 | } |
1651 | 1703 | ||
1652 | /** | 1704 | /** |
@@ -1768,32 +1820,33 @@ static void fc_seq_ls_rjt(struct fc_frame *rx_fp, enum fc_els_rjt_reason reason, | |||
1768 | /** | 1820 | /** |
1769 | * fc_exch_reset() - Reset an exchange | 1821 | * fc_exch_reset() - Reset an exchange |
1770 | * @ep: The exchange to be reset | 1822 | * @ep: The exchange to be reset |
1823 | * | ||
1824 | * Note: May sleep if invoked from outside a response handler. | ||
1771 | */ | 1825 | */ |
1772 | static void fc_exch_reset(struct fc_exch *ep) | 1826 | static void fc_exch_reset(struct fc_exch *ep) |
1773 | { | 1827 | { |
1774 | struct fc_seq *sp; | 1828 | struct fc_seq *sp; |
1775 | void (*resp)(struct fc_seq *, struct fc_frame *, void *); | ||
1776 | void *arg; | ||
1777 | int rc = 1; | 1829 | int rc = 1; |
1778 | 1830 | ||
1779 | spin_lock_bh(&ep->ex_lock); | 1831 | spin_lock_bh(&ep->ex_lock); |
1780 | fc_exch_abort_locked(ep, 0); | 1832 | fc_exch_abort_locked(ep, 0); |
1781 | ep->state |= FC_EX_RST_CLEANUP; | 1833 | ep->state |= FC_EX_RST_CLEANUP; |
1782 | fc_exch_timer_cancel(ep); | 1834 | fc_exch_timer_cancel(ep); |
1783 | resp = ep->resp; | ||
1784 | ep->resp = NULL; | ||
1785 | if (ep->esb_stat & ESB_ST_REC_QUAL) | 1835 | if (ep->esb_stat & ESB_ST_REC_QUAL) |
1786 | atomic_dec(&ep->ex_refcnt); /* drop hold for rec_qual */ | 1836 | atomic_dec(&ep->ex_refcnt); /* drop hold for rec_qual */ |
1787 | ep->esb_stat &= ~ESB_ST_REC_QUAL; | 1837 | ep->esb_stat &= ~ESB_ST_REC_QUAL; |
1788 | arg = ep->arg; | ||
1789 | sp = &ep->seq; | 1838 | sp = &ep->seq; |
1790 | rc = fc_exch_done_locked(ep); | 1839 | rc = fc_exch_done_locked(ep); |
1791 | spin_unlock_bh(&ep->ex_lock); | 1840 | spin_unlock_bh(&ep->ex_lock); |
1841 | |||
1842 | fc_exch_hold(ep); | ||
1843 | |||
1792 | if (!rc) | 1844 | if (!rc) |
1793 | fc_exch_delete(ep); | 1845 | fc_exch_delete(ep); |
1794 | 1846 | ||
1795 | if (resp) | 1847 | fc_invoke_resp(ep, sp, ERR_PTR(-FC_EX_CLOSED)); |
1796 | resp(sp, ERR_PTR(-FC_EX_CLOSED), arg); | 1848 | fc_seq_set_resp(sp, NULL, ep->arg); |
1849 | fc_exch_release(ep); | ||
1797 | } | 1850 | } |
1798 | 1851 | ||
1799 | /** | 1852 | /** |