Blame nptl/tpp.c

Packit 6c4009
/* Thread Priority Protect helpers.
Packit 6c4009
   Copyright (C) 2006-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Jakub Jelinek <jakub@redhat.com>, 2006.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <atomic.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <pthreadP.h>
Packit 6c4009
#include <sched.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <atomic.h>
Packit 6c4009
Packit 6c4009
Packit 6c4009
int __sched_fifo_min_prio = -1;
Packit 6c4009
int __sched_fifo_max_prio = -1;
Packit 6c4009
Packit 6c4009
/* We only want to initialize __sched_fifo_min_prio and __sched_fifo_max_prio
Packit 6c4009
   once.  The standard solution would be similar to pthread_once, but then
Packit 6c4009
   readers would need to use an acquire fence.  In this specific case,
Packit 6c4009
   initialization is comprised of just idempotent writes to two variables
Packit 6c4009
   that have an initial value of -1.  Therefore, we can treat each variable as
Packit 6c4009
   a separate, at-least-once initialized value.  This enables using just
Packit 6c4009
   relaxed MO loads and stores, but requires that consumers check for
Packit 6c4009
   initialization of each value that is to be used; see
Packit 6c4009
   __pthread_tpp_change_priority for an example.
Packit 6c4009
 */
Packit 6c4009
void
Packit 6c4009
__init_sched_fifo_prio (void)
Packit 6c4009
{
Packit 6c4009
  atomic_store_relaxed (&__sched_fifo_max_prio,
Packit 6c4009
			__sched_get_priority_max (SCHED_FIFO));
Packit 6c4009
  atomic_store_relaxed (&__sched_fifo_min_prio,
Packit 6c4009
			__sched_get_priority_min (SCHED_FIFO));
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
__pthread_tpp_change_priority (int previous_prio, int new_prio)
Packit 6c4009
{
Packit 6c4009
  struct pthread *self = THREAD_SELF;
Packit 6c4009
  struct priority_protection_data *tpp = THREAD_GETMEM (self, tpp);
Packit 6c4009
  int fifo_min_prio = atomic_load_relaxed (&__sched_fifo_min_prio);
Packit 6c4009
  int fifo_max_prio = atomic_load_relaxed (&__sched_fifo_max_prio);
Packit 6c4009
Packit 6c4009
  if (tpp == NULL)
Packit 6c4009
    {
Packit 6c4009
      /* See __init_sched_fifo_prio.  We need both the min and max prio,
Packit 6c4009
         so need to check both, and run initialization if either one is
Packit 6c4009
         not initialized.  The memory model's write-read coherence rule
Packit 6c4009
         makes this work.  */
Packit 6c4009
      if (fifo_min_prio == -1 || fifo_max_prio == -1)
Packit 6c4009
	{
Packit 6c4009
	  __init_sched_fifo_prio ();
Packit 6c4009
	  fifo_min_prio = atomic_load_relaxed (&__sched_fifo_min_prio);
Packit 6c4009
	  fifo_max_prio = atomic_load_relaxed (&__sched_fifo_max_prio);
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      size_t size = sizeof *tpp;
Packit 6c4009
      size += (fifo_max_prio - fifo_min_prio + 1)
Packit 6c4009
	      * sizeof (tpp->priomap[0]);
Packit 6c4009
      tpp = calloc (size, 1);
Packit 6c4009
      if (tpp == NULL)
Packit 6c4009
	return ENOMEM;
Packit 6c4009
      tpp->priomax = fifo_min_prio - 1;
Packit 6c4009
      THREAD_SETMEM (self, tpp, tpp);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  assert (new_prio == -1
Packit 6c4009
	  || (new_prio >= fifo_min_prio
Packit 6c4009
	      && new_prio <= fifo_max_prio));
Packit 6c4009
  assert (previous_prio == -1
Packit 6c4009
	  || (previous_prio >= fifo_min_prio
Packit 6c4009
	      && previous_prio <= fifo_max_prio));
Packit 6c4009
Packit 6c4009
  int priomax = tpp->priomax;
Packit 6c4009
  int newpriomax = priomax;
Packit 6c4009
  if (new_prio != -1)
Packit 6c4009
    {
Packit 6c4009
      if (tpp->priomap[new_prio - fifo_min_prio] + 1 == 0)
Packit 6c4009
	return EAGAIN;
Packit 6c4009
      ++tpp->priomap[new_prio - fifo_min_prio];
Packit 6c4009
      if (new_prio > priomax)
Packit 6c4009
	newpriomax = new_prio;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (previous_prio != -1)
Packit 6c4009
    {
Packit 6c4009
      if (--tpp->priomap[previous_prio - fifo_min_prio] == 0
Packit 6c4009
	  && priomax == previous_prio
Packit 6c4009
	  && previous_prio > new_prio)
Packit 6c4009
	{
Packit 6c4009
	  int i;
Packit 6c4009
	  for (i = previous_prio - 1; i >= fifo_min_prio; --i)
Packit 6c4009
	    if (tpp->priomap[i - fifo_min_prio])
Packit 6c4009
	      break;
Packit 6c4009
	  newpriomax = i;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (priomax == newpriomax)
Packit 6c4009
    return 0;
Packit 6c4009
Packit 6c4009
  /* See CREATE THREAD NOTES in nptl/pthread_create.c.  */
Packit 6c4009
  lll_lock (self->lock, LLL_PRIVATE);
Packit 6c4009
Packit 6c4009
  tpp->priomax = newpriomax;
Packit 6c4009
Packit 6c4009
  int result = 0;
Packit 6c4009
Packit 6c4009
  if ((self->flags & ATTR_FLAG_SCHED_SET) == 0)
Packit 6c4009
    {
Packit 6c4009
      if (__sched_getparam (self->tid, &self->schedparam) != 0)
Packit 6c4009
	result = errno;
Packit 6c4009
      else
Packit 6c4009
	self->flags |= ATTR_FLAG_SCHED_SET;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if ((self->flags & ATTR_FLAG_POLICY_SET) == 0)
Packit 6c4009
    {
Packit 6c4009
      self->schedpolicy = __sched_getscheduler (self->tid);
Packit 6c4009
      if (self->schedpolicy == -1)
Packit 6c4009
	result = errno;
Packit 6c4009
      else
Packit 6c4009
	self->flags |= ATTR_FLAG_POLICY_SET;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (result == 0)
Packit 6c4009
    {
Packit 6c4009
      struct sched_param sp = self->schedparam;
Packit 6c4009
      if (sp.sched_priority < newpriomax || sp.sched_priority < priomax)
Packit 6c4009
	{
Packit 6c4009
	  if (sp.sched_priority < newpriomax)
Packit 6c4009
	    sp.sched_priority = newpriomax;
Packit 6c4009
Packit 6c4009
	  if (__sched_setscheduler (self->tid, self->schedpolicy, &sp) < 0)
Packit 6c4009
	    result = errno;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  lll_unlock (self->lock, LLL_PRIVATE);
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
__pthread_current_priority (void)
Packit 6c4009
{
Packit 6c4009
  struct pthread *self = THREAD_SELF;
Packit 6c4009
  if ((self->flags & (ATTR_FLAG_POLICY_SET | ATTR_FLAG_SCHED_SET))
Packit 6c4009
      == (ATTR_FLAG_POLICY_SET | ATTR_FLAG_SCHED_SET))
Packit 6c4009
    return self->schedparam.sched_priority;
Packit 6c4009
Packit 6c4009
  int result = 0;
Packit 6c4009
Packit 6c4009
  /* See CREATE THREAD NOTES in nptl/pthread_create.c.  */
Packit 6c4009
  lll_lock (self->lock, LLL_PRIVATE);
Packit 6c4009
Packit 6c4009
  if ((self->flags & ATTR_FLAG_SCHED_SET) == 0)
Packit 6c4009
    {
Packit 6c4009
      if (__sched_getparam (self->tid, &self->schedparam) != 0)
Packit 6c4009
	result = -1;
Packit 6c4009
      else
Packit 6c4009
	self->flags |= ATTR_FLAG_SCHED_SET;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if ((self->flags & ATTR_FLAG_POLICY_SET) == 0)
Packit 6c4009
    {
Packit 6c4009
      self->schedpolicy = __sched_getscheduler (self->tid);
Packit 6c4009
      if (self->schedpolicy == -1)
Packit 6c4009
	result = -1;
Packit 6c4009
      else
Packit 6c4009
	self->flags |= ATTR_FLAG_POLICY_SET;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (result != -1)
Packit 6c4009
    result = self->schedparam.sched_priority;
Packit 6c4009
Packit 6c4009
  lll_unlock (self->lock, LLL_PRIVATE);
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}