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//------------------------------------------------------------------------------
28struct tb_sched_param
29{
30 int sched_priority;
31};
32
33int 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//------------------------------------------------------------------------------
45static 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
54void 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
69exit:
70 tb_futex_unlock(&owner->lock);
71}
72
73//------------------------------------------------------------------------------
74// Un-schedule a protected mutex
75//------------------------------------------------------------------------------
76void 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//------------------------------------------------------------------------------
97struct inh_mutex {
98 tbthread_mutex_t *mutex;
99 uint16_t sched_info;
100};
101
102void 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
122exit:
123 tb_futex_unlock(&owner->lock);
124}
125
126//------------------------------------------------------------------------------
127// Un-schedule an inherit mutex
128//------------------------------------------------------------------------------
129static int mutex_equal(void *this, void *other)
130{
131 struct inh_mutex *o = other;
132 return this == o->mutex;
133}
134
135void 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
155exit:
156 tb_futex_unlock(&owner->lock);
157}
158
159//------------------------------------------------------------------------------
160// Schedule an inherit mutex
161//------------------------------------------------------------------------------
162void 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
194exit:
195 tb_futex_unlock(&owner->lock);
196}
197
198//------------------------------------------------------------------------------
199// Compute scheduler
200//------------------------------------------------------------------------------
201int 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//------------------------------------------------------------------------------
254int 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
275exit:
276 tbthread_mutex_unlock(&desc_mutex);
277 return ret;
278}
279
280//------------------------------------------------------------------------------
281// Get scheduling parameters
282//------------------------------------------------------------------------------
283int 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
297exit:
298 tbthread_mutex_unlock(&desc_mutex);
299 return ret;
300}
301
302//------------------------------------------------------------------------------
303// Set attribute scheduling policy
304//------------------------------------------------------------------------------
305int 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//------------------------------------------------------------------------------
316int 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//------------------------------------------------------------------------------
326int 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