IB/hfi1: Remove race conditions in user_sdma send path
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: <stable@vger.kernel.org> # 4.9.0 Reviewed-by: Mitko Haralanov <mitko.haralanov@intel.com> Reviewed-by: Mike Marciniszyn <mike.marciniszyn@intel.com> Signed-off-by: Michael J. Ruhl <michael.j.ruhl@intel.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
0715895a55
commit
d395117fac
@ -151,10 +151,6 @@ MODULE_PARM_DESC(sdma_comp_size, "Size of User SDMA completion ring. Default: 12
|
||||
#define SDMA_REQ_HAVE_AHG 1
|
||||
#define SDMA_REQ_HAS_ERROR 2
|
||||
|
||||
#define SDMA_PKT_Q_INACTIVE BIT(0)
|
||||
#define SDMA_PKT_Q_ACTIVE BIT(1)
|
||||
#define SDMA_PKT_Q_DEFERRED BIT(2)
|
||||
|
||||
/*
|
||||
* Maximum retry attempts to submit a TX request
|
||||
* before putting the process to sleep.
|
||||
@ -408,7 +404,6 @@ int hfi1_user_sdma_alloc_queues(struct hfi1_ctxtdata *uctxt, struct file *fp)
|
||||
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);
|
||||
@ -491,7 +486,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);
|
||||
@ -527,6 +522,13 @@ static u8 dlid_to_selector(u16 dlid)
|
||||
return mapping[hash];
|
||||
}
|
||||
|
||||
/**
|
||||
* hfi1_user_sdma_process_request() - Process and start a user sdma request
|
||||
* @fp: valid file pointer
|
||||
* @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 file *fp, struct iovec *iovec,
|
||||
unsigned long dim, unsigned long *count)
|
||||
{
|
||||
@ -768,20 +770,12 @@ int hfi1_user_sdma_process_request(struct file *fp, struct iovec *iovec,
|
||||
}
|
||||
|
||||
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
|
||||
@ -1526,10 +1520,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)
|
||||
|
@ -53,6 +53,11 @@
|
||||
|
||||
extern uint extended_psn;
|
||||
|
||||
enum pkt_q_sdma_state {
|
||||
SDMA_PKT_Q_ACTIVE,
|
||||
SDMA_PKT_Q_DEFERRED,
|
||||
};
|
||||
|
||||
struct hfi1_user_sdma_pkt_q {
|
||||
struct list_head list;
|
||||
unsigned ctxt;
|
||||
@ -65,7 +70,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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user