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