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 
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 	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 
97 int rxe_init_task(void *obj, struct rxe_task *task,
98 		  void *arg, int (*func)(void *), char *name)
99 {
100 	task->obj	= obj;
101 	task->arg	= arg;
102 	task->func	= func;
103 	snprintf(task->name, sizeof(task->name), "%s", name);
104 	task->destroyed	= false;
105 
106 	tasklet_setup(&task->tasklet, rxe_do_task);
107 
108 	task->state = TASK_STATE_START;
109 	spin_lock_init(&task->state_lock);
110 
111 	return 0;
112 }
113 
114 void rxe_cleanup_task(struct rxe_task *task)
115 {
116 	bool idle;
117 
118 	/*
119 	 * Mark the task, then wait for it to finish. It might be
120 	 * running in a non-tasklet (direct call) context.
121 	 */
122 	task->destroyed = true;
123 
124 	do {
125 		spin_lock_bh(&task->state_lock);
126 		idle = (task->state == TASK_STATE_START);
127 		spin_unlock_bh(&task->state_lock);
128 	} while (!idle);
129 
130 	tasklet_kill(&task->tasklet);
131 }
132 
133 void rxe_run_task(struct rxe_task *task, int sched)
134 {
135 	if (task->destroyed)
136 		return;
137 
138 	if (sched)
139 		tasklet_schedule(&task->tasklet);
140 	else
141 		rxe_do_task(&task->tasklet);
142 }
143 
144 void rxe_disable_task(struct rxe_task *task)
145 {
146 	tasklet_disable(&task->tasklet);
147 }
148 
149 void rxe_enable_task(struct rxe_task *task)
150 {
151 	tasklet_enable(&task->tasklet);
152 }
153