1 | /* Thread Priority Protect helpers. |
2 | Copyright (C) 2006-2021 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 | <https://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 <atomic.h> |
27 | |
28 | int __sched_fifo_min_prio = -1; |
29 | libc_hidden_data_def (__sched_fifo_min_prio) |
30 | int __sched_fifo_max_prio = -1; |
31 | libc_hidden_data_def (__sched_fifo_max_prio) |
32 | |
33 | /* We only want to initialize __sched_fifo_min_prio and __sched_fifo_max_prio |
34 | once. The standard solution would be similar to pthread_once, but then |
35 | readers would need to use an acquire fence. In this specific case, |
36 | initialization is comprised of just idempotent writes to two variables |
37 | that have an initial value of -1. Therefore, we can treat each variable as |
38 | a separate, at-least-once initialized value. This enables using just |
39 | relaxed MO loads and stores, but requires that consumers check for |
40 | initialization of each value that is to be used; see |
41 | __pthread_tpp_change_priority for an example. |
42 | */ |
43 | void |
44 | __init_sched_fifo_prio (void) |
45 | { |
46 | atomic_store_relaxed (&__sched_fifo_max_prio, |
47 | __sched_get_priority_max (SCHED_FIFO)); |
48 | atomic_store_relaxed (&__sched_fifo_min_prio, |
49 | __sched_get_priority_min (SCHED_FIFO)); |
50 | } |
51 | libc_hidden_def (__init_sched_fifo_prio) |
52 | |
53 | int |
54 | __pthread_tpp_change_priority (int previous_prio, int new_prio) |
55 | { |
56 | struct pthread *self = THREAD_SELF; |
57 | struct priority_protection_data *tpp = THREAD_GETMEM (self, tpp); |
58 | int fifo_min_prio = atomic_load_relaxed (&__sched_fifo_min_prio); |
59 | int fifo_max_prio = atomic_load_relaxed (&__sched_fifo_max_prio); |
60 | |
61 | if (tpp == NULL) |
62 | { |
63 | /* See __init_sched_fifo_prio. We need both the min and max prio, |
64 | so need to check both, and run initialization if either one is |
65 | not initialized. The memory model's write-read coherence rule |
66 | makes this work. */ |
67 | if (fifo_min_prio == -1 || fifo_max_prio == -1) |
68 | { |
69 | __init_sched_fifo_prio (); |
70 | fifo_min_prio = atomic_load_relaxed (&__sched_fifo_min_prio); |
71 | fifo_max_prio = atomic_load_relaxed (&__sched_fifo_max_prio); |
72 | } |
73 | |
74 | size_t size = sizeof *tpp; |
75 | size += (fifo_max_prio - fifo_min_prio + 1) |
76 | * sizeof (tpp->priomap[0]); |
77 | tpp = calloc (size, 1); |
78 | if (tpp == NULL) |
79 | return ENOMEM; |
80 | tpp->priomax = fifo_min_prio - 1; |
81 | THREAD_SETMEM (self, tpp, tpp); |
82 | } |
83 | |
84 | assert (new_prio == -1 |
85 | || (new_prio >= fifo_min_prio |
86 | && new_prio <= fifo_max_prio)); |
87 | assert (previous_prio == -1 |
88 | || (previous_prio >= fifo_min_prio |
89 | && previous_prio <= fifo_max_prio)); |
90 | |
91 | int priomax = tpp->priomax; |
92 | int newpriomax = priomax; |
93 | if (new_prio != -1) |
94 | { |
95 | if (tpp->priomap[new_prio - fifo_min_prio] + 1 == 0) |
96 | return EAGAIN; |
97 | ++tpp->priomap[new_prio - fifo_min_prio]; |
98 | if (new_prio > priomax) |
99 | newpriomax = new_prio; |
100 | } |
101 | |
102 | if (previous_prio != -1) |
103 | { |
104 | if (--tpp->priomap[previous_prio - fifo_min_prio] == 0 |
105 | && priomax == previous_prio |
106 | && previous_prio > new_prio) |
107 | { |
108 | int i; |
109 | for (i = previous_prio - 1; i >= fifo_min_prio; --i) |
110 | if (tpp->priomap[i - fifo_min_prio]) |
111 | break; |
112 | newpriomax = i; |
113 | } |
114 | } |
115 | |
116 | if (priomax == newpriomax) |
117 | return 0; |
118 | |
119 | /* See CREATE THREAD NOTES in nptl/pthread_create.c. */ |
120 | lll_lock (self->lock, LLL_PRIVATE); |
121 | |
122 | tpp->priomax = newpriomax; |
123 | |
124 | int result = 0; |
125 | |
126 | if ((self->flags & ATTR_FLAG_SCHED_SET) == 0) |
127 | { |
128 | if (__sched_getparam (self->tid, &self->schedparam) != 0) |
129 | result = errno; |
130 | else |
131 | self->flags |= ATTR_FLAG_SCHED_SET; |
132 | } |
133 | |
134 | if ((self->flags & ATTR_FLAG_POLICY_SET) == 0) |
135 | { |
136 | self->schedpolicy = __sched_getscheduler (self->tid); |
137 | if (self->schedpolicy == -1) |
138 | result = errno; |
139 | else |
140 | self->flags |= ATTR_FLAG_POLICY_SET; |
141 | } |
142 | |
143 | if (result == 0) |
144 | { |
145 | struct sched_param sp = self->schedparam; |
146 | if (sp.sched_priority < newpriomax || sp.sched_priority < priomax) |
147 | { |
148 | if (sp.sched_priority < newpriomax) |
149 | sp.sched_priority = newpriomax; |
150 | |
151 | if (__sched_setscheduler (self->tid, self->schedpolicy, &sp) < 0) |
152 | result = errno; |
153 | } |
154 | } |
155 | |
156 | lll_unlock (self->lock, LLL_PRIVATE); |
157 | |
158 | return result; |
159 | } |
160 | libc_hidden_def (__pthread_tpp_change_priority) |
161 | |
162 | int |
163 | __pthread_current_priority (void) |
164 | { |
165 | struct pthread *self = THREAD_SELF; |
166 | if ((self->flags & (ATTR_FLAG_POLICY_SET | ATTR_FLAG_SCHED_SET)) |
167 | == (ATTR_FLAG_POLICY_SET | ATTR_FLAG_SCHED_SET)) |
168 | return self->schedparam.sched_priority; |
169 | |
170 | int result = 0; |
171 | |
172 | /* See CREATE THREAD NOTES in nptl/pthread_create.c. */ |
173 | lll_lock (self->lock, LLL_PRIVATE); |
174 | |
175 | if ((self->flags & ATTR_FLAG_SCHED_SET) == 0) |
176 | { |
177 | if (__sched_getparam (self->tid, &self->schedparam) != 0) |
178 | result = -1; |
179 | else |
180 | self->flags |= ATTR_FLAG_SCHED_SET; |
181 | } |
182 | |
183 | if ((self->flags & ATTR_FLAG_POLICY_SET) == 0) |
184 | { |
185 | self->schedpolicy = __sched_getscheduler (self->tid); |
186 | if (self->schedpolicy == -1) |
187 | result = -1; |
188 | else |
189 | self->flags |= ATTR_FLAG_POLICY_SET; |
190 | } |
191 | |
192 | if (result != -1) |
193 | result = self->schedparam.sched_priority; |
194 | |
195 | lll_unlock (self->lock, LLL_PRIVATE); |
196 | |
197 | return result; |
198 | } |
199 | libc_hidden_def (__pthread_current_priority) |
200 | |