tpp.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. /* Thread Priority Protect helpers.
  2. Copyright (C) 2006-2019 Free Software Foundation, Inc.
  3. This file is part of the GNU C Library.
  4. Contributed by Jakub Jelinek <jakub@redhat.com>, 2006.
  5. The GNU C Library is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU Lesser General Public
  7. License as published by the Free Software Foundation; either
  8. version 2.1 of the License, or (at your option) any later version.
  9. The GNU C Library is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. Lesser General Public License for more details.
  13. You should have received a copy of the GNU Lesser General Public
  14. License along with the GNU C Library; if not, see
  15. <http://www.gnu.org/licenses/>. */
  16. #include <assert.h>
  17. #include <atomic.h>
  18. #include <errno.h>
  19. #include <pthreadP.h>
  20. #include <sched.h>
  21. #include <stdlib.h>
  22. #include <atomic.h>
  23. int __sched_fifo_min_prio = -1;
  24. int __sched_fifo_max_prio = -1;
  25. /* We only want to initialize __sched_fifo_min_prio and __sched_fifo_max_prio
  26. once. The standard solution would be similar to pthread_once, but then
  27. readers would need to use an acquire fence. In this specific case,
  28. initialization is comprised of just idempotent writes to two variables
  29. that have an initial value of -1. Therefore, we can treat each variable as
  30. a separate, at-least-once initialized value. This enables using just
  31. relaxed MO loads and stores, but requires that consumers check for
  32. initialization of each value that is to be used; see
  33. __pthread_tpp_change_priority for an example.
  34. */
  35. void
  36. __init_sched_fifo_prio (void)
  37. {
  38. atomic_store_relaxed (&__sched_fifo_max_prio,
  39. __sched_get_priority_max (SCHED_FIFO));
  40. atomic_store_relaxed (&__sched_fifo_min_prio,
  41. __sched_get_priority_min (SCHED_FIFO));
  42. }
  43. int
  44. __pthread_tpp_change_priority (int previous_prio, int new_prio)
  45. {
  46. struct pthread *self = THREAD_SELF;
  47. struct priority_protection_data *tpp = THREAD_GETMEM (self, tpp);
  48. int fifo_min_prio = atomic_load_relaxed (&__sched_fifo_min_prio);
  49. int fifo_max_prio = atomic_load_relaxed (&__sched_fifo_max_prio);
  50. if (tpp == NULL)
  51. {
  52. /* See __init_sched_fifo_prio. We need both the min and max prio,
  53. so need to check both, and run initialization if either one is
  54. not initialized. The memory model's write-read coherence rule
  55. makes this work. */
  56. if (fifo_min_prio == -1 || fifo_max_prio == -1)
  57. {
  58. __init_sched_fifo_prio ();
  59. fifo_min_prio = atomic_load_relaxed (&__sched_fifo_min_prio);
  60. fifo_max_prio = atomic_load_relaxed (&__sched_fifo_max_prio);
  61. }
  62. size_t size = sizeof *tpp;
  63. size += (fifo_max_prio - fifo_min_prio + 1)
  64. * sizeof (tpp->priomap[0]);
  65. tpp = calloc (size, 1);
  66. if (tpp == NULL)
  67. return ENOMEM;
  68. tpp->priomax = fifo_min_prio - 1;
  69. THREAD_SETMEM (self, tpp, tpp);
  70. }
  71. assert (new_prio == -1
  72. || (new_prio >= fifo_min_prio
  73. && new_prio <= fifo_max_prio));
  74. assert (previous_prio == -1
  75. || (previous_prio >= fifo_min_prio
  76. && previous_prio <= fifo_max_prio));
  77. int priomax = tpp->priomax;
  78. int newpriomax = priomax;
  79. if (new_prio != -1)
  80. {
  81. if (tpp->priomap[new_prio - fifo_min_prio] + 1 == 0)
  82. return EAGAIN;
  83. ++tpp->priomap[new_prio - fifo_min_prio];
  84. if (new_prio > priomax)
  85. newpriomax = new_prio;
  86. }
  87. if (previous_prio != -1)
  88. {
  89. if (--tpp->priomap[previous_prio - fifo_min_prio] == 0
  90. && priomax == previous_prio
  91. && previous_prio > new_prio)
  92. {
  93. int i;
  94. for (i = previous_prio - 1; i >= fifo_min_prio; --i)
  95. if (tpp->priomap[i - fifo_min_prio])
  96. break;
  97. newpriomax = i;
  98. }
  99. }
  100. if (priomax == newpriomax)
  101. return 0;
  102. /* See CREATE THREAD NOTES in nptl/pthread_create.c. */
  103. lll_lock (self->lock, LLL_PRIVATE);
  104. tpp->priomax = newpriomax;
  105. int result = 0;
  106. if ((self->flags & ATTR_FLAG_SCHED_SET) == 0)
  107. {
  108. if (__sched_getparam (self->tid, &self->schedparam) != 0)
  109. result = errno;
  110. else
  111. self->flags |= ATTR_FLAG_SCHED_SET;
  112. }
  113. if ((self->flags & ATTR_FLAG_POLICY_SET) == 0)
  114. {
  115. self->schedpolicy = __sched_getscheduler (self->tid);
  116. if (self->schedpolicy == -1)
  117. result = errno;
  118. else
  119. self->flags |= ATTR_FLAG_POLICY_SET;
  120. }
  121. if (result == 0)
  122. {
  123. struct sched_param sp = self->schedparam;
  124. if (sp.sched_priority < newpriomax || sp.sched_priority < priomax)
  125. {
  126. if (sp.sched_priority < newpriomax)
  127. sp.sched_priority = newpriomax;
  128. if (__sched_setscheduler (self->tid, self->schedpolicy, &sp) < 0)
  129. result = errno;
  130. }
  131. }
  132. lll_unlock (self->lock, LLL_PRIVATE);
  133. return result;
  134. }
  135. int
  136. __pthread_current_priority (void)
  137. {
  138. struct pthread *self = THREAD_SELF;
  139. if ((self->flags & (ATTR_FLAG_POLICY_SET | ATTR_FLAG_SCHED_SET))
  140. == (ATTR_FLAG_POLICY_SET | ATTR_FLAG_SCHED_SET))
  141. return self->schedparam.sched_priority;
  142. int result = 0;
  143. /* See CREATE THREAD NOTES in nptl/pthread_create.c. */
  144. lll_lock (self->lock, LLL_PRIVATE);
  145. if ((self->flags & ATTR_FLAG_SCHED_SET) == 0)
  146. {
  147. if (__sched_getparam (self->tid, &self->schedparam) != 0)
  148. result = -1;
  149. else
  150. self->flags |= ATTR_FLAG_SCHED_SET;
  151. }
  152. if ((self->flags & ATTR_FLAG_POLICY_SET) == 0)
  153. {
  154. self->schedpolicy = __sched_getscheduler (self->tid);
  155. if (self->schedpolicy == -1)
  156. result = -1;
  157. else
  158. self->flags |= ATTR_FLAG_POLICY_SET;
  159. }
  160. if (result != -1)
  161. result = self->schedparam.sched_priority;
  162. lll_unlock (self->lock, LLL_PRIVATE);
  163. return result;
  164. }