LINUX.ORG.RU

[ARM] clock_nanosleep и RT_PREEMPT

 


0

1

есть одна железка на АРМ с RT_PREEMPT ядром. задача простая: через ethernet отправлять пакеты с заданным интервалом - 250мкс. проблема в том, что интервал плавает. вот тестовый код:

#define _POSIX_C_SOURCE  200112L

#include <stdio.h>
#include <time.h>
#include <sched.h>
#include <stdlib.h>

int main() {
    sched_setscheduler(0, SCHED_FIFO, &(struct sched_param) {
        .sched_priority = sched_get_priority_max(SCHED_FIFO)
    });

    for (int n = 10; n--;) {
        struct timespec a, b;
        clock_gettime(CLOCK_MONOTONIC, &a);

        clock_nanosleep(CLOCK_MONOTONIC, 0, &(struct timespec) {
            .tv_sec  = 0,
            .tv_nsec = 250000
        }, NULL);

        clock_gettime(CLOCK_MONOTONIC, &b);
        printf("%ld\n", b.tv_nsec - a.tv_nsec + (b.tv_sec - a.tv_sec) * 1000000000l);
    }
    return 0;
}
выдает такие результаты:
482984
402445
361446
378612
365238
362738
359113
359113
363654
358821
как видно задержка в некоторых случаях почти двукратная.

cat /proc/timer_list говорит, что используется hi-res timer. на всякий случай полный вывод:

# cat /proc/timer_list
Timer List Version: v0.5
HRTIMER_MAX_CLOCK_BASES: 2
now at 9784755808068 nsecs

cpu: 0
 clock 0:
  .base:       c03fbfa8
  .index:      0
  .resolution: 1 nsecs
  .get_time:   ktime_get_real
  .offset:     1330069279622958539 nsecs
active timers:
 clock 1:
  .base:       c03fbfe0
  .index:      1
  .resolution: 1 nsecs
  .get_time:   ktime_get
  .offset:     0 nsecs
active timers:
 #0: <c03fc550>, tick_sched_timer, S:01
 # expires at 9784760000000-9784760000000 nsecs [in 4191932 to 4191932 nsecs]
 #1: <c0423a10>, sched_rt_period_timer, S:01
 # expires at 9785000000000-9785000000000 nsecs [in 244191932 to 244191932 nsecs]
 #2: <c1c1da98>, hrtimer_wakeup, S:01
 # expires at 9787882326277-9787887326259 nsecs [in 3126518209 to 3131518191 nsecs]
  .expires_next   : 9784760000000 nsecs
  .hres_active    : 1
  .nr_events      : 4303769
  .nr_retries     : 2718
  .nr_hangs       : 0
  .max_hang_time  : 0 nsecs
  .nohz_mode      : 2
  .idle_tick      : 9784730000000 nsecs
  .tick_stopped   : 0
  .idle_jiffies   : 948472
  .idle_calls     : 4027935
  .idle_sleeps    : 3643985
  .idle_entrytime : 9784720504270 nsecs
  .idle_waketime  : 9784735100438 nsecs
  .idle_exittime  : 9784735153646 nsecs
  .idle_sleeptime : 7111169596582 nsecs
  .last_jiffies   : 948472
  .next_jiffies   : 948491
  .idle_expires   : 9784910000000 nsecs
jiffies: 948475


Tick Device: mode:     1
Per CPU device: 0
Clock Event Device: timer0_0
 max_delta_ns:   178956970763
 min_delta_ns:   50000
 mult:           103079215
 shift:          32
 mode:           3
 next_event:     9784760000000 nsecs
 set_next_event: davinci_set_next_event
 set_mode:       davinci_set_mode
 event_handler:  hrtimer_interrupt
тут вроде бы все ок.

подскажите, как приблизиться к заветным 250мкс? куда копать?

убедись, что в конфиге ядра CONFIG_PREEMPT_RT_FULL=y, и HZ поставь в 1000

anonymous
()

Just for fun проверил на компе (resolution 1nsec), то же самое. А вот MONOTONIC_RAW - ~1200

anonymous
()

через ethernet отправлять пакеты с заданным интервалом - 250мкс

Нереально. Ethernet вообще для реалтайма мало пригоден, а для таких микро интервалов и подавно.

golodranez ★★★★
()

Тут недавно arsi(*) пробовал подобный код на arm без rt ядра, у него задержка была до 17мс. Так что я думаю тут сам arm тормозит.

golodranez ★★★★
()

Ну как обычно. Прочитай ман полностью на clock_nanosleep. Там же красным по русскому написано почему он лучше чем нанослип. Указывай не скока времени ждать а до какого времени ждать. Тогда и узнаешь арм тормозит или линукс говно.

mmarkk
()
Ответ на: комментарий от golodranez

> arsi(*)

> *

фигасе ты меня опустил… :)

> Так что я думаю тут сам arm тормозит.

не факт, это у меня арм тормозит :) /proc/timer_list: «.resolution: 10000000 nsecs», да и проц 200МГц.

arsi ★★★★★
()
Ответ на: комментарий от ttnl

Сколько работает clock_gettime (тот же код, но убрать nanosleep)?

8958
7291
26417
3500
3292
3292
3750
4042
2833
2625

почему на 3 итерации 26417 нс занимает вызов, не понятно. но это почти все время повторяется, именно на 3-ий раз.

Кстати, пересобрал ядро. Отключил CONFIG_NO_HZ и поставил CONFIG_HZ = 1000. Только почти ничего изменилось.

so_much_light
() автор топика
Ответ на: комментарий от exception13

отправляешь «сырые» езернет фреймы или прогоняешь через сетевой стек?

«сырые». просто ethernet кадр с необходимыми данными.

so_much_light
() автор топика
Ответ на: комментарий от so_much_light

Отключил CONFIG_NO_HZ

1)По логике лучше включить.

2)Можешь попробовать привязать этот процесс к одному процессору, а все остальные - к другим. И прерывания отвязать.

ttnl ★★★★★
()
Ответ на: комментарий от so_much_light

> почему на 3 итерации 26417 нс занимает вызов, не понятно. но это почти все время повторяется, именно на 3-ий раз.

1. прогони 100 раз, а не 10, м.б. это что-то периодическое…

2. в самом начале поставь sleep(1);

3. подгрузи страницы для стека в самом начале:

static void __attribute__((noinline)) map_stack_pages(void) {
    char t[1024*1024];
    memset(t, 0, sizeof t);
}

int main() {
    map_stack_pages();
    ...
}
arsi ★★★★★
()
Ответ на: комментарий от ttnl

1)По логике лучше включить.

насколько я понял, CONFIG_NO_HZ позволяет экономить расход энергии при простое ядра. у меня простой не предвидится. но опцию попробую вернуть.

2)Можешь попробовать привязать этот процесс к одному процессору, а все остальные - к другим. И прерывания отвязать.

можно об этом подробнее?) физически у меня только одни процессор и имеется, АРМ9.

so_much_light
() автор топика
Ответ на: комментарий от so_much_light

«сырые». просто ethernet кадр с необходимыми данными.

может тогда в виде ядреного модуля оформить с колбеками в юзерспейс например?

exception13 ★★★★★
()
serge@blacktablet:~$ cat sched.c 
#define _POSIX_C_SOURCE  200112L

#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sched.h>
#include <stdlib.h>
#include <sys/mman.h>

int main() {
    mlockall(MCL_FUTURE);

    sched_setscheduler(0, SCHED_FIFO, &(struct sched_param) {
        .sched_priority = sched_get_priority_max(SCHED_FIFO)
    });

    struct timespec a, b, c;

    c.tv_sec  = 0;
    c.tv_nsec = 150000;

    for (int n = 10; n--;) {
        clock_gettime(CLOCK_MONOTONIC, &a);

        clock_nanosleep(CLOCK_MONOTONIC, 0, &c, NULL);

        clock_gettime(CLOCK_MONOTONIC, &b);

        fprintf(stdout, "%ld\n", b.tv_nsec - a.tv_nsec + (b.tv_sec - a.tv_sec) * 1000000000l);

        fflush(stdout);
    };

    return 0;
}
serge@blacktablet:~$ sudo ./sched 
192273
181238
176699
179213
191016
180260
180749
184311
184172
179142

но проверял на обычном ядре.

exception13 ★★★★★
()
Ответ на: комментарий от exception13
serge@blacktablet:~$ sudo ./sched 
177816
166222
165664
165315
165244
165244
164546
165035
165035
165175

если убрать из основного цикла все io

exception13 ★★★★★
()

250мкс из юзерспейса... какова частота процессора? И да, для начала следует убрать printf.

tailgunner ★★★★★
()

> как приблизиться к заветным 250мкс?

примерно так ;)

#define _POSIX_C_SOURCE  200112L

#include <stdio.h>
#include <time.h>
#include <sched.h>
#include <stdlib.h>

int main() {
    sched_setscheduler(0, SCHED_FIFO, &(struct sched_param) {
        .sched_priority = sched_get_priority_max(SCHED_FIFO)
    });

    const clockid_t clock = CLOCK_REALTIME;
    struct timespec a, b, t = { .tv_nsec = 250000 };

    for (int i = 1; i <= 100; ++i) {
        clock_gettime(clock, &a);
        clock_nanosleep(clock, 0, &t, NULL);
        clock_gettime(clock, &b);
        long dt = b.tv_nsec - a.tv_nsec + (b.tv_sec - a.tv_sec) * 1000000000l;
        printf("%ld\n", dt);
        t.tv_nsec -= (dt - 250000) / i;
    }
}
arsi ★★★★★
()

Случайно отладочные опции для мутексов/семафоров/etc в Kernel Hacking не включены?

AptGet ★★★
()
Ответ на: комментарий от arsi

у меня на таком говнокоде

#define _POSIX_C_SOURCE  200112L

#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sched.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>

int main() {
  mlockall(MCL_FUTURE);

  sched_setscheduler(0, SCHED_FIFO, &(struct sched_param) {
    .sched_priority = sched_get_priority_max(SCHED_FIFO)
  });

  static struct timespec a, b, c;

  static struct sockaddr_in sa;

  static int n, s;

  static unsigned long t[10];
  static unsigned char buf[512];

  memset((char *) &sa, 0x00, sizeof(sa));

  s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

  sa.sin_family = AF_INET;
  sa.sin_port = htons(0xFF);

  inet_aton("192.168.1.1", &sa.sin_addr);

  c.tv_sec  = 0;
  c.tv_nsec = 250000;

  for (n = 10; n--;) {
    clock_gettime(CLOCK_MONOTONIC, &a);

    clock_nanosleep(CLOCK_MONOTONIC, 0, &c, NULL);

    clock_gettime(CLOCK_MONOTONIC, &b);

    sendto(s, buf, sizeof(buf), 0, &sa, sizeof(sa));

    t[n] = b.tv_nsec - a.tv_nsec;
  };

  for (n = 10; n--;) {
    fprintf(stdout, "%ld\n", t[n]);
  };

  return 0;
}

выдает

serge@blacktablet:~$ sudo ./sched 
288654
279993
278526
280831
317149
273079
272590
276990
314076
282019
exception13 ★★★★★
()
Ответ на: комментарий от exception13

только для получения реальной картины надо поменять

   clock_gettime(CLOCK_MONOTONIC, &b);
   sendto(s, buf, sizeof(buf), 0, &sa, sizeof(sa));

местами

exception13 ★★★★★
()
Ответ на: комментарий от tailgunner

Может из-за «NTP adjustments»?.. Выше, намекалось, намекалось.

anonymous
()
Ответ на: комментарий от arsi

фигасе ты меня опустил… :)

я не хотел)) можешь писать меня golodranez() :)))

golodranez ★★★★
()
Ответ на: комментарий от exception13

Вот ты вся тупо с слипом пытаешься чтото сделать, а я тебе по опыту поворю - ethernet для realtime не подходит, периодически будут некислые задержки. Тут из протокола TCP/IP уже это понятно.

golodranez ★★★★
()
Ответ на: комментарий от golodranez

Вот ты вся тупо с слипом пытаешься чтото сделать, а я тебе по опыту поворю - ethernet для realtime не подходит, периодически будут некислые задержки. Тут из протокола TCP/IP уже это понятно.

это я с стеком тестил j4f. ТС'у надо слать сырые езернет кадры а если у него на этом ифейсе ничего не будет + девайсы соединены p2p то вполне может прокатить.

exception13 ★★★★★
()

внял всем вашим советам и выдавать пакеты с интервалом ~250 мкс. получилось, так что задачу можно считать решенной. время конечно плавает, но в целом держится около заданной отметки. теперь другая проблема, после продолжительной работы отваливается ethernet, ругаясь на ошибку MDIO. трудноуловимый баг, буду разбираться.

Всем огромное спасибо!

so_much_light
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.