Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

scsi: libfc: Avoid invoking response handler twice if ep is already completed

A race condition exists between the response handler getting called because
of exchange_mgr_reset() (which clears out all the active XIDs) and the
response we get via an interrupt.

Sequence of events:

rport ba0200: Port timeout, state PLOGI
rport ba0200: Port entered PLOGI state from PLOGI state
xid 1052: Exchange timer armed : 20000 msecs  xid timer armed here
rport ba0200: Received LOGO request while in state PLOGI
rport ba0200: Delete port
rport ba0200: work event 3
rport ba0200: lld callback ev 3
bnx2fc: rport_event_hdlr: event = 3, port_id = 0xba0200
bnx2fc: ba0200 - rport not created Yet!!
/* Here we reset any outstanding exchanges before
freeing rport using the exch_mgr_reset() */
xid 1052: Exchange timer canceled
/* Here we got two responses for one xid */
xid 1052: invoking resp(), esb 20000000 state 3
xid 1052: invoking resp(), esb 20000000 state 3
xid 1052: fc_rport_plogi_resp() : ep->resp_active 2
xid 1052: fc_rport_plogi_resp() : ep->resp_active 2

Skip the response if the exchange is already completed.

Link: https://lore.kernel.org/r/20201215194731.2326-1-jhasan@marvell.com
Signed-off-by: Javed Hasan <jhasan@marvell.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>

authored by

Javed Hasan and committed by
Martin K. Petersen
b2b0f16f 72eeb7c7

+14 -2
+14 -2
drivers/scsi/libfc/fc_exch.c
··· 1623 1623 rc = fc_exch_done_locked(ep); 1624 1624 WARN_ON(fc_seq_exch(sp) != ep); 1625 1625 spin_unlock_bh(&ep->ex_lock); 1626 - if (!rc) 1626 + if (!rc) { 1627 1627 fc_exch_delete(ep); 1628 + } else { 1629 + FC_EXCH_DBG(ep, "ep is completed already," 1630 + "hence skip calling the resp\n"); 1631 + goto skip_resp; 1632 + } 1628 1633 } 1629 1634 1630 1635 /* ··· 1648 1643 if (!fc_invoke_resp(ep, sp, fp)) 1649 1644 fc_frame_free(fp); 1650 1645 1646 + skip_resp: 1651 1647 fc_exch_release(ep); 1652 1648 return; 1653 1649 rel: ··· 1905 1899 1906 1900 fc_exch_hold(ep); 1907 1901 1908 - if (!rc) 1902 + if (!rc) { 1909 1903 fc_exch_delete(ep); 1904 + } else { 1905 + FC_EXCH_DBG(ep, "ep is completed already," 1906 + "hence skip calling the resp\n"); 1907 + goto skip_resp; 1908 + } 1910 1909 1911 1910 fc_invoke_resp(ep, sp, ERR_PTR(-FC_EX_CLOSED)); 1911 + skip_resp: 1912 1912 fc_seq_set_resp(sp, NULL, ep->arg); 1913 1913 fc_exch_release(ep); 1914 1914 }