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_task.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
38 spin_lock_irqsave(&task->state_lock, flags);
39 switch (task->state) {
40 case TASK_STATE_START:
41 task->state = TASK_STATE_BUSY;
42 spin_unlock_irqrestore(&task->state_lock, flags);
43 break;
44
45 case TASK_STATE_BUSY:
46 task->state = TASK_STATE_ARMED;
47 fallthrough;
48 case TASK_STATE_ARMED:
49 spin_unlock_irqrestore(&task->state_lock, flags);
50 return;
51
52 default:
53 spin_unlock_irqrestore(&task->state_lock, flags);
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_irqsave(&task->state_lock, flags);
63 switch (task->state) {
64 case TASK_STATE_BUSY:
65 if (ret)
66 task->state = TASK_STATE_START;
67 else
68 cont = 1;
69 break;
70
71 /* soneone tried to run the task since the last time we called
72 * func, so we will call one more time regardless of the
73 * return value
74 */
75 case TASK_STATE_ARMED:
76 task->state = TASK_STATE_BUSY;
77 cont = 1;
78 break;
79
80 default:
81 pr_warn("%s failed with bad state %d\n", __func__,
82 task->state);
83 }
84 spin_unlock_irqrestore(&task->state_lock, flags);
85 } while (cont);
86
87 task->ret = ret;
88 }
89
rxe_init_task(void * obj,struct rxe_task * task,void * arg,int (* func)(void *),char * name)90 int rxe_init_task(void *obj, struct rxe_task *task,
91 void *arg, int (*func)(void *), char *name)
92 {
93 task->obj = obj;
94 task->arg = arg;
95 task->func = func;
96 snprintf(task->name, sizeof(task->name), "%s", name);
97 task->destroyed = false;
98
99 tasklet_setup(&task->tasklet, rxe_do_task);
100
101 task->state = TASK_STATE_START;
102 spin_lock_init(&task->state_lock);
103
104 return 0;
105 }
106
rxe_cleanup_task(struct rxe_task * task)107 void rxe_cleanup_task(struct rxe_task *task)
108 {
109 unsigned long flags;
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_irqsave(&task->state_lock, flags);
120 idle = (task->state == TASK_STATE_START);
121 spin_unlock_irqrestore(&task->state_lock, flags);
122 } while (!idle);
123
124 tasklet_kill(&task->tasklet);
125 }
126
rxe_run_task(struct rxe_task * task,int sched)127 void rxe_run_task(struct rxe_task *task, int sched)
128 {
129 if (task->destroyed)
130 return;
131
132 if (sched)
133 tasklet_schedule(&task->tasklet);
134 else
135 rxe_do_task(&task->tasklet);
136 }
137
rxe_disable_task(struct rxe_task * task)138 void rxe_disable_task(struct rxe_task *task)
139 {
140 tasklet_disable(&task->tasklet);
141 }
142
rxe_enable_task(struct rxe_task * task)143 void rxe_enable_task(struct rxe_task *task)
144 {
145 tasklet_enable(&task->tasklet);
146 }
147