cxgbei: Wait for the final CPL to be received in icl_cxgbei_conn_close.

A socket in the FIN_WAIT_1 state is marked disconnected by
do_close_con_rpl() even though there might still receive data pending.
This is because the socket at that point has set SBS_CANTRCVMORE which
causes the protocol layer to discard any data received before the FIN.
However, icl_cxgbei_conn_close needs to wait until all the data has
been discarded.  Replace the wait for SS_ISDISCONNECTED with instead
waiting for final_cpl_received() to be called.

Reported by:	Jithesh Arakkan @ Chelsio
Sponsored by:	Chelsio Communications
This commit is contained in:
John Baldwin 2021-08-12 08:48:14 -07:00
parent 95941b9636
commit 2eb0e53a6b
3 changed files with 33 additions and 8 deletions

View File

@ -947,6 +947,18 @@ icl_cxgbei_conn_close(struct icl_conn *ic)
icl_cxgbei_pdu_done(ip, ENOTCONN);
}
SOCKBUF_UNLOCK(sb);
/*
* Grab a reference to use when waiting for the final
* CPL to be received. If toep->inp is NULL, then
* final_cpl_received() has already been called (e.g.
* due to the peer sending a RST).
*/
if (toep->inp != NULL) {
toep = hold_toepcb(toep);
toep->flags |= TPF_WAITING_FOR_FINAL;
} else
toep = NULL;
}
INP_WUNLOCK(inp);
@ -959,7 +971,6 @@ icl_cxgbei_conn_close(struct icl_conn *ic)
* queues were purged instead of delivered reliably but soabort isn't
* really general purpose and wouldn't do the right thing here.
*/
soref(so);
soclose(so);
/*
@ -969,12 +980,15 @@ icl_cxgbei_conn_close(struct icl_conn *ic)
* Callers assume that it is safe to free buffers for tasks
* and transfers after this function returns.
*/
SOCK_LOCK(so);
while ((so->so_state & SS_ISDISCONNECTED) == 0)
mtx_sleep(&so->so_timeo, SOCK_MTX(so), PSOCK, "conclo2", 0);
CURVNET_SET(so->so_vnet);
sorele(so);
CURVNET_RESTORE();
if (toep != NULL) {
struct mtx *lock = mtx_pool_find(mtxpool_sleep, toep);
mtx_lock(lock);
while ((toep->flags & TPF_WAITING_FOR_FINAL) != 0)
mtx_sleep(toep, lock, PSOCK, "conclo2", 0);
mtx_unlock(lock);
free_toepcb(toep);
}
}
static void

View File

@ -1008,6 +1008,7 @@ void
final_cpl_received(struct toepcb *toep)
{
struct inpcb *inp = toep->inp;
bool need_wakeup;
KASSERT(inp != NULL, ("%s: inp is NULL", __func__));
INP_WLOCK_ASSERT(inp);
@ -1022,7 +1023,8 @@ final_cpl_received(struct toepcb *toep)
else if (ulp_mode(toep) == ULP_MODE_TLS)
tls_detach(toep);
toep->inp = NULL;
toep->flags &= ~TPF_CPL_PENDING;
need_wakeup = (toep->flags & TPF_WAITING_FOR_FINAL) != 0;
toep->flags &= ~(TPF_CPL_PENDING | TPF_WAITING_FOR_FINAL);
mbufq_drain(&toep->ulp_pduq);
mbufq_drain(&toep->ulp_pdu_reclaimq);
@ -1031,6 +1033,14 @@ final_cpl_received(struct toepcb *toep)
if (!in_pcbrele_wlocked(inp))
INP_WUNLOCK(inp);
if (need_wakeup) {
struct mtx *lock = mtx_pool_find(mtxpool_sleep, toep);
mtx_lock(lock);
wakeup(toep);
mtx_unlock(lock);
}
}
void

View File

@ -76,6 +76,7 @@ enum {
TPF_INITIALIZED = (1 << 12), /* init_toepcb has been called */
TPF_TLS_RECEIVE = (1 << 13), /* should receive TLS records */
TPF_TLS_ESTABLISHED = (1 << 14), /* TLS handshake timer initialized */
TPF_WAITING_FOR_FINAL = (1<< 15), /* waiting for wakeup on final CPL */
};
enum {