1 /* Thread Priority Protect helpers.
2    Copyright (C) 2006, 2007 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Jakub Jelinek <jakub@redhat.com>, 2006.
5 
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10 
11    The GNU C Library 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 GNU
14    Lesser General Public License for more details.
15 
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, see
18    <http://www.gnu.org/licenses/>.  */
19 
20 #include <assert.h>
21 #include <atomic.h>
22 #include <errno.h>
23 #include <pthreadP.h>
24 #include <sched.h>
25 #include <stdlib.h>
26 #include "uClibc-glue.h"
27 
28 int __sched_fifo_min_prio = -1;
29 int __sched_fifo_max_prio = -1;
30 
31 void
__init_sched_fifo_prio(void)32 __init_sched_fifo_prio (void)
33 {
34   __sched_fifo_max_prio = sched_get_priority_max (SCHED_FIFO);
35   atomic_write_barrier ();
36   __sched_fifo_min_prio = sched_get_priority_min (SCHED_FIFO);
37 }
38 
39 int
__pthread_tpp_change_priority(int previous_prio,int new_prio)40 __pthread_tpp_change_priority (int previous_prio, int new_prio)
41 {
42   struct pthread *self = THREAD_SELF;
43   struct priority_protection_data *tpp = THREAD_GETMEM (self, tpp);
44 
45   if (tpp == NULL)
46     {
47       if (__sched_fifo_min_prio == -1)
48 	__init_sched_fifo_prio ();
49 
50       size_t size = sizeof *tpp;
51       size += (__sched_fifo_max_prio - __sched_fifo_min_prio + 1)
52 	      * sizeof (tpp->priomap[0]);
53       tpp = calloc (size, 1);
54       if (tpp == NULL)
55 	return ENOMEM;
56       tpp->priomax = __sched_fifo_min_prio - 1;
57       THREAD_SETMEM (self, tpp, tpp);
58     }
59 
60   assert (new_prio == -1
61 	  || (new_prio >= __sched_fifo_min_prio
62 	      && new_prio <= __sched_fifo_max_prio));
63   assert (previous_prio == -1
64 	  || (previous_prio >= __sched_fifo_min_prio
65 	      && previous_prio <= __sched_fifo_max_prio));
66 
67   int priomax = tpp->priomax;
68   int newpriomax = priomax;
69   if (new_prio != -1)
70     {
71       if (tpp->priomap[new_prio - __sched_fifo_min_prio] + 1 == 0)
72 	return EAGAIN;
73       ++tpp->priomap[new_prio - __sched_fifo_min_prio];
74       if (new_prio > priomax)
75 	newpriomax = new_prio;
76     }
77 
78   if (previous_prio != -1)
79     {
80       if (--tpp->priomap[previous_prio - __sched_fifo_min_prio] == 0
81 	  && priomax == previous_prio
82 	  && previous_prio > new_prio)
83 	{
84 	  int i;
85 	  for (i = previous_prio - 1; i >= __sched_fifo_min_prio; --i)
86 	    if (tpp->priomap[i - __sched_fifo_min_prio])
87 	      break;
88 	  newpriomax = i;
89 	}
90     }
91 
92   if (priomax == newpriomax)
93     return 0;
94 
95   lll_lock (self->lock, LLL_PRIVATE);
96 
97   tpp->priomax = newpriomax;
98 
99   int result = 0;
100 
101   if ((self->flags & ATTR_FLAG_SCHED_SET) == 0)
102     {
103       if (__sched_getparam (self->tid, &self->schedparam) != 0)
104 	result = errno;
105       else
106 	self->flags |= ATTR_FLAG_SCHED_SET;
107     }
108 
109   if ((self->flags & ATTR_FLAG_POLICY_SET) == 0)
110     {
111       self->schedpolicy = __sched_getscheduler (self->tid);
112       if (self->schedpolicy == -1)
113 	result = errno;
114       else
115 	self->flags |= ATTR_FLAG_POLICY_SET;
116     }
117 
118   if (result == 0)
119     {
120       struct sched_param sp = self->schedparam;
121       if (sp.sched_priority < newpriomax || sp.sched_priority < priomax)
122 	{
123 	  if (sp.sched_priority < newpriomax)
124 	    sp.sched_priority = newpriomax;
125 
126 	  if (__sched_setscheduler (self->tid, self->schedpolicy, &sp) < 0)
127 	    result = errno;
128 	}
129     }
130 
131   lll_unlock (self->lock, LLL_PRIVATE);
132 
133   return result;
134 }
135 
136 int
__pthread_current_priority(void)137 __pthread_current_priority (void)
138 {
139   struct pthread *self = THREAD_SELF;
140   if ((self->flags & (ATTR_FLAG_POLICY_SET | ATTR_FLAG_SCHED_SET))
141       == (ATTR_FLAG_POLICY_SET | ATTR_FLAG_SCHED_SET))
142     return self->schedparam.sched_priority;
143 
144   int result = 0;
145 
146   lll_lock (self->lock, LLL_PRIVATE);
147 
148   if ((self->flags & ATTR_FLAG_SCHED_SET) == 0)
149     {
150       if (__sched_getparam (self->tid, &self->schedparam) != 0)
151 	result = -1;
152       else
153 	self->flags |= ATTR_FLAG_SCHED_SET;
154     }
155 
156   if ((self->flags & ATTR_FLAG_POLICY_SET) == 0)
157     {
158       self->schedpolicy = __sched_getscheduler (self->tid);
159       if (self->schedpolicy == -1)
160 	result = -1;
161       else
162 	self->flags |= ATTR_FLAG_POLICY_SET;
163     }
164 
165   if (result != -1)
166     result = self->schedparam.sched_priority;
167 
168   lll_unlock (self->lock, LLL_PRIVATE);
169 
170   return result;
171 }
172