LCOV - code coverage report
Current view: top level - disco/waker - fd_waker.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 58 84 69.0 %
Date: 2026-09-17 04:28:31 Functions: 3 3 100.0 %

          Line data    Source code
       1             : #define _GNU_SOURCE /* pipe2 */
       2             : #include "fd_waker.h"
       3             : 
       4             : #include "../../util/log/fd_log.h"
       5             : 
       6             : #include <errno.h>
       7             : #include <fcntl.h>
       8             : #include <sched.h>
       9             : #include <stdlib.h>
      10             : #include <unistd.h>
      11             : #include <sys/epoll.h>
      12             : #include <sys/syscall.h>
      13             : #include <sys/wait.h>
      14             : 
      15             : /* The wake protocol needs two nested-epoll behaviors: (a) an event
      16             :    in an inner set wakes a BLOCKED outer epoll_wait (broken on
      17             :    unpatched v5.5-v5.12), and (b) EPOLL_CTL_MOD re-polls, so a rearm
      18             :    with readiness pending re-fires (what makes clear/drain/rearm
      19             :    lost-wake-proof).  (a) only shows with a sleeping waiter, so fork
      20             :    a child that waits until /proc/<parent>/syscall shows us blocked
      21             :    in epoll_pwait, then makes an inner fd ready, and require the
      22             :    wake.  Runs once pre-fork; logs err on a broken kernel. */
      23             : 
      24             : static void
      25           3 : self_test( void ) {
      26           3 :   int outer = epoll_create1( 0 );
      27           3 :   int inner = epoll_create1( 0 );
      28           3 :   if( FD_UNLIKELY( -1==outer || -1==inner ) ) FD_LOG_ERR(( "epoll_create1() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      29             : 
      30           3 :   int pfd[ 2 ];
      31           3 :   if( FD_UNLIKELY( pipe2( pfd, O_CLOEXEC ) ) ) FD_LOG_ERR(( "pipe2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      32             : 
      33           3 :   struct epoll_event ev = { .events = EPOLLIN };
      34           3 :   if( FD_UNLIKELY( -1==epoll_ctl( inner, EPOLL_CTL_ADD, pfd[ 0 ], &ev ) ) ) FD_LOG_ERR(( "epoll_ctl(ADD) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      35           3 :   ev = (struct epoll_event){ .events = EPOLLIN|EPOLLONESHOT };
      36           3 :   if( FD_UNLIKELY( -1==epoll_ctl( outer, EPOLL_CTL_ADD, inner, &ev ) ) ) FD_LOG_ERR(( "epoll_ctl(ADD) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      37             : 
      38           3 :   char path[ 32 ];
      39           3 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/proc/%d/syscall", (int)getpid() ) );
      40             : 
      41           3 :   pid_t pid = fork();
      42           3 :   if( FD_UNLIKELY( -1==pid ) ) FD_LOG_ERR(( "fork() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      43           3 :   if( FD_UNLIKELY( !pid ) ) {
      44             :     /* child: spin until the parent is observably blocked in the outer
      45             :        epoll_pwait (first field of /proc/<pid>/syscall is the syscall
      46             :        number, or "running"), then make the inner fd ready.  Bounded;
      47             :        on exhaustion write anyway and report the coverage as
      48             :        unconfirmed (exit 2). */
      49           0 :     int blocked = 0;
      50           0 :     for( ulong i=0UL; i<1000000UL; i++ ) {
      51           0 :       int fd = open( path, O_RDONLY );
      52           0 :       if( FD_UNLIKELY( -1==fd ) ) break;
      53           0 :       char buf[ 64 ];
      54           0 :       long rd = read( fd, buf, sizeof(buf)-1UL );
      55           0 :       close( fd );
      56           0 :       if( FD_UNLIKELY( rd<=0L ) ) break;
      57           0 :       buf[ rd ] = '\0';
      58           0 :       long nr = strtol( buf, NULL, 10 );
      59           0 :       int is_epoll_wait = nr==__NR_epoll_pwait;
      60           0 : #ifdef __NR_epoll_wait
      61           0 :       is_epoll_wait |= nr==__NR_epoll_wait;
      62           0 : #endif
      63           0 :       if( FD_LIKELY( is_epoll_wait ) ) { blocked = 1; break; }
      64           0 :       sched_yield();
      65           0 :     }
      66           0 :     long n = write( pfd[ 1 ], "x", 1UL );
      67           0 :     _exit( 1L!=n ? 1 : ( blocked ? 0 : 2 ) );
      68           0 :   }
      69             : 
      70           3 :   int n;
      71           3 :   do n = epoll_pwait( outer, &ev, 1, 2000, NULL );
      72           3 :   while( FD_UNLIKELY( -1==n && errno==EINTR ) );
      73           3 :   if( FD_UNLIKELY( -1==n ) ) FD_LOG_ERR(( "epoll_pwait() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      74           3 :   int wstatus;
      75           3 :   if( FD_UNLIKELY( pid!=waitpid( pid, &wstatus, 0 ) ) ) FD_LOG_ERR(( "waitpid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      76           3 :   if( FD_UNLIKELY( !WIFEXITED( wstatus ) || WEXITSTATUS( wstatus )==1 ) ) FD_LOG_ERR(( "waker self test child failed" ));
      77           3 :   if( FD_UNLIKELY( WEXITSTATUS( wstatus )==2 ) )
      78           0 :     FD_LOG_WARNING(( "waker self test could not observe the parent blocked in epoll_pwait via %s; blocked-waiter coverage unconfirmed", path ));
      79           3 :   if( FD_UNLIKELY( 1!=n ) )
      80           0 :     FD_LOG_ERR(( "waker self test failed: an event inside a nested epoll set did not wake a blocked "
      81           3 :                  "epoll_wait on the outer set.  This kernel is missing the nested-epoll wakeup fixes "
      82           3 :                  "(present in v5.13+ and in the 5.4.y/5.10.y stable series); the waker tile would "
      83           3 :                  "lose wakes on it." ));
      84             : 
      85             :   /* (b) entry now disarmed, inner set still ready (byte unread):
      86             :      must stay silent until the MOD rearm re-fires it. */
      87           3 :   if( FD_UNLIKELY( 0!=epoll_wait( outer, &ev, 1, 0 ) ) ) FD_LOG_ERR(( "waker self test failed: EPOLLONESHOT entry not disarmed after delivery" ));
      88           3 :   ev = (struct epoll_event){ .events = EPOLLIN|EPOLLONESHOT };
      89           3 :   if( FD_UNLIKELY( -1==epoll_ctl( outer, EPOLL_CTL_MOD, inner, &ev ) ) ) FD_LOG_ERR(( "epoll_ctl(MOD) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      90           3 :   if( FD_UNLIKELY( 1!=epoll_wait( outer, &ev, 1, 0 ) ) )
      91           0 :     FD_LOG_ERR(( "waker self test failed: EPOLL_CTL_MOD rearm did not re-fire pending readiness; "
      92           3 :                  "the waker rearm protocol would lose wakes on this kernel." ));
      93             : 
      94           3 :   if( FD_UNLIKELY( close( pfd[ 0 ] ) || close( pfd[ 1 ] ) || close( inner ) || close( outer ) ) )
      95           0 :     FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      96           3 : }
      97             : 
      98             : void
      99           3 : fd_waker_install( ulong client_cnt ) {
     100           3 :   FD_TEST( client_cnt<=FD_WAKER_CLIENT_MAX );
     101             : 
     102           3 :   if( FD_LIKELY( client_cnt ) ) self_test();
     103             : 
     104           3 :   int outer = epoll_create1( 0 );
     105           3 :   if( FD_UNLIKELY( -1==outer ) ) FD_LOG_ERR(( "epoll_create1() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     106           3 :   if( FD_LIKELY( outer!=FD_WAKER_OUTER_FD ) ) {
     107           3 :     if( FD_UNLIKELY( -1==dup2( outer, FD_WAKER_OUTER_FD ) ) ) FD_LOG_ERR(( "dup2(%d,%d) failed (%i-%s)", outer, FD_WAKER_OUTER_FD, errno, fd_io_strerror( errno ) ));
     108           3 :     if( FD_UNLIKELY( close( outer ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     109           3 :   }
     110             : 
     111           9 :   for( ulong i=0UL; i<client_cnt; i++ ) {
     112           6 :     int inner = epoll_create1( 0 );
     113           6 :     if( FD_UNLIKELY( -1==inner ) ) FD_LOG_ERR(( "epoll_create1() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     114           6 :     if( FD_LIKELY( inner!=FD_WAKER_INNER_FD( i ) ) ) {
     115           6 :       if( FD_UNLIKELY( -1==dup2( inner, FD_WAKER_INNER_FD( i ) ) ) ) FD_LOG_ERR(( "dup2(%d,%d) failed (%i-%s)", inner, FD_WAKER_INNER_FD( i ), errno, fd_io_strerror( errno ) ));
     116           6 :       if( FD_UNLIKELY( close( inner ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     117           6 :     }
     118             : 
     119           6 :     struct epoll_event ev = { .events = EPOLLIN|EPOLLONESHOT, .data.u64 = i };
     120           6 :     if( FD_UNLIKELY( -1==epoll_ctl( FD_WAKER_OUTER_FD, EPOLL_CTL_ADD, FD_WAKER_INNER_FD( i ), &ev ) ) )
     121           0 :       FD_LOG_ERR(( "epoll_ctl(ADD,inner[%lu]) failed (%i-%s)", i, errno, fd_io_strerror( errno ) ));
     122           6 :   }
     123           3 : }
     124             : 
     125             : void
     126           9 : fd_waker_client_rearm( ulong idx ) {
     127           9 :   struct epoll_event ev = { .events = EPOLLIN|EPOLLONESHOT, .data.u64 = idx };
     128           9 :   if( FD_UNLIKELY( -1==epoll_ctl( FD_WAKER_OUTER_FD, EPOLL_CTL_MOD, FD_WAKER_INNER_FD( idx ), &ev ) ) )
     129           0 :     FD_LOG_ERR(( "epoll_ctl(MOD,inner[%lu]) failed (%i-%s)", idx, errno, fd_io_strerror( errno ) ));
     130           9 : }

Generated by: LCOV version 1.14