DF-5834 / ppslockdemo.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | /* * DF-5834 PoC: PPS_IOC_FETCH blocks inside pps_fetch_timeout() * (sys/kern/kern_clock.c:1626) holding the caller's GLOBAL tty_token * (sioioctl() default case, sio.c:2164-2172) -> every other tty operation * system-wide blocks for the whole (user-chosen) duration. * * Single-process fork design: one fd, child wedges, parent measures. * * ppslockdemo 0 control: FETCH timeout {0,0} -> no timeout path, * ioctl returns instantly, token never held. * ppslockdemo 8 child: PPS_IOC_FETCH with 8s timeout (holds tty_token * ~8s); parent after 2s times PPS_IOC_GETCAP (instant * once token acquired) -> expect ~6s if wedged. * ppslockdemo inf child: FETCH with degenerate finite timeout {0 s,1 ns} * -> tstohz_low()==0 -> tsleep(...,0) sleeps FOREVER, * holding tty_token until SIGKILL; parent arms alarm(20) * and reports STILL_BLOCKED if GETCAP never returns. * * The child pid is written to /tmp/ppschild.pid for orchestration. * * Build: cc -O2 -Wall -o ppslockdemo ppslockdemo.c */ #include <sys/ioctl.h> #include <sys/signal.h> #include <sys/time.h> #include <sys/timepps.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/wait.h> #include <time.h> #include <unistd.h> static double monoton(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ((double)ts.tv_sec + (double)ts.tv_nsec / 1e9); } static void on_alrm(int sig __attribute__((unused))) { /* only armed in "inf" mode */ printf("parent: GETCAP STILL_BLOCKED after alarm(20) fired -> " "tty_token held indefinitely by sleeping FETCH child\n"); _exit(3); } int main(int argc, char **argv) { struct pps_fetch_args fa; int fd, r, cap = -1, sec; double t0; pid_t pid; const char *mode = argc > 1 ? argv[1] : "0"; if (!strcmp(mode, "inf")) sec = -1; else sec = atoi(mode); fd = open("/dev/ttyd0", O_RDWR | O_NONBLOCK | O_NOCTTY); if (fd < 0) { perror("open /dev/ttyd0"); exit(1); } pid = fork(); if (pid == 0) { /* ---- child: the wedge ---- */ memset(&fa, 0, sizeof(fa)); fa.tsformat = PPS_TSFMT_TSPEC; if (sec < 0) { /* inf */ fa.timeout.tv_sec = 0; fa.timeout.tv_nsec = 1; } else { /* finite sec (0 = control) */ fa.timeout.tv_sec = sec; fa.timeout.tv_nsec = 0; } fprintf(stderr, "child %d: PPS_IOC_FETCH timeout=%lds %ldns\n", getpid(), fa.timeout.tv_sec, fa.timeout.tv_nsec); fflush(stderr); r = ioctl(fd, PPS_IOC_FETCH, &fa); fprintf(stderr, "child %d: FETCH returned %d errno=%d (%s)\n", getpid(), r, errno, strerror(errno)); _exit(0); } fprintf(stderr, "parent %d: child=%d mode=%s\n", getpid(), pid, mode); fflush(stderr); if (sec < 0) signal(SIGALRM, on_alrm); sleep(2); /* let the child grab the token */ if (sec < 0) alarm(20); t0 = monoton(); r = ioctl(fd, PPS_IOC_GETCAP, &cap); printf("parent: GETCAP r=%d cap=0x%x elapsed=%.3f s errno=%d%s\n", r, cap, monoton() - t0, errno, sec > 0 ? " <-- WEDGED for ~6s if bug present" : sec < 0 ? "" : " <-- control, expect ~0.000s"); if (sec >= 0) waitpid(pid, NULL, 0); return (0); } |