Commit 9b4b7c1f authored by Bob Pearson's avatar Bob Pearson Committed by Jason Gunthorpe

RDMA/rxe: Add workqueue support for rxe tasks

Replace tasklets by work queues for the three main rxe tasklets:
rxe_requester, rxe_completer and rxe_responder.

work queues are a more modern way to process work from an IRQ and provide
more control over how that work is run for future patches.

Link: https://lore.kernel.org/r/20230428171321.5774-1-rpearsonhpe@gmail.comSigned-off-by: default avatarIan Ziemba <ian.ziemba@hpe.com>
Signed-off-by: default avatarBob Pearson <rpearsonhpe@gmail.com>
Reviewed-by: default avatarDaisuke Matsuda <matsuda-daisuke@fujitsu.com>
Tested-by: default avatarDaisuke Matsuda <matsuda-daisuke@fujitsu.com>
Signed-off-by: default avatarJason Gunthorpe <jgg@nvidia.com>
parent f1fcbaa1
...@@ -212,10 +212,16 @@ static int __init rxe_module_init(void) ...@@ -212,10 +212,16 @@ static int __init rxe_module_init(void)
{ {
int err; int err;
err = rxe_net_init(); err = rxe_alloc_wq();
if (err) if (err)
return err; return err;
err = rxe_net_init();
if (err) {
rxe_destroy_wq();
return err;
}
rdma_link_register(&rxe_link_ops); rdma_link_register(&rxe_link_ops);
pr_info("loaded\n"); pr_info("loaded\n");
return 0; return 0;
...@@ -226,6 +232,7 @@ static void __exit rxe_module_exit(void) ...@@ -226,6 +232,7 @@ static void __exit rxe_module_exit(void)
rdma_link_unregister(&rxe_link_ops); rdma_link_unregister(&rxe_link_ops);
ib_unregister_driver(RDMA_DRIVER_RXE); ib_unregister_driver(RDMA_DRIVER_RXE);
rxe_net_exit(); rxe_net_exit();
rxe_destroy_wq();
pr_info("unloaded\n"); pr_info("unloaded\n");
} }
......
...@@ -6,8 +6,24 @@ ...@@ -6,8 +6,24 @@
#include "rxe.h" #include "rxe.h"
static struct workqueue_struct *rxe_wq;
int rxe_alloc_wq(void)
{
rxe_wq = alloc_workqueue("rxe_wq", WQ_UNBOUND, WQ_MAX_ACTIVE);
if (!rxe_wq)
return -ENOMEM;
return 0;
}
void rxe_destroy_wq(void)
{
destroy_workqueue(rxe_wq);
}
/* Check if task is idle i.e. not running, not scheduled in /* Check if task is idle i.e. not running, not scheduled in
* tasklet queue and not draining. If so move to busy to * work queue and not draining. If so move to busy to
* reserve a slot in do_task() by setting to busy and taking * reserve a slot in do_task() by setting to busy and taking
* a qp reference to cover the gap from now until the task finishes. * a qp reference to cover the gap from now until the task finishes.
* state will move out of busy if task returns a non zero value * state will move out of busy if task returns a non zero value
...@@ -21,9 +37,6 @@ static bool __reserve_if_idle(struct rxe_task *task) ...@@ -21,9 +37,6 @@ static bool __reserve_if_idle(struct rxe_task *task)
{ {
WARN_ON(rxe_read(task->qp) <= 0); WARN_ON(rxe_read(task->qp) <= 0);
if (task->tasklet.state & BIT(TASKLET_STATE_SCHED))
return false;
if (task->state == TASK_STATE_IDLE) { if (task->state == TASK_STATE_IDLE) {
rxe_get(task->qp); rxe_get(task->qp);
task->state = TASK_STATE_BUSY; task->state = TASK_STATE_BUSY;
...@@ -38,7 +51,7 @@ static bool __reserve_if_idle(struct rxe_task *task) ...@@ -38,7 +51,7 @@ static bool __reserve_if_idle(struct rxe_task *task)
} }
/* check if task is idle or drained and not currently /* check if task is idle or drained and not currently
* scheduled in the tasklet queue. This routine is * scheduled in the work queue. This routine is
* called by rxe_cleanup_task or rxe_disable_task to * called by rxe_cleanup_task or rxe_disable_task to
* see if the queue is empty. * see if the queue is empty.
* Context: caller should hold task->lock. * Context: caller should hold task->lock.
...@@ -46,7 +59,7 @@ static bool __reserve_if_idle(struct rxe_task *task) ...@@ -46,7 +59,7 @@ static bool __reserve_if_idle(struct rxe_task *task)
*/ */
static bool __is_done(struct rxe_task *task) static bool __is_done(struct rxe_task *task)
{ {
if (task->tasklet.state & BIT(TASKLET_STATE_SCHED)) if (work_pending(&task->work))
return false; return false;
if (task->state == TASK_STATE_IDLE || if (task->state == TASK_STATE_IDLE ||
...@@ -77,23 +90,23 @@ static bool is_done(struct rxe_task *task) ...@@ -77,23 +90,23 @@ static bool is_done(struct rxe_task *task)
* schedules the task. They must call __reserve_if_idle to * schedules the task. They must call __reserve_if_idle to
* move the task to busy before calling or scheduling. * move the task to busy before calling or scheduling.
* The task can also be moved to drained or invalid * The task can also be moved to drained or invalid
* by calls to rxe-cleanup_task or rxe_disable_task. * by calls to rxe_cleanup_task or rxe_disable_task.
* In that case tasks which get here are not executed but * In that case tasks which get here are not executed but
* just flushed. The tasks are designed to look to see if * just flushed. The tasks are designed to look to see if
* there is work to do and do part of it before returning * there is work to do and then do part of it before returning
* here with a return value of zero until all the work * here with a return value of zero until all the work
* has been consumed then it retuens a non-zero value. * has been consumed then it returns a non-zero value.
* The number of times the task can be run is limited by * The number of times the task can be run is limited by
* max iterations so one task cannot hold the cpu forever. * max iterations so one task cannot hold the cpu forever.
* If the limit is hit and work remains the task is rescheduled.
*/ */
static void do_task(struct tasklet_struct *t) static void do_task(struct rxe_task *task)
{ {
int cont;
int ret;
struct rxe_task *task = from_tasklet(task, t, tasklet);
unsigned int iterations; unsigned int iterations;
unsigned long flags; unsigned long flags;
int resched = 0; int resched = 0;
int cont;
int ret;
WARN_ON(rxe_read(task->qp) <= 0); WARN_ON(rxe_read(task->qp) <= 0);
...@@ -115,25 +128,22 @@ static void do_task(struct tasklet_struct *t) ...@@ -115,25 +128,22 @@ static void do_task(struct tasklet_struct *t)
} while (ret == 0 && iterations-- > 0); } while (ret == 0 && iterations-- > 0);
spin_lock_irqsave(&task->lock, flags); spin_lock_irqsave(&task->lock, flags);
switch (task->state) { /* we're not done yet but we ran out of iterations.
case TASK_STATE_BUSY: * yield the cpu and reschedule the task
if (ret) {
task->state = TASK_STATE_IDLE;
} else {
/* This can happen if the client
* can add work faster than the
* tasklet can finish it.
* Reschedule the tasklet and exit
* the loop to give up the cpu
*/ */
if (!ret) {
task->state = TASK_STATE_IDLE; task->state = TASK_STATE_IDLE;
resched = 1; resched = 1;
goto exit;
} }
switch (task->state) {
case TASK_STATE_BUSY:
task->state = TASK_STATE_IDLE;
break; break;
/* someone tried to run the task since the last time we called /* someone tried to schedule the task while we
* func, so we will call one more time regardless of the * were running, keep going
* return value
*/ */
case TASK_STATE_ARMED: case TASK_STATE_ARMED:
task->state = TASK_STATE_BUSY; task->state = TASK_STATE_BUSY;
...@@ -141,21 +151,23 @@ static void do_task(struct tasklet_struct *t) ...@@ -141,21 +151,23 @@ static void do_task(struct tasklet_struct *t)
break; break;
case TASK_STATE_DRAINING: case TASK_STATE_DRAINING:
if (ret)
task->state = TASK_STATE_DRAINED; task->state = TASK_STATE_DRAINED;
else
cont = 1;
break; break;
default: default:
WARN_ON(1); WARN_ON(1);
rxe_info_qp(task->qp, "unexpected task state = %d", task->state); rxe_dbg_qp(task->qp, "unexpected task state = %d",
task->state);
task->state = TASK_STATE_IDLE;
} }
exit:
if (!cont) { if (!cont) {
task->num_done++; task->num_done++;
if (WARN_ON(task->num_done != task->num_sched)) if (WARN_ON(task->num_done != task->num_sched))
rxe_err_qp(task->qp, "%ld tasks scheduled, %ld tasks done", rxe_dbg_qp(
task->qp,
"%ld tasks scheduled, %ld tasks done",
task->num_sched, task->num_done); task->num_sched, task->num_done);
} }
spin_unlock_irqrestore(&task->lock, flags); spin_unlock_irqrestore(&task->lock, flags);
...@@ -169,6 +181,12 @@ static void do_task(struct tasklet_struct *t) ...@@ -169,6 +181,12 @@ static void do_task(struct tasklet_struct *t)
rxe_put(task->qp); rxe_put(task->qp);
} }
/* wrapper around do_task to fix argument for work queue */
static void do_work(struct work_struct *work)
{
do_task(container_of(work, struct rxe_task, work));
}
int rxe_init_task(struct rxe_task *task, struct rxe_qp *qp, int rxe_init_task(struct rxe_task *task, struct rxe_qp *qp,
int (*func)(struct rxe_qp *)) int (*func)(struct rxe_qp *))
{ {
...@@ -176,11 +194,9 @@ int rxe_init_task(struct rxe_task *task, struct rxe_qp *qp, ...@@ -176,11 +194,9 @@ int rxe_init_task(struct rxe_task *task, struct rxe_qp *qp,
task->qp = qp; task->qp = qp;
task->func = func; task->func = func;
tasklet_setup(&task->tasklet, do_task);
task->state = TASK_STATE_IDLE; task->state = TASK_STATE_IDLE;
spin_lock_init(&task->lock); spin_lock_init(&task->lock);
INIT_WORK(&task->work, do_work);
return 0; return 0;
} }
...@@ -213,8 +229,6 @@ void rxe_cleanup_task(struct rxe_task *task) ...@@ -213,8 +229,6 @@ void rxe_cleanup_task(struct rxe_task *task)
while (!is_done(task)) while (!is_done(task))
cond_resched(); cond_resched();
tasklet_kill(&task->tasklet);
spin_lock_irqsave(&task->lock, flags); spin_lock_irqsave(&task->lock, flags);
task->state = TASK_STATE_INVALID; task->state = TASK_STATE_INVALID;
spin_unlock_irqrestore(&task->lock, flags); spin_unlock_irqrestore(&task->lock, flags);
...@@ -226,7 +240,7 @@ void rxe_cleanup_task(struct rxe_task *task) ...@@ -226,7 +240,7 @@ void rxe_cleanup_task(struct rxe_task *task)
void rxe_run_task(struct rxe_task *task) void rxe_run_task(struct rxe_task *task)
{ {
unsigned long flags; unsigned long flags;
int run; bool run;
WARN_ON(rxe_read(task->qp) <= 0); WARN_ON(rxe_read(task->qp) <= 0);
...@@ -235,11 +249,11 @@ void rxe_run_task(struct rxe_task *task) ...@@ -235,11 +249,11 @@ void rxe_run_task(struct rxe_task *task)
spin_unlock_irqrestore(&task->lock, flags); spin_unlock_irqrestore(&task->lock, flags);
if (run) if (run)
do_task(&task->tasklet); do_task(task);
} }
/* schedule the task to run later as a tasklet. /* schedule the task to run later as a work queue entry.
* the tasklet)schedule call can be called holding * the queue_work call can be called holding
* the lock. * the lock.
*/ */
void rxe_sched_task(struct rxe_task *task) void rxe_sched_task(struct rxe_task *task)
...@@ -250,7 +264,7 @@ void rxe_sched_task(struct rxe_task *task) ...@@ -250,7 +264,7 @@ void rxe_sched_task(struct rxe_task *task)
spin_lock_irqsave(&task->lock, flags); spin_lock_irqsave(&task->lock, flags);
if (__reserve_if_idle(task)) if (__reserve_if_idle(task))
tasklet_schedule(&task->tasklet); queue_work(rxe_wq, &task->work);
spin_unlock_irqrestore(&task->lock, flags); spin_unlock_irqrestore(&task->lock, flags);
} }
...@@ -277,7 +291,9 @@ void rxe_disable_task(struct rxe_task *task) ...@@ -277,7 +291,9 @@ void rxe_disable_task(struct rxe_task *task)
while (!is_done(task)) while (!is_done(task))
cond_resched(); cond_resched();
tasklet_disable(&task->tasklet); spin_lock_irqsave(&task->lock, flags);
task->state = TASK_STATE_DRAINED;
spin_unlock_irqrestore(&task->lock, flags);
} }
void rxe_enable_task(struct rxe_task *task) void rxe_enable_task(struct rxe_task *task)
...@@ -291,7 +307,7 @@ void rxe_enable_task(struct rxe_task *task) ...@@ -291,7 +307,7 @@ void rxe_enable_task(struct rxe_task *task)
spin_unlock_irqrestore(&task->lock, flags); spin_unlock_irqrestore(&task->lock, flags);
return; return;
} }
task->state = TASK_STATE_IDLE; task->state = TASK_STATE_IDLE;
tasklet_enable(&task->tasklet);
spin_unlock_irqrestore(&task->lock, flags); spin_unlock_irqrestore(&task->lock, flags);
} }
...@@ -22,7 +22,7 @@ enum { ...@@ -22,7 +22,7 @@ enum {
* called again. * called again.
*/ */
struct rxe_task { struct rxe_task {
struct tasklet_struct tasklet; struct work_struct work;
int state; int state;
spinlock_t lock; spinlock_t lock;
struct rxe_qp *qp; struct rxe_qp *qp;
...@@ -32,6 +32,10 @@ struct rxe_task { ...@@ -32,6 +32,10 @@ struct rxe_task {
long num_done; long num_done;
}; };
int rxe_alloc_wq(void);
void rxe_destroy_wq(void);
/* /*
* init rxe_task structure * init rxe_task structure
* qp => parameter to pass to func * qp => parameter to pass to func
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment