1 | //------------------------------------------------------------------------------ |
2 | // Copyright (c) 2016 by Lukasz Janyst <lukasz@jany.st> |
3 | //------------------------------------------------------------------------------ |
4 | // This file is part of thread-bites. |
5 | // |
6 | // thread-bites is free software: you can redistribute it and/or modify |
7 | // it under the terms of the GNU General Public License as published by |
8 | // the Free Software Foundation, either version 3 of the License, or |
9 | // (at your option) any later version. |
10 | // |
11 | // thread-bites is distributed in the hope that it will be useful, |
12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | // GNU General Public License for more details. |
15 | // |
16 | // You should have received a copy of the GNU General Public License |
17 | // along with thread-bites. If not, see <http://www.gnu.org/licenses/>. |
18 | //------------------------------------------------------------------------------ |
19 | |
20 | #include "tb.h" |
21 | #include "tb-private.h" |
22 | |
23 | #include <string.h> |
24 | |
25 | //------------------------------------------------------------------------------ |
26 | // Set scheduler |
27 | //------------------------------------------------------------------------------ |
28 | struct tb_sched_param |
29 | { |
30 | int sched_priority; |
31 | }; |
32 | |
33 | int tb_set_sched(tbthread_t thread, int policy, int priority) |
34 | { |
35 | struct tb_sched_param p; p.sched_priority = priority; |
36 | int ret = SYSCALL3(__NR_sched_setscheduler, thread->tid, policy, &p); |
37 | if(!ret) |
38 | thread->sched_info = SCHED_INFO_PACK(policy, priority); |
39 | return ret; |
40 | } |
41 | |
42 | //------------------------------------------------------------------------------ |
43 | // Schedule a protected mutex |
44 | //------------------------------------------------------------------------------ |
45 | static int mutex_prio_smaller(void *this, void *next) |
46 | { |
47 | tbthread_mutex_t *t = this; |
48 | tbthread_mutex_t *n = next; |
49 | if(SCHED_INFO_PRIORITY(n->sched_info) < SCHED_INFO_PRIORITY(t->sched_info)) |
50 | return 1; |
51 | return 0; |
52 | } |
53 | |
54 | void tb_protect_mutex_sched(tbthread_mutex_t *mutex) |
55 | { |
56 | tbthread_t owner = mutex->owner; |
57 | tb_futex_lock(&owner->lock); |
58 | |
59 | list_t *node = malloc(sizeof(list_t)); |
60 | if(!node) |
61 | goto exit; |
62 | |
63 | node->element = mutex; |
64 | list_add_here(&owner->protect_mutexes, node, mutex_prio_smaller); |
65 | |
66 | if(node->prev == &owner->protect_mutexes) |
67 | tb_compute_sched(owner); |
68 | |
69 | exit: |
70 | tb_futex_unlock(&owner->lock); |
71 | } |
72 | |
73 | //------------------------------------------------------------------------------ |
74 | // Un-schedule a protected mutex |
75 | //------------------------------------------------------------------------------ |
76 | void tb_protect_mutex_unsched(tbthread_mutex_t *mutex) |
77 | { |
78 | tbthread_t owner = mutex->owner; |
79 | tb_futex_lock(&owner->lock); |
80 | int reschedule = 0; |
81 | list_t *node = list_find_elem(&owner->protect_mutexes, mutex); |
82 | if(node) { |
83 | if(node->prev == &owner->protect_mutexes) |
84 | reschedule = 1; |
85 | list_rm(node); |
86 | free(node); |
87 | } |
88 | |
89 | if(reschedule) |
90 | tb_compute_sched(owner); |
91 | tb_futex_unlock(&owner->lock); |
92 | } |
93 | |
94 | //------------------------------------------------------------------------------ |
95 | // Add an inherit mutex |
96 | //------------------------------------------------------------------------------ |
97 | struct inh_mutex { |
98 | tbthread_mutex_t *mutex; |
99 | uint16_t sched_info; |
100 | }; |
101 | |
102 | void tb_inherit_mutex_add(tbthread_mutex_t *mutex) |
103 | { |
104 | tbthread_t owner = mutex->owner; |
105 | tb_futex_lock(&owner->lock); |
106 | |
107 | list_t *node = malloc(sizeof(list_t)); |
108 | if(!node) |
109 | goto exit; |
110 | |
111 | struct inh_mutex *el = malloc(sizeof(struct inh_mutex)); |
112 | if(!el) { |
113 | free(node); |
114 | goto exit; |
115 | } |
116 | memset(el, 0, sizeof(struct inh_mutex)); |
117 | |
118 | el->mutex = mutex; |
119 | node->element = el; |
120 | list_add(&owner->inherit_mutexes, node, 0); |
121 | |
122 | exit: |
123 | tb_futex_unlock(&owner->lock); |
124 | } |
125 | |
126 | //------------------------------------------------------------------------------ |
127 | // Un-schedule an inherit mutex |
128 | //------------------------------------------------------------------------------ |
129 | static int mutex_equal(void *this, void *other) |
130 | { |
131 | struct inh_mutex *o = other; |
132 | return this == o->mutex; |
133 | } |
134 | |
135 | void tb_inherit_mutex_unsched(tbthread_mutex_t *mutex) |
136 | { |
137 | tbthread_t owner = mutex->owner; |
138 | tb_futex_lock(&owner->lock); |
139 | |
140 | list_t *node = list_find_elem_func(&owner->inherit_mutexes, mutex, |
141 | mutex_equal); |
142 | |
143 | if(!node) |
144 | goto exit; |
145 | |
146 | struct inh_mutex *im = node->element; |
147 | list_rm(node); |
148 | free(node); |
149 | |
150 | if(im->sched_info == owner->sched_info) |
151 | tb_compute_sched(owner); |
152 | |
153 | free(im); |
154 | |
155 | exit: |
156 | tb_futex_unlock(&owner->lock); |
157 | } |
158 | |
159 | //------------------------------------------------------------------------------ |
160 | // Schedule an inherit mutex |
161 | //------------------------------------------------------------------------------ |
162 | void tb_inherit_mutex_sched(tbthread_mutex_t *mutex, tbthread_t thread) |
163 | { |
164 | tb_futex_lock(&thread->lock); |
165 | int th_sched_info = thread->sched_info; |
166 | tb_futex_unlock(&thread->lock); |
167 | |
168 | tbthread_t owner = mutex->owner; |
169 | tb_futex_lock(&owner->lock); |
170 | |
171 | list_t *node = list_find_elem_func(&owner->inherit_mutexes, mutex, |
172 | mutex_equal); |
173 | |
174 | if(!node) |
175 | goto exit; |
176 | |
177 | struct inh_mutex *im = node->element; |
178 | int reschedule = 0; |
179 | |
180 | if(SCHED_INFO_PRIORITY(th_sched_info) > SCHED_INFO_PRIORITY(im->sched_info)) { |
181 | im->sched_info = th_sched_info; |
182 | reschedule = 1; |
183 | } |
184 | else |
185 | if(SCHED_INFO_PRIORITY(th_sched_info) == SCHED_INFO_PRIORITY(im->sched_info) |
186 | && SCHED_INFO_POLICY(im->sched_info) == SCHED_RR) { |
187 | im->sched_info = th_sched_info; |
188 | reschedule = 1; |
189 | } |
190 | |
191 | if(reschedule) |
192 | tb_compute_sched(owner); |
193 | |
194 | exit: |
195 | tb_futex_unlock(&owner->lock); |
196 | } |
197 | |
198 | //------------------------------------------------------------------------------ |
199 | // Compute scheduler |
200 | //------------------------------------------------------------------------------ |
201 | int tb_compute_sched(tbthread_t thread) |
202 | { |
203 | //---------------------------------------------------------------------------- |
204 | // Take the user set scheduler into account |
205 | //---------------------------------------------------------------------------- |
206 | uint16_t sched_info = thread->user_sched_info; |
207 | uint8_t policy = SCHED_INFO_POLICY(sched_info); |
208 | uint8_t priority = SCHED_INFO_PRIORITY(sched_info); |
209 | |
210 | //---------------------------------------------------------------------------- |
211 | // Look at priority protect mutexes owned by the thread |
212 | //---------------------------------------------------------------------------- |
213 | if(thread->protect_mutexes.next) |
214 | { |
215 | tbthread_mutex_t *protect_mutex = thread->protect_mutexes.next->element; |
216 | uint16_t prot_sched_info = protect_mutex->sched_info; |
217 | uint8_t prot_policy = SCHED_INFO_POLICY(prot_sched_info); |
218 | uint8_t prot_priority = SCHED_INFO_PRIORITY(prot_sched_info); |
219 | |
220 | if(prot_priority > priority) { |
221 | priority = prot_priority; |
222 | policy = prot_policy; |
223 | } |
224 | else if(prot_priority == priority && policy == SCHED_RR) |
225 | policy = prot_policy; |
226 | } |
227 | |
228 | //---------------------------------------------------------------------------- |
229 | // Look at priority inherit mutexes owned by the thread |
230 | //---------------------------------------------------------------------------- |
231 | if(thread->inherit_mutexes.next) |
232 | { |
233 | list_t *cursor = thread->inherit_mutexes.next; |
234 | for( ; cursor; cursor = cursor->next) { |
235 | struct inh_mutex *im = cursor->element; |
236 | uint8_t im_policy = SCHED_INFO_POLICY(im->sched_info); |
237 | uint8_t im_priority = SCHED_INFO_PRIORITY(im->sched_info); |
238 | |
239 | if(im_priority > priority) { |
240 | priority = im_priority; |
241 | policy = im_policy; |
242 | } |
243 | else if(im_priority == priority && policy == SCHED_RR) |
244 | policy = im_policy; |
245 | } |
246 | } |
247 | |
248 | return tb_set_sched(thread, policy, priority); |
249 | } |
250 | |
251 | //------------------------------------------------------------------------------ |
252 | // Set scheduling parameters |
253 | //------------------------------------------------------------------------------ |
254 | int tbthread_setschedparam(tbthread_t thread, int policy, int priority) |
255 | { |
256 | if(policy != SCHED_NORMAL && policy != SCHED_FIFO && policy != SCHED_RR) |
257 | return -EINVAL; |
258 | |
259 | if(priority < 0 || priority > 99) |
260 | return -EINVAL; |
261 | |
262 | int ret = 0; |
263 | tbthread_mutex_lock(&desc_mutex); |
264 | |
265 | if(!list_find_elem(&used_desc, thread)) { |
266 | ret = -ESRCH; |
267 | goto exit; |
268 | } |
269 | |
270 | thread->user_sched_info = SCHED_INFO_PACK(policy, priority); |
271 | tb_futex_lock(&thread->lock); |
272 | ret = tb_compute_sched(thread); |
273 | tb_futex_unlock(&thread->lock); |
274 | |
275 | exit: |
276 | tbthread_mutex_unlock(&desc_mutex); |
277 | return ret; |
278 | } |
279 | |
280 | //------------------------------------------------------------------------------ |
281 | // Get scheduling parameters |
282 | //------------------------------------------------------------------------------ |
283 | int tbthread_getschedparam(tbthread_t thread, int *policy, int *priority) |
284 | { |
285 | int ret = 0; |
286 | tbthread_mutex_lock(&desc_mutex); |
287 | |
288 | if(!list_find_elem(&used_desc, thread)) { |
289 | ret = -ESRCH; |
290 | goto exit; |
291 | } |
292 | |
293 | uint16_t si = thread->sched_info; |
294 | *policy = SCHED_INFO_POLICY(si); |
295 | *priority = SCHED_INFO_PRIORITY(si); |
296 | |
297 | exit: |
298 | tbthread_mutex_unlock(&desc_mutex); |
299 | return ret; |
300 | } |
301 | |
302 | //------------------------------------------------------------------------------ |
303 | // Set attribute scheduling policy |
304 | //------------------------------------------------------------------------------ |
305 | int tbthread_attr_setschedpolicy(tbthread_attr_t *attr, int policy) |
306 | { |
307 | if(policy != SCHED_NORMAL && policy != SCHED_FIFO && policy != SCHED_RR) |
308 | return -EINVAL; |
309 | attr->sched_policy = policy; |
310 | return 0; |
311 | } |
312 | |
313 | //------------------------------------------------------------------------------ |
314 | // Get attribute scheduling priority |
315 | //------------------------------------------------------------------------------ |
316 | int tbthread_attr_setschedpriority(tbthread_attr_t *attr, int priority) |
317 | { |
318 | if(priority < 0 || priority > 99) |
319 | return -EINVAL; |
320 | attr->sched_priority = priority; |
321 | } |
322 | |
323 | //------------------------------------------------------------------------------ |
324 | // Inherit scheduler attributes |
325 | //------------------------------------------------------------------------------ |
326 | int tbthread_attr_setinheritsched(tbthread_attr_t *attr, int inheritsched) |
327 | { |
328 | if(inheritsched != TBTHREAD_INHERIT_SCHED && |
329 | inheritsched != TBTHREAD_EXPLICIT_SCHED) |
330 | return -EINVAL; |
331 | attr->sched_inherit = inheritsched; |
332 | return 0; |
333 | } |
334 | |