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 unsigned long flags;
36 struct rxe_task *task = from_tasklet(task, t, tasklet);
37 unsigned int iterations = RXE_MAX_ITERATIONS;
38
39 spin_lock_irqsave(&task->state_lock, flags);
40 switch (task->state) {
41 case TASK_STATE_START:
42 task->state = TASK_STATE_BUSY;
43 spin_unlock_irqrestore(&task->state_lock, flags);
44 break;
45
46 case TASK_STATE_BUSY:
47 task->state = TASK_STATE_ARMED;
48 fallthrough;
49 case TASK_STATE_ARMED:
50 spin_unlock_irqrestore(&task->state_lock, flags);
51 return;
52
53 default:
54 spin_unlock_irqrestore(&task->state_lock, flags);
55 pr_warn("%s failed with bad state %d\n", __func__, task->state);
56 return;
57 }
58
59 do {
60 cont = 0;
61 ret = task->func(task->arg);
62
63 spin_lock_irqsave(&task->state_lock, flags);
64 switch (task->state) {
65 case TASK_STATE_BUSY:
66 if (ret) {
67 task->state = TASK_STATE_START;
68 } else if (iterations--) {
69 cont = 1;
70 } else {
71 /* reschedule the tasklet and exit
72 * the loop to give up the cpu
73 */
74 tasklet_schedule(&task->tasklet);
75 task->state = TASK_STATE_START;
76 }
77 break;
78
79 /* someone tried to run the task since the last time we called
80 * func, so we will call one more time regardless of the
81 * return value
82 */
83 case TASK_STATE_ARMED:
84 task->state = TASK_STATE_BUSY;
85 cont = 1;
86 break;
87
88 default:
89 pr_warn("%s failed with bad state %d\n", __func__,
90 task->state);
91 }
92 spin_unlock_irqrestore(&task->state_lock, flags);
93 } while (cont);
94
95 task->ret = ret;
96 }
97
rxe_init_task(void * obj,struct rxe_task * task,void * arg,int (* func)(void *),char * name)98 int rxe_init_task(void *obj, struct rxe_task *task,
99 void *arg, int (*func)(void *), char *name)
100 {
101 task->obj = obj;
102 task->arg = arg;
103 task->func = func;
104 snprintf(task->name, sizeof(task->name), "%s", name);
105 task->destroyed = false;
106
107 tasklet_setup(&task->tasklet, rxe_do_task);
108
109 task->state = TASK_STATE_START;
110 spin_lock_init(&task->state_lock);
111
112 return 0;
113 }
114
rxe_cleanup_task(struct rxe_task * task)115 void rxe_cleanup_task(struct rxe_task *task)
116 {
117 unsigned long flags;
118 bool idle;
119
120 /*
121 * Mark the task, then wait for it to finish. It might be
122 * running in a non-tasklet (direct call) context.
123 */
124 task->destroyed = true;
125
126 do {
127 spin_lock_irqsave(&task->state_lock, flags);
128 idle = (task->state == TASK_STATE_START);
129 spin_unlock_irqrestore(&task->state_lock, flags);
130 } while (!idle);
131
132 tasklet_kill(&task->tasklet);
133 }
134
rxe_run_task(struct rxe_task * task,int sched)135 void rxe_run_task(struct rxe_task *task, int sched)
136 {
137 if (task->destroyed)
138 return;
139
140 if (sched)
141 tasklet_schedule(&task->tasklet);
142 else
143 rxe_do_task(&task->tasklet);
144 }
145
rxe_disable_task(struct rxe_task * task)146 void rxe_disable_task(struct rxe_task *task)
147 {
148 tasklet_disable(&task->tasklet);
149 }
150
rxe_enable_task(struct rxe_task * task)151 void rxe_enable_task(struct rxe_task *task)
152 {
153 tasklet_enable(&task->tasklet);
154 }
155