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(struct rxe_task * task,void * arg,int (* func)(void *))98 int rxe_init_task(struct rxe_task *task, void *arg, int (*func)(void *))
99 {
100 task->arg = arg;
101 task->func = func;
102 task->destroyed = false;
103
104 tasklet_setup(&task->tasklet, rxe_do_task);
105
106 task->state = TASK_STATE_START;
107 spin_lock_init(&task->state_lock);
108
109 return 0;
110 }
111
rxe_cleanup_task(struct rxe_task * task)112 void rxe_cleanup_task(struct rxe_task *task)
113 {
114 unsigned long flags;
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_irqsave(&task->state_lock, flags);
125 idle = (task->state == TASK_STATE_START);
126 spin_unlock_irqrestore(&task->state_lock, flags);
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