Bug 217598 - setitimer works incorrectly
Summary: setitimer works incorrectly
Status: NEW
Alias: None
Product: Linux
Classification: Unclassified
Component: Kernel (show other bugs)
Hardware: All Linux
: P3 normal
Assignee: Virtual assignee for kernel bugs
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2023-06-26 22:01 UTC by Vladimir Mezentsev
Modified: 2024-09-13 21:58 UTC (History)
2 users (show)

See Also:
Kernel Version:
Subsystem: POSIX CLOCKS and TIMERS
Regression: No
Bisected commit-id:
mricon: bugbot+


Attachments

Description Vladimir Mezentsev 2023-06-26 22:01:26 UTC
A small test below shows the problem with the itimer setting on OL8 (x86_64 / aarch64)

% cat sig.c
#include<stdio.h>
#include <stdlib.h>
#include<signal.h>
#include <time.h>
#include <sys/time.h>
#include <string.h>

#ifndef MY_TIMER
#define MY_TIMER 1000
#endif

static time_t start;
static int sigprof_cnt = 0;

time_t
gethrtime (void)
{
  struct timespec tp;
  time_t rc = 0;
  int r = clock_gettime (CLOCK_MONOTONIC, &tp);
  if (r == 0)
    rc = ((time_t) tp.tv_sec) * 1e9 + (time_t) tp.tv_nsec;
  return rc;
}

static void sigprof_handler(int signo, siginfo_t* info, void *context)
{
  if (++sigprof_cnt >= 3)
    exit(0);
  static struct itimerval t;
  memset(&t, 0, sizeof(t));
  if (getitimer(ITIMER_PROF, &t) != 0)
    printf("getitimer failed\n");
  printf("sigprof_handler: it_interval.tv_sec=%lld it_interval.tv_usec=%lld\n"
         "                    it_value.tv_sec=%lld it_value.tv_usec=%lld\n",
    (long long) t.it_interval.tv_sec, (long long) t.it_interval.tv_usec,
    (long long) t.it_value.tv_sec, (long long) t.it_value.tv_usec);
}

volatile long x; /* temp variable for long calculation */

int
main (int argc, char **argv)
{
  long long count = 0;
  start = gethrtime ();

  struct sigaction sa;
  memset(&sa, 0, sizeof(struct sigaction));
  sa.sa_sigaction = sigprof_handler;
  sa.sa_flags = SA_RESTART | SA_SIGINFO;
  sigemptyset(&sa.sa_mask);

  if (sigaction(SIGPROF, &sa, NULL) == -1)
    {
       perror("sigaction");
       return 1;
    }

  static struct itimerval timer;
  memset(&timer, 0, sizeof(timer));
  timer.it_interval.tv_usec = MY_TIMER;
  timer.it_value.tv_usec = MY_TIMER;
  if (setitimer(ITIMER_PROF, &timer, NULL) != 0)
    {
      printf("Timer could not be initialized \n");
      return 1;
    }

  struct itimerval t;
  memset(&t, 0, sizeof(t));
  if (getitimer(ITIMER_PROF, &t) != 0)
    {
      printf("getitimer failed\n");
      return 1;
    }
  printf("After setitimer: it_interval.tv_sec=%lld it_interval.tv_usec=%lld\n"
         "                    it_value.tv_sec=%lld it_value.tv_usec=%lld\n",
    (long long) t.it_interval.tv_sec, (long long) t.it_interval.tv_usec,
    (long long) t.it_value.tv_sec, (long long) t.it_value.tv_usec);

  do
    {
      x = 0;
      for (int j = 0; j < 1000000; j++)
        x = x + 1;
      count++;
    }
  while (start + 1e9 / 4 > gethrtime ());
  printf("count=%lld  x=%lld\n", count, x);
  return 0;
}



It is from man page:
% man setitimer
...
   setitimer()
       If either field in new_value.it_value is nonzero, then the timer is arme to initially expire at the specified time.


But this is not right on x86_64 and aarch64. I run this test on OL8.

On x86_64 / OL8:
% gcc -DMY_TIMER=1000 sig.c; ./a.out
After setitimer: it_interval.tv_sec=0   it_interval.tv_usec=1000
                    it_value.tv_sec=0      it_value.tv_usec=2000 <<<<<< this should be <= 1000 because I set it_value.tv_usec to 1000 in setitimer

sigprof_handler: it_interval.tv_sec=0   it_interval.tv_usec=1000
                    it_value.tv_sec=0      it_value.tv_usec=23
sigprof_handler: it_interval.tv_sec=0   it_interval.tv_usec=1000
                    it_value.tv_sec=0      it_value.tv_usec=27



On aarch64 / OL8:

% gcc -DMY_TIMER=1000 sig.c; ./a.out
After setitimer: it_interval.tv_sec=0   it_interval.tv_usec=1000
                    it_value.tv_sec=0 it_value.tv_usec=5000    <<<<<< Same as on x86_64

sigprof_handler: it_interval.tv_sec=0   it_interval.tv_usec=1000
                    it_value.tv_sec=0 it_value.tv_usec=4000 <<<<<< this must be <= 1000 because it_interval.tv_usec is 1000

sigprof_handler: it_interval.tv_sec=0   it_interval.tv_usec=1000
                    it_value.tv_sec=0      it_value.tv_usec=4000<<<<<< this must be <= 1000 because it_interval.tv_usec is 1000
Comment 1 Alok Swaminathan 2024-08-26 12:37:05 UTC
I believe this issue stems from the fact that on your system the tick interval is 0.004 seconds (4000 microseconds), so setitimer will not be able to accurately work with intervals under that number.

Note You need to log in before you can comment on or make changes to this bug.