163fa15dbSBob Pearson // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
28700e3e7SMoni Shoua /*
38700e3e7SMoni Shoua * Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
48700e3e7SMoni Shoua * Copyright (c) 2015 System Fabric Works, Inc. All rights reserved.
58700e3e7SMoni Shoua */
68700e3e7SMoni Shoua
7eff6d998SBob Pearson #include "rxe.h"
88700e3e7SMoni Shoua
99b4b7c1fSBob Pearson static struct workqueue_struct *rxe_wq;
109b4b7c1fSBob Pearson
rxe_alloc_wq(void)119b4b7c1fSBob Pearson int rxe_alloc_wq(void)
129b4b7c1fSBob Pearson {
139b4b7c1fSBob Pearson rxe_wq = alloc_workqueue("rxe_wq", WQ_UNBOUND, WQ_MAX_ACTIVE);
149b4b7c1fSBob Pearson if (!rxe_wq)
159b4b7c1fSBob Pearson return -ENOMEM;
169b4b7c1fSBob Pearson
179b4b7c1fSBob Pearson return 0;
189b4b7c1fSBob Pearson }
199b4b7c1fSBob Pearson
rxe_destroy_wq(void)209b4b7c1fSBob Pearson void rxe_destroy_wq(void)
219b4b7c1fSBob Pearson {
229b4b7c1fSBob Pearson destroy_workqueue(rxe_wq);
239b4b7c1fSBob Pearson }
249b4b7c1fSBob Pearson
25d9467163SBob Pearson /* Check if task is idle i.e. not running, not scheduled in
269b4b7c1fSBob Pearson * work queue and not draining. If so move to busy to
27d9467163SBob Pearson * reserve a slot in do_task() by setting to busy and taking
28d9467163SBob Pearson * a qp reference to cover the gap from now until the task finishes.
29d9467163SBob Pearson * state will move out of busy if task returns a non zero value
30d9467163SBob Pearson * in do_task(). If state is already busy it is raised to armed
31d9467163SBob Pearson * to indicate to do_task that additional pass should be made
32d9467163SBob Pearson * over the task.
33d9467163SBob Pearson * Context: caller should hold task->lock.
34d9467163SBob Pearson * Returns: true if state transitioned from idle to busy else false.
35d9467163SBob Pearson */
__reserve_if_idle(struct rxe_task * task)36d9467163SBob Pearson static bool __reserve_if_idle(struct rxe_task *task)
37d9467163SBob Pearson {
38d9467163SBob Pearson WARN_ON(rxe_read(task->qp) <= 0);
39d9467163SBob Pearson
40d9467163SBob Pearson if (task->state == TASK_STATE_IDLE) {
41d9467163SBob Pearson rxe_get(task->qp);
42d9467163SBob Pearson task->state = TASK_STATE_BUSY;
43d9467163SBob Pearson task->num_sched++;
44d9467163SBob Pearson return true;
45d9467163SBob Pearson }
46d9467163SBob Pearson
47d9467163SBob Pearson if (task->state == TASK_STATE_BUSY)
48d9467163SBob Pearson task->state = TASK_STATE_ARMED;
49d9467163SBob Pearson
50d9467163SBob Pearson return false;
51d9467163SBob Pearson }
52d9467163SBob Pearson
53d9467163SBob Pearson /* check if task is idle or drained and not currently
549b4b7c1fSBob Pearson * scheduled in the work queue. This routine is
55d9467163SBob Pearson * called by rxe_cleanup_task or rxe_disable_task to
56d9467163SBob Pearson * see if the queue is empty.
57d9467163SBob Pearson * Context: caller should hold task->lock.
58d9467163SBob Pearson * Returns true if done else false.
59d9467163SBob Pearson */
__is_done(struct rxe_task * task)60d9467163SBob Pearson static bool __is_done(struct rxe_task *task)
61d9467163SBob Pearson {
629b4b7c1fSBob Pearson if (work_pending(&task->work))
63d9467163SBob Pearson return false;
64d9467163SBob Pearson
65d9467163SBob Pearson if (task->state == TASK_STATE_IDLE ||
66d9467163SBob Pearson task->state == TASK_STATE_DRAINED) {
67d9467163SBob Pearson return true;
68d9467163SBob Pearson }
69d9467163SBob Pearson
70d9467163SBob Pearson return false;
71d9467163SBob Pearson }
72d9467163SBob Pearson
73d9467163SBob Pearson /* a locked version of __is_done */
is_done(struct rxe_task * task)74d9467163SBob Pearson static bool is_done(struct rxe_task *task)
75d9467163SBob Pearson {
76d9467163SBob Pearson unsigned long flags;
77d9467163SBob Pearson int done;
78d9467163SBob Pearson
79d9467163SBob Pearson spin_lock_irqsave(&task->lock, flags);
80d9467163SBob Pearson done = __is_done(task);
81d9467163SBob Pearson spin_unlock_irqrestore(&task->lock, flags);
82d9467163SBob Pearson
83d9467163SBob Pearson return done;
84d9467163SBob Pearson }
85d9467163SBob Pearson
86d9467163SBob Pearson /* do_task is a wrapper for the three tasks (requester,
87d9467163SBob Pearson * completer, responder) and calls them in a loop until
88d9467163SBob Pearson * they return a non-zero value. It is called either
89d9467163SBob Pearson * directly by rxe_run_task or indirectly if rxe_sched_task
90d9467163SBob Pearson * schedules the task. They must call __reserve_if_idle to
91d9467163SBob Pearson * move the task to busy before calling or scheduling.
92d9467163SBob Pearson * The task can also be moved to drained or invalid
939b4b7c1fSBob Pearson * by calls to rxe_cleanup_task or rxe_disable_task.
94d9467163SBob Pearson * In that case tasks which get here are not executed but
95d9467163SBob Pearson * just flushed. The tasks are designed to look to see if
969b4b7c1fSBob Pearson * there is work to do and then do part of it before returning
97d9467163SBob Pearson * here with a return value of zero until all the work
989b4b7c1fSBob Pearson * has been consumed then it returns a non-zero value.
99d9467163SBob Pearson * The number of times the task can be run is limited by
100d9467163SBob Pearson * max iterations so one task cannot hold the cpu forever.
1019b4b7c1fSBob Pearson * If the limit is hit and work remains the task is rescheduled.
1028700e3e7SMoni Shoua */
do_task(struct rxe_task * task)1039b4b7c1fSBob Pearson static void do_task(struct rxe_task *task)
1048700e3e7SMoni Shoua {
105d9467163SBob Pearson unsigned int iterations;
106d9467163SBob Pearson unsigned long flags;
107d9467163SBob Pearson int resched = 0;
1089b4b7c1fSBob Pearson int cont;
1099b4b7c1fSBob Pearson int ret;
1108700e3e7SMoni Shoua
111d9467163SBob Pearson WARN_ON(rxe_read(task->qp) <= 0);
1128700e3e7SMoni Shoua
113d9467163SBob Pearson spin_lock_irqsave(&task->lock, flags);
114d9467163SBob Pearson if (task->state >= TASK_STATE_DRAINED) {
115d9467163SBob Pearson rxe_put(task->qp);
116d9467163SBob Pearson task->num_done++;
117d9467163SBob Pearson spin_unlock_irqrestore(&task->lock, flags);
1188700e3e7SMoni Shoua return;
1198700e3e7SMoni Shoua }
120d9467163SBob Pearson spin_unlock_irqrestore(&task->lock, flags);
1218700e3e7SMoni Shoua
1228700e3e7SMoni Shoua do {
123d9467163SBob Pearson iterations = RXE_MAX_ITERATIONS;
1248700e3e7SMoni Shoua cont = 0;
1258700e3e7SMoni Shoua
126d9467163SBob Pearson do {
127d9467163SBob Pearson ret = task->func(task->qp);
128d9467163SBob Pearson } while (ret == 0 && iterations-- > 0);
129d9467163SBob Pearson
130d9467163SBob Pearson spin_lock_irqsave(&task->lock, flags);
1319b4b7c1fSBob Pearson /* we're not done yet but we ran out of iterations.
1329b4b7c1fSBob Pearson * yield the cpu and reschedule the task
133eff6d998SBob Pearson */
1349b4b7c1fSBob Pearson if (!ret) {
135d9467163SBob Pearson task->state = TASK_STATE_IDLE;
136d9467163SBob Pearson resched = 1;
1379b4b7c1fSBob Pearson goto exit;
138eff6d998SBob Pearson }
1399b4b7c1fSBob Pearson
1409b4b7c1fSBob Pearson switch (task->state) {
1419b4b7c1fSBob Pearson case TASK_STATE_BUSY:
1429b4b7c1fSBob Pearson task->state = TASK_STATE_IDLE;
1438700e3e7SMoni Shoua break;
1448700e3e7SMoni Shoua
1459b4b7c1fSBob Pearson /* someone tried to schedule the task while we
1469b4b7c1fSBob Pearson * were running, keep going
1478700e3e7SMoni Shoua */
1488700e3e7SMoni Shoua case TASK_STATE_ARMED:
1498700e3e7SMoni Shoua task->state = TASK_STATE_BUSY;
1508700e3e7SMoni Shoua cont = 1;
1518700e3e7SMoni Shoua break;
1528700e3e7SMoni Shoua
153d9467163SBob Pearson case TASK_STATE_DRAINING:
154d9467163SBob Pearson task->state = TASK_STATE_DRAINED;
155d9467163SBob Pearson break;
156d9467163SBob Pearson
1578700e3e7SMoni Shoua default:
158d9467163SBob Pearson WARN_ON(1);
159*5dfd5a88SLi Zhijian rxe_dbg_qp(task->qp, "unexpected task state = %d\n",
1609b4b7c1fSBob Pearson task->state);
1619b4b7c1fSBob Pearson task->state = TASK_STATE_IDLE;
1628700e3e7SMoni Shoua }
163d9467163SBob Pearson
1649b4b7c1fSBob Pearson exit:
165d9467163SBob Pearson if (!cont) {
166d9467163SBob Pearson task->num_done++;
167d9467163SBob Pearson if (WARN_ON(task->num_done != task->num_sched))
1689b4b7c1fSBob Pearson rxe_dbg_qp(
1699b4b7c1fSBob Pearson task->qp,
170*5dfd5a88SLi Zhijian "%ld tasks scheduled, %ld tasks done\n",
171d9467163SBob Pearson task->num_sched, task->num_done);
172d9467163SBob Pearson }
173d9467163SBob Pearson spin_unlock_irqrestore(&task->lock, flags);
1748700e3e7SMoni Shoua } while (cont);
1758700e3e7SMoni Shoua
1768700e3e7SMoni Shoua task->ret = ret;
177d9467163SBob Pearson
178d9467163SBob Pearson if (resched)
179d9467163SBob Pearson rxe_sched_task(task);
180d9467163SBob Pearson
181d9467163SBob Pearson rxe_put(task->qp);
1828700e3e7SMoni Shoua }
1838700e3e7SMoni Shoua
1849b4b7c1fSBob Pearson /* wrapper around do_task to fix argument for work queue */
do_work(struct work_struct * work)1859b4b7c1fSBob Pearson static void do_work(struct work_struct *work)
1869b4b7c1fSBob Pearson {
1879b4b7c1fSBob Pearson do_task(container_of(work, struct rxe_task, work));
1889b4b7c1fSBob Pearson }
1899b4b7c1fSBob Pearson
rxe_init_task(struct rxe_task * task,struct rxe_qp * qp,int (* func)(struct rxe_qp *))1903946fc2aSBob Pearson int rxe_init_task(struct rxe_task *task, struct rxe_qp *qp,
1913946fc2aSBob Pearson int (*func)(struct rxe_qp *))
1928700e3e7SMoni Shoua {
193d9467163SBob Pearson WARN_ON(rxe_read(qp) <= 0);
194d9467163SBob Pearson
1953946fc2aSBob Pearson task->qp = qp;
1968700e3e7SMoni Shoua task->func = func;
197d9467163SBob Pearson task->state = TASK_STATE_IDLE;
19863a18baeSBob Pearson spin_lock_init(&task->lock);
1999b4b7c1fSBob Pearson INIT_WORK(&task->work, do_work);
2008700e3e7SMoni Shoua
2018700e3e7SMoni Shoua return 0;
2028700e3e7SMoni Shoua }
2038700e3e7SMoni Shoua
204d9467163SBob Pearson /* rxe_cleanup_task is only called from rxe_do_qp_cleanup in
205d9467163SBob Pearson * process context. The qp is already completed with no
206d9467163SBob Pearson * remaining references. Once the queue is drained the
207d9467163SBob Pearson * task is moved to invalid and returns. The qp cleanup
208d9467163SBob Pearson * code then calls the task functions directly without
209d9467163SBob Pearson * using the task struct to drain any late arriving packets
210d9467163SBob Pearson * or work requests.
211d9467163SBob Pearson */
rxe_cleanup_task(struct rxe_task * task)2128700e3e7SMoni Shoua void rxe_cleanup_task(struct rxe_task *task)
2138700e3e7SMoni Shoua {
214d9467163SBob Pearson unsigned long flags;
21507bf9627SAndrew Boyer
216d9467163SBob Pearson spin_lock_irqsave(&task->lock, flags);
217d9467163SBob Pearson if (!__is_done(task) && task->state < TASK_STATE_DRAINED) {
218d9467163SBob Pearson task->state = TASK_STATE_DRAINING;
219d9467163SBob Pearson } else {
220d9467163SBob Pearson task->state = TASK_STATE_INVALID;
221d9467163SBob Pearson spin_unlock_irqrestore(&task->lock, flags);
222d9467163SBob Pearson return;
223d9467163SBob Pearson }
224d9467163SBob Pearson spin_unlock_irqrestore(&task->lock, flags);
225d9467163SBob Pearson
226d9467163SBob Pearson /* now the task cannot be scheduled or run just wait
227d9467163SBob Pearson * for the previously scheduled tasks to finish.
22807bf9627SAndrew Boyer */
229d9467163SBob Pearson while (!is_done(task))
230d9467163SBob Pearson cond_resched();
23107bf9627SAndrew Boyer
232d9467163SBob Pearson spin_lock_irqsave(&task->lock, flags);
233d9467163SBob Pearson task->state = TASK_STATE_INVALID;
234d9467163SBob Pearson spin_unlock_irqrestore(&task->lock, flags);
2358700e3e7SMoni Shoua }
2368700e3e7SMoni Shoua
237d9467163SBob Pearson /* run the task inline if it is currently idle
238d9467163SBob Pearson * cannot call do_task holding the lock
239d9467163SBob Pearson */
rxe_run_task(struct rxe_task * task)240dccb23f6SBob Pearson void rxe_run_task(struct rxe_task *task)
2418700e3e7SMoni Shoua {
242d9467163SBob Pearson unsigned long flags;
2439b4b7c1fSBob Pearson bool run;
24407bf9627SAndrew Boyer
245d9467163SBob Pearson WARN_ON(rxe_read(task->qp) <= 0);
246d9467163SBob Pearson
247d9467163SBob Pearson spin_lock_irqsave(&task->lock, flags);
248d9467163SBob Pearson run = __reserve_if_idle(task);
249d9467163SBob Pearson spin_unlock_irqrestore(&task->lock, flags);
250d9467163SBob Pearson
251d9467163SBob Pearson if (run)
2529b4b7c1fSBob Pearson do_task(task);
2538700e3e7SMoni Shoua }
2548700e3e7SMoni Shoua
2559b4b7c1fSBob Pearson /* schedule the task to run later as a work queue entry.
2569b4b7c1fSBob Pearson * the queue_work call can be called holding
257d9467163SBob Pearson * the lock.
258d9467163SBob Pearson */
rxe_sched_task(struct rxe_task * task)259dccb23f6SBob Pearson void rxe_sched_task(struct rxe_task *task)
260dccb23f6SBob Pearson {
261d9467163SBob Pearson unsigned long flags;
262dccb23f6SBob Pearson
263d9467163SBob Pearson WARN_ON(rxe_read(task->qp) <= 0);
264d9467163SBob Pearson
265d9467163SBob Pearson spin_lock_irqsave(&task->lock, flags);
266d9467163SBob Pearson if (__reserve_if_idle(task))
2679b4b7c1fSBob Pearson queue_work(rxe_wq, &task->work);
268d9467163SBob Pearson spin_unlock_irqrestore(&task->lock, flags);
269dccb23f6SBob Pearson }
270dccb23f6SBob Pearson
271d9467163SBob Pearson /* rxe_disable/enable_task are only called from
272d9467163SBob Pearson * rxe_modify_qp in process context. Task is moved
273d9467163SBob Pearson * to the drained state by do_task.
274d9467163SBob Pearson */
rxe_disable_task(struct rxe_task * task)2758700e3e7SMoni Shoua void rxe_disable_task(struct rxe_task *task)
2768700e3e7SMoni Shoua {
277d9467163SBob Pearson unsigned long flags;
278d9467163SBob Pearson
279d9467163SBob Pearson WARN_ON(rxe_read(task->qp) <= 0);
280d9467163SBob Pearson
281d9467163SBob Pearson spin_lock_irqsave(&task->lock, flags);
282d9467163SBob Pearson if (!__is_done(task) && task->state < TASK_STATE_DRAINED) {
283d9467163SBob Pearson task->state = TASK_STATE_DRAINING;
284d9467163SBob Pearson } else {
285d9467163SBob Pearson task->state = TASK_STATE_DRAINED;
286d9467163SBob Pearson spin_unlock_irqrestore(&task->lock, flags);
287d9467163SBob Pearson return;
288d9467163SBob Pearson }
289d9467163SBob Pearson spin_unlock_irqrestore(&task->lock, flags);
290d9467163SBob Pearson
291d9467163SBob Pearson while (!is_done(task))
292d9467163SBob Pearson cond_resched();
293d9467163SBob Pearson
2949b4b7c1fSBob Pearson spin_lock_irqsave(&task->lock, flags);
2959b4b7c1fSBob Pearson task->state = TASK_STATE_DRAINED;
2969b4b7c1fSBob Pearson spin_unlock_irqrestore(&task->lock, flags);
2978700e3e7SMoni Shoua }
2988700e3e7SMoni Shoua
rxe_enable_task(struct rxe_task * task)2998700e3e7SMoni Shoua void rxe_enable_task(struct rxe_task *task)
3008700e3e7SMoni Shoua {
301d9467163SBob Pearson unsigned long flags;
302d9467163SBob Pearson
303d9467163SBob Pearson WARN_ON(rxe_read(task->qp) <= 0);
304d9467163SBob Pearson
305d9467163SBob Pearson spin_lock_irqsave(&task->lock, flags);
306d9467163SBob Pearson if (task->state == TASK_STATE_INVALID) {
307d9467163SBob Pearson spin_unlock_irqrestore(&task->lock, flags);
308d9467163SBob Pearson return;
309d9467163SBob Pearson }
3109b4b7c1fSBob Pearson
311d9467163SBob Pearson task->state = TASK_STATE_IDLE;
312d9467163SBob Pearson spin_unlock_irqrestore(&task->lock, flags);
3138700e3e7SMoni Shoua }
314