cxgbe/tom: don't leak resources tied to an active open request that
cannot be sent to the chip because a prerequisite L2 resolution failed. Submitted by: Hariprasad at chelsio dot com (original version) MFC after: 2 weeks.
This commit is contained in:
parent
52fd30aa97
commit
ddd65a514c
@ -273,6 +273,7 @@ union opcode_tid {
|
||||
|
||||
/* extract the TID from a CPL command */
|
||||
#define GET_TID(cmd) (G_TID(ntohl(OPCODE_TID(cmd))))
|
||||
#define GET_OPCODE(cmd) ((cmd)->ot.opcode)
|
||||
|
||||
/* partitioning of TID fields that also carry a queue id */
|
||||
#define S_TID_TID 0
|
||||
|
@ -115,8 +115,8 @@ do_act_establish(struct sge_iq *iq, const struct rss_header *rss,
|
||||
{
|
||||
struct adapter *sc = iq->adapter;
|
||||
const struct cpl_act_establish *cpl = (const void *)(rss + 1);
|
||||
unsigned int tid = GET_TID(cpl);
|
||||
unsigned int atid = G_TID_TID(ntohl(cpl->tos_atid));
|
||||
u_int tid = GET_TID(cpl);
|
||||
u_int atid = G_TID_TID(ntohl(cpl->tos_atid));
|
||||
struct toepcb *toep = lookup_atid(sc, atid);
|
||||
struct inpcb *inp = toep->inp;
|
||||
|
||||
@ -178,17 +178,34 @@ act_open_rpl_status_to_errno(int status)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
act_open_failure_cleanup(struct adapter *sc, u_int atid, u_int status)
|
||||
{
|
||||
struct toepcb *toep = lookup_atid(sc, atid);
|
||||
struct inpcb *inp = toep->inp;
|
||||
struct toedev *tod = &toep->td->tod;
|
||||
|
||||
free_atid(sc, atid);
|
||||
toep->tid = -1;
|
||||
|
||||
if (status != EAGAIN)
|
||||
INP_INFO_WLOCK(&V_tcbinfo);
|
||||
INP_WLOCK(inp);
|
||||
toe_connect_failed(tod, inp, status);
|
||||
final_cpl_received(toep); /* unlocks inp */
|
||||
if (status != EAGAIN)
|
||||
INP_INFO_WUNLOCK(&V_tcbinfo);
|
||||
}
|
||||
|
||||
static int
|
||||
do_act_open_rpl(struct sge_iq *iq, const struct rss_header *rss,
|
||||
struct mbuf *m)
|
||||
{
|
||||
struct adapter *sc = iq->adapter;
|
||||
const struct cpl_act_open_rpl *cpl = (const void *)(rss + 1);
|
||||
unsigned int atid = G_TID_TID(G_AOPEN_ATID(be32toh(cpl->atid_status)));
|
||||
unsigned int status = G_AOPEN_STATUS(be32toh(cpl->atid_status));
|
||||
u_int atid = G_TID_TID(G_AOPEN_ATID(be32toh(cpl->atid_status)));
|
||||
u_int status = G_AOPEN_STATUS(be32toh(cpl->atid_status));
|
||||
struct toepcb *toep = lookup_atid(sc, atid);
|
||||
struct inpcb *inp = toep->inp;
|
||||
struct toedev *tod = &toep->td->tod;
|
||||
int rc;
|
||||
|
||||
KASSERT(m == NULL, ("%s: wasn't expecting payload", __func__));
|
||||
@ -200,20 +217,11 @@ do_act_open_rpl(struct sge_iq *iq, const struct rss_header *rss,
|
||||
if (negative_advice(status))
|
||||
return (0);
|
||||
|
||||
free_atid(sc, atid);
|
||||
toep->tid = -1;
|
||||
|
||||
if (status && act_open_has_tid(status))
|
||||
release_tid(sc, GET_TID(cpl), toep->ctrlq);
|
||||
|
||||
rc = act_open_rpl_status_to_errno(status);
|
||||
if (rc != EAGAIN)
|
||||
INP_INFO_WLOCK(&V_tcbinfo);
|
||||
INP_WLOCK(inp);
|
||||
toe_connect_failed(tod, inp, rc);
|
||||
final_cpl_received(toep); /* unlocks inp */
|
||||
if (rc != EAGAIN)
|
||||
INP_INFO_WUNLOCK(&V_tcbinfo);
|
||||
act_open_failure_cleanup(sc, atid, rc);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
@ -98,6 +98,7 @@ static void t4_clip_task(void *, int);
|
||||
static void update_clip_table(struct adapter *, struct tom_data *);
|
||||
static void destroy_clip_table(struct adapter *, struct tom_data *);
|
||||
static void free_tom_data(struct adapter *, struct tom_data *);
|
||||
static void reclaim_wr_resources(void *, int);
|
||||
|
||||
static int in6_ifaddr_gen;
|
||||
static eventhandler_tag ifaddr_evhandler;
|
||||
@ -903,6 +904,8 @@ free_tom_data(struct adapter *sc, struct tom_data *td)
|
||||
if (td->listen_mask != 0)
|
||||
hashdestroy(td->listen_hash, M_CXGBE, td->listen_mask);
|
||||
|
||||
if (mtx_initialized(&td->unsent_wr_lock))
|
||||
mtx_destroy(&td->unsent_wr_lock);
|
||||
if (mtx_initialized(&td->lctx_hash_lock))
|
||||
mtx_destroy(&td->lctx_hash_lock);
|
||||
if (mtx_initialized(&td->toep_list_lock))
|
||||
@ -912,6 +915,44 @@ free_tom_data(struct adapter *sc, struct tom_data *td)
|
||||
free(td, M_CXGBE);
|
||||
}
|
||||
|
||||
static void
|
||||
reclaim_wr_resources(void *arg, int count)
|
||||
{
|
||||
struct tom_data *td = arg;
|
||||
STAILQ_HEAD(, wrqe) twr_list = STAILQ_HEAD_INITIALIZER(twr_list);
|
||||
struct cpl_act_open_req *cpl;
|
||||
u_int opcode, atid;
|
||||
struct wrqe *wr;
|
||||
struct adapter *sc;
|
||||
|
||||
mtx_lock(&td->unsent_wr_lock);
|
||||
STAILQ_SWAP(&td->unsent_wr_list, &twr_list, wrqe);
|
||||
mtx_unlock(&td->unsent_wr_lock);
|
||||
|
||||
while ((wr = STAILQ_FIRST(&twr_list)) != NULL) {
|
||||
STAILQ_REMOVE_HEAD(&twr_list, link);
|
||||
|
||||
cpl = wrtod(wr);
|
||||
opcode = GET_OPCODE(cpl);
|
||||
|
||||
switch (opcode) {
|
||||
case CPL_ACT_OPEN_REQ:
|
||||
case CPL_ACT_OPEN_REQ6:
|
||||
atid = G_TID_TID(be32toh(OPCODE_TID(cpl)));
|
||||
sc = td_adapter(td);
|
||||
|
||||
CTR2(KTR_CXGBE, "%s: atid %u ", __func__, atid);
|
||||
act_open_failure_cleanup(sc, atid, EHOSTUNREACH);
|
||||
free(wr, M_CXGBE);
|
||||
break;
|
||||
default:
|
||||
log(LOG_ERR, "%s: leaked work request %p, wr_len %d, "
|
||||
"opcode %x\n", __func__, wr, wr->wr_len, opcode);
|
||||
/* WR not freed here; go look at it with a debugger. */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ground control to Major TOM
|
||||
* Commencing countdown, engines on
|
||||
@ -939,6 +980,11 @@ t4_tom_activate(struct adapter *sc)
|
||||
td->listen_hash = hashinit_flags(LISTEN_HASH_SIZE, M_CXGBE,
|
||||
&td->listen_mask, HASH_NOWAIT);
|
||||
|
||||
/* List of WRs for which L2 resolution failed */
|
||||
mtx_init(&td->unsent_wr_lock, "Unsent WR list lock", NULL, MTX_DEF);
|
||||
STAILQ_INIT(&td->unsent_wr_list);
|
||||
TASK_INIT(&td->reclaim_wr_resources, 0, reclaim_wr_resources, td);
|
||||
|
||||
/* TID tables */
|
||||
rc = alloc_tid_tabs(&sc->tids);
|
||||
if (rc != 0)
|
||||
@ -1012,6 +1058,12 @@ t4_tom_deactivate(struct adapter *sc)
|
||||
rc = EBUSY;
|
||||
mtx_unlock(&td->lctx_hash_lock);
|
||||
|
||||
taskqueue_drain(taskqueue_thread, &td->reclaim_wr_resources);
|
||||
mtx_lock(&td->unsent_wr_lock);
|
||||
if (!STAILQ_EMPTY(&td->unsent_wr_list))
|
||||
rc = EBUSY;
|
||||
mtx_unlock(&td->unsent_wr_lock);
|
||||
|
||||
if (rc == 0) {
|
||||
unregister_toedev(sc->tom_softc);
|
||||
free_tom_data(sc, td);
|
||||
|
@ -210,6 +210,11 @@ struct tom_data {
|
||||
struct mtx clip_table_lock;
|
||||
struct clip_head clip_table;
|
||||
int clip_gen;
|
||||
|
||||
/* WRs that will not be sent to the chip because L2 resolution failed */
|
||||
struct mtx unsent_wr_lock;
|
||||
STAILQ_HEAD(, wrqe) unsent_wr_list;
|
||||
struct task reclaim_wr_resources;
|
||||
};
|
||||
|
||||
static inline struct tom_data *
|
||||
@ -252,6 +257,7 @@ void release_lip(struct tom_data *, struct clip_entry *);
|
||||
void t4_init_connect_cpl_handlers(struct adapter *);
|
||||
int t4_connect(struct toedev *, struct socket *, struct rtentry *,
|
||||
struct sockaddr *);
|
||||
void act_open_failure_cleanup(struct adapter *, u_int, u_int);
|
||||
|
||||
/* t4_listen.c */
|
||||
void t4_init_listen_cpl_handlers(struct adapter *);
|
||||
|
@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/rwlock.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sbuf.h>
|
||||
#include <sys/taskqueue.h>
|
||||
#include <net/if.h>
|
||||
#include <net/if_types.h>
|
||||
#include <net/ethernet.h>
|
||||
@ -161,25 +162,17 @@ send_pending(struct adapter *sc, struct l2t_entry *e)
|
||||
}
|
||||
|
||||
static void
|
||||
resolution_failed_for_wr(struct wrqe *wr)
|
||||
resolution_failed(struct adapter *sc, struct l2t_entry *e)
|
||||
{
|
||||
log(LOG_ERR, "%s: leaked work request %p, wr_len %d\n", __func__, wr,
|
||||
wr->wr_len);
|
||||
|
||||
/* free(wr, M_CXGBE); */
|
||||
}
|
||||
|
||||
static void
|
||||
resolution_failed(struct l2t_entry *e)
|
||||
{
|
||||
struct wrqe *wr;
|
||||
struct tom_data *td = sc->tom_softc;
|
||||
|
||||
mtx_assert(&e->lock, MA_OWNED);
|
||||
|
||||
while ((wr = STAILQ_FIRST(&e->wr_list)) != NULL) {
|
||||
STAILQ_REMOVE_HEAD(&e->wr_list, link);
|
||||
resolution_failed_for_wr(wr);
|
||||
}
|
||||
mtx_lock(&td->unsent_wr_lock);
|
||||
STAILQ_CONCAT(&td->unsent_wr_list, &e->wr_list);
|
||||
mtx_unlock(&td->unsent_wr_lock);
|
||||
|
||||
taskqueue_enqueue(taskqueue_thread, &td->reclaim_wr_resources);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -203,7 +196,7 @@ update_entry(struct adapter *sc, struct l2t_entry *e, uint8_t *lladdr,
|
||||
* need to wlock the table).
|
||||
*/
|
||||
e->state = L2T_STATE_FAILED;
|
||||
resolution_failed(e);
|
||||
resolution_failed(sc, e);
|
||||
return;
|
||||
|
||||
} else if (lladdr == NULL) {
|
||||
@ -305,12 +298,11 @@ t4_l2t_send_slow(struct adapter *sc, struct wrqe *wr, struct l2t_entry *e)
|
||||
if (e->state == L2T_STATE_VALID && !STAILQ_EMPTY(&e->wr_list))
|
||||
send_pending(sc, e);
|
||||
if (e->state == L2T_STATE_FAILED)
|
||||
resolution_failed(e);
|
||||
resolution_failed(sc, e);
|
||||
mtx_unlock(&e->lock);
|
||||
break;
|
||||
|
||||
case L2T_STATE_FAILED:
|
||||
resolution_failed_for_wr(wr);
|
||||
return (EHOSTUNREACH);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user