LCOV - code coverage report
Current view: top level - app/shared/commands/configure - fd_irqbalance_client.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 180 0.0 %
Date: 2026-08-09 04:49:18 Functions: 0 7 0.0 %

          Line data    Source code
       1             : #define _GNU_SOURCE
       2             : #include "fd_irqbalance_client.h"
       3             : #include <errno.h>
       4             : #include <dirent.h>
       5             : #include <sys/socket.h>
       6             : #include <sys/types.h>
       7             : #include <sys/un.h>
       8             : #include <unistd.h>
       9             : 
      10             : #define IRQBALANCE_RUN_DIR "/run/irqbalance"
      11             : 
      12             : static int
      13           0 : fd_irqbalance_connect1( char const * path ) {
      14           0 :   int sock = socket( AF_UNIX, SOCK_STREAM, 0 );
      15           0 :   if( FD_UNLIKELY( sock<0 ) ) FD_LOG_ERR(( "socket(AF_UNIX,SOCK_STREAM) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      16             : 
      17           0 :   int err;
      18             : 
      19           0 :   if( FD_UNLIKELY( setsockopt( sock, SOL_SOCKET, SO_PASSCRED, &(int){1}, sizeof(int) )<0 ) ) {
      20           0 :     FD_LOG_ERR(( "setsockopt(SO_PASSCRED) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      21           0 :   }
      22             : 
      23           0 :   struct sockaddr_un addr;
      24           0 :   fd_memset( &addr, 0, sizeof(addr) );
      25           0 :   addr.sun_family = AF_UNIX;
      26           0 :   if( FD_UNLIKELY( strlen( path ) >= sizeof(addr.sun_path) ) ) {
      27           0 :     err = ENOENT;
      28           0 :     goto cleanup;
      29           0 :   }
      30           0 :   strcpy( addr.sun_path, path );
      31             : 
      32           0 :   if( FD_UNLIKELY( connect( sock, fd_type_pun( &addr ), sizeof(addr) )<0 ) ) {
      33           0 :     err = errno;
      34           0 :     if( FD_UNLIKELY( err!=ENOENT && err!=ECONNREFUSED && err!=EACCES ) ) {
      35           0 :       FD_LOG_ERR(( "connect(`%s`) failed (%i-%s)", path, err, fd_io_strerror( err ) ));
      36           0 :     }
      37           0 :     goto cleanup;
      38           0 :   }
      39             : 
      40           0 :   return sock;
      41             : 
      42           0 : cleanup:
      43           0 :   if( FD_UNLIKELY( 0!=close( sock ) ) ) {
      44           0 :     FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      45           0 :   }
      46           0 :   errno = err;
      47           0 :   return -1;
      48           0 : }
      49             : 
      50             : static int
      51           0 : fd_irqbalance_connect( void ) {
      52             :   /* Unfortunately the irqbalance daemon does not use a fixed file path */
      53           0 :   char path[ PATH_MAX ];
      54           0 :   DIR * dir = opendir( "/run/irqbalance" );
      55           0 :   if( FD_UNLIKELY( !dir ) ) {
      56           0 :     if( FD_UNLIKELY( errno!=ENOENT && errno!=EACCES ) ) {
      57           0 :       FD_LOG_ERR(( "opendir(`%s`) failed (%i-%s)", IRQBALANCE_RUN_DIR, errno, fd_io_strerror( errno ) ));
      58           0 :     }
      59           0 :     return -1;
      60           0 :   }
      61           0 :   struct dirent * entry;
      62           0 :   int sock = -1;
      63           0 :   int err = ENOENT;
      64           0 :   while( (entry = readdir( dir )) ) {
      65           0 :     if( FD_UNLIKELY( entry->d_type!=DT_SOCK ) ) continue;
      66           0 :     if( FD_UNLIKELY( strncmp( entry->d_name, "irqbalance", 10 ) ) ) continue;
      67           0 :     FD_TEST( fd_cstr_printf_check( path, PATH_MAX, NULL, "%s/%s", IRQBALANCE_RUN_DIR, entry->d_name ) );
      68           0 :     sock = fd_irqbalance_connect1( path );
      69           0 :     if( FD_UNLIKELY( sock<0 ) ) {
      70           0 :       err = errno;
      71           0 :       if( FD_UNLIKELY( errno==EACCES ) ) break;
      72           0 :       continue;
      73           0 :     }
      74           0 :     break;
      75           0 :   }
      76           0 :   if( FD_UNLIKELY( closedir( dir ) ) ) {
      77           0 :     FD_LOG_ERR(( "closedir() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      78           0 :   }
      79           0 :   if( FD_UNLIKELY( sock<0 ) ) errno = err;
      80           0 :   return sock;
      81           0 : }
      82             : 
      83             : static int
      84             : fd_irqbalance_send( int          fd,
      85             :                     void const * req,
      86           0 :                     ulong        req_len ) {
      87           0 :   struct iovec iov = {
      88           0 :     .iov_base = (void *)req,
      89           0 :     .iov_len  = req_len
      90           0 :   };
      91             : 
      92           0 :   union {
      93           0 :     struct cmsghdr hdr;
      94           0 :     uchar          buf[ CMSG_SPACE( sizeof(struct ucred) ) ];
      95           0 :   } ctrl;
      96           0 :   fd_memset( &ctrl, 0, sizeof(ctrl) );
      97             : 
      98           0 :   struct msghdr msg;
      99           0 :   fd_memset( &msg, 0, sizeof(msg) );
     100           0 :   msg.msg_iov        = &iov;
     101           0 :   msg.msg_iovlen     = 1U;
     102           0 :   msg.msg_control    = ctrl.buf;
     103           0 :   msg.msg_controllen = sizeof(ctrl.buf);
     104             : 
     105           0 :   struct cmsghdr * cmsg = CMSG_FIRSTHDR( &msg );
     106           0 :   cmsg->cmsg_level = SOL_SOCKET;
     107           0 :   cmsg->cmsg_type  = SCM_CREDENTIALS;
     108           0 :   cmsg->cmsg_len   = CMSG_LEN( sizeof(struct ucred) );
     109             : 
     110           0 :   struct ucred cred = {
     111           0 :     .pid = getpid(),
     112           0 :     .uid = geteuid(),
     113           0 :     .gid = getegid()
     114           0 :   };
     115           0 :   fd_memcpy( CMSG_DATA( cmsg ), &cred, sizeof(cred) );
     116             : 
     117           0 :   return sendmsg( fd, &msg, MSG_NOSIGNAL )==(long)req_len;
     118           0 : }
     119             : 
     120             : static int
     121             : fd_irqbalance_request( void const * req,
     122             :                        ulong        req_len,
     123             :                        char *       resp,
     124           0 :                        ulong        resp_sz ) {
     125             : 
     126           0 :   if( FD_LIKELY( resp && resp_sz ) ) resp[0] = '\0';
     127             : 
     128           0 :   int fd = fd_irqbalance_connect();
     129           0 :   if( FD_UNLIKELY( fd<0 ) ) return -1;
     130             : 
     131           0 :   if( FD_UNLIKELY( !fd_irqbalance_send( fd, req, req_len ) ) ) {
     132           0 :     FD_LOG_ERR(( "failed to send command to irqbalance (%i-%s)", errno, fd_io_strerror( errno ) ));
     133           0 :   }
     134           0 :   if( FD_UNLIKELY( 0!=shutdown( fd, SHUT_WR ) ) ) {
     135           0 :     FD_LOG_ERR(( "shutdown(irqbalance_sock_fd) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     136           0 :   }
     137           0 :   if( FD_LIKELY( resp ) ) {
     138           0 :     ulong off = 0UL;
     139           0 :     for(;;) {
     140           0 :       long n = recv( fd, resp+off, resp_sz-off-1UL, 0 );
     141           0 :       if( FD_UNLIKELY( n<0 ) ) {
     142           0 :         if( FD_UNLIKELY( errno==EINTR ) ) continue;
     143           0 :         FD_LOG_ERR(( "failed to recv response from irqbalance (%i-%s)", errno, fd_io_strerror( errno ) ));
     144           0 :       }
     145           0 :       if( !n ) break; /* EOF */
     146           0 :       off += (ulong)n;
     147           0 :       if( FD_UNLIKELY( off>=resp_sz-1UL ) ) {
     148           0 :         FD_LOG_ERR(( "response from irqbalance is too large for the receive buffer (>=%lu)", resp_sz ));
     149           0 :       }
     150           0 :     }
     151           0 :     if( FD_UNLIKELY( !off ) ) {
     152             :       /* The daemon accepted the connection but closed it without replying.
     153             :          It does this when it rejects our SCM_CREDENTIALS (non-root), so
     154             :          treat an empty response as the expected permission error. */
     155           0 :       if( FD_UNLIKELY( 0!=close( fd ) ) ) {
     156           0 :         FD_LOG_ERR(( "close(irqbalance_sock_fd) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     157           0 :       }
     158           0 :       errno = EACCES;
     159           0 :       return -1;
     160           0 :     }
     161           0 :     resp[ off ] = '\0';
     162           0 :   }
     163             : 
     164           0 :   if( FD_UNLIKELY( 0!=close( fd ) ) ) {
     165           0 :     FD_LOG_ERR(( "close(irqbalance_sock_fd) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     166           0 :   }
     167           0 :   return 0;
     168           0 : }
     169             : 
     170             : int
     171           0 : fd_irqbalance_ban_cpus_set( fd_cpuset_t const * cpuset ) {
     172             :   /* 1024x (FD_TILE_MAX) comma-separated 4 char strings is approx 5kB */
     173           0 :   char req[ 16384 ];
     174           0 :   char * p = fd_cstr_init( req );
     175           0 :   p = fd_cstr_append_cstr( p, "settings cpus " );
     176           0 :   int first = 1;
     177           0 :   for( ulong iter = fd_cpuset_const_iter_init( cpuset );
     178           0 :        !fd_cpuset_const_iter_done( iter );
     179           0 :        iter = fd_cpuset_const_iter_next( cpuset, iter ) ) {
     180           0 :     if( FD_LIKELY( !first ) ) p = fd_cstr_append_char( p, ',' );
     181           0 :     else                      first = 0;
     182           0 :     if( FD_UNLIKELY( p-req+32 >= (long)sizeof(req) ) ) break; /* unreachable */
     183           0 :     p = fd_cstr_append_ulong_as_text( p, 0, 0, iter, fd_ulong_base10_dig_cnt( iter ) );
     184           0 :   }
     185           0 :   if( FD_UNLIKELY( first ) ) p = fd_cstr_append_cstr( p, "NULL" );
     186           0 :   ulong len = (ulong)( p - req );
     187           0 :   fd_cstr_fini( p );
     188             : 
     189           0 :   return fd_irqbalance_request( req, len, NULL, 0UL );
     190           0 : }
     191             : 
     192             : static int
     193           0 : fd_irqbalance_hex_digit( char c ) {
     194           0 :   if( FD_LIKELY( (c>='0') & (c<='9') ) ) return c-'0';
     195           0 :   if( FD_LIKELY( (c>='a') & (c<='f') ) ) return 10+c-'a';
     196           0 :   if( FD_LIKELY( (c>='A') & (c<='F') ) ) return 10+c-'A';
     197           0 :   return -1;
     198           0 : }
     199             : 
     200             : int
     201           0 : fd_irqbalance_ban_cpus_get( fd_cpuset_t * cpuset ) {
     202           0 :   char resp[ 16384 ];
     203           0 :   fd_cpuset_new( cpuset );
     204             : 
     205           0 :   int err = fd_irqbalance_request( "setup", 5UL, resp, sizeof(resp) );
     206           0 :   if( FD_UNLIKELY( -1==err ) ) return -1;
     207             : 
     208           0 :   char * banned = strstr( resp, "BANNED " );
     209           0 :   if( FD_UNLIKELY( !banned ) ) {
     210           0 :     FD_LOG_ERR(( "failed to parse irqbalance setup response: missing BANNED token" ));
     211           0 :   }
     212             : 
     213           0 :   banned += 7UL;
     214           0 :   char * end = banned;
     215           0 :   while( FD_LIKELY( *end && (*end!=' ') ) ) end++;
     216             : 
     217           0 :   ulong cpu = 0UL;
     218           0 :   for( char * p=end; p>banned; ) {
     219           0 :     p--;
     220           0 :     if( FD_UNLIKELY( *p==',' ) ) continue;
     221             : 
     222           0 :     int digit = fd_irqbalance_hex_digit( *p );
     223           0 :     if( FD_UNLIKELY( digit<0 ) ) {
     224           0 :       FD_LOG_ERR(( "failed to parse irqbalance banned CPU mask" ));
     225           0 :     }
     226             : 
     227           0 :     for( ulong bit=0UL; bit<4UL; bit++ ) {
     228           0 :       if( FD_UNLIKELY( cpu>=FD_TILE_MAX ) ) {
     229           0 :         FD_LOG_ERR(( "irqbalance banned CPU mask exceeds FD_TILE_MAX (%lu) CPUs", (ulong)FD_TILE_MAX ));
     230           0 :       }
     231           0 :       if( FD_UNLIKELY( digit & (1<<bit) ) ) fd_cpuset_insert( cpuset, cpu );
     232           0 :       cpu++;
     233           0 :     }
     234           0 :   }
     235             : 
     236           0 :   return 0;
     237           0 : }

Generated by: LCOV version 1.14