IB/hfi1: Remove race conditions in user_sdma send path
Subsystems (3)
PATCH 1/1 IB/hfi1: Remove race conditions in user_sdma send path
From: Michael J. Ruhl <[email protected]>
commit 28a9a9e83ceae2cee25b9af9ad20d53aaa9ab951 upstream
Packet queue state is over used to determine SDMA descriptor availablitity and packet queue request state.
cpu 0 ret = user_sdma_send_pkts(req, pcount); cpu 0 if (atomic_read(&pq->n_reqs)) cpu 1 IRQ user_sdma_txreq_cb calls pq_update() (state to _INACTIVE)
cpu 0 xchg(&pq->state, SDMA_PKT_Q_ACTIVE);
At this point pq->n_reqs == 0 and pq->state is incorrectly SDMA_PKT_Q_ACTIVE. The close path will hang waiting for the state to return to _INACTIVE.
This can also change the state from _DEFERRED to _ACTIVE. However, this is a mostly benign race.
Remove the racy code path.
Use n_reqs to determine if a packet queue is active or not.
Cc <[email protected]> # 4.14.0>
Reviewed-by Mitko Haralanov <[email protected]>
Reviewed-by Mike Marciniszyn <[email protected]>
Signed-off-by Michael J. Ruhl <[email protected]>
--- drivers/infiniband/hw/hfi1/user_sdma.c | 24 ++++++++++-------------- drivers/infiniband/hw/hfi1/user_sdma.h | 9 +++++---- 2 files changed, 15 insertions(+), 18 deletions(-)
drivers/infiniband/hw/hfi1/user_sdma.c +10 -14
@@ -187,7 +187,6 @@ int hfi1_user_sdma_alloc_queues(struct hfi1_ctxtdata *uctxt, pq->ctxt = uctxt->ctxt; pq->subctxt = fd->subctxt; pq->n_max_reqs = hfi1_sdma_comp_ring_size; - pq->state =SDMA_PKT_Q_INACTIVE ;atomic_set (&pq->n_reqs,0 );init_waitqueue_head (&pq->wait);atomic_set (&pq->n_locked,0 );
@@ -276,7 +275,7 @@ int hfi1_user_sdma_free_queues(struct hfi1_filedata *fd,/* Wait until all requests have been freed. */ wait_event_interruptible ( pq->wait, - (ACCESS_ONCE (pq->state) ==SDMA_PKT_Q_INACTIVE )); + !atomic_read (&pq->n_reqs));kfree (pq->reqs);kfree (pq->req_in_use);kmem_cache_destroy (pq->txreq_cache);
@@ -312,6 +311,13 @@ static u8 dlid_to_selector(u16 dlid)return mapping[hash]; } +/** +* hfi1_user_sdma_process_request() - Process and start a user sdma request +* @fd: valid file descriptor +* @iovec: array of io vectors to process +* @dim: overall iovec array size +* @count: number of io vector array entries processed +*/ int hfi1_user_sdma_process_request (struct hfi1_filedata *fd,struct iovec *iovec,unsigned long dim,unsigned long *count)
@@ -560,21 +566,13 @@ int hfi1_user_sdma_process_request(struct hfi1_filedata *fd, req->ahg_idx =sdma_ahg_alloc (req->sde);set_comp_state (pq, cq, info.comp_idx,QUEUED ,0 ); + pq->state =SDMA_PKT_Q_ACTIVE ;/* Send the first N packets in the request to buy us some time */ ret =user_sdma_send_pkts (req, pcount);if (unlikely (ret <0 && ret != -EBUSY )) goto free_req;/* -* It is possible that the SDMA engine would have processed all the -* submitted packets by the time we get here. Therefore, only set -* packet queue state to ACTIVE if there are still uncompleted -* requests. -*/ -if (atomic_read (&pq->n_reqs)) -xchg (&pq->state,SDMA_PKT_Q_ACTIVE ); - -/* * This is a somewhat blocking send implementation. * The driver will block the caller until all packets of the * request have been submitted to the SDMA engine. However, it
@@ -1391,10 +1389,8 @@ static void user_sdma_txreq_cb(struct sdma_txreq *txreq, int status)static inline void pq_update (struct hfi1_user_sdma_pkt_q *pq) { -if (atomic_dec_and_test (&pq->n_reqs)) { -xchg (&pq->state,SDMA_PKT_Q_INACTIVE ); +if (atomic_dec_and_test (&pq->n_reqs))wake_up (&pq->wait); - } }static void user_sdma_free_request (struct user_sdma_request *req,bool unpin)
drivers/infiniband/hw/hfi1/user_sdma.h +5 -4
@@ -94,9 +94,10 @@#define TXREQ_FLAGS_REQ_ACK BIT(0)/* Set the ACK bit in the header */ #define TXREQ_FLAGS_REQ_DISABLE_SH BIT(1)/* Disable header suppression */ -#define SDMA_PKT_Q_INACTIVE BIT(0) -#define SDMA_PKT_Q_ACTIVE BIT(1) -#define SDMA_PKT_Q_DEFERRED BIT(2) +enum pkt_q_sdma_state { +SDMA_PKT_Q_ACTIVE , +SDMA_PKT_Q_DEFERRED , +};/* * Maximum retry attempts to submit a TX request
@@ -124,7 +125,7 @@ struct hfi1_user_sdma_pkt_q {struct user_sdma_request *reqs;unsigned long *req_in_use;struct iowait busy; -unsigned state; +enum pkt_q_sdma_state state;wait_queue_head_t wait;unsigned long unpinned;struct mmu_rb_handler *handler;