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 <linux/kernel.h>
8 #include <linux/interrupt.h>
9 #include <linux/hardirq.h>
10
11 #include "rxe.h"
12
__rxe_do_task(struct rxe_task * task)13 int __rxe_do_task(struct rxe_task *task)
14
15 {
16 int ret;
17
18 while ((ret = task->func(task->arg)) == 0)
19 ;
20
21 task->ret = ret;
22
23 return ret;
24 }
25
26 /*
27 * this locking is due to a potential race where
28 * a second caller finds the task already running
29 * but looks just after the last call to func
30 */
rxe_do_task(struct tasklet_struct * t)31 void rxe_do_task(struct tasklet_struct *t)
32 {
33 int cont;
34 int ret;
35 struct rxe_task *task = from_tasklet(task, t, tasklet);
36 unsigned int iterations = RXE_MAX_ITERATIONS;
37
38 spin_lock_bh(&task->state_lock);
39 switch (task->state) {
40 case TASK_STATE_START:
41 task->state = TASK_STATE_BUSY;
42 spin_unlock_bh(&task->state_lock);
43 break;
44
45 case TASK_STATE_BUSY:
46 task->state = TASK_STATE_ARMED;
47 fallthrough;
48 case TASK_STATE_ARMED:
49 spin_unlock_bh(&task->state_lock);
50 return;
51
52 default:
53 spin_unlock_bh(&task->state_lock);
54 pr_warn("%s failed with bad state %d\n", __func__, task->state);
55 return;
56 }
57
58 do {
59 cont = 0;
60 ret = task->func(task->arg);
61
62 spin_lock_bh(&task->state_lock);
63 switch (task->state) {
64 case TASK_STATE_BUSY:
65 if (ret) {
66 task->state = TASK_STATE_START;
67 } else if (iterations--) {
68 cont = 1;
69 } else {
70 /* reschedule the tasklet and exit
71 * the loop to give up the cpu
72 */
73 tasklet_schedule(&task->tasklet);
74 task->state = TASK_STATE_START;
75 }
76 break;
77
78 /* someone tried to run the task since the last time we called
79 * func, so we will call one more time regardless of the
80 * return value
81 */
82 case TASK_STATE_ARMED:
83 task->state = TASK_STATE_BUSY;
84 cont = 1;
85 break;
86
87 default:
88 pr_warn("%s failed with bad state %d\n", __func__,
89 task->state);
90 }
91 spin_unlock_bh(&task->state_lock);
92 } while (cont);
93
94 task->ret = ret;
95 }
96
rxe_init_task(struct rxe_task * task,void * arg,int (* func)(void *),char * name)97 int rxe_init_task(struct rxe_task *task,
98 void *arg, int (*func)(void *), char *name)
99 {
100 task->arg = arg;
101 task->func = func;
102 snprintf(task->name, sizeof(task->name), "%s", name);
103 task->destroyed = false;
104
105 tasklet_setup(&task->tasklet, rxe_do_task);
106
107 task->state = TASK_STATE_START;
108 spin_lock_init(&task->state_lock);
109
110 return 0;
111 }
112
rxe_cleanup_task(struct rxe_task * task)113 void rxe_cleanup_task(struct rxe_task *task)
114 {
115 bool idle;
116
117 /*
118 * Mark the task, then wait for it to finish. It might be
119 * running in a non-tasklet (direct call) context.
120 */
121 task->destroyed = true;
122
123 do {
124 spin_lock_bh(&task->state_lock);
125 idle = (task->state == TASK_STATE_START);
126 spin_unlock_bh(&task->state_lock);
127 } while (!idle);
128
129 tasklet_kill(&task->tasklet);
130 }
131
rxe_run_task(struct rxe_task * task,int sched)132 void rxe_run_task(struct rxe_task *task, int sched)
133 {
134 if (task->destroyed)
135 return;
136
137 if (sched)
138 tasklet_schedule(&task->tasklet);
139 else
140 rxe_do_task(&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