diff options
author | Steve Wise <swise@opengridcomputing.com> | 2017-09-26 22:13:17 +0200 |
---|---|---|
committer | Doug Ledford <dledford@redhat.com> | 2017-09-29 17:46:41 +0200 |
commit | 2015f26cfadec126265fabfbb0e6566e2cca94b4 (patch) | |
tree | caa86cf55d8624e5a7633eadb6e4e9da7f0ea372 /drivers/infiniband/hw/cxgb4/cm.c | |
parent | iw_cxgb4: allocate wait object for each ep object (diff) | |
download | linux-2015f26cfadec126265fabfbb0e6566e2cca94b4.tar.xz linux-2015f26cfadec126265fabfbb0e6566e2cca94b4.zip |
iw_cxgb4: add referencing to wait objects
For messages sent from the host to fw that solicit a reply from fw,
the c4iw_wr_wait struct pointer is passed in the host->fw message, and
included in the fw->host fw6_msg reply. This allows the sender to wait
until the reply is received, and the code processing the ingress reply
to wake up the sender.
If c4iw_wait_for_reply() times out, however, we need to keep the
c4iw_wr_wait object around in case the reply eventually does arrive.
Otherwise we have touch-after-free bugs in the wake_up paths.
This was hit due to a bad kernel driver that blocked ingress processing
of cxgb4 for a long time, causing iw_cxgb4 timeouts, but eventually
resuming ingress processing and thus hitting the touch-after-free bug.
So I want to fix iw_cxgb4 such that we'll at least keep the wait object
around until the reply comes. If it never comes we leak a small amount of
memory, but if it does come late, we won't potentially crash the system.
So add a kref struct in the c4iw_wr_wait struct, and take a reference
before sending a message to FW that will generate a FW6 reply. And remove
the reference (and potentially free the wait object) when the reply
is processed.
The ep code also uses the wr_wait for non FW6 CPL messages and doesn't
embed the c4iw_wr_wait object in the message sent to firmware. So for
those cases we add c4iw_wake_up_noref().
The mr/mw, cq, and qp object create/destroy paths do need this reference
logic. For these paths, c4iw_ref_send_wait() is introduced to take the
wr_wait reference, send the msg to fw, and then wait for the reply.
So going forward, iw_cxgb4 either uses c4iw_ofld_send(),
c4iw_wait_for_reply() and c4iw_wake_up_noref() like is done in the some
of the endpoint logic, or c4iw_ref_send_wait() and c4iw_wake_up_deref()
(formerly c4iw_wake_up()) when sending messages with the c4iw_wr_wait
object pointer embedded in the message and resulting FW6 reply.
Signed-off-by: Steve Wise <swise@opengridcomputing.com>
Signed-off-by: Doug Ledford <dledford@redhat.com>
Diffstat (limited to 'drivers/infiniband/hw/cxgb4/cm.c')
-rw-r--r-- | drivers/infiniband/hw/cxgb4/cm.c | 20 |
1 files changed, 10 insertions, 10 deletions
diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 72a2d349c7fa..e395afcd7d33 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -318,7 +318,7 @@ static void *alloc_ep(int size, gfp_t gfp) epc = kzalloc(size, gfp); if (epc) { - epc->wr_waitp = kzalloc(sizeof(*epc->wr_waitp), gfp); + epc->wr_waitp = c4iw_alloc_wr_wait(gfp); if (!epc->wr_waitp) { kfree(epc); epc = NULL; @@ -414,7 +414,7 @@ void _c4iw_free_ep(struct kref *kref) } if (!skb_queue_empty(&ep->com.ep_skb_list)) skb_queue_purge(&ep->com.ep_skb_list); - kfree(ep->com.wr_waitp); + c4iw_put_wr_wait(ep->com.wr_waitp); kfree(ep); } @@ -1880,7 +1880,7 @@ static int abort_rpl(struct c4iw_dev *dev, struct sk_buff *skb) mutex_lock(&ep->com.mutex); switch (ep->com.state) { case ABORTING: - c4iw_wake_up(ep->com.wr_waitp, -ECONNRESET); + c4iw_wake_up_noref(ep->com.wr_waitp, -ECONNRESET); __state_set(&ep->com, DEAD); release = 1; break; @@ -2327,7 +2327,7 @@ static int pass_open_rpl(struct c4iw_dev *dev, struct sk_buff *skb) } pr_debug("ep %p status %d error %d\n", ep, rpl->status, status2errno(rpl->status)); - c4iw_wake_up(ep->com.wr_waitp, status2errno(rpl->status)); + c4iw_wake_up_noref(ep->com.wr_waitp, status2errno(rpl->status)); c4iw_put_ep(&ep->com); out: return 0; @@ -2344,7 +2344,7 @@ static int close_listsrv_rpl(struct c4iw_dev *dev, struct sk_buff *skb) goto out; } pr_debug("ep %p\n", ep); - c4iw_wake_up(ep->com.wr_waitp, status2errno(rpl->status)); + c4iw_wake_up_noref(ep->com.wr_waitp, status2errno(rpl->status)); c4iw_put_ep(&ep->com); out: return 0; @@ -2679,12 +2679,12 @@ static int peer_close(struct c4iw_dev *dev, struct sk_buff *skb) */ __state_set(&ep->com, CLOSING); pr_debug("waking up ep %p tid %u\n", ep, ep->hwtid); - c4iw_wake_up(ep->com.wr_waitp, -ECONNRESET); + c4iw_wake_up_noref(ep->com.wr_waitp, -ECONNRESET); break; case MPA_REP_SENT: __state_set(&ep->com, CLOSING); pr_debug("waking up ep %p tid %u\n", ep, ep->hwtid); - c4iw_wake_up(ep->com.wr_waitp, -ECONNRESET); + c4iw_wake_up_noref(ep->com.wr_waitp, -ECONNRESET); break; case FPDU_MODE: start_ep_timer(ep); @@ -2766,7 +2766,7 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb) * MPA_REQ_SENT */ if (ep->com.state != MPA_REQ_SENT) - c4iw_wake_up(ep->com.wr_waitp, -ECONNRESET); + c4iw_wake_up_noref(ep->com.wr_waitp, -ECONNRESET); mutex_lock(&ep->com.mutex); switch (ep->com.state) { @@ -4187,7 +4187,7 @@ static int fw6_msg(struct c4iw_dev *dev, struct sk_buff *skb) wr_waitp = (struct c4iw_wr_wait *)(__force unsigned long) rpl->data[1]; pr_debug("wr_waitp %p ret %u\n", wr_waitp, ret); if (wr_waitp) - c4iw_wake_up(wr_waitp, ret ? -ret : 0); + c4iw_wake_up_deref(wr_waitp, ret ? -ret : 0); kfree_skb(skb); break; case FW6_TYPE_CQE: @@ -4224,7 +4224,7 @@ static int peer_abort_intr(struct c4iw_dev *dev, struct sk_buff *skb) } pr_debug("ep %p tid %u state %u\n", ep, ep->hwtid, ep->com.state); - c4iw_wake_up(ep->com.wr_waitp, -ECONNRESET); + c4iw_wake_up_noref(ep->com.wr_waitp, -ECONNRESET); out: sched(dev, skb); return 0; |