1  /* Copyright (C) 1994-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       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  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <stddef.h>
      19  #include <errno.h>
      20  #include <sys/time.h>
      21  #include <time.h>
      22  #include <hurd.h>
      23  #include <hurd/signal.h>
      24  #include <hurd/sigpreempt.h>
      25  #include <hurd/msg_request.h>
      26  #include <mach.h>
      27  #include <mach/message.h>
      28  #include <mach/setup-thread.h>
      29  
      30  /* XXX Temporary cheezoid implementation of ITIMER_REAL/SIGALRM.  */
      31  
      32  spin_lock_t _hurd_itimer_lock = SPIN_LOCK_INITIALIZER;
      33  struct itimerval _hurd_itimerval; /* Current state of the timer.  */
      34  mach_port_t _hurd_itimer_port;	/* Port the timer thread blocks on.  */
      35  thread_t _hurd_itimer_thread;	/* Thread waiting for timeout.  */
      36  int _hurd_itimer_thread_suspended; /* Nonzero if that thread is suspended.  */
      37  vm_address_t _hurd_itimer_thread_stack_base; /* Base of its stack.  */
      38  vm_size_t _hurd_itimer_thread_stack_size; /* Size of its stack.  */
      39  struct timeval _hurd_itimer_started; /* Time the thread started waiting.  */
      40  
      41  static void
      42  quantize_timeval (struct timeval *tv)
      43  {
      44    static time_t quantum = -1;
      45  
      46    if (quantum == -1)
      47      quantum = 1000000 / __getclktck ();
      48  
      49    tv->tv_usec = ((tv->tv_usec + (quantum - 1)) / quantum) * quantum;
      50    if (tv->tv_usec >= 1000000)
      51      {
      52        ++tv->tv_sec;
      53        tv->tv_usec -= 1000000;
      54      }
      55  }
      56  
      57  static inline void
      58  subtract_timeval (struct timeval *from, const struct timeval *subtract)
      59  {
      60    from->tv_usec -= subtract->tv_usec;
      61    from->tv_sec -= subtract->tv_sec;
      62    while (from->tv_usec < 0)
      63      {
      64        --from->tv_sec;
      65        from->tv_usec += 1000000;
      66      }
      67  }
      68  
      69  /* Function run by the itimer thread.
      70     This code must be very careful not ever to require a MiG reply port.  */
      71  
      72  static void
      73  timer_thread (void)
      74  {
      75    while (1)
      76      {
      77        error_t err;
      78        /* The only message we ever expect to receive is the reply from the
      79           signal thread to a sig_post call we did.  We never examine the
      80  	 contents.  */
      81        struct
      82  	{
      83  	  mach_msg_header_t header;
      84  	  mach_msg_type_t return_code_type;
      85  	  error_t return_code;
      86  	} msg;
      87  
      88        /* Wait for a message on a port that no one sends to.  The purpose is
      89  	 the receive timeout.  Notice interrupts so that if we are
      90  	 thread_abort'd, we will loop around and fetch new values from
      91  	 _hurd_itimerval.  */
      92        err = __mach_msg (&msg.header,
      93  			MACH_RCV_MSG|MACH_RCV_TIMEOUT|MACH_RCV_INTERRUPT,
      94  			0, sizeof(msg), _hurd_itimer_port,
      95  			_hurd_itimerval.it_value.tv_sec * 1000
      96  			+ _hurd_itimerval.it_value.tv_usec / 1000,
      97  			MACH_PORT_NULL);
      98        switch (err)
      99  	{
     100  	case MACH_RCV_TIMED_OUT:
     101  	  /* We got the expected timeout.  Send a message to the signal
     102  	     thread to tell it to post a SIGALRM signal.  We use
     103  	     _hurd_itimer_port as the reply port just so we will block until
     104  	     the signal thread has frobnicated things to reload the itimer or
     105  	     has terminated this thread.  */
     106  	  __msg_sig_post_request (_hurd_msgport,
     107  				  _hurd_itimer_port,
     108  				  MACH_MSG_TYPE_MAKE_SEND_ONCE,
     109  				  SIGALRM, SI_TIMER, __mach_task_self ());
     110  	  break;
     111  
     112  	case MACH_RCV_INTERRUPTED:
     113  	  /* We were thread_abort'd.  This is to tell us that
     114  	     _hurd_itimerval has changed and we need to reexamine it
     115  	     and start waiting with the new timeout value.  */
     116  	  break;
     117  
     118  	case MACH_MSG_SUCCESS:
     119  	  /* We got the reply message from the sig_post_request above.
     120  	     Ignore it and reexamine the timer value.  */
     121  	  __mach_msg_destroy (&msg.header); /* Just in case.  */
     122  	  break;
     123  
     124  	default:
     125  	  /* Unexpected lossage.  Oh well, keep trying.  */
     126  	  break;
     127  	}
     128      }
     129  }
     130  
     131  
     132  /* Forward declaration.  */
     133  static int setitimer_locked (const struct itimerval *new,
     134  			     struct itimerval *old, void *crit,
     135  			     int hurd_siglocked);
     136  
     137  static sighandler_t
     138  restart_itimer (struct hurd_signal_preemptor *preemptor,
     139  		struct hurd_sigstate *ss,
     140  		int *signo, struct hurd_signal_detail *detail)
     141  {
     142    /* This function gets called in the signal thread
     143       each time a SIGALRM is arriving (even if blocked).  */
     144    struct itimerval it;
     145  
     146    /* Either reload or disable the itimer.  */
     147    __spin_lock (&_hurd_itimer_lock);
     148    it.it_value = it.it_interval = _hurd_itimerval.it_interval;
     149    setitimer_locked (&it, NULL, NULL, 1);
     150  
     151    /* Continue with normal delivery (or hold, etc.) of SIGALRM.  */
     152    return SIG_ERR;
     153  }
     154  
     155  
     156  /* Called before any normal SIGALRM signal is delivered.
     157     Reload the itimer, or disable the itimer.  */
     158  
     159  static int
     160  setitimer_locked (const struct itimerval *new, struct itimerval *old,
     161  		  void *crit, int hurd_siglocked)
     162  {
     163    struct itimerval newval;
     164    struct timeval now, remaining, elapsed;
     165    struct timeval old_interval;
     166    error_t err;
     167  
     168    inline void kill_itimer_thread (void)
     169      {
     170        __thread_terminate (_hurd_itimer_thread);
     171        __vm_deallocate (__mach_task_self (),
     172  		       _hurd_itimer_thread_stack_base,
     173  		       _hurd_itimer_thread_stack_size);
     174        _hurd_itimer_thread = MACH_PORT_NULL;
     175      }
     176  
     177    if (!new)
     178      {
     179        /* Just return the current value in OLD without changing anything.
     180  	 This is what BSD does, even though it's not documented. */
     181        if (old)
     182  	*old = _hurd_itimerval;
     183        spin_unlock (&_hurd_itimer_lock);
     184        _hurd_critical_section_unlock (crit);
     185        return 0;
     186      }
     187  
     188    newval = *new;
     189    quantize_timeval (&newval.it_interval);
     190    quantize_timeval (&newval.it_value);
     191    if ((newval.it_value.tv_sec | newval.it_value.tv_usec) != 0)
     192      {
     193        /* Make sure the itimer thread is set up.  */
     194  
     195        /* Set up a signal preemptor global for all threads to
     196  	 run `restart_itimer' each time a SIGALRM would arrive.  */
     197        static struct hurd_signal_preemptor preemptor =
     198  	{
     199  	  __sigmask (SIGALRM), SI_TIMER, SI_TIMER,
     200  	  &restart_itimer,
     201  	};
     202        if (!hurd_siglocked)
     203  	__mutex_lock (&_hurd_siglock);
     204        if (! preemptor.next && _hurdsig_preemptors != &preemptor)
     205  	{
     206  	  preemptor.next = _hurdsig_preemptors;
     207  	  _hurdsig_preemptors = &preemptor;
     208  	  _hurdsig_preempted_set |= preemptor.signals;
     209  	}
     210        if (!hurd_siglocked)
     211  	__mutex_unlock (&_hurd_siglock);
     212  
     213        if (_hurd_itimer_port == MACH_PORT_NULL)
     214  	{
     215  	  /* Allocate a receive right that the itimer thread will
     216  	     block waiting for a message on.  */
     217  	  if (err = __mach_port_allocate (__mach_task_self (),
     218  					  MACH_PORT_RIGHT_RECEIVE,
     219  					  &_hurd_itimer_port))
     220  	    goto out;
     221  	}
     222  
     223        if (_hurd_itimer_thread == MACH_PORT_NULL)
     224  	{
     225  	  /* Start up the itimer thread running `timer_thread' (below).  */
     226  	  if (err = __thread_create (__mach_task_self (),
     227  				     &_hurd_itimer_thread))
     228  	    goto out;
     229  	  _hurd_itimer_thread_stack_base = 0; /* Anywhere.  */
     230  	  _hurd_itimer_thread_stack_size = __vm_page_size; /* Small stack.  */
     231  	  if ((err = __mach_setup_thread_call (__mach_task_self (),
     232  					       _hurd_itimer_thread,
     233  					       &timer_thread,
     234  					       &_hurd_itimer_thread_stack_base,
     235  					       &_hurd_itimer_thread_stack_size))
     236  	      || (err = __mach_setup_tls(_hurd_itimer_thread)))
     237  	    {
     238  	      __thread_terminate (_hurd_itimer_thread);
     239  	      _hurd_itimer_thread = MACH_PORT_NULL;
     240  	      goto out;
     241  	    }
     242  	  _hurd_itimer_thread_suspended = 1;
     243  	}
     244      }
     245  
     246    if ((newval.it_value.tv_sec | newval.it_value.tv_usec) != 0 || old != NULL)
     247      {
     248        /* Calculate how much time is remaining for the pending alarm.  */
     249        {
     250  	time_value_t tv;
     251  	__host_get_time (__mach_host_self (), &tv);
     252  	now.tv_sec = tv.seconds;
     253  	now.tv_usec = tv.microseconds;
     254        }
     255        elapsed = now;
     256        subtract_timeval (&elapsed, &_hurd_itimer_started);
     257        remaining = _hurd_itimerval.it_value;
     258        if (timercmp (&remaining, &elapsed, <))
     259  	{
     260  	  /* Hmm.  The timer should have just gone off, but has not been reset.
     261  	     This is a possible timing glitch.  The alarm will signal soon. */
     262  	  /* XXX wrong */
     263  	  remaining.tv_sec = 0;
     264  	  remaining.tv_usec = 0;
     265  	}
     266        else
     267  	subtract_timeval (&remaining, &elapsed);
     268  
     269        /* Remember the old reload interval before changing it.  */
     270        old_interval = _hurd_itimerval.it_interval;
     271  
     272        /* Record the starting time that the timer interval relates to.  */
     273        _hurd_itimer_started = now;
     274      }
     275  
     276    /* Load the new itimer value.  */
     277    _hurd_itimerval = newval;
     278  
     279    if ((newval.it_value.tv_sec | newval.it_value.tv_usec) == 0)
     280      {
     281        /* Disable the itimer.  */
     282        if (_hurd_itimer_thread && !_hurd_itimer_thread_suspended)
     283  	{
     284  	  /* Suspend the itimer thread so it does nothing.  Then abort its
     285  	     kernel context so that when the thread is resumed, mach_msg
     286  	     will return to timer_thread (below) and it will fetch new
     287  	     values from _hurd_itimerval.  */
     288  	  if ((err = __thread_suspend (_hurd_itimer_thread))
     289  	      || (err = __thread_abort (_hurd_itimer_thread)))
     290  	    /* If we can't save it for later, nuke it.  */
     291  	    kill_itimer_thread ();
     292  	  else
     293  	    _hurd_itimer_thread_suspended = 1;
     294  	}
     295      }
     296    /* See if the timeout changed.  If so, we must alert the itimer thread.  */
     297    else if (remaining.tv_sec != newval.it_value.tv_sec
     298  	   || remaining.tv_usec != newval.it_value.tv_usec)
     299      {
     300        /* The timeout value is changing.  Tell the itimer thread to
     301  	 reexamine it and start counting down.  If the itimer thread is
     302  	 marked as suspended, either we just created it, or it was
     303  	 suspended and thread_abort'd last time the itimer was disabled;
     304  	 either way it will wake up and start waiting for the new timeout
     305  	 value when we resume it.  If it is not suspended, the itimer
     306  	 thread is waiting to deliver a pending alarm that we will override
     307  	 (since it would come later than the new alarm being set);
     308  	 thread_abort will make mach_msg return MACH_RCV_INTERRUPTED, so it
     309  	 will loop around and use the new timeout value.  */
     310        if (err = (_hurd_itimer_thread_suspended
     311  		 ? __thread_resume : __thread_abort) (_hurd_itimer_thread))
     312  	{
     313  	  kill_itimer_thread ();
     314  	  goto out;
     315  	}
     316        _hurd_itimer_thread_suspended = 0;
     317      }
     318  
     319    __spin_unlock (&_hurd_itimer_lock);
     320    _hurd_critical_section_unlock (crit);
     321  
     322    if (old != NULL)
     323      {
     324        old->it_value = remaining;
     325        old->it_interval = old_interval;
     326      }
     327    return 0;
     328  
     329   out:
     330    __spin_unlock (&_hurd_itimer_lock);
     331    _hurd_critical_section_unlock (crit);
     332    return __hurd_fail (err);
     333  }
     334  
     335  /* Set the timer WHICH to *NEW.  If OLD is not NULL,
     336     set *OLD to the old value of timer WHICH.
     337     Returns 0 on success, -1 on errors.  */
     338  int
     339  __setitimer (enum __itimer_which which, const struct itimerval *new,
     340  	     struct itimerval *old)
     341  {
     342    void *crit;
     343    int ret;
     344  
     345    switch (which)
     346      {
     347      default:
     348        return __hurd_fail (EINVAL);
     349  
     350      case ITIMER_VIRTUAL:
     351      case ITIMER_PROF:
     352        return __hurd_fail (ENOSYS);
     353  
     354      case ITIMER_REAL:
     355        break;
     356      }
     357  
     358  retry:
     359    crit = _hurd_critical_section_lock ();
     360    __spin_lock (&_hurd_itimer_lock);
     361    ret = setitimer_locked (new, old, crit, 0);
     362    if (ret == -1 && errno == EINTR)
     363      /* Got a signal while inside an RPC of the critical section, retry again */
     364      goto retry;
     365  
     366    return ret;
     367  }
     368  
     369  static void
     370  fork_itimer (void)
     371  {
     372    /* We must restart the itimer in the child.  */
     373  
     374    struct itimerval it;
     375  
     376    __spin_lock (&_hurd_itimer_lock);
     377    _hurd_itimer_thread = MACH_PORT_NULL;
     378    it = _hurd_itimerval;
     379    it.it_value = it.it_interval;
     380  
     381    setitimer_locked (&it, NULL, NULL, 0);
     382  
     383    (void) &fork_itimer;		/* Avoid gcc optimizing out the function.  */
     384  }
     385  text_set_element (_hurd_fork_child_hook, fork_itimer);
     386  
     387  weak_alias (__setitimer, setitimer)