1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /*
3 * Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
4 * Copyright (c) 2015 System Fabric Works, Inc. All rights reserved.
5 */
6
7 #include "rxe.h"
8
__rxe_do_task(struct rxe_task * task)9 int __rxe_do_task(struct rxe_task *task)
10
11 {
12 int ret;
13
14 while ((ret = task->func(task->arg)) == 0)
15 ;
16
17 task->ret = ret;
18
19 return ret;
20 }
21
22 /*
23 * this locking is due to a potential race where
24 * a second caller finds the task already running
25 * but looks just after the last call to func
26 */
do_task(struct tasklet_struct * t)27 static void do_task(struct tasklet_struct *t)
28 {
29 int cont;
30 int ret;
31 struct rxe_task *task = from_tasklet(task, t, tasklet);
32 struct rxe_qp *qp = (struct rxe_qp *)task->arg;
33 unsigned int iterations = RXE_MAX_ITERATIONS;
34
35 spin_lock_bh(&task->lock);
36 switch (task->state) {
37 case TASK_STATE_START:
38 task->state = TASK_STATE_BUSY;
39 spin_unlock_bh(&task->lock);
40 break;
41
42 case TASK_STATE_BUSY:
43 task->state = TASK_STATE_ARMED;
44 fallthrough;
45 case TASK_STATE_ARMED:
46 spin_unlock_bh(&task->lock);
47 return;
48
49 default:
50 spin_unlock_bh(&task->lock);
51 rxe_dbg_qp(qp, "failed with bad state %d\n", task->state);
52 return;
53 }
54
55 do {
56 cont = 0;
57 ret = task->func(task->arg);
58
59 spin_lock_bh(&task->lock);
60 switch (task->state) {
61 case TASK_STATE_BUSY:
62 if (ret) {
63 task->state = TASK_STATE_START;
64 } else if (iterations--) {
65 cont = 1;
66 } else {
67 /* reschedule the tasklet and exit
68 * the loop to give up the cpu
69 */
70 tasklet_schedule(&task->tasklet);
71 task->state = TASK_STATE_START;
72 }
73 break;
74
75 /* someone tried to run the task since the last time we called
76 * func, so we will call one more time regardless of the
77 * return value
78 */
79 case TASK_STATE_ARMED:
80 task->state = TASK_STATE_BUSY;
81 cont = 1;
82 break;
83
84 default:
85 rxe_dbg_qp(qp, "failed with bad state %d\n",
86 task->state);
87 }
88 spin_unlock_bh(&task->lock);
89 } while (cont);
90
91 task->ret = ret;
92 }
93
rxe_init_task(struct rxe_task * task,void * arg,int (* func)(void *))94 int rxe_init_task(struct rxe_task *task, void *arg, int (*func)(void *))
95 {
96 task->arg = arg;
97 task->func = func;
98 task->destroyed = false;
99
100 tasklet_setup(&task->tasklet, do_task);
101
102 task->state = TASK_STATE_START;
103 spin_lock_init(&task->lock);
104
105 return 0;
106 }
107
rxe_cleanup_task(struct rxe_task * task)108 void rxe_cleanup_task(struct rxe_task *task)
109 {
110 bool idle;
111
112 /*
113 * Mark the task, then wait for it to finish. It might be
114 * running in a non-tasklet (direct call) context.
115 */
116 task->destroyed = true;
117
118 do {
119 spin_lock_bh(&task->lock);
120 idle = (task->state == TASK_STATE_START);
121 spin_unlock_bh(&task->lock);
122 } while (!idle);
123
124 tasklet_kill(&task->tasklet);
125 }
126
rxe_run_task(struct rxe_task * task)127 void rxe_run_task(struct rxe_task *task)
128 {
129 if (task->destroyed)
130 return;
131
132 do_task(&task->tasklet);
133 }
134
rxe_sched_task(struct rxe_task * task)135 void rxe_sched_task(struct rxe_task *task)
136 {
137 if (task->destroyed)
138 return;
139
140 tasklet_schedule(&task->tasklet);
141 }
142
rxe_disable_task(struct rxe_task * task)143 void rxe_disable_task(struct rxe_task *task)
144 {
145 tasklet_disable(&task->tasklet);
146 }
147
rxe_enable_task(struct rxe_task * task)148 void rxe_enable_task(struct rxe_task *task)
149 {
150 tasklet_enable(&task->tasklet);
151 }
152