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