xref: /linux/drivers/infiniband/sw/rxe/rxe_task.c (revision 8a922b7728a93d837954315c98b84f6b78de0c4f)
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 "rxe.h"
8 
9 int __rxe_do_task(struct rxe_task *task)
10 
11 {
12 	int ret;
13 
14 	while ((ret = task->func(task->arg)) == 0)
15 		;
16 
17 	task->ret = ret;
18 
19 	return ret;
20 }
21 
22 /*
23  * this locking is due to a potential race where
24  * a second caller finds the task already running
25  * but looks just after the last call to func
26  */
27 static void do_task(struct tasklet_struct *t)
28 {
29 	int cont;
30 	int ret;
31 	struct rxe_task *task = from_tasklet(task, t, tasklet);
32 	struct rxe_qp *qp = (struct rxe_qp *)task->arg;
33 	unsigned int iterations = RXE_MAX_ITERATIONS;
34 
35 	spin_lock_bh(&task->lock);
36 	switch (task->state) {
37 	case TASK_STATE_START:
38 		task->state = TASK_STATE_BUSY;
39 		spin_unlock_bh(&task->lock);
40 		break;
41 
42 	case TASK_STATE_BUSY:
43 		task->state = TASK_STATE_ARMED;
44 		fallthrough;
45 	case TASK_STATE_ARMED:
46 		spin_unlock_bh(&task->lock);
47 		return;
48 
49 	default:
50 		spin_unlock_bh(&task->lock);
51 		rxe_dbg_qp(qp, "failed with bad state %d\n", task->state);
52 		return;
53 	}
54 
55 	do {
56 		cont = 0;
57 		ret = task->func(task->arg);
58 
59 		spin_lock_bh(&task->lock);
60 		switch (task->state) {
61 		case TASK_STATE_BUSY:
62 			if (ret) {
63 				task->state = TASK_STATE_START;
64 			} else if (iterations--) {
65 				cont = 1;
66 			} else {
67 				/* reschedule the tasklet and exit
68 				 * the loop to give up the cpu
69 				 */
70 				tasklet_schedule(&task->tasklet);
71 				task->state = TASK_STATE_START;
72 			}
73 			break;
74 
75 		/* someone tried to run the task since the last time we called
76 		 * func, so we will call one more time regardless of the
77 		 * return value
78 		 */
79 		case TASK_STATE_ARMED:
80 			task->state = TASK_STATE_BUSY;
81 			cont = 1;
82 			break;
83 
84 		default:
85 			rxe_dbg_qp(qp, "failed with bad state %d\n",
86 					task->state);
87 		}
88 		spin_unlock_bh(&task->lock);
89 	} while (cont);
90 
91 	task->ret = ret;
92 }
93 
94 int rxe_init_task(struct rxe_task *task, void *arg, int (*func)(void *))
95 {
96 	task->arg	= arg;
97 	task->func	= func;
98 	task->destroyed	= false;
99 
100 	tasklet_setup(&task->tasklet, do_task);
101 
102 	task->state = TASK_STATE_START;
103 	spin_lock_init(&task->lock);
104 
105 	return 0;
106 }
107 
108 void rxe_cleanup_task(struct rxe_task *task)
109 {
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_bh(&task->lock);
120 		idle = (task->state == TASK_STATE_START);
121 		spin_unlock_bh(&task->lock);
122 	} while (!idle);
123 
124 	tasklet_kill(&task->tasklet);
125 }
126 
127 void rxe_run_task(struct rxe_task *task)
128 {
129 	if (task->destroyed)
130 		return;
131 
132 	do_task(&task->tasklet);
133 }
134 
135 void rxe_sched_task(struct rxe_task *task)
136 {
137 	if (task->destroyed)
138 		return;
139 
140 	tasklet_schedule(&task->tasklet);
141 }
142 
143 void rxe_disable_task(struct rxe_task *task)
144 {
145 	tasklet_disable(&task->tasklet);
146 }
147 
148 void rxe_enable_task(struct rxe_task *task)
149 {
150 	tasklet_enable(&task->tasklet);
151 }
152