LCOV - code coverage report
Current view: top level - disco/net/mlx5 - fd_mlx5.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 94 1300 7.2 %
Date: 2026-09-17 04:28:31 Functions: 4 52 7.7 %

          Line data    Source code
       1             : #include "fd_mlx5_private.h"
       2             : #include "../../../util/log/fd_log.h"
       3             : #include "../../../util/net/fd_eth.h"
       4             : #include "../../../util/net/fd_ip4.h"
       5             : #include "../../../util/rng/fd_rng.h"
       6             : #include "../fd_linux_bond.h"
       7             : 
       8             : #include <ctype.h>
       9             : #include <dirent.h>
      10             : #include <errno.h>
      11             : #include <fcntl.h>
      12             : #include <limits.h>
      13             : #include <net/if.h>
      14             : #include <stddef.h>
      15             : #include <stdio.h>
      16             : #include <stdlib.h>
      17             : #include <sys/ioctl.h>
      18             : #include <sys/mman.h>
      19             : #include <sys/socket.h>
      20             : #include <sys/stat.h>
      21             : #include <unistd.h>
      22             : 
      23             : #include <linux/netlink.h>
      24             : #include <rdma/ib_user_ioctl_cmds.h>
      25             : #include <rdma/ib_user_ioctl_verbs.h>
      26             : #include <rdma/ib_user_verbs.h>
      27             : #include <rdma/mlx5-abi.h>
      28             : #include <rdma/mlx5_user_ioctl_cmds.h>
      29             : #include <rdma/mlx5_user_ioctl_verbs.h>
      30             : #include <rdma/rdma_netlink.h>
      31             : #include <rdma/rdma_user_ioctl_cmds.h>
      32             : 
      33             : /* Provide uverbs and mlx5 UAPI definitions missing from older Linux headers. */
      34             : #ifndef IB_UVERBS_ACCESS_LOCAL_WRITE
      35           0 : #define IB_UVERBS_ACCESS_LOCAL_WRITE (1U)
      36             : #endif
      37             : #ifndef IB_UVERBS_QPT_RAW_PACKET
      38           0 : #define IB_UVERBS_QPT_RAW_PACKET (8U)
      39             : #endif
      40             : #ifndef IB_UVERBS_CREATE_QP_MASK_IND_TABLE
      41           0 : #define IB_UVERBS_CREATE_QP_MASK_IND_TABLE (1U)
      42             : #endif
      43             : #ifndef IB_UVERBS_WQT_RQ
      44           0 : #define IB_UVERBS_WQT_RQ (0U)
      45             : #endif
      46             : #ifndef MLX5_LIB_CAP_DYN_UAR
      47           0 : #define MLX5_LIB_CAP_DYN_UAR (2UL)
      48             : #endif
      49             : #ifndef MLX5_IB_OBJECT_UAR
      50           0 : #define MLX5_IB_OBJECT_UAR (4104U)
      51             : #endif
      52             : #ifndef MLX5_IB_METHOD_UAR_OBJ_ALLOC
      53           0 : #define MLX5_IB_METHOD_UAR_OBJ_ALLOC (4096U)
      54             : #endif
      55             : #ifndef MLX5_IB_METHOD_UAR_OBJ_DESTROY
      56           0 : #define MLX5_IB_METHOD_UAR_OBJ_DESTROY (4097U)
      57             : #endif
      58             : #ifndef MLX5_IB_ATTR_UAR_OBJ_ALLOC_HANDLE
      59           0 : #define MLX5_IB_ATTR_UAR_OBJ_ALLOC_HANDLE (4096U)
      60             : #endif
      61             : #ifndef MLX5_IB_ATTR_UAR_OBJ_ALLOC_TYPE
      62           0 : #define MLX5_IB_ATTR_UAR_OBJ_ALLOC_TYPE (4097U)
      63             : #endif
      64             : #ifndef MLX5_IB_ATTR_UAR_OBJ_ALLOC_MMAP_OFFSET
      65           0 : #define MLX5_IB_ATTR_UAR_OBJ_ALLOC_MMAP_OFFSET (4098U)
      66             : #endif
      67             : #ifndef MLX5_IB_ATTR_UAR_OBJ_ALLOC_MMAP_LENGTH
      68           0 : #define MLX5_IB_ATTR_UAR_OBJ_ALLOC_MMAP_LENGTH (4099U)
      69             : #endif
      70             : #ifndef MLX5_IB_ATTR_UAR_OBJ_ALLOC_PAGE_ID
      71           0 : #define MLX5_IB_ATTR_UAR_OBJ_ALLOC_PAGE_ID (4100U)
      72             : #endif
      73             : #ifndef MLX5_IB_ATTR_UAR_OBJ_DESTROY_HANDLE
      74           0 : #define MLX5_IB_ATTR_UAR_OBJ_DESTROY_HANDLE (4096U)
      75             : #endif
      76             : #ifndef MLX5_IB_UAPI_UAR_ALLOC_TYPE_NC
      77           0 : #define MLX5_IB_UAPI_UAR_ALLOC_TYPE_NC (1U)
      78             : #endif
      79             : #ifndef MLX5_IB_CREATE_CQ_FLAGS_UAR_PAGE_INDEX
      80           0 : #define MLX5_IB_CREATE_CQ_FLAGS_UAR_PAGE_INDEX (2U)
      81             : #endif
      82             : #ifndef MLX5_QP_FLAG_UAR_PAGE_INDEX
      83           0 : #define MLX5_QP_FLAG_UAR_PAGE_INDEX (1024U)
      84             : #endif
      85             : #ifndef RDMA_NLDEV_CMD_STAT_SET
      86             : #define RDMA_NLDEV_CMD_STAT_SET (16U)
      87             : #endif
      88             : #ifndef RDMA_NLDEV_CMD_STAT_GET
      89             : #define RDMA_NLDEV_CMD_STAT_GET (17U)
      90             : #endif
      91             : #ifndef RDMA_NLDEV_ATTR_STAT_MODE
      92             : #define RDMA_NLDEV_ATTR_STAT_MODE (74U)
      93             : #endif
      94             : #ifndef RDMA_NLDEV_ATTR_STAT_RES
      95             : #define RDMA_NLDEV_ATTR_STAT_RES (75U)
      96             : #endif
      97             : #ifndef RDMA_NLDEV_ATTR_STAT_COUNTER
      98           0 : #define RDMA_NLDEV_ATTR_STAT_COUNTER (77U)
      99             : #endif
     100             : #ifndef RDMA_NLDEV_ATTR_STAT_COUNTER_ENTRY
     101           0 : #define RDMA_NLDEV_ATTR_STAT_COUNTER_ENTRY (78U)
     102             : #endif
     103             : #ifndef RDMA_NLDEV_ATTR_STAT_COUNTER_ID
     104           0 : #define RDMA_NLDEV_ATTR_STAT_COUNTER_ID (79U)
     105             : #endif
     106             : #ifndef RDMA_NLDEV_ATTR_STAT_HWCOUNTERS
     107           0 : #define RDMA_NLDEV_ATTR_STAT_HWCOUNTERS (80U)
     108             : #endif
     109             : #ifndef RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY
     110           0 : #define RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY (81U)
     111             : #endif
     112             : #ifndef RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME
     113           0 : #define RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME (82U)
     114             : #endif
     115             : #ifndef RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE
     116           0 : #define RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE (83U)
     117             : #endif
     118             : #ifndef RDMA_COUNTER_MODE_MANUAL
     119             : #define RDMA_COUNTER_MODE_MANUAL (2U)
     120             : #endif
     121             : 
     122             : /* FD_MLX5_UAR_DB_OFFSET is SQ doorbell register offset */
     123           0 : #define FD_MLX5_UAR_DB_OFFSET       (0x800UL) /* MLX5_BF_OFFSET */
     124           0 : #define FD_MLX5_ETH_INLINE_HDR_SZ   (18UL)    /* MLX5_ETH_L2_INLINE_HEADER_SIZE */
     125             : #define FD_MLX5_LINK_LAYER_ETHERNET (2U)      /* IB_LINK_LAYER_ETHERNET */
     126           0 : #define FD_UVERBS_NAME_MAX          (32UL)
     127             : 
     128           0 : #define FD_MLX5_QPS_INIT (1U) /* IB_QPS_INIT */
     129             : #define FD_MLX5_QPS_RTR  (2U) /* IB_QPS_RTR */
     130             : #define FD_MLX5_QPS_RTS  (3U) /* IB_QPS_RTS */
     131             : 
     132           0 : #define FD_MLX5_QP_ATTR_STATE (1U)    /* IB_QP_STATE */
     133           0 : #define FD_MLX5_QP_ATTR_PORT  (1U<<5) /* IB_QP_PORT */
     134             : 
     135           0 : #define FD_MLX5_WQS_RESET (0U) /* IB_WQS_RESET */
     136           0 : #define FD_MLX5_WQS_RDY   (1U) /* IB_WQS_RDY */
     137             : 
     138           0 : #define FD_MLX5_WQ_ATTR_STATE     (1U)    /* IB_WQ_STATE */
     139           0 : #define FD_MLX5_WQ_ATTR_CUR_STATE (1U<<1) /* IB_WQ_CUR_STATE */
     140             : 
     141           0 : #define FD_MLX5_RX_HASH_FUNC_TOEPLITZ (1U)  /* MLX5_RX_HASH_FUNC_TOEPLITZ */
     142           0 : #define FD_MLX5_RX_HASH_KEY_SZ        (40U) /* MLX5_FLD_SZ_BYTES(tirc, rx_hash_toeplitz_key) */
     143           0 : #define FD_MLX5_RX_HASH_SRC_IPV4      (1UL<<0)  /* MLX5_RX_HASH_SRC_IPV4 */
     144           0 : #define FD_MLX5_RX_HASH_DST_IPV4      (1UL<<1)  /* MLX5_RX_HASH_DST_IPV4 */
     145           0 : #define FD_MLX5_RX_HASH_SRC_PORT_UDP  (1UL<<6)  /* MLX5_RX_HASH_SRC_PORT_UDP */
     146           0 : #define FD_MLX5_RX_HASH_DST_PORT_UDP  (1UL<<7)  /* MLX5_RX_HASH_DST_PORT_UDP */
     147           0 : #define FD_MLX5_RX_HASH_INNER         (1UL<<31) /* MLX5_RX_HASH_INNER */
     148           0 : #define FD_MLX5_RX_HASH_IPV4_UDP      ( FD_MLX5_RX_HASH_SRC_IPV4     | FD_MLX5_RX_HASH_DST_IPV4     | \
     149           0 :                                         FD_MLX5_RX_HASH_SRC_PORT_UDP | FD_MLX5_RX_HASH_DST_PORT_UDP )
     150             : #define FD_MLX5_QP_TUNNEL_OFFLOADS    (1U<<2)   /* MLX5_QP_FLAG_TUNNEL_OFFLOADS */
     151             : #define FD_MLX5_TUNNEL_OFFLOADS_GRE   (1U<<1)   /* MLX5_IB_TUNNELED_OFFLOADS_GRE */
     152             : #define FD_MLX5_INDIRECTION_MAX       (1UL<<13) /* 1UL << IB_USER_VERBS_MAX_LOG_IND_TBL_SIZE */
     153             : 
     154             : static int
     155             : fd_mlx5_rdma_port_contains_if( char const * rdma_name,
     156             :                                uint         port,
     157           3 :                                char const * interface_name ) {
     158           3 :   char netdev_dir_path[ PATH_MAX ];
     159           3 :   int path_sz = snprintf( netdev_dir_path, sizeof(netdev_dir_path),
     160           3 :                           "/sys/class/infiniband/%s/ports/%u/gid_attrs/ndevs", rdma_name, port );
     161           3 :   if( FD_UNLIKELY( path_sz<=0 || (ulong)path_sz>=sizeof(netdev_dir_path) ) ) {
     162           0 :     errno = ENAMETOOLONG;
     163           0 :     return -1;
     164           0 :   }
     165             : 
     166           3 :   DIR * netdev_dir = opendir( netdev_dir_path );
     167           3 :   if( FD_UNLIKELY( !netdev_dir ) ) return errno==ENOENT ? 0 : -1;
     168             : 
     169           3 :   int result = 0;
     170         138 :   for(;;) {
     171         138 :     errno = 0;
     172         138 :     struct dirent * netdev_entry = readdir( netdev_dir );
     173         138 :     if( FD_UNLIKELY( !netdev_entry ) ) {
     174           0 :       if( errno ) result = -1;
     175           0 :       break;
     176           0 :     }
     177         138 :     if( netdev_entry->d_name[0]=='.' ) continue;
     178             : 
     179         132 :     char netdev_name_path[ PATH_MAX ];
     180         132 :     int netdev_name_path_sz = snprintf( netdev_name_path, sizeof(netdev_name_path),
     181         132 :                                         "%s/%s", netdev_dir_path, netdev_entry->d_name );
     182         132 :     if( FD_UNLIKELY( netdev_name_path_sz<=0 || (ulong)netdev_name_path_sz>=sizeof(netdev_name_path) ) ) {
     183           0 :       errno  = ENAMETOOLONG;
     184           0 :       result = -1;
     185           0 :       break;
     186           0 :     }
     187             : 
     188         132 :     int netdev_fd = open( netdev_name_path, O_RDONLY );
     189         132 :     if( FD_UNLIKELY( netdev_fd<0 ) ) {
     190           0 :       if( errno==ENOENT ) continue;
     191           0 :       result = -1;
     192           0 :       break;
     193           0 :     }
     194             : 
     195         132 :     char netdev_name[ IF_NAMESIZE+1 ];
     196         132 :     ssize_t read_sz = read( netdev_fd, netdev_name, IF_NAMESIZE );
     197         132 :     int const read_err  = read_sz<0          ? errno : 0;
     198         132 :     int const close_err = close( netdev_fd ) ? errno : 0;
     199         132 :     if( FD_UNLIKELY( (read_err && read_err!=EINVAL) || close_err ) ) {
     200           0 :       errno  = read_err && read_err!=EINVAL ? read_err : close_err;
     201           0 :       result = -1;
     202           0 :       break;
     203           0 :     }
     204             :     /* Empty Linux RDMA GID attribute entries return EINVAL. */
     205         132 :     if( FD_UNLIKELY( read_err==EINVAL ) ) continue;
     206             : 
     207           3 :     if( FD_UNLIKELY( !read_sz ) ) continue;
     208           6 :     while( read_sz>0 && (netdev_name[read_sz-1]=='\n' || netdev_name[read_sz-1]=='\r') ) read_sz--;
     209           3 :     netdev_name[read_sz] = '\0';
     210           3 :     if( !strcmp( netdev_name, interface_name ) ) {
     211           3 :       result = 1;
     212           3 :       break;
     213           3 :     }
     214           3 :   }
     215             : 
     216           3 :   int err = result<0 ? errno : 0;
     217           3 :   if( FD_UNLIKELY( closedir( netdev_dir ) && !err ) ) err = errno;
     218           3 :   if( FD_UNLIKELY( err ) ) {
     219           0 :     errno = err;
     220           0 :     return -1;
     221           0 :   }
     222           3 :   return result;
     223           3 : }
     224             : 
     225             : static int
     226             : fd_mlx5_rdma_port_matches_if( char const * rdma_name,
     227             :                               uint         port,
     228           3 :                               char const * interface_name ) {
     229           3 :   int match = fd_mlx5_rdma_port_contains_if( rdma_name, port, interface_name );
     230           3 :   if( FD_UNLIKELY( match<0 ) ) return -1;
     231           3 :   if( match ) return 1;
     232           0 :   if( !fd_bonding_is_master( interface_name ) ) return 0;
     233             : 
     234           0 :   fd_bonding_slave_iter_t   iter_mem[1];
     235           0 :   fd_bonding_slave_iter_t * iter = fd_bonding_slave_iter_init( iter_mem, interface_name );
     236           0 :   while( !fd_bonding_slave_iter_done( iter ) ) {
     237           0 :     char const * slave_interface_name = fd_bonding_slave_iter_ele( iter );
     238           0 :     match = fd_mlx5_rdma_port_contains_if( rdma_name, port, slave_interface_name );
     239           0 :     if( FD_UNLIKELY( match<0 ) ) return -1;
     240           0 :     if( match ) return 1;
     241           0 :     fd_bonding_slave_iter_next( iter );
     242           0 :   }
     243             : 
     244           0 :   return 0;
     245           0 : }
     246             : 
     247             : int
     248             : fd_mlx5_rdma_dev_find( char         rdma_name[ FD_MLX5_RDMA_NAME_MAX ],
     249             :                        uint *       rdma_port,
     250           3 :                        char const * interface_name ) {
     251           3 :   rdma_name[0] = '\0';
     252           3 :   *rdma_port   = 0U;
     253             : 
     254           3 :   DIR * infiniband_dir = opendir( "/sys/class/infiniband" );
     255           3 :   if( FD_UNLIKELY( !infiniband_dir ) ) return 0;
     256             : 
     257           3 :   ulong match_cnt = 0UL;
     258           3 :   int err = 0;
     259          12 :   for(;;) {
     260          12 :     errno = 0;
     261          12 :     struct dirent * device_entry = readdir( infiniband_dir );
     262          12 :     if( FD_UNLIKELY( !device_entry ) ) {
     263           3 :       err = errno;
     264           3 :       break;
     265           3 :     }
     266           9 :     if( device_entry->d_name[0]=='.' ) continue;
     267             : 
     268           3 :     char device_ports_path[ PATH_MAX ];
     269           3 :     int const device_ports_path_sz = snprintf( device_ports_path, sizeof(device_ports_path),
     270           3 :                                                "/sys/class/infiniband/%s/ports", device_entry->d_name );
     271           3 :     if( FD_UNLIKELY( device_ports_path_sz<=0 || (ulong)device_ports_path_sz>=sizeof(device_ports_path) ) ) {
     272           0 :       err = ENAMETOOLONG;
     273           0 :       break;
     274           0 :     }
     275             : 
     276           3 :     DIR * ports_dir = opendir( device_ports_path );
     277           3 :     if( FD_UNLIKELY( !ports_dir ) ) {
     278           0 :       if( errno==ENOENT ) continue;
     279           0 :       err = errno;
     280           0 :       break;
     281           0 :     }
     282             : 
     283          12 :     for(;;) {
     284          12 :       errno = 0;
     285          12 :       struct dirent * port_entry = readdir( ports_dir );
     286          12 :       if( FD_UNLIKELY( !port_entry ) ) {
     287           3 :         err = errno;
     288           3 :         break;
     289           3 :       }
     290           9 :       char * port_end;
     291           9 :       ulong const port_num = strtoul( port_entry->d_name, &port_end, 10 );
     292           9 :       if( !port_num || *port_end || port_num>(ulong)UCHAR_MAX ) continue;
     293           3 :       int const match = fd_mlx5_rdma_port_matches_if( device_entry->d_name, (uint)port_num, interface_name );
     294           3 :       if( FD_UNLIKELY( match<0 ) ) {
     295           0 :         err = errno;
     296           0 :         break;
     297           0 :       }
     298           3 :       if( !match ) continue;
     299             : 
     300           3 :       match_cnt++;
     301           3 :       if( match_cnt==1UL ) {
     302           3 :         fd_cstr_ncpy( rdma_name, device_entry->d_name, FD_MLX5_RDMA_NAME_MAX );
     303           3 :         *rdma_port = (uint)port_num;
     304           3 :       }
     305           3 :     }
     306           3 :     if( FD_UNLIKELY( closedir( ports_dir ) && !err ) ) err = errno;
     307           3 :     if( FD_UNLIKELY( err ) ) break;
     308           3 :   }
     309             : 
     310           3 :   if( FD_UNLIKELY( closedir( infiniband_dir ) && !err ) ) err = errno;
     311           3 :   if( FD_UNLIKELY( err ) ) {
     312           0 :     rdma_name[0] = '\0';
     313           0 :     *rdma_port   = 0U;
     314           0 :     errno        = err;
     315           0 :     return 0;
     316           0 :   }
     317           3 :   if( FD_UNLIKELY( match_cnt!=1UL ) ) {
     318           0 :     errno = match_cnt ? EEXIST : ENOENT;
     319           0 :     rdma_name[0] = '\0';
     320           0 :     *rdma_port   = 0U;
     321           0 :     return 0;
     322           0 :   }
     323           3 :   return 1;
     324           3 : }
     325             : 
     326             : int
     327           3 : fd_mlx5_uverbs_avail( void ) {
     328           3 :   struct stat class_stat;
     329           3 :   return !stat( "/sys/class/infiniband_verbs", &class_stat ) && S_ISDIR( class_stat.st_mode );
     330           3 : }
     331             : 
     332             : struct fd_mlx5_pd {
     333             :   fd_uverbs_ctx_t * ctx;    /* uverbs context */
     334             :   uint              handle; /* protection domain handle */
     335             : };
     336             : typedef struct fd_mlx5_pd fd_mlx5_pd_t;
     337             : 
     338             : struct fd_mlx5_caps {
     339             :   ulong max_mr_size;
     340             :   uint  max_send_wqe; /* max_send_wqebb, SQ WQE capacity */
     341             :   uint  max_recv_wr;
     342             :   uint  max_cqe;
     343             :   uchar eth_min_inline_hdr_sz;
     344             : };
     345             : typedef struct fd_mlx5_caps fd_mlx5_caps_t;
     346             : 
     347             : /* fd_uverbs_* types define Linux uverbs requests and responses */
     348             : struct fd_uverbs_ex_hdr {
     349             :   struct ib_uverbs_cmd_hdr    cmd;
     350             :   struct ib_uverbs_ex_cmd_hdr ex;
     351             : };
     352             : typedef struct fd_uverbs_ex_hdr fd_uverbs_ex_hdr_t;
     353             : FD_STATIC_ASSERT( sizeof(fd_uverbs_ex_hdr_t)==24UL, uverbs_ex_hdr_sz );
     354             : 
     355             : struct fd_uverbs_get_context_req {
     356             :   struct ib_uverbs_cmd_hdr             hdr;
     357             :   ulong                                response;
     358             :   struct mlx5_ib_alloc_ucontext_req_v2 mlx5;
     359             : };
     360             : typedef struct fd_uverbs_get_context_req fd_uverbs_get_context_req_t;
     361             : FD_STATIC_ASSERT( sizeof(fd_uverbs_get_context_req_t)==48UL, uverbs_get_context_req_sz );
     362             : 
     363             : struct fd_uverbs_get_context_resp {
     364             :   uint                               async_fd;
     365             :   uint                               num_comp_vectors;
     366             :   struct mlx5_ib_alloc_ucontext_resp mlx5;
     367             : };
     368             : typedef struct fd_uverbs_get_context_resp fd_uverbs_get_context_resp_t;
     369             : FD_STATIC_ASSERT( sizeof(fd_uverbs_get_context_resp_t)==80UL, uverbs_get_context_resp_sz );
     370             : 
     371             : struct fd_uverbs_query_device_req {
     372             :   fd_uverbs_ex_hdr_t                 hdr;
     373             :   struct ib_uverbs_ex_query_device   core;
     374             : };
     375             : typedef struct fd_uverbs_query_device_req fd_uverbs_query_device_req_t;
     376             : 
     377             : struct fd_uverbs_query_device_resp {
     378             :   struct ib_uverbs_ex_query_device_resp core;
     379             :   struct mlx5_ib_query_device_resp      mlx5;
     380             : };
     381             : typedef struct fd_uverbs_query_device_resp fd_uverbs_query_device_resp_t;
     382             : 
     383             : struct fd_uverbs_query_port_req {
     384             :   struct ib_uverbs_cmd_hdr hdr;
     385             :   ulong                    response;
     386             :   uchar                    port_num;
     387             :   uchar                    reserved[ 7 ];
     388             : };
     389             : typedef struct fd_uverbs_query_port_req fd_uverbs_query_port_req_t;
     390             : FD_STATIC_ASSERT( sizeof(fd_uverbs_query_port_req_t)==24UL, uverbs_query_port_req_sz );
     391             : 
     392             : struct fd_uverbs_alloc_pd_req {
     393             :   struct ib_uverbs_cmd_hdr hdr;
     394             :   ulong                    response;
     395             : };
     396             : typedef struct fd_uverbs_alloc_pd_req fd_uverbs_alloc_pd_req_t;
     397             : FD_STATIC_ASSERT( sizeof(fd_uverbs_alloc_pd_req_t)==16UL, uverbs_alloc_pd_req_sz );
     398             : 
     399             : struct fd_uverbs_alloc_pd_resp {
     400             :   uint                         pd_handle;
     401             :   struct mlx5_ib_alloc_pd_resp mlx5;
     402             : };
     403             : typedef struct fd_uverbs_alloc_pd_resp fd_uverbs_alloc_pd_resp_t;
     404             : FD_STATIC_ASSERT( sizeof(fd_uverbs_alloc_pd_resp_t)==8UL, uverbs_alloc_pd_resp_sz );
     405             : 
     406             : struct fd_uverbs_reg_mr_req {
     407             :   struct ib_uverbs_cmd_hdr hdr;
     408             :   ulong                    response;
     409             :   ulong                    start;
     410             :   ulong                    length;
     411             :   ulong                    hca_va;
     412             :   uint                     pd_handle;
     413             :   uint                     access_flags;
     414             : };
     415             : typedef struct fd_uverbs_reg_mr_req fd_uverbs_reg_mr_req_t;
     416             : FD_STATIC_ASSERT( sizeof(fd_uverbs_reg_mr_req_t)==48UL, uverbs_reg_mr_req_sz );
     417             : 
     418             : struct fd_uverbs_reg_mr_resp {
     419             :   uint mr_handle;
     420             :   uint lkey;
     421             :   uint rkey;
     422             : };
     423             : typedef struct fd_uverbs_reg_mr_resp fd_uverbs_reg_mr_resp_t;
     424             : FD_STATIC_ASSERT( sizeof(fd_uverbs_reg_mr_resp_t)==12UL, uverbs_reg_mr_resp_sz );
     425             : 
     426             : struct fd_uverbs_ioctl_hdr {
     427             :   ushort length;
     428             :   ushort object_id;
     429             :   ushort method_id;
     430             :   ushort num_attrs;
     431             :   ulong  reserved1;
     432             :   uint   driver_id;
     433             :   uint   reserved2;
     434             : };
     435             : typedef struct fd_uverbs_ioctl_hdr fd_uverbs_ioctl_hdr_t;
     436             : FD_STATIC_ASSERT( sizeof(fd_uverbs_ioctl_hdr_t)==24UL, uverbs_ioctl_hdr_sz );
     437             : 
     438             : struct fd_uverbs_alloc_uar_req {
     439             :   fd_uverbs_ioctl_hdr_t hdr;
     440             :   struct ib_uverbs_attr attrs[ 5 ];
     441             : };
     442             : typedef struct fd_uverbs_alloc_uar_req fd_uverbs_alloc_uar_req_t;
     443             : FD_STATIC_ASSERT( sizeof(fd_uverbs_alloc_uar_req_t)==104UL, uverbs_alloc_uar_req_sz );
     444             : 
     445             : struct fd_uverbs_destroy_uar_req {
     446             :   fd_uverbs_ioctl_hdr_t hdr;
     447             :   struct ib_uverbs_attr attrs[ 1 ];
     448             : };
     449             : typedef struct fd_uverbs_destroy_uar_req fd_uverbs_destroy_uar_req_t;
     450             : FD_STATIC_ASSERT( sizeof(fd_uverbs_destroy_uar_req_t)==40UL, uverbs_destroy_uar_req_sz );
     451             : 
     452             : struct fd_uverbs_create_cq_req {
     453             :   struct ib_uverbs_cmd_hdr hdr;
     454             :   ulong                    response;
     455             :   ulong                    user_handle;
     456             :   uint                     cqe;
     457             :   uint                     comp_vector;
     458             :   int                      comp_channel;
     459             :   uint                     reserved;
     460             :   /* mlx5_ib_create_cq gained uar_page_index in Linux 5.7 */
     461             :   union {
     462             :     struct mlx5_ib_create_cq fields;
     463             :     struct {
     464             :       uchar  prefix[ 24 ];
     465             :       ushort uar_page_index;
     466             :     } uar;
     467             :   } mlx5;
     468             : };
     469             : typedef struct fd_uverbs_create_cq_req fd_uverbs_create_cq_req_t;
     470             : 
     471             : struct fd_uverbs_create_cq_resp {
     472             :   uint                          cq_handle;
     473             :   uint                          cqe;
     474             :   struct mlx5_ib_create_cq_resp mlx5;
     475             : };
     476             : typedef struct fd_uverbs_create_cq_resp fd_uverbs_create_cq_resp_t;
     477             : FD_STATIC_ASSERT( sizeof(fd_uverbs_create_cq_resp_t)==16UL, uverbs_create_cq_resp_sz );
     478             : 
     479             : struct fd_uverbs_destroy_cq_req {
     480             :   struct ib_uverbs_cmd_hdr hdr;
     481             :   ulong                    response;
     482             :   uint                     cq_handle;
     483             :   uint                     reserved;
     484             : };
     485             : typedef struct fd_uverbs_destroy_cq_req fd_uverbs_destroy_cq_req_t;
     486             : FD_STATIC_ASSERT( sizeof(fd_uverbs_destroy_cq_req_t)==24UL, uverbs_destroy_cq_req_sz );
     487             : 
     488             : struct fd_uverbs_create_tx_qp_req {
     489             :   struct ib_uverbs_cmd_hdr hdr;
     490             :   ulong                    response;
     491             :   ulong                    user_handle;
     492             :   uint                     pd_handle;
     493             :   uint                     send_cq_handle;
     494             :   uint                     recv_cq_handle;
     495             :   uint                     srq_handle;
     496             :   uint                     max_send_wr;
     497             :   uint                     max_recv_wr;
     498             :   uint                     max_send_sge;
     499             :   uint                     max_recv_sge;
     500             :   uint                     max_inline_data;
     501             :   uchar                    sq_sig_all;
     502             :   uchar                    qp_type;
     503             :   uchar                    is_srq;
     504             :   uchar                    reserved;
     505             :   struct mlx5_ib_create_qp mlx5;
     506             : };
     507             : typedef struct fd_uverbs_create_tx_qp_req fd_uverbs_create_tx_qp_req_t;
     508             : 
     509             : struct fd_uverbs_create_tx_qp_resp {
     510             :   struct ib_uverbs_create_qp_resp core;
     511             :   struct mlx5_ib_create_qp_resp   mlx5;
     512             : };
     513             : typedef struct fd_uverbs_create_tx_qp_resp fd_uverbs_create_tx_qp_resp_t;
     514             : 
     515             : struct fd_uverbs_modify_qp_req {
     516             :   struct ib_uverbs_cmd_hdr   hdr;
     517             :   struct ib_uverbs_modify_qp core;
     518             : };
     519             : typedef struct fd_uverbs_modify_qp_req fd_uverbs_modify_qp_req_t;
     520             : FD_STATIC_ASSERT( sizeof(fd_uverbs_modify_qp_req_t)==120UL, uverbs_modify_qp_req_sz );
     521             : 
     522             : struct fd_uverbs_create_rx_wq_req {
     523             :   fd_uverbs_ex_hdr_t            hdr;
     524             :   struct ib_uverbs_ex_create_wq core;
     525             :   struct mlx5_ib_create_wq      mlx5;
     526             : };
     527             : typedef struct fd_uverbs_create_rx_wq_req fd_uverbs_create_rx_wq_req_t;
     528             : 
     529             : struct fd_uverbs_create_rx_wq_resp {
     530             :   struct ib_uverbs_ex_create_wq_resp core;
     531             :   struct mlx5_ib_create_wq_resp      mlx5;
     532             : };
     533             : typedef struct fd_uverbs_create_rx_wq_resp fd_uverbs_create_rx_wq_resp_t;
     534             : 
     535             : struct fd_uverbs_modify_rx_wq_req {
     536             :   fd_uverbs_ex_hdr_t            hdr;
     537             :   struct ib_uverbs_ex_modify_wq core;
     538             :   struct mlx5_ib_modify_wq      mlx5;
     539             : };
     540             : typedef struct fd_uverbs_modify_rx_wq_req fd_uverbs_modify_rx_wq_req_t;
     541             : 
     542             : struct fd_uverbs_create_rwq_ind_table_req {
     543             :   fd_uverbs_ex_hdr_t hdr;
     544             :   uint               comp_mask;
     545             :   uint               log_ind_tbl_size;
     546             :   uint               wq_handles[];
     547             : };
     548             : typedef struct fd_uverbs_create_rwq_ind_table_req fd_uverbs_create_rwq_ind_table_req_t;
     549             : 
     550             : struct fd_uverbs_create_rss_qp_req {
     551             :   fd_uverbs_ex_hdr_t                 hdr;
     552             :   struct ib_uverbs_ex_create_qp      core;
     553             :   struct mlx5_ib_create_qp_rss       mlx5;
     554             : };
     555             : typedef struct fd_uverbs_create_rss_qp_req fd_uverbs_create_rss_qp_req_t;
     556             : 
     557             : struct fd_uverbs_create_rss_qp_resp {
     558             :   struct ib_uverbs_ex_create_qp_resp core;
     559             :   struct mlx5_ib_create_qp_resp      mlx5;
     560             : };
     561             : typedef struct fd_uverbs_create_rss_qp_resp fd_uverbs_create_rss_qp_resp_t;
     562             : 
     563             : struct fd_uverbs_create_udp_flow_req {
     564             :   fd_uverbs_ex_hdr_t                 hdr;
     565             :   uint                               comp_mask;
     566             :   uint                               qp_handle;
     567             :   uint                               type;
     568             :   ushort                             size;
     569             :   ushort                             priority;
     570             :   uchar                              num_of_specs;
     571             :   uchar                              reserved[2];
     572             :   uchar                              port;
     573             :   uint                               flags;
     574             :   struct ib_uverbs_flow_spec_eth     eth;
     575             :   struct ib_uverbs_flow_spec_ipv4    ipv4;
     576             :   struct ib_uverbs_flow_spec_tcp_udp udp;
     577             :   uint                               mlx5_ncounters_data;
     578             :   uint                               mlx5_reserved;
     579             : };
     580             : typedef struct fd_uverbs_create_udp_flow_req fd_uverbs_create_udp_flow_req_t;
     581             : FD_STATIC_ASSERT( sizeof(fd_uverbs_create_udp_flow_req_t)==144UL, uverbs_create_udp_flow_req_sz );
     582             : 
     583             : struct fd_uverbs_create_gre_flow_req {
     584             :   fd_uverbs_ex_hdr_t                 hdr;
     585             :   uint                               comp_mask;
     586             :   uint                               qp_handle;
     587             :   uint                               type;
     588             :   ushort                             size;
     589             :   ushort                             priority;
     590             :   uchar                              num_of_specs;
     591             :   uchar                              reserved[2];
     592             :   uchar                              port;
     593             :   uint                               flags;
     594             :   struct ib_uverbs_flow_spec_eth     eth;
     595             :   struct ib_uverbs_flow_spec_ipv4    outer_ipv4;
     596             :   struct ib_uverbs_flow_spec_gre     gre;
     597             :   struct ib_uverbs_flow_spec_ipv4    inner_ipv4;
     598             :   struct ib_uverbs_flow_spec_tcp_udp inner_udp;
     599             :   uint                               mlx5_ncounters_data;
     600             :   uint                               mlx5_reserved;
     601             : };
     602             : typedef struct fd_uverbs_create_gre_flow_req fd_uverbs_create_gre_flow_req_t;
     603             : FD_STATIC_ASSERT( sizeof(fd_uverbs_create_gre_flow_req_t)==200UL, uverbs_create_gre_flow_req_sz );
     604             : 
     605             : #define FD_RDMA_PATH_MAX (256UL)
     606             : 
     607           0 : #define FD_MLX5_FLOW_SPEC_ETH   (0x20U)  /* IB_FLOW_SPEC_ETH */
     608           0 : #define FD_MLX5_FLOW_SPEC_IPV4  (0x30U)  /* IB_FLOW_SPEC_IPV4 */
     609           0 : #define FD_MLX5_FLOW_SPEC_UDP   (0x41U)  /* IB_FLOW_SPEC_UDP */
     610           0 : #define FD_MLX5_FLOW_SPEC_GRE   (0x51U)  /* IB_FLOW_SPEC_GRE */
     611           0 : #define FD_MLX5_FLOW_SPEC_INNER (0x100U) /* IB_FLOW_SPEC_INNER */
     612             : 
     613             : /* fd_rdma_* helpers discover and inspect the selected Linux RDMA device */
     614             : static int
     615             : fd_rdma_read_text( char const * path,
     616             :                    char *       buf,
     617           0 :                    ulong        buf_sz ) {
     618           0 :   FD_TEST( buf_sz>1UL );
     619             : 
     620           0 :   int text_fd = open( path, O_RDONLY | O_CLOEXEC );
     621           0 :   if( FD_UNLIKELY( text_fd<0 ) ) return -1;
     622             : 
     623           0 :   long read_sz = read( text_fd, buf, buf_sz );
     624           0 :   int  err     = 0;
     625           0 :   if( FD_UNLIKELY( read_sz<0L ) )                  err = errno;
     626           0 :   else if( FD_UNLIKELY( (ulong)read_sz==buf_sz ) ) err = EOVERFLOW;
     627           0 :   if( FD_UNLIKELY( close( text_fd ) && !err ) )    err = errno;
     628           0 :   if( FD_UNLIKELY( err ) ) {
     629           0 :     errno = err;
     630           0 :     return -1;
     631           0 :   }
     632             : 
     633           0 :   ulong text_sz = (ulong)read_sz;
     634           0 :   while( text_sz && fd_isspace( (int)(uchar)buf[ text_sz-1UL ] ) ) text_sz--;
     635           0 :   if( FD_UNLIKELY( !text_sz ) ) {
     636           0 :     errno = EPROTO;
     637           0 :     return -1;
     638           0 :   }
     639           0 :   buf[ text_sz ] = '\0';
     640           0 :   return 0;
     641           0 : }
     642             : 
     643             : static int
     644             : fd_rdma_read_uint( char const * path,
     645           0 :                    uint *       value ) {
     646           0 :   char buf[ 32 ];
     647           0 :   if( FD_UNLIKELY( fd_rdma_read_text( path, buf, sizeof(buf) ) ) ) return -1;
     648             : 
     649           0 :   char * end;
     650           0 :   ulong parsed_value = strtoul( buf, &end, 10 );
     651           0 :   if( FD_UNLIKELY( *end || parsed_value>UINT_MAX ) ) {
     652           0 :     errno = EPROTO;
     653           0 :     return -1;
     654           0 :   }
     655           0 :   *value = (uint)parsed_value;
     656           0 :   return 0;
     657           0 : }
     658             : 
     659             : static int
     660             : fd_rdma_name_valid( char const * name,
     661           0 :                     ulong        name_max ) {
     662           0 :   if( FD_UNLIKELY( !name || !name[0] ) ) return 0;
     663           0 :   if( FD_UNLIKELY( name[0]=='.' && (!name[1] || (name[1]=='.' && !name[2])) ) ) return 0;
     664           0 :   for( ulong i=0UL; i<name_max; i++ ) {
     665           0 :     uchar c = (uchar)name[ i ];
     666           0 :     if( !c ) return 1;
     667           0 :     if( !((c>=(uchar)'a' && c<=(uchar)'z') ||
     668           0 :           (c>=(uchar)'A' && c<=(uchar)'Z') ||
     669           0 :           (c>=(uchar)'0' && c<=(uchar)'9') ||
     670           0 :           c==(uchar)'_' || c==(uchar)'-' || c==(uchar)'.') ) return 0;
     671           0 :   }
     672           0 :   return 0;
     673           0 : }
     674             : 
     675             : static int
     676           0 : fd_mlx5_check_driver( char const * rdma_name ) {
     677           0 :   char path[ FD_RDMA_PATH_MAX ];
     678           0 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL,
     679           0 :                                  "/sys/class/infiniband/%s/device/driver", rdma_name ) );
     680             : 
     681           0 :   char target[ FD_RDMA_PATH_MAX ];
     682           0 :   ssize_t target_sz = readlink( path, target, sizeof(target)-1UL );
     683           0 :   if( FD_UNLIKELY( target_sz<0 ) ) return -1;
     684           0 :   if( FD_UNLIKELY( (ulong)target_sz==sizeof(target)-1UL ) ) {
     685           0 :     errno = ENAMETOOLONG;
     686           0 :     return -1;
     687           0 :   }
     688           0 :   target[ target_sz ] = '\0';
     689             : 
     690           0 :   char const * driver = strrchr( target, '/' );
     691           0 :   driver = driver ? driver+1 : target;
     692           0 :   if( FD_UNLIKELY( strcmp( driver, "mlx5_core" ) ) ) {
     693           0 :     errno = ENODEV;
     694           0 :     return -1;
     695           0 :   }
     696           0 :   return 0;
     697           0 : }
     698             : 
     699             : /* fd_uverbs_* helpers build and submit Linux uverbs commands */
     700             : static int
     701           0 : fd_uverbs_name_valid( char const * name ) {
     702           0 :   ulong name_sz = strnlen( name, FD_UVERBS_NAME_MAX );
     703           0 :   if( name_sz==FD_UVERBS_NAME_MAX || name_sz==6UL || strncmp( name, "uverbs", 6UL ) ) return 0;
     704           0 :   for( char const * digit_cur=name+6; *digit_cur; digit_cur++ )
     705           0 :     if( *digit_cur<'0' || *digit_cur>'9' ) return 0;
     706           0 :   return 1;
     707           0 : }
     708             : 
     709             : static int
     710             : fd_uverbs_resolve( char         uverbs_name[ FD_UVERBS_NAME_MAX ],
     711           0 :                    char const * rdma_name ) {
     712           0 :   if( FD_UNLIKELY( !fd_rdma_name_valid( rdma_name, FD_MLX5_RDMA_NAME_MAX ) ) ) {
     713           0 :     errno = EINVAL;
     714           0 :     return -1;
     715           0 :   }
     716           0 :   if( FD_UNLIKELY( fd_mlx5_check_driver( rdma_name ) ) ) return -1;
     717             : 
     718           0 :   DIR * uverbs_dir = opendir( "/sys/class/infiniband_verbs" );
     719           0 :   if( FD_UNLIKELY( !uverbs_dir ) ) return -1;
     720             : 
     721           0 :   char matched_uverbs_name[ FD_UVERBS_NAME_MAX ];
     722           0 :   int  err;
     723           0 :   for(;;) {
     724           0 :     errno = 0;
     725           0 :     struct dirent * uverbs_entry = readdir( uverbs_dir );
     726           0 :     if( !uverbs_entry ) {
     727           0 :       err = errno ? errno : ENODEV;
     728           0 :       break;
     729           0 :     }
     730           0 :     if( !fd_uverbs_name_valid( uverbs_entry->d_name ) ) continue;
     731             : 
     732           0 :     char path[ FD_RDMA_PATH_MAX ];
     733           0 :     char entry_rdma_name[ FD_MLX5_RDMA_NAME_MAX ];
     734           0 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL,
     735           0 :                                    "/sys/class/infiniband_verbs/%s/ibdev", uverbs_entry->d_name ) );
     736           0 :     if( FD_UNLIKELY( fd_rdma_read_text( path, entry_rdma_name, sizeof(entry_rdma_name) ) ) ) {
     737           0 :       err = errno;
     738           0 :       break;
     739           0 :     }
     740           0 :     if( strcmp( entry_rdma_name, rdma_name ) ) continue;
     741             : 
     742           0 :     ulong name_sz = strlen( uverbs_entry->d_name );
     743           0 :     fd_memcpy( matched_uverbs_name, uverbs_entry->d_name, name_sz+1UL );
     744           0 :     err = 0;
     745           0 :     break;
     746           0 :   }
     747           0 :   closedir( uverbs_dir );
     748           0 :   if( FD_UNLIKELY( err ) ) {
     749           0 :     errno = err;
     750           0 :     return -1;
     751           0 :   }
     752             : 
     753           0 :   char path[ FD_RDMA_PATH_MAX ];
     754           0 :   uint core_abi;
     755           0 :   uint provider_abi;
     756           0 :   if( FD_UNLIKELY( fd_rdma_read_uint( "/sys/class/infiniband_verbs/abi_version", &core_abi ) ) ) return -1;
     757           0 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL,
     758           0 :                                  "/sys/class/infiniband_verbs/%s/abi_version", matched_uverbs_name ) );
     759           0 :   if( FD_UNLIKELY( fd_rdma_read_uint( path, &provider_abi ) ) ) return -1;
     760           0 :   if( FD_UNLIKELY( core_abi!=IB_USER_VERBS_ABI_VERSION ||
     761           0 :                    provider_abi!=MLX5_IB_UVERBS_ABI_VERSION ) ) {
     762           0 :     FD_LOG_WARNING(( "mlx5 uverbs ABI mismatch (core %u, expected %u, provider %u, expected %u)",
     763           0 :                      core_abi, (uint)IB_USER_VERBS_ABI_VERSION,
     764           0 :                      provider_abi, (uint)MLX5_IB_UVERBS_ABI_VERSION ));
     765           0 :     errno = EPROTONOSUPPORT;
     766           0 :     return -1;
     767           0 :   }
     768             : 
     769           0 :   fd_memcpy( uverbs_name, matched_uverbs_name, strlen(matched_uverbs_name)+1UL );
     770           0 :   return 0;
     771           0 : }
     772             : 
     773             : static int
     774             : fd_uverbs_init_cmd_hdr( struct ib_uverbs_cmd_hdr * hdr,
     775             :                         uint                       command,
     776             :                         ulong                      request_sz,
     777           0 :                         ulong                      response_sz ) {
     778           0 :   if( FD_UNLIKELY( !hdr || command>IB_USER_VERBS_CMD_COMMAND_MASK || request_sz<sizeof(*hdr) ) ) return -1;
     779             : 
     780           0 :   hdr->command   = command;
     781           0 :   hdr->in_words  = (ushort)(request_sz / 4UL);
     782           0 :   hdr->out_words = (ushort)(response_sz / 4UL);
     783           0 :   return 0;
     784           0 : }
     785             : 
     786             : static int
     787             : fd_uverbs_init_ex_hdr( fd_uverbs_ex_hdr_t * hdr,
     788             :                        uint                 command,
     789             :                        ulong                core_request_sz,
     790             :                        ulong                request_sz,
     791             :                        void *               response,
     792             :                        ulong                core_response_sz,
     793           0 :                        ulong                response_sz ) {
     794           0 :   ulong const hdr_sz = sizeof(*hdr);
     795           0 :   if( FD_UNLIKELY( !hdr || command>IB_USER_VERBS_CMD_COMMAND_MASK ||
     796           0 :                    core_request_sz<hdr_sz || request_sz<core_request_sz ||
     797           0 :                    response_sz<core_response_sz || (response_sz && !response) ) ) return -1;
     798             : 
     799           0 :   hdr->cmd.command           = IB_USER_VERBS_CMD_FLAG_EXTENDED | command;
     800           0 :   hdr->cmd.in_words          = (ushort)((core_request_sz-hdr_sz) / 8UL);
     801           0 :   hdr->cmd.out_words         = (ushort)(core_response_sz / 8UL);
     802           0 :   hdr->ex.provider_in_words  = (ushort)((request_sz-core_request_sz) / 8UL);
     803           0 :   hdr->ex.provider_out_words = (ushort)((response_sz-core_response_sz) / 8UL);
     804           0 :   hdr->ex.response           = (ulong)response;
     805           0 :   hdr->ex.cmd_hdr_reserved   = 0U;
     806           0 :   return 0;
     807           0 : }
     808             : 
     809             : static int
     810             : fd_uverbs_write_cmd( int          cmd_fd,
     811             :                      void const * req,
     812           0 :                      ulong        req_sz ) {
     813           0 :   ssize_t write_sz = write( cmd_fd, req, req_sz );
     814           0 :   if( FD_UNLIKELY( write_sz<0 ) ) return -1;
     815           0 :   if( FD_UNLIKELY( (ulong)write_sz!=req_sz ) ) {
     816           0 :     errno = EPROTO;
     817           0 :     return -1;
     818           0 :   }
     819           0 :   return 0;
     820           0 : }
     821             : 
     822             : static int
     823             : fd_uverbs_get_context( fd_uverbs_ctx_t * ctx,
     824           0 :                        fd_mlx5_caps_t *  caps ) {
     825           0 :   fd_uverbs_get_context_req_t req [1];
     826           0 :   fd_uverbs_get_context_resp_t resp[1];
     827           0 :   fd_memset( req,  0, sizeof(req ) );
     828           0 :   fd_memset( resp, 0, sizeof(resp) );
     829             : 
     830           0 :   req->response                    = (ulong)resp;
     831           0 :   req->mlx5.total_num_bfregs       = 16U;
     832           0 :   req->mlx5.num_low_latency_bfregs = 4U;
     833           0 :   req->mlx5.max_cqe_version        = 1U;
     834           0 :   req->mlx5.lib_caps               = MLX5_LIB_CAP_4K_UAR | MLX5_LIB_CAP_DYN_UAR;
     835           0 :   FD_TEST( !fd_uverbs_init_cmd_hdr( &req->hdr, IB_USER_VERBS_CMD_GET_CONTEXT,
     836           0 :                                     sizeof(req), sizeof(resp) ) );
     837           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( ctx->cmd_fd, req, sizeof(req) ) ) ) return -1;
     838           0 :   if( FD_UNLIKELY( resp->async_fd>(uint)INT_MAX ) ) {
     839           0 :     errno = EPROTO;
     840           0 :     return -1;
     841           0 :   }
     842             : 
     843           0 :   ctx->async_fd = (int)resp->async_fd;
     844           0 :   int fd_flags = fcntl( ctx->async_fd, F_GETFD );
     845           0 :   if( FD_UNLIKELY( fd_flags<0 || fcntl( ctx->async_fd, F_SETFD, fd_flags | FD_CLOEXEC ) ) ) return -1;
     846             : 
     847           0 :   ulong const uar_resp_sz = offsetof( struct mlx5_ib_alloc_ucontext_resp, num_uars_per_page )+
     848           0 :                             sizeof(resp->mlx5.num_uars_per_page);
     849           0 :   if( FD_UNLIKELY( resp->mlx5.response_length<uar_resp_sz                              ||
     850           0 :                    resp->mlx5.cqe_version>1U                                           ||
     851           0 :                    resp->mlx5.max_sq_desc_sz<sizeof(fd_mlx5_tx_wqe_t)                  ||
     852           0 :                    resp->mlx5.max_rq_desc_sz<sizeof(fd_mlx5_rx_wqe_t)                  ||
     853           0 :                    resp->mlx5.log_uar_size!=(uint)fd_ulong_find_lsb( FD_MLX5_PAGE_SZ ) ||
     854           0 :                    resp->mlx5.num_uars_per_page!=1U                                    ||
     855           0 :                    resp->mlx5.tot_bfregs ) ) {
     856           0 :     FD_LOG_WARNING(( "unsupported mlx5 context capabilities (response length %u, CQE version %u, "
     857           0 :                      "max SQ descriptor %u, max RQ descriptor %u, log UAR size %u, UARs per page %u, "
     858           0 :                      "legacy BF registers %u)",
     859           0 :                      resp->mlx5.response_length, (uint)resp->mlx5.cqe_version,
     860           0 :                      (uint)resp->mlx5.max_sq_desc_sz, (uint)resp->mlx5.max_rq_desc_sz,
     861           0 :                      resp->mlx5.log_uar_size, resp->mlx5.num_uars_per_page, resp->mlx5.tot_bfregs ));
     862           0 :     errno = EPROTONOSUPPORT;
     863           0 :     return -1;
     864           0 :   }
     865             : 
     866           0 :   caps->max_send_wqe = resp->mlx5.max_send_wqebb;
     867           0 :   caps->max_recv_wr  = resp->mlx5.max_recv_wr;
     868           0 :   switch( resp->mlx5.eth_min_inline ) {
     869           0 :   case MLX5_USER_INLINE_MODE_NONE: caps->eth_min_inline_hdr_sz = 0U;                        break;
     870           0 :   case MLX5_USER_INLINE_MODE_L2:   caps->eth_min_inline_hdr_sz = FD_MLX5_ETH_INLINE_HDR_SZ; break;
     871           0 :   default:
     872           0 :     FD_LOG_WARNING(( "unsupported mlx5 minimum inline mode %u", (uint)resp->mlx5.eth_min_inline ));
     873           0 :     errno = EPROTONOSUPPORT;
     874           0 :     return -1;
     875           0 :   }
     876           0 :   return 0;
     877           0 : }
     878             : 
     879             : static int
     880             : fd_uverbs_query_device( fd_uverbs_ctx_t * ctx,
     881           0 :                         fd_mlx5_caps_t *  caps ) {
     882           0 :   fd_uverbs_query_device_req_t  req [1];
     883           0 :   fd_uverbs_query_device_resp_t resp[1];
     884           0 :   fd_memset( req,  0, sizeof(req ) );
     885           0 :   fd_memset( resp, 0, sizeof(resp) );
     886             : 
     887           0 :   FD_TEST( !fd_uverbs_init_ex_hdr( &req->hdr, IB_USER_VERBS_EX_CMD_QUERY_DEVICE,
     888           0 :                                    sizeof(req), sizeof(req), resp,
     889           0 :                                    sizeof(resp->core), sizeof(resp) ) );
     890           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( ctx->cmd_fd, req, sizeof(req) ) ) ) return -1;
     891             : 
     892           0 :   struct ib_uverbs_query_device_resp const * core = &resp->core.base;
     893           0 :   caps->max_mr_size = core->max_mr_size;
     894           0 :   caps->max_cqe     = core->max_cqe;
     895           0 :   if( FD_UNLIKELY( resp->core.response_length<sizeof(*core) ||
     896           0 :                    !caps->max_mr_size                       ||
     897           0 :                    !core->max_qp_wr                         ||
     898           0 :                    !core->max_sge                           ||
     899           0 :                    !caps->max_cqe                           ||
     900           0 :                    core->phys_port_cnt<ctx->port_num ) ) {
     901           0 :     FD_LOG_WARNING(( "unsupported mlx5 device capabilities (max MR size %lu, max QP WRs %u, max SGEs %u, "
     902           0 :                      "max CQEs %u, physical ports %u, requested port %u)",
     903           0 :                      caps->max_mr_size, core->max_qp_wr, core->max_sge, caps->max_cqe,
     904           0 :                      (uint)core->phys_port_cnt, ctx->port_num ));
     905           0 :     errno = EPROTONOSUPPORT;
     906           0 :     return -1;
     907           0 :   }
     908             : 
     909           0 :   ulong const tunnel_caps_sz = offsetof( struct mlx5_ib_query_device_resp, tunnel_offloads_caps )+
     910           0 :                                sizeof(resp->mlx5.tunnel_offloads_caps);
     911           0 :   ulong const required_hash_fields = FD_MLX5_RX_HASH_IPV4_UDP | FD_MLX5_RX_HASH_INNER;
     912             : 
     913           0 :   if( FD_UNLIKELY( resp->mlx5.response_length<tunnel_caps_sz                                              ||
     914           0 :                    !(resp->mlx5.rss_caps.rx_hash_function & FD_MLX5_RX_HASH_FUNC_TOEPLITZ)                ||
     915           0 :                    (resp->mlx5.rss_caps.rx_hash_fields_mask & required_hash_fields)!=required_hash_fields ||
     916           0 :                    !(resp->mlx5.tunnel_offloads_caps & FD_MLX5_TUNNEL_OFFLOADS_GRE) ) ) {
     917           0 :     FD_LOG_WARNING(( "unsupported mlx5 GRE RSS capabilities (response length %u, hash functions %#x, "
     918           0 :                      "hash fields %#lx, tunnel offloads %#x)",
     919           0 :                      resp->mlx5.response_length, (uint)resp->mlx5.rss_caps.rx_hash_function,
     920           0 :                      (ulong)resp->mlx5.rss_caps.rx_hash_fields_mask, resp->mlx5.tunnel_offloads_caps ));
     921           0 :     errno = EPROTONOSUPPORT;
     922           0 :     return -1;
     923           0 :   }
     924           0 :   return 0;
     925           0 : }
     926             : 
     927             : static int
     928           0 : fd_uverbs_query_port( fd_uverbs_ctx_t * ctx ) {
     929           0 :   fd_uverbs_query_port_req_t       req [1];
     930           0 :   struct ib_uverbs_query_port_resp resp[1];
     931           0 :   fd_memset( req,  0, sizeof(req ) );
     932           0 :   fd_memset( resp, 0, sizeof(resp) );
     933             : 
     934           0 :   req->response = (ulong)resp;
     935           0 :   req->port_num = (uchar)ctx->port_num;
     936           0 :   FD_TEST( !fd_uverbs_init_cmd_hdr( &req->hdr, IB_USER_VERBS_CMD_QUERY_PORT,
     937           0 :                                     sizeof(req), sizeof(resp) ) );
     938           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( ctx->cmd_fd, req, sizeof(req) ) ) ) return -1;
     939             : 
     940           0 :   if( FD_UNLIKELY( resp->link_layer!=FD_MLX5_LINK_LAYER_ETHERNET ) ) {
     941           0 :     FD_LOG_WARNING(( "unsupported mlx5 link layer %u, expected Ethernet (%u)",
     942           0 :                      (uint)resp->link_layer, FD_MLX5_LINK_LAYER_ETHERNET ));
     943           0 :     errno = EPROTONOSUPPORT;
     944           0 :     return -1;
     945           0 :   }
     946           0 :   return 0;
     947           0 : }
     948             : 
     949             : static fd_uverbs_ctx_t *
     950             : fd_uverbs_open_context( fd_uverbs_ctx_t * ctx,
     951             :                         fd_mlx5_caps_t *  caps,
     952             :                         char const *      rdma_name,
     953           0 :                         uint              port_num ) {
     954           0 :   fd_memset( ctx, 0, sizeof(*ctx) );
     955           0 :   fd_memset( caps, 0, sizeof(*caps) );
     956           0 :   ctx->cmd_fd   = -1;
     957           0 :   ctx->async_fd = -1;
     958           0 :   if( FD_UNLIKELY( !port_num || port_num>(uint)UCHAR_MAX ) ) {
     959           0 :     errno = EINVAL;
     960           0 :     return NULL;
     961           0 :   }
     962           0 :   ctx->port_num = port_num;
     963             : 
     964           0 :   char uverbs_name[ FD_UVERBS_NAME_MAX ];
     965           0 :   if( FD_UNLIKELY( fd_uverbs_resolve( uverbs_name, rdma_name ) ) ) return NULL;
     966             : 
     967           0 :   char path[ FD_RDMA_PATH_MAX ];
     968           0 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/dev/infiniband/%s", uverbs_name ) );
     969           0 :   ctx->cmd_fd = open( path, O_RDWR | O_CLOEXEC );
     970           0 :   if( FD_UNLIKELY( ctx->cmd_fd<0 ) ) return NULL;
     971             : 
     972           0 :   struct stat cmd_fd_stat;
     973           0 :   if( FD_UNLIKELY( fstat( ctx->cmd_fd, &cmd_fd_stat ) ) ) return NULL;
     974           0 :   if( FD_UNLIKELY( !S_ISCHR( cmd_fd_stat.st_mode ) ) ) {
     975           0 :     errno = ENODEV;
     976           0 :     return NULL;
     977           0 :   }
     978             : 
     979           0 :   if( FD_UNLIKELY( fd_uverbs_get_context( ctx, caps )  ||
     980           0 :                    fd_uverbs_query_device( ctx, caps ) ||
     981           0 :                    fd_uverbs_query_port( ctx ) ) ) return NULL;
     982           0 :   return ctx;
     983           0 : }
     984             : 
     985             : static fd_mlx5_pd_t *
     986             : fd_uverbs_alloc_pd( fd_mlx5_pd_t *      pd,
     987           0 :                     fd_uverbs_ctx_t *   ctx ) {
     988           0 :   if( FD_UNLIKELY( !pd ) ) {
     989           0 :     errno = EINVAL;
     990           0 :     return NULL;
     991           0 :   }
     992           0 :   fd_memset( pd, 0, sizeof(*pd) );
     993           0 :   if( FD_UNLIKELY( !ctx || ctx->cmd_fd<0 ) ) {
     994           0 :     errno = EINVAL;
     995           0 :     return NULL;
     996           0 :   }
     997             : 
     998           0 :   fd_uverbs_alloc_pd_req_t  req [1];
     999           0 :   fd_uverbs_alloc_pd_resp_t resp[1];
    1000           0 :   fd_memset( req,  0, sizeof(req ) );
    1001           0 :   fd_memset( resp, 0, sizeof(resp) );
    1002           0 :   req->response = (ulong)resp;
    1003           0 :   FD_TEST( !fd_uverbs_init_cmd_hdr( &req->hdr, IB_USER_VERBS_CMD_ALLOC_PD,
    1004           0 :                                     sizeof(req), sizeof(resp) ) );
    1005           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( ctx->cmd_fd, req, sizeof(req) ) ) ) return NULL;
    1006             : 
    1007           0 :   pd->ctx    = ctx;
    1008           0 :   pd->handle = resp->pd_handle;
    1009           0 :   return pd;
    1010           0 : }
    1011             : 
    1012             : static uint *
    1013             : fd_uverbs_register_mr( uint *         lkey,
    1014             :                        fd_mlx5_pd_t * pd,
    1015             :                        void *         memory,
    1016             :                        ulong          memory_sz,
    1017             :                        ulong          iova,
    1018           0 :                        ulong          max_mr_size ) {
    1019           0 :   ulong memory_addr = (ulong)memory;
    1020           0 :   if( FD_UNLIKELY( !memory || !memory_sz || memory_sz>ULONG_MAX-memory_addr || memory_sz>ULONG_MAX-iova ||
    1021           0 :                    memory_sz>max_mr_size ||
    1022           0 :                    (memory_addr & (FD_MLX5_PAGE_SZ-1UL))!=(iova & (FD_MLX5_PAGE_SZ-1UL)) ) ) {
    1023           0 :     errno = EINVAL;
    1024           0 :     return NULL;
    1025           0 :   }
    1026             : 
    1027           0 :   fd_uverbs_reg_mr_req_t  req [1];
    1028           0 :   fd_uverbs_reg_mr_resp_t resp[1];
    1029           0 :   fd_memset( req,  0, sizeof(req ) );
    1030           0 :   fd_memset( resp, 0, sizeof(resp) );
    1031           0 :   req->response     = (ulong)resp;
    1032           0 :   req->start        = memory_addr;
    1033           0 :   req->length       = memory_sz;
    1034           0 :   req->hca_va       = iova;
    1035           0 :   req->pd_handle    = pd->handle;
    1036           0 :   req->access_flags = IB_UVERBS_ACCESS_LOCAL_WRITE;
    1037           0 :   FD_TEST( !fd_uverbs_init_cmd_hdr( &req->hdr, IB_USER_VERBS_CMD_REG_MR,
    1038           0 :                                     sizeof(req), sizeof(resp) ) );
    1039           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( pd->ctx->cmd_fd, req, sizeof(req) ) ) ) return NULL;
    1040             : 
    1041           0 :   *lkey = resp->lkey;
    1042           0 :   return lkey;
    1043           0 : }
    1044             : 
    1045             : static void
    1046             : fd_uverbs_init_ioctl_attr( struct ib_uverbs_attr * attr,
    1047             :                            ushort                  attr_id,
    1048             :                            ushort                  len,
    1049           0 :                            ulong                   data ) {
    1050           0 :   fd_memset( attr, 0, sizeof(*attr) );
    1051           0 :   attr->attr_id = attr_id;
    1052           0 :   attr->len     = len;
    1053           0 :   attr->flags   = UVERBS_ATTR_F_MANDATORY;
    1054           0 :   attr->data    = data;
    1055           0 : }
    1056             : 
    1057             : static int
    1058             : fd_uverbs_init_alloc_uar_req( fd_uverbs_alloc_uar_req_t * req,
    1059             :                               ulong *                     mmap_offset,
    1060             :                               uint *                      mmap_sz,
    1061           0 :                               uint *                      page_id ) {
    1062           0 :   if( FD_UNLIKELY( !req || !mmap_offset || !mmap_sz || !page_id ) ) return -1;
    1063           0 :   fd_memset( req, 0, sizeof(*req) );
    1064           0 :   req->hdr.length    = (ushort)sizeof(*req);
    1065           0 :   req->hdr.object_id = MLX5_IB_OBJECT_UAR;
    1066           0 :   req->hdr.method_id = MLX5_IB_METHOD_UAR_OBJ_ALLOC;
    1067           0 :   req->hdr.num_attrs = 5U;
    1068           0 :   req->hdr.driver_id = RDMA_DRIVER_MLX5;
    1069           0 :   fd_uverbs_init_ioctl_attr( req->attrs+0, MLX5_IB_ATTR_UAR_OBJ_ALLOC_HANDLE,      0U, 0UL );
    1070           0 :   fd_uverbs_init_ioctl_attr( req->attrs+1, MLX5_IB_ATTR_UAR_OBJ_ALLOC_TYPE,        8U, MLX5_IB_UAPI_UAR_ALLOC_TYPE_NC );
    1071           0 :   fd_uverbs_init_ioctl_attr( req->attrs+2, MLX5_IB_ATTR_UAR_OBJ_ALLOC_MMAP_OFFSET, 8U, (ulong)mmap_offset );
    1072           0 :   fd_uverbs_init_ioctl_attr( req->attrs+3, MLX5_IB_ATTR_UAR_OBJ_ALLOC_MMAP_LENGTH, 4U, (ulong)mmap_sz );
    1073           0 :   fd_uverbs_init_ioctl_attr( req->attrs+4, MLX5_IB_ATTR_UAR_OBJ_ALLOC_PAGE_ID,     4U, (ulong)page_id );
    1074           0 :   return 0;
    1075           0 : }
    1076             : 
    1077             : static int
    1078             : fd_uverbs_init_destroy_uar_req( fd_uverbs_destroy_uar_req_t * req,
    1079           0 :                                 uint                          handle ) {
    1080           0 :   if( FD_UNLIKELY( !req ) ) return -1;
    1081           0 :   fd_memset( req, 0, sizeof(*req) );
    1082           0 :   req->hdr.length    = (ushort)sizeof(*req);
    1083           0 :   req->hdr.object_id = MLX5_IB_OBJECT_UAR;
    1084           0 :   req->hdr.method_id = MLX5_IB_METHOD_UAR_OBJ_DESTROY;
    1085           0 :   req->hdr.num_attrs = 1U;
    1086           0 :   req->hdr.driver_id = RDMA_DRIVER_MLX5;
    1087           0 :   fd_uverbs_init_ioctl_attr( req->attrs, MLX5_IB_ATTR_UAR_OBJ_DESTROY_HANDLE, 0U, handle );
    1088           0 :   return 0;
    1089           0 : }
    1090             : 
    1091             : static int
    1092             : fd_uverbs_destroy_uar( fd_uverbs_ctx_t * ctx,
    1093           0 :                        uint              handle ) {
    1094           0 :   fd_uverbs_destroy_uar_req_t req[1];
    1095           0 :   if( FD_UNLIKELY( fd_uverbs_init_destroy_uar_req( req, handle ) ) ) {
    1096           0 :     errno = EINVAL;
    1097           0 :     return -1;
    1098           0 :   }
    1099           0 :   return ioctl( ctx->cmd_fd, RDMA_VERBS_IOCTL, &req->hdr );
    1100           0 : }
    1101             : 
    1102             : static ulong *
    1103             : fd_uverbs_alloc_uar( fd_uverbs_ctx_t * ctx,
    1104             :                      uint *            page_id,
    1105           0 :                      ulong *           mmap_offset_out ) {
    1106           0 :   ulong mmap_offset = 0UL;
    1107           0 :   uint  mmap_sz     = 0U;
    1108           0 :   uint  uar_page_id = 0U;
    1109           0 :   fd_uverbs_alloc_uar_req_t req[1];
    1110           0 :   if( FD_UNLIKELY( fd_uverbs_init_alloc_uar_req( req, &mmap_offset, &mmap_sz, &uar_page_id ) ) ) {
    1111           0 :     errno = EINVAL;
    1112           0 :     return NULL;
    1113           0 :   }
    1114           0 :   if( FD_UNLIKELY( ioctl( ctx->cmd_fd, RDMA_VERBS_IOCTL, &req->hdr ) ) ) return NULL;
    1115             : 
    1116           0 :   ulong handle_raw = req->attrs[0].data;
    1117           0 :   if( FD_UNLIKELY( handle_raw>UINT_MAX || mmap_sz!=FD_MLX5_PAGE_SZ ||
    1118           0 :                    (mmap_offset & (FD_MLX5_PAGE_SZ-1UL)) || mmap_offset>(ulong)LONG_MAX ) ) {
    1119           0 :     FD_LOG_WARNING(( "invalid mlx5 UAR response (handle %lu, mapping size %u, mapping offset %#lx)",
    1120           0 :                      handle_raw, mmap_sz, mmap_offset ));
    1121           0 :     int err = EPROTO;
    1122           0 :     if( handle_raw<=UINT_MAX ) fd_uverbs_destroy_uar( ctx, (uint)handle_raw );
    1123           0 :     errno = err;
    1124           0 :     return NULL;
    1125           0 :   }
    1126             : 
    1127           0 :   *page_id         = uar_page_id;
    1128           0 :   *mmap_offset_out = mmap_offset;
    1129           0 :   return mmap_offset_out;
    1130           0 : }
    1131             : 
    1132             : volatile uchar *
    1133             : fd_uverbs_map_uar( fd_uverbs_ctx_t * ctx,
    1134           0 :                    ulong             mmap_offset ) {
    1135           0 :   if( FD_UNLIKELY( !ctx || ctx->cmd_fd<0 ||
    1136           0 :                    (mmap_offset & (FD_MLX5_PAGE_SZ-1UL)) || mmap_offset>(ulong)LONG_MAX ) ) {
    1137           0 :     errno = EINVAL;
    1138           0 :     return NULL;
    1139           0 :   }
    1140           0 :   void * uar_mapping = mmap( NULL, FD_MLX5_PAGE_SZ, PROT_WRITE, MAP_SHARED,
    1141           0 :                              ctx->cmd_fd, (off_t)mmap_offset );
    1142           0 :   if( FD_UNLIKELY( uar_mapping==MAP_FAILED ) ) return NULL;
    1143           0 :   return (volatile uchar *)uar_mapping + FD_MLX5_UAR_DB_OFFSET;
    1144           0 : }
    1145             : 
    1146             : static uint *
    1147             : fd_uverbs_create_cq( uint *               handle,
    1148             :                      fd_uverbs_ctx_t *    ctx,
    1149             :                      fd_mlx5_cq_t const * cq,
    1150             :                      uint                 page_id,
    1151           0 :                      uint                 max_cqe ) {
    1152           0 :   if( FD_UNLIKELY( !handle ) ) {
    1153           0 :     errno = EINVAL;
    1154           0 :     return NULL;
    1155           0 :   }
    1156           0 :   *handle = 0U;
    1157           0 :   if( FD_UNLIKELY( !ctx || ctx->cmd_fd<0 || !cq || !cq->entries || !cq->control ||
    1158           0 :                    !fd_uint_is_pow2( cq->depth ) || cq->depth-1U>max_cqe || page_id>(uint)USHRT_MAX ) ) {
    1159           0 :     errno = EINVAL;
    1160           0 :     return NULL;
    1161           0 :   }
    1162             : 
    1163           0 :   fd_uverbs_create_cq_req_t  req [1];
    1164           0 :   fd_uverbs_create_cq_resp_t resp[1];
    1165           0 :   fd_memset( req,  0, sizeof(req ) );
    1166           0 :   fd_memset( resp, 0, sizeof(resp) );
    1167             : 
    1168           0 :   req->response                = (ulong)resp;
    1169           0 :   req->user_handle             = (ulong)cq;
    1170           0 :   req->cqe                     = cq->depth-1U;
    1171           0 :   req->comp_channel            = -1;
    1172           0 :   req->mlx5.fields.buf_addr    = (ulong)cq->entries;
    1173           0 :   req->mlx5.fields.db_addr     = (ulong)cq->control;
    1174           0 :   req->mlx5.fields.cqe_size    = sizeof(fd_mlx5_cqe_t);
    1175           0 :   req->mlx5.fields.flags       = MLX5_IB_CREATE_CQ_FLAGS_UAR_PAGE_INDEX;
    1176           0 :   req->mlx5.uar.uar_page_index = (ushort)page_id;
    1177           0 :   FD_TEST( !fd_uverbs_init_cmd_hdr( &req->hdr, IB_USER_VERBS_CMD_CREATE_CQ,
    1178           0 :                                     sizeof(req), sizeof(resp) ) );
    1179           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( ctx->cmd_fd, req, sizeof(req) ) ) ) return NULL;
    1180           0 :   if( FD_UNLIKELY( resp->cqe!=cq->depth-1U ) ) {
    1181           0 :     fd_uverbs_destroy_cq_req_t       destroy[1];
    1182           0 :     struct ib_uverbs_destroy_cq_resp destroy_resp[1];
    1183           0 :     fd_memset( destroy,      0, sizeof(destroy)      );
    1184           0 :     fd_memset( destroy_resp, 0, sizeof(destroy_resp) );
    1185             : 
    1186           0 :     destroy->response  = (ulong)destroy_resp;
    1187           0 :     destroy->cq_handle = resp->cq_handle;
    1188           0 :     FD_TEST( !fd_uverbs_init_cmd_hdr( &destroy->hdr, IB_USER_VERBS_CMD_DESTROY_CQ,
    1189           0 :                                       sizeof(destroy), sizeof(destroy_resp) ) );
    1190           0 :     fd_uverbs_write_cmd( ctx->cmd_fd, destroy, sizeof(destroy) );
    1191           0 :     FD_LOG_WARNING(( "invalid mlx5 CQ response (CQEs %u, requested %u)", resp->cqe, cq->depth-1U ));
    1192           0 :     errno = EPROTO;
    1193           0 :     return NULL;
    1194           0 :   }
    1195             : 
    1196           0 :   *handle = resp->cq_handle;
    1197           0 :   return handle;
    1198           0 : }
    1199             : 
    1200             : static int
    1201             : fd_uverbs_modify_qp( fd_uverbs_ctx_t * uverbs,
    1202             :                      fd_mlx5_tx_qp_t * tx_qp,
    1203           0 :                      uint              state ) {
    1204           0 :   fd_uverbs_modify_qp_req_t req[1];
    1205           0 :   fd_memset( req, 0, sizeof(req) );
    1206           0 :   req->core.qp_handle = tx_qp->handle;
    1207           0 :   req->core.attr_mask = FD_MLX5_QP_ATTR_STATE;
    1208           0 :   req->core.qp_state  = (uchar)state;
    1209           0 :   if( state==FD_MLX5_QPS_INIT ) {
    1210           0 :     req->core.attr_mask |= FD_MLX5_QP_ATTR_PORT;
    1211           0 :     req->core.port_num   = (uchar)uverbs->port_num;
    1212           0 :   }
    1213           0 :   FD_TEST( !fd_uverbs_init_cmd_hdr( &req->hdr, IB_USER_VERBS_CMD_MODIFY_QP,
    1214           0 :                                     sizeof(req), 0UL ) );
    1215           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( uverbs->cmd_fd, req, sizeof(req) ) ) ) return -1;
    1216           0 :   return 0;
    1217           0 : }
    1218             : 
    1219             : static fd_mlx5_tx_qp_t *
    1220             : fd_uverbs_create_tx_qp( fd_uverbs_ctx_t * uverbs,
    1221             :                         fd_mlx5_tx_qp_t * tx_qp,
    1222             :                         fd_mlx5_pd_t *    pd,
    1223             :                         uint              tx_cq_handle,
    1224           0 :                         uint              uar_page_id ) {
    1225           0 :   if( FD_UNLIKELY( !uverbs || !tx_qp || !tx_qp->tx_cq || !tx_qp->sq ||
    1226           0 :                    !tx_qp->control || !pd || pd->ctx!=uverbs ) ) {
    1227           0 :     errno = EINVAL;
    1228           0 :     return NULL;
    1229           0 :   }
    1230             : 
    1231           0 :   fd_uverbs_create_tx_qp_req_t  req [1];
    1232           0 :   fd_uverbs_create_tx_qp_resp_t resp[1];
    1233           0 :   fd_memset( req,  0, sizeof(req ) );
    1234           0 :   fd_memset( resp, 0, sizeof(resp) );
    1235             : 
    1236           0 :   req->response          = (ulong)resp;
    1237           0 :   req->user_handle       = (ulong)tx_qp;
    1238           0 :   req->pd_handle         = pd->handle;
    1239           0 :   req->send_cq_handle    = tx_cq_handle;
    1240             :   /* Linux requires both CQ handles even when max_recv_wr is zero. */
    1241           0 :   req->recv_cq_handle    = tx_cq_handle;
    1242           0 :   req->max_send_wr       = tx_qp->tx_depth;
    1243           0 :   req->max_send_sge      = 1U;
    1244           0 :   req->qp_type           = IB_UVERBS_QPT_RAW_PACKET;
    1245           0 :   req->mlx5.db_addr      = (ulong)tx_qp->control;
    1246           0 :   req->mlx5.sq_wqe_count = tx_qp->tx_depth;
    1247           0 :   req->mlx5.flags        = MLX5_QP_FLAG_UAR_PAGE_INDEX;
    1248           0 :   req->mlx5.uidx         = 0U;
    1249           0 :   req->mlx5.bfreg_index  = uar_page_id;
    1250           0 :   req->mlx5.sq_buf_addr  = (ulong)tx_qp->sq;
    1251           0 :   FD_TEST( !fd_uverbs_init_cmd_hdr( &req->hdr, IB_USER_VERBS_CMD_CREATE_QP,
    1252           0 :                                     sizeof(req), sizeof(resp) ) );
    1253           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( uverbs->cmd_fd, req, sizeof(req) ) ) ) return NULL;
    1254             : 
    1255           0 :   if( FD_UNLIKELY( resp->core.max_send_wr<tx_qp->tx_depth ||
    1256           0 :                    resp->core.max_recv_wr                 ||
    1257           0 :                    resp->core.max_send_sge<1U             ||
    1258           0 :                    resp->core.max_recv_sge                ||
    1259           0 :                    resp->core.qpn>0xffffffU ) ) {
    1260           0 :     FD_LOG_WARNING(( "invalid mlx5 QP response (max send WRs %u, max receive WRs %u, max send SGEs %u, "
    1261           0 :                      "max receive SGEs %u, QPN %u)",
    1262           0 :                      resp->core.max_send_wr, resp->core.max_recv_wr, resp->core.max_send_sge,
    1263           0 :                      resp->core.max_recv_sge, resp->core.qpn ));
    1264           0 :     errno = EPROTO;
    1265           0 :     return NULL;
    1266           0 :   }
    1267             : 
    1268           0 :   tx_qp->handle = resp->core.qp_handle;
    1269           0 :   tx_qp->qpn    = resp->core.qpn;
    1270           0 :   return tx_qp;
    1271           0 : }
    1272             : 
    1273             : static fd_mlx5_tx_qp_t *
    1274             : fd_uverbs_start_tx_qp( fd_uverbs_ctx_t * uverbs,
    1275           0 :                        fd_mlx5_tx_qp_t * tx_qp ) {
    1276           0 :   if( FD_UNLIKELY( fd_uverbs_modify_qp( uverbs, tx_qp, FD_MLX5_QPS_INIT ) ||
    1277           0 :                    fd_uverbs_modify_qp( uverbs, tx_qp, FD_MLX5_QPS_RTR  ) ||
    1278           0 :                    fd_uverbs_modify_qp( uverbs, tx_qp, FD_MLX5_QPS_RTS  ) ) ) return NULL;
    1279           0 :   return tx_qp;
    1280           0 : }
    1281             : 
    1282             : static fd_mlx5_rx_wq_t *
    1283             : fd_uverbs_create_rx_wq( fd_uverbs_ctx_t * uverbs,
    1284             :                         fd_mlx5_rx_wq_t * rx_wq,
    1285             :                         fd_mlx5_pd_t *    pd,
    1286           0 :                         uint              rx_cq_handle ) {
    1287           0 :   fd_uverbs_create_rx_wq_req_t  req [1];
    1288           0 :   fd_uverbs_create_rx_wq_resp_t resp[1];
    1289           0 :   fd_memset( req,  0, sizeof(req ) );
    1290           0 :   fd_memset( resp, 0, sizeof(resp) );
    1291             : 
    1292           0 :   req->core.wq_type      = IB_UVERBS_WQT_RQ;
    1293           0 :   req->core.user_handle  = (ulong)rx_wq;
    1294           0 :   req->core.pd_handle    = pd->handle;
    1295           0 :   req->core.cq_handle    = rx_cq_handle;
    1296           0 :   req->core.max_wr       = rx_wq->rx_depth;
    1297           0 :   req->core.max_sge      = 1U;
    1298           0 :   req->mlx5.buf_addr     = (ulong)rx_wq->rq;
    1299           0 :   req->mlx5.db_addr      = (ulong)rx_wq->control;
    1300           0 :   req->mlx5.rq_wqe_count = rx_wq->rx_depth;
    1301           0 :   req->mlx5.rq_wqe_shift = (uint)fd_ulong_find_lsb( sizeof(fd_mlx5_rx_wqe_t) );
    1302           0 :   ulong const core_req_sz  = offsetof( fd_uverbs_create_rx_wq_req_t,  mlx5 );
    1303           0 :   ulong const core_resp_sz = offsetof( fd_uverbs_create_rx_wq_resp_t, mlx5 );
    1304           0 :   FD_TEST( !fd_uverbs_init_ex_hdr( &req->hdr, IB_USER_VERBS_EX_CMD_CREATE_WQ,
    1305           0 :                                    core_req_sz, sizeof(req), resp,
    1306           0 :                                    core_resp_sz, sizeof(resp) ) );
    1307           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( uverbs->cmd_fd, req, sizeof(req) ) ) ) return NULL;
    1308           0 :   if( FD_UNLIKELY( resp->core.max_wr<rx_wq->rx_depth ||
    1309           0 :                    resp->core.max_sge<1U             ||
    1310           0 :                    resp->core.wqn>0xffffffU ) ) {
    1311           0 :     FD_LOG_WARNING(( "invalid mlx5 receive WQ response (max WRs %u, max SGEs %u, WQN %u)",
    1312           0 :                      resp->core.max_wr, resp->core.max_sge, resp->core.wqn ));
    1313           0 :     errno = EPROTO;
    1314           0 :     return NULL;
    1315           0 :   }
    1316             : 
    1317           0 :   rx_wq->handle = resp->core.wq_handle;
    1318           0 :   rx_wq->wqn    = resp->core.wqn;
    1319           0 :   return rx_wq;
    1320           0 : }
    1321             : 
    1322             : static fd_mlx5_rx_wq_t *
    1323             : fd_uverbs_start_rx_wq( fd_uverbs_ctx_t * uverbs,
    1324           0 :                        fd_mlx5_rx_wq_t * rx_wq ) {
    1325           0 :   fd_uverbs_modify_rx_wq_req_t req[1];
    1326           0 :   fd_memset( req, 0, sizeof(req) );
    1327           0 :   req->core.attr_mask     = FD_MLX5_WQ_ATTR_STATE | FD_MLX5_WQ_ATTR_CUR_STATE;
    1328           0 :   req->core.wq_handle     = rx_wq->handle;
    1329           0 :   req->core.wq_state      = FD_MLX5_WQS_RDY;
    1330           0 :   req->core.curr_wq_state = FD_MLX5_WQS_RESET;
    1331           0 :   ulong const core_req_sz = offsetof( fd_uverbs_modify_rx_wq_req_t, mlx5 );
    1332           0 :   FD_TEST( !fd_uverbs_init_ex_hdr( &req->hdr, IB_USER_VERBS_EX_CMD_MODIFY_WQ,
    1333           0 :                                    core_req_sz, sizeof(req), NULL, 0UL, 0UL ) );
    1334           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( uverbs->cmd_fd, req, sizeof(req) ) ) ) return NULL;
    1335           0 :   return rx_wq;
    1336           0 : }
    1337             : 
    1338             : static uint *
    1339             : fd_uverbs_create_rwq_ind_table( uint *                        handle,
    1340             :                                 fd_uverbs_ctx_t *             uverbs,
    1341             :                                 fd_mlx5_uverbs_tile_t const * tiles,
    1342           0 :                                 ulong                         tile_cnt ) {
    1343           0 :   if( FD_UNLIKELY( !fd_ulong_is_pow2( tile_cnt ) || tile_cnt>FD_MLX5_INDIRECTION_MAX ) ) {
    1344           0 :     errno = EINVAL;
    1345           0 :     return NULL;
    1346           0 :   }
    1347           0 :   ulong const req_sz = fd_ulong_align_up( sizeof(fd_uverbs_create_rwq_ind_table_req_t)+tile_cnt*sizeof(uint), 8UL );
    1348           0 :   fd_uverbs_create_rwq_ind_table_req_t * req = aligned_alloc( 8UL, req_sz );
    1349           0 :   if( FD_UNLIKELY( !req ) ) return NULL;
    1350           0 :   struct ib_uverbs_ex_create_rwq_ind_table_resp resp[1];
    1351           0 :   fd_memset( req,  0, req_sz );
    1352           0 :   fd_memset( resp, 0, sizeof(resp) );
    1353           0 :   req->log_ind_tbl_size = (uint)fd_ulong_find_lsb( tile_cnt );
    1354           0 :   for( ulong i=0UL; i<tile_cnt; i++ ) req->wq_handles[ i ] = tiles[ i ].rx_wq->handle;
    1355           0 :   FD_TEST( !fd_uverbs_init_ex_hdr( &req->hdr, IB_USER_VERBS_EX_CMD_CREATE_RWQ_IND_TBL,
    1356           0 :                                    req_sz, req_sz, resp,
    1357           0 :                                    sizeof(resp), sizeof(resp) ) );
    1358           0 :   int err = fd_uverbs_write_cmd( uverbs->cmd_fd, req, req_sz );
    1359           0 :   int saved_errno = errno;
    1360           0 :   free( req );
    1361           0 :   if( FD_UNLIKELY( err ) ) {
    1362           0 :     errno = saved_errno;
    1363           0 :     return NULL;
    1364           0 :   }
    1365           0 :   *handle = resp->ind_tbl_handle;
    1366           0 :   return handle;
    1367           0 : }
    1368             : 
    1369             : static fd_mlx5_rss_qp_t *
    1370             : fd_uverbs_create_rss_qp( fd_uverbs_ctx_t *  uverbs,
    1371             :                          fd_mlx5_rss_qp_t * rss_qp,
    1372             :                          fd_mlx5_pd_t *     pd,
    1373             :                          uint               rwq_ind_table_handle,
    1374             :                          ulong              hash_fields,
    1375           0 :                          uint               flags ) {
    1376           0 :   fd_uverbs_create_rss_qp_req_t  req [1];
    1377           0 :   fd_uverbs_create_rss_qp_resp_t resp[1];
    1378           0 :   fd_memset( req,  0, sizeof(req ) );
    1379           0 :   fd_memset( resp, 0, sizeof(resp) );
    1380           0 :   req->core.user_handle         = (ulong)rss_qp;
    1381           0 :   req->core.pd_handle           = pd->handle;
    1382           0 :   req->core.qp_type             = IB_UVERBS_QPT_RAW_PACKET;
    1383           0 :   req->core.comp_mask           = IB_UVERBS_CREATE_QP_MASK_IND_TABLE;
    1384           0 :   req->core.rwq_ind_tbl_handle  = rwq_ind_table_handle;
    1385           0 :   req->mlx5.rx_hash_fields_mask = hash_fields;
    1386           0 :   req->mlx5.rx_hash_function    = FD_MLX5_RX_HASH_FUNC_TOEPLITZ;
    1387           0 :   req->mlx5.rx_key_len          = FD_MLX5_RX_HASH_KEY_SZ;
    1388           0 :   req->mlx5.flags               = flags;
    1389             : 
    1390           0 :   if( FD_UNLIKELY( !fd_rng_secure( req->mlx5.rx_hash_key, FD_MLX5_RX_HASH_KEY_SZ ) ) ) return NULL;
    1391             : 
    1392           0 :   ulong const core_req_sz  = offsetof( fd_uverbs_create_rss_qp_req_t,  mlx5 );
    1393           0 :   ulong const core_resp_sz = offsetof( fd_uverbs_create_rss_qp_resp_t, mlx5 );
    1394             : 
    1395           0 :   FD_TEST( !fd_uverbs_init_ex_hdr( &req->hdr, IB_USER_VERBS_EX_CMD_CREATE_QP,
    1396           0 :                                    core_req_sz, sizeof(req), resp,
    1397           0 :                                    core_resp_sz, sizeof(resp) ) );
    1398           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( uverbs->cmd_fd, req, sizeof(req) ) ) ) return NULL;
    1399           0 :   rss_qp->handle = resp->core.base.qp_handle;
    1400           0 :   return rss_qp;
    1401           0 : }
    1402             : 
    1403             : static int
    1404             : fd_uverbs_init_udp_flow_req( fd_uverbs_create_udp_flow_req_t *   req,
    1405             :                              struct ib_uverbs_create_flow_resp * resp,
    1406             :                              uint                                qp_handle,
    1407             :                              uint                                port_num,
    1408             :                              uint                                dst_ip,
    1409           0 :                              ushort                              dst_port ) {
    1410           0 :   if( FD_UNLIKELY( !req || !resp || !port_num || port_num>UCHAR_MAX || !dst_port ) ) return -1;
    1411             : 
    1412           0 :   fd_memset( req,  0, sizeof(*req ) );
    1413           0 :   fd_memset( resp, 0, sizeof(*resp) );
    1414           0 :   ulong const core_req_sz = offsetof( fd_uverbs_create_udp_flow_req_t, mlx5_ncounters_data );
    1415           0 :   FD_TEST( !fd_uverbs_init_ex_hdr( &req->hdr, IB_USER_VERBS_EX_CMD_CREATE_FLOW,
    1416           0 :                                    core_req_sz, sizeof(*req), resp,
    1417           0 :                                    sizeof(*resp), sizeof(*resp) ) );
    1418             : 
    1419           0 :   req->qp_handle           = qp_handle;
    1420           0 :   req->size                = sizeof(req->eth)+sizeof(req->ipv4)+sizeof(req->udp);
    1421           0 :   req->num_of_specs        = 3U;
    1422           0 :   req->port                = (uchar)port_num;
    1423           0 :   req->eth.type            = FD_MLX5_FLOW_SPEC_ETH;
    1424           0 :   req->eth.size            = sizeof(req->eth);
    1425           0 :   req->eth.val.ether_type  = fd_ushort_bswap( FD_ETH_HDR_TYPE_IP );
    1426           0 :   req->eth.mask.ether_type = USHORT_MAX;
    1427           0 :   req->ipv4.type           = FD_MLX5_FLOW_SPEC_IPV4;
    1428           0 :   req->ipv4.size           = sizeof(req->ipv4);
    1429           0 :   req->ipv4.val.dst_ip     = dst_ip;
    1430           0 :   req->ipv4.mask.dst_ip    = dst_ip ? UINT_MAX : 0U;
    1431           0 :   req->udp.type            = FD_MLX5_FLOW_SPEC_UDP;
    1432           0 :   req->udp.size            = sizeof(req->udp);
    1433           0 :   req->udp.val.dst_port    = fd_ushort_bswap( dst_port );
    1434           0 :   req->udp.mask.dst_port   = USHORT_MAX;
    1435           0 :   return 0;
    1436           0 : }
    1437             : 
    1438             : int
    1439             : fd_uverbs_create_udp_flow( fd_uverbs_ctx_t *        uverbs,
    1440             :                            fd_mlx5_rss_qp_t const * rss_qp,
    1441             :                            uint                     dst_ip,
    1442           0 :                            ushort                   dst_port ) {
    1443           0 :   if( FD_UNLIKELY( !uverbs || uverbs->cmd_fd<0 || !rss_qp || !dst_port ) ) {
    1444           0 :     errno = EINVAL;
    1445           0 :     return -1;
    1446           0 :   }
    1447             : 
    1448           0 :   fd_uverbs_create_udp_flow_req_t   req [1];
    1449           0 :   struct ib_uverbs_create_flow_resp resp[1];
    1450           0 :   if( FD_UNLIKELY( fd_uverbs_init_udp_flow_req( req, resp, rss_qp->handle,
    1451           0 :                                                 uverbs->port_num, dst_ip,
    1452           0 :                                                 dst_port ) ) ) {
    1453           0 :     errno = EINVAL;
    1454           0 :     return -1;
    1455           0 :   }
    1456           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( uverbs->cmd_fd, req, sizeof(req) ) ) ) return -1;
    1457           0 :   return 0;
    1458           0 : }
    1459             : 
    1460             : static int
    1461             : fd_uverbs_init_gre_flow_req( fd_uverbs_create_gre_flow_req_t *   req,
    1462             :                              struct ib_uverbs_create_flow_resp * resp,
    1463             :                              uint                                qp_handle,
    1464             :                              uint                                port_num,
    1465             :                              uint                                inner_dst_ip,
    1466           0 :                              ushort                              inner_dst_port ) {
    1467           0 :   if( FD_UNLIKELY( !req || !resp || !port_num || port_num>UCHAR_MAX || !inner_dst_port ) ) return -1;
    1468             : 
    1469           0 :   fd_memset( req,  0, sizeof(*req ) );
    1470           0 :   fd_memset( resp, 0, sizeof(*resp) );
    1471           0 :   ulong const core_req_sz = offsetof( fd_uverbs_create_gre_flow_req_t, mlx5_ncounters_data );
    1472           0 :   FD_TEST( !fd_uverbs_init_ex_hdr( &req->hdr, IB_USER_VERBS_EX_CMD_CREATE_FLOW,
    1473           0 :                                    core_req_sz, sizeof(*req), resp,
    1474           0 :                                    sizeof(*resp), sizeof(*resp) ) );
    1475             : 
    1476           0 :   req->qp_handle    = qp_handle;
    1477           0 :   req->size         = sizeof(req->eth)+sizeof(req->outer_ipv4)+sizeof(req->gre)+
    1478           0 :                       sizeof(req->inner_ipv4)+sizeof(req->inner_udp);
    1479           0 :   req->num_of_specs = 5U;
    1480           0 :   req->port         = (uchar)port_num;
    1481           0 :   req->eth = (struct ib_uverbs_flow_spec_eth) {
    1482           0 :     .type = FD_MLX5_FLOW_SPEC_ETH,
    1483           0 :     .size = sizeof(req->eth),
    1484           0 :     .val  = { .ether_type = fd_ushort_bswap( FD_ETH_HDR_TYPE_IP ) },
    1485           0 :     .mask = { .ether_type = USHORT_MAX },
    1486           0 :   };
    1487           0 :   req->outer_ipv4 = (struct ib_uverbs_flow_spec_ipv4) {
    1488           0 :     .type = FD_MLX5_FLOW_SPEC_IPV4,
    1489           0 :     .size = sizeof(req->outer_ipv4),
    1490           0 :     .val  = { .proto = FD_IP4_HDR_PROTOCOL_GRE },
    1491           0 :     .mask = { .proto = UCHAR_MAX },
    1492           0 :   };
    1493           0 :   req->gre = (struct ib_uverbs_flow_spec_gre) {
    1494           0 :     .type = FD_MLX5_FLOW_SPEC_GRE,
    1495           0 :     .size = sizeof(req->gre),
    1496           0 :     .val  = { .protocol = fd_ushort_bswap( FD_ETH_HDR_TYPE_IP ) },
    1497           0 :     .mask = { .protocol = USHORT_MAX },
    1498           0 :   };
    1499           0 :   req->inner_ipv4 = (struct ib_uverbs_flow_spec_ipv4) {
    1500           0 :     .type = FD_MLX5_FLOW_SPEC_INNER | FD_MLX5_FLOW_SPEC_IPV4,
    1501           0 :     .size = sizeof(req->inner_ipv4),
    1502           0 :     .val  = { .dst_ip = inner_dst_ip, .proto = FD_IP4_HDR_PROTOCOL_UDP },
    1503           0 :     .mask = { .dst_ip = inner_dst_ip ? UINT_MAX : 0U, .proto = UCHAR_MAX },
    1504           0 :   };
    1505           0 :   req->inner_udp = (struct ib_uverbs_flow_spec_tcp_udp) {
    1506           0 :     .type = FD_MLX5_FLOW_SPEC_INNER | FD_MLX5_FLOW_SPEC_UDP,
    1507           0 :     .size = sizeof(req->inner_udp),
    1508           0 :     .val  = { .dst_port = fd_ushort_bswap( inner_dst_port ) },
    1509           0 :     .mask = { .dst_port = USHORT_MAX },
    1510           0 :   };
    1511           0 :   return 0;
    1512           0 : }
    1513             : 
    1514             : int
    1515             : fd_uverbs_create_gre_udp_flow( fd_uverbs_ctx_t *        uverbs,
    1516             :                                fd_mlx5_rss_qp_t const * rss_qp,
    1517             :                                uint                     inner_dst_ip,
    1518           0 :                                ushort                   inner_dst_port ) {
    1519           0 :   if( FD_UNLIKELY( !uverbs || uverbs->cmd_fd<0 || !rss_qp || !inner_dst_port ) ) {
    1520           0 :     errno = EINVAL;
    1521           0 :     return -1;
    1522           0 :   }
    1523             : 
    1524           0 :   fd_uverbs_create_gre_flow_req_t   req [1];
    1525           0 :   struct ib_uverbs_create_flow_resp resp[1];
    1526           0 :   if( FD_UNLIKELY( fd_uverbs_init_gre_flow_req( req, resp, rss_qp->handle,
    1527           0 :                                                 uverbs->port_num, inner_dst_ip,
    1528           0 :                                                 inner_dst_port ) ) ) {
    1529           0 :     errno = EINVAL;
    1530           0 :     return -1;
    1531           0 :   }
    1532           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( uverbs->cmd_fd, req, sizeof(req) ) ) ) return -1;
    1533           0 :   return 0;
    1534           0 : }
    1535             : 
    1536             : fd_mlx5_rss_qp_t *
    1537             : fd_uverbs_init( fd_uverbs_ctx_t *       uverbs,
    1538             :                 fd_mlx5_uverbs_tile_t * tiles,
    1539             :                 ulong                   tile_cnt,
    1540             :                 fd_mlx5_rss_qp_t *      outer_rss_qp,
    1541             :                 fd_mlx5_rss_qp_t *      gre_rss_qp,
    1542             :                 char const *            rdma_name,
    1543           0 :                 uint                    port_num ) {
    1544           0 :   if( FD_UNLIKELY( !uverbs || !tiles || !tile_cnt || !fd_ulong_is_pow2( tile_cnt ) ||
    1545           0 :                    tile_cnt>FD_MLX5_INDIRECTION_MAX || !outer_rss_qp || !gre_rss_qp ) ) {
    1546           0 :     errno = EINVAL;
    1547           0 :     return NULL;
    1548           0 :   }
    1549           0 :   for( ulong i=0UL; i<tile_cnt; i++ ) {
    1550           0 :     fd_mlx5_uverbs_tile_t const * tile = tiles+i;
    1551           0 :     if( FD_UNLIKELY( !tile->rx_cq || !tile->tx_cq || !tile->rx_wq || !tile->tx_qp || !tile->lkey ||
    1552           0 :                      !tile->packet_memory || !tile->packet_memory_sz || !tile->rx_wq->rq ||
    1553           0 :                      !tile->rx_wq->control || tile->rx_wq->rx_cq!=tile->rx_cq || tile->tx_qp->tx_cq!=tile->tx_cq ||
    1554           0 :                      tile->rx_wq->rx_depth!=tile->rx_cq->depth || tile->tx_qp->tx_depth!=tile->tx_cq->depth ) ) {
    1555           0 :       errno = EINVAL;
    1556           0 :       return NULL;
    1557           0 :     }
    1558           0 :   }
    1559             : 
    1560           0 :   fd_mlx5_caps_t caps[1];
    1561           0 :   fd_mlx5_pd_t   pd[1];
    1562           0 :   uint           rwq_ind_table_handle;
    1563           0 :   if( FD_UNLIKELY( !fd_uverbs_open_context( uverbs, caps, rdma_name, port_num ) ) ) return NULL;
    1564           0 :   if( FD_UNLIKELY( !fd_uverbs_alloc_pd( pd, uverbs ) ) ) return NULL;
    1565             : 
    1566           0 :   for( ulong i=0UL; i<tile_cnt; i++ ) {
    1567           0 :     fd_mlx5_uverbs_tile_t * tile = tiles+i;
    1568           0 :     fd_mlx5_rx_wq_t * rx_wq = tile->rx_wq;
    1569           0 :     fd_mlx5_tx_qp_t * tx_qp = tile->tx_qp;
    1570           0 :     if( FD_UNLIKELY( !fd_uint_is_pow2( rx_wq->rx_depth ) ||
    1571           0 :                      !fd_uint_is_pow2( tx_qp->tx_depth ) ||
    1572           0 :                      rx_wq->rx_depth>caps->max_recv_wr   ||
    1573           0 :                      tx_qp->tx_depth>caps->max_send_wqe  ||
    1574           0 :                      rx_wq->rx_depth-1U>caps->max_cqe    ||
    1575           0 :                      tx_qp->tx_depth-1U>caps->max_cqe ) ) {
    1576           0 :       errno = EINVAL;
    1577           0 :       return NULL;
    1578           0 :     }
    1579             : 
    1580           0 :     uint uar_page_id;
    1581           0 :     uint rx_cq_handle;
    1582           0 :     uint tx_cq_handle;
    1583           0 :     if( FD_UNLIKELY( !fd_uverbs_alloc_uar( uverbs, &uar_page_id, &tx_qp->uar_mmap_offset )                  ||
    1584           0 :                      !fd_uverbs_create_cq( &rx_cq_handle, uverbs, tile->rx_cq, uar_page_id, caps->max_cqe ) ||
    1585           0 :                      !fd_uverbs_create_cq( &tx_cq_handle, uverbs, tile->tx_cq, uar_page_id, caps->max_cqe ) ||
    1586           0 :                      !fd_uverbs_register_mr( tile->lkey, pd, tile->packet_memory, tile->packet_memory_sz,
    1587           0 :                                              tile->packet_iova, caps->max_mr_size )                         ||
    1588           0 :                      !fd_uverbs_create_tx_qp( uverbs, tx_qp, pd, tx_cq_handle, uar_page_id )                ||
    1589           0 :                      !fd_uverbs_start_tx_qp( uverbs, tx_qp )                                                ||
    1590           0 :                      !fd_uverbs_create_rx_wq( uverbs, rx_wq, pd, rx_cq_handle )                             ||
    1591           0 :                      !fd_uverbs_start_rx_wq( uverbs, rx_wq ) ) ) {
    1592           0 :       return NULL;
    1593           0 :     }
    1594           0 :     tx_qp->tx_inline_hdr_sz = caps->eth_min_inline_hdr_sz;
    1595           0 :   }
    1596             : 
    1597           0 :   if( FD_UNLIKELY( !fd_uverbs_create_rwq_ind_table( &rwq_ind_table_handle, uverbs, tiles, tile_cnt ) ||
    1598           0 :                    !fd_uverbs_create_rss_qp( uverbs, outer_rss_qp, pd, rwq_ind_table_handle,
    1599           0 :                                              FD_MLX5_RX_HASH_IPV4_UDP, 0U ) ||
    1600           0 :                    !fd_uverbs_create_rss_qp( uverbs, gre_rss_qp, pd, rwq_ind_table_handle,
    1601           0 :                                              FD_MLX5_RX_HASH_IPV4_UDP | FD_MLX5_RX_HASH_INNER,
    1602           0 :                                              FD_MLX5_QP_TUNNEL_OFFLOADS ) ) ) return NULL;
    1603           0 :   return outer_rss_qp;
    1604           0 : }
    1605             : 
    1606             : #define FD_MLX5_NL_RECV_BUF_SZ (8192UL)
    1607             : 
    1608             : struct fd_mlx5_nl_req {
    1609             :   struct nlmsghdr nlh;
    1610             :   uchar           attrs[ 5UL*(sizeof(struct nlattr)+sizeof(uint)) ];
    1611             : };
    1612             : typedef struct fd_mlx5_nl_req fd_mlx5_nl_req_t;
    1613             : 
    1614             : typedef int (*fd_mlx5_nl_parse_fn_t)( struct nlmsghdr const * nlh,
    1615             :                                       void *                  parse_arg );
    1616             : 
    1617             : static void
    1618             : fd_mlx5_nl_req_init( fd_mlx5_nl_req_t * req,
    1619             :                      uint               type,
    1620             :                      uint               flags,
    1621           0 :                      uint               request_seq ) {
    1622           0 :   fd_memset( req, 0, sizeof(*req) );
    1623           0 :   req->nlh.nlmsg_len   = sizeof(struct nlmsghdr);
    1624           0 :   req->nlh.nlmsg_type  = (ushort)type;
    1625           0 :   req->nlh.nlmsg_flags = (ushort)flags;
    1626           0 :   req->nlh.nlmsg_seq   = request_seq;
    1627           0 : }
    1628             : 
    1629             : static int
    1630             : fd_mlx5_nl_req_u32( fd_mlx5_nl_req_t * req,
    1631             :                     ushort             type,
    1632           0 :                     uint               value ) {
    1633           0 :   ulong const attr_sz = sizeof(struct nlattr)+sizeof(uint);
    1634           0 :   if( FD_UNLIKELY( (ulong)req->nlh.nlmsg_len+attr_sz>sizeof(*req) ) ) {
    1635           0 :     errno = ENOSPC;
    1636           0 :     return -1;
    1637           0 :   }
    1638           0 :   struct nlattr * attr = (struct nlattr *)((uchar *)req+req->nlh.nlmsg_len);
    1639           0 :   attr->nla_len  = (ushort)attr_sz;
    1640           0 :   attr->nla_type = type;
    1641           0 :   fd_memcpy( (uchar *)(attr+1), &value, sizeof(value) );
    1642           0 :   req->nlh.nlmsg_len += (uint)attr_sz;
    1643           0 :   return 0;
    1644           0 : }
    1645             : 
    1646             : static int
    1647             : fd_mlx5_nla_next( uchar const **         attr_cur,
    1648             :                   ulong *                attr_rem,
    1649           0 :                   struct nlattr const ** attr ) {
    1650           0 :   if( FD_UNLIKELY( !*attr_rem ) ) return 0;
    1651           0 :   if( FD_UNLIKELY( *attr_rem<sizeof(struct nlattr) ) ) {
    1652           0 :     errno = EPROTO;
    1653           0 :     return -1;
    1654           0 :   }
    1655             : 
    1656           0 :   struct nlattr const * next_attr       = (struct nlattr const *)*attr_cur;
    1657           0 :   ulong const           attr_sz         = next_attr->nla_len;
    1658           0 :   ulong const           aligned_attr_sz = fd_ulong_align_up( attr_sz, NLA_ALIGNTO );
    1659           0 :   if( FD_UNLIKELY( attr_sz<sizeof(*next_attr) || aligned_attr_sz>*attr_rem ) ) {
    1660           0 :     errno = EPROTO;
    1661           0 :     return -1;
    1662           0 :   }
    1663             : 
    1664           0 :   *attr      = next_attr;
    1665           0 :   *attr_cur += aligned_attr_sz;
    1666           0 :   *attr_rem -= aligned_attr_sz;
    1667           0 :   return 1;
    1668           0 : }
    1669             : 
    1670             : static int
    1671             : fd_mlx5_nla_find( void const *           attr_data,
    1672             :                   ulong                  attr_data_sz,
    1673             :                   ushort                 type,
    1674           0 :                   struct nlattr const ** result_attr ) {
    1675           0 :   *result_attr = NULL;
    1676           0 :   uchar const * attr_cur = attr_data;
    1677           0 :   while( attr_data_sz ) {
    1678           0 :     struct nlattr const * attr;
    1679           0 :     int next_result = fd_mlx5_nla_next( &attr_cur, &attr_data_sz, &attr );
    1680           0 :     if( FD_UNLIKELY( next_result<0 ) ) return -1;
    1681           0 :     if( (attr->nla_type & (ushort)NLA_TYPE_MASK)==type ) {
    1682           0 :       *result_attr = attr;
    1683           0 :       return 1;
    1684           0 :     }
    1685           0 :   }
    1686           0 :   return 0;
    1687           0 : }
    1688             : 
    1689             : static int
    1690             : fd_mlx5_nla_u32( struct nlattr const * attr,
    1691           0 :                  uint *                value ) {
    1692           0 :   if( FD_UNLIKELY( attr->nla_len!=sizeof(*attr)+sizeof(uint) ) ) {
    1693           0 :     errno = EPROTO;
    1694           0 :     return -1;
    1695           0 :   }
    1696           0 :   fd_memcpy( value, attr+1, sizeof(*value) );
    1697           0 :   return 0;
    1698           0 : }
    1699             : 
    1700             : static int
    1701             : fd_mlx5_nl_send( int                      netlink_fd,
    1702           0 :                  fd_mlx5_nl_req_t const * req ) {
    1703           0 :   struct sockaddr_nl kernel_addr = { .nl_family=AF_NETLINK };
    1704           0 :   ssize_t send_sz = sendto( netlink_fd, req, req->nlh.nlmsg_len, 0,
    1705           0 :                             fd_type_pun_const( &kernel_addr ), sizeof(kernel_addr) );
    1706           0 :   if( FD_UNLIKELY( send_sz!=(ssize_t)req->nlh.nlmsg_len ) ) {
    1707           0 :     if( send_sz>=0 ) errno = EIO;
    1708           0 :     return -1;
    1709           0 :   }
    1710           0 :   return 0;
    1711           0 : }
    1712             : 
    1713             : static int
    1714             : fd_mlx5_nl_recv( int                   netlink_fd,
    1715             :                  uint                  request_seq,
    1716             :                  int                   multipart,
    1717             :                  fd_mlx5_nl_parse_fn_t parse_fn,
    1718           0 :                  void *                parse_arg ) {
    1719           0 :   uchar msg_buf[ FD_MLX5_NL_RECV_BUF_SZ ] __attribute__((aligned(alignof(struct nlmsghdr))));
    1720           0 :   for(;;) {
    1721           0 :     ssize_t recv_sz;
    1722           0 :     do recv_sz = recvfrom( netlink_fd, msg_buf, sizeof(msg_buf), MSG_TRUNC, NULL, NULL );
    1723           0 :     while( FD_UNLIKELY( recv_sz<0 && errno==EINTR ) );
    1724           0 :     if( FD_UNLIKELY( recv_sz<=0 || (ulong)recv_sz>sizeof(msg_buf) ) ) {
    1725           0 :       if( !recv_sz ) errno = EPROTO;
    1726           0 :       else if( recv_sz>0 ) errno = EMSGSIZE;
    1727           0 :       return -1;
    1728           0 :     }
    1729             : 
    1730           0 :     uchar const * msg_cur = msg_buf;
    1731           0 :     ulong msg_rem = (ulong)recv_sz;
    1732           0 :     while( msg_rem ) {
    1733           0 :       if( FD_UNLIKELY( msg_rem<sizeof(struct nlmsghdr) ) ) {
    1734           0 :         errno = EPROTO;
    1735           0 :         return -1;
    1736           0 :       }
    1737           0 :       struct nlmsghdr const * nlh = (struct nlmsghdr const *)msg_cur;
    1738           0 :       ulong const msg_sz          = nlh->nlmsg_len;
    1739           0 :       ulong const aligned_msg_sz  = fd_ulong_align_up( msg_sz, NLMSG_ALIGNTO );
    1740           0 :       if( FD_UNLIKELY( msg_sz<sizeof(struct nlmsghdr) || aligned_msg_sz>msg_rem ) ) {
    1741           0 :         errno = EPROTO;
    1742           0 :         return -1;
    1743           0 :       }
    1744           0 :       if( FD_UNLIKELY( nlh->nlmsg_seq!=request_seq ) ) {
    1745           0 :         errno = EPROTO;
    1746           0 :         return -1;
    1747           0 :       }
    1748           0 :       if( FD_UNLIKELY( nlh->nlmsg_flags & NLM_F_DUMP_INTR ) ) {
    1749           0 :         errno = EAGAIN;
    1750           0 :         return -1;
    1751           0 :       }
    1752           0 :       if( nlh->nlmsg_type==NLMSG_DONE ) {
    1753           0 :         ulong const payload_sz = msg_sz-sizeof(*nlh);
    1754           0 :         if( payload_sz ) {
    1755           0 :           if( FD_UNLIKELY( payload_sz<sizeof(int) ) ) {
    1756           0 :             errno = EPROTO;
    1757           0 :             return -1;
    1758           0 :           }
    1759           0 :           int done_err;
    1760           0 :           fd_memcpy( &done_err, nlh+1, sizeof(done_err) );
    1761           0 :           if( FD_UNLIKELY( done_err>0 ) ) {
    1762           0 :             errno = EPROTO;
    1763           0 :             return -1;
    1764           0 :           }
    1765           0 :           if( FD_UNLIKELY( done_err<0 ) ) {
    1766           0 :             errno = -done_err;
    1767           0 :             return -1;
    1768           0 :           }
    1769           0 :         }
    1770           0 :         if( FD_UNLIKELY( !multipart ) ) {
    1771           0 :           errno = EPROTO;
    1772           0 :           return -1;
    1773           0 :         }
    1774           0 :         return 0;
    1775           0 :       }
    1776           0 :       if( nlh->nlmsg_type==NLMSG_ERROR ) {
    1777           0 :         if( FD_UNLIKELY( msg_sz<sizeof(struct nlmsghdr)+sizeof(struct nlmsgerr) ) ) {
    1778           0 :           errno = EPROTO;
    1779           0 :           return -1;
    1780           0 :         }
    1781           0 :         int err = ((struct nlmsgerr const *)(nlh+1))->error;
    1782           0 :         if( FD_UNLIKELY( err ) ) {
    1783           0 :           errno = -err;
    1784           0 :           return -1;
    1785           0 :         }
    1786           0 :         if( FD_UNLIKELY( multipart ) ) {
    1787           0 :           errno = EPROTO;
    1788           0 :           return -1;
    1789           0 :         }
    1790           0 :         return 0;
    1791           0 :       }
    1792           0 :       if( FD_UNLIKELY( parse_fn( nlh, parse_arg ) ) ) return -1;
    1793           0 :       msg_cur += aligned_msg_sz;
    1794           0 :       msg_rem -= aligned_msg_sz;
    1795           0 :     }
    1796           0 :   }
    1797           0 : }
    1798             : 
    1799             : struct fd_mlx5_nl_dev_find {
    1800             :   char const * rdma_name;
    1801             :   uint         dev_idx;
    1802             : };
    1803             : typedef struct fd_mlx5_nl_dev_find fd_mlx5_nl_dev_find_t;
    1804             : 
    1805             : static int
    1806             : fd_mlx5_nl_parse_dev( struct nlmsghdr const * nlh,
    1807           0 :                       void *                  parse_arg ) {
    1808           0 :   if( FD_UNLIKELY( nlh->nlmsg_type!=RDMA_NL_GET_TYPE( RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET ) ) ) {
    1809           0 :     errno = EPROTO;
    1810           0 :     return -1;
    1811           0 :   }
    1812             : 
    1813           0 :   fd_mlx5_nl_dev_find_t * dev_find     = parse_arg;
    1814           0 :   void const *            attr_data    = nlh+1;
    1815           0 :   ulong                   attr_data_sz  = nlh->nlmsg_len-sizeof(*nlh);
    1816             : 
    1817           0 :   struct nlattr const * dev_idx_attr;
    1818           0 :   int found = fd_mlx5_nla_find( attr_data, attr_data_sz, RDMA_NLDEV_ATTR_DEV_INDEX, &dev_idx_attr );
    1819           0 :   if( FD_UNLIKELY( found<=0 ) ) return found;
    1820           0 :   struct nlattr const * dev_name_attr;
    1821           0 :   found     = fd_mlx5_nla_find( attr_data, attr_data_sz, RDMA_NLDEV_ATTR_DEV_NAME, &dev_name_attr );
    1822           0 :   if( FD_UNLIKELY( found<=0 ) ) return found;
    1823             : 
    1824           0 :   uint dev_idx;
    1825           0 :   if( FD_UNLIKELY( fd_mlx5_nla_u32( dev_idx_attr, &dev_idx ) ) ) return -1;
    1826             : 
    1827           0 :   ulong const expected_name_sz = strlen( dev_find->rdma_name )+1UL;
    1828           0 :   ulong const dev_name_sz      = dev_name_attr->nla_len-sizeof(*dev_name_attr);
    1829           0 :   if( dev_name_sz==expected_name_sz && !memcmp( dev_name_attr+1, dev_find->rdma_name, expected_name_sz ) ) {
    1830           0 :     dev_find->dev_idx = dev_idx;
    1831           0 :   }
    1832             : 
    1833           0 :   return 0;
    1834           0 : }
    1835             : 
    1836             : struct fd_mlx5_nl_counter_find {
    1837             :   uint counter_id;
    1838             :   int  found;
    1839             : };
    1840             : typedef struct fd_mlx5_nl_counter_find fd_mlx5_nl_counter_find_t;
    1841             : 
    1842             : static int
    1843             : fd_mlx5_nl_parse_counter_id( struct nlmsghdr const * nlh,
    1844           0 :                              void *                  parse_arg ) {
    1845           0 :   if( FD_UNLIKELY( nlh->nlmsg_type!=RDMA_NL_GET_TYPE( RDMA_NL_NLDEV, RDMA_NLDEV_CMD_STAT_SET ) ) ) {
    1846           0 :     errno = EPROTO;
    1847           0 :     return -1;
    1848           0 :   }
    1849           0 :   fd_mlx5_nl_counter_find_t * counter_find = parse_arg;
    1850           0 :   struct nlattr const * counter_id_attr;
    1851           0 :   int counter_id_found = fd_mlx5_nla_find( nlh+1, nlh->nlmsg_len-sizeof(*nlh),
    1852           0 :                                            RDMA_NLDEV_ATTR_STAT_COUNTER_ID, &counter_id_attr );
    1853           0 :   if( FD_UNLIKELY( counter_id_found<=0 ) ) {
    1854           0 :     if( !counter_id_found ) errno = EPROTO;
    1855           0 :     return -1;
    1856           0 :   }
    1857           0 :   if( FD_UNLIKELY( fd_mlx5_nla_u32( counter_id_attr, &counter_find->counter_id ) ) ) return -1;
    1858           0 :   counter_find->found = 1;
    1859           0 :   return 0;
    1860           0 : }
    1861             : 
    1862             : struct fd_mlx5_nl_stat_find {
    1863             :   uint  counter_id;
    1864             :   ulong out_of_buffer;
    1865             :   int   found;
    1866             : };
    1867             : typedef struct fd_mlx5_nl_stat_find fd_mlx5_nl_stat_find_t;
    1868             : 
    1869             : static int
    1870             : fd_mlx5_nl_parse_hw_counters( uchar const *            attr_cur,
    1871             :                               ulong                    attr_rem,
    1872           0 :                               fd_mlx5_nl_stat_find_t * stat_find ) {
    1873           0 :   while( attr_rem ) {
    1874           0 :     struct nlattr const * entry_attr;
    1875           0 :     int next_result = fd_mlx5_nla_next( &attr_cur, &attr_rem, &entry_attr );
    1876           0 :     if( FD_UNLIKELY( next_result<=0 ) ) return next_result;
    1877           0 :     if( (entry_attr->nla_type & (ushort)NLA_TYPE_MASK)!=RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY ) continue;
    1878             : 
    1879           0 :     void const * entry_data = entry_attr+1;
    1880           0 :     ulong entry_data_sz     = entry_attr->nla_len-sizeof(*entry_attr);
    1881           0 :     struct nlattr const * name_attr;
    1882           0 :     struct nlattr const * value_attr;
    1883           0 :     int name_found  = fd_mlx5_nla_find( entry_data, entry_data_sz,
    1884           0 :                                         RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME, &name_attr );
    1885           0 :     int value_found = fd_mlx5_nla_find( entry_data, entry_data_sz,
    1886           0 :                                         RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE, &value_attr );
    1887           0 :     if( FD_UNLIKELY( name_found<0 || value_found<0 ) ) return -1;
    1888           0 :     if( !name_found || name_attr->nla_len!=sizeof(*name_attr)+sizeof("out_of_buffer") ||
    1889           0 :         memcmp( name_attr+1, "out_of_buffer", sizeof("out_of_buffer") ) ) continue;
    1890           0 :     if( FD_UNLIKELY( !value_found || value_attr->nla_len!=sizeof(*value_attr)+sizeof(ulong) ) ) {
    1891           0 :       errno = EPROTO;
    1892           0 :       return -1;
    1893           0 :     }
    1894           0 :     fd_memcpy( &stat_find->out_of_buffer, value_attr+1, sizeof(ulong) );
    1895           0 :     stat_find->found = 1;
    1896           0 :   }
    1897           0 :   return 0;
    1898           0 : }
    1899             : 
    1900             : static int
    1901             : fd_mlx5_nl_parse_counter_entry( uchar const *            attr_cur,
    1902             :                                 ulong                    attr_rem,
    1903           0 :                                 fd_mlx5_nl_stat_find_t * stat_find ) {
    1904           0 :   struct nlattr const * counter_id_attr;
    1905           0 :   int counter_id_found = fd_mlx5_nla_find( attr_cur, attr_rem, RDMA_NLDEV_ATTR_STAT_COUNTER_ID, &counter_id_attr );
    1906           0 :   if( FD_UNLIKELY( counter_id_found<0 ) ) return -1;
    1907           0 :   uint counter_id;
    1908           0 :   if( !counter_id_found ) return 0;
    1909           0 :   if( FD_UNLIKELY( fd_mlx5_nla_u32( counter_id_attr, &counter_id ) ) ) return -1;
    1910           0 :   if( counter_id!=stat_find->counter_id ) return 0;
    1911           0 :   struct nlattr const * hw_counters_attr;
    1912           0 :   int hw_counters_found = fd_mlx5_nla_find( attr_cur, attr_rem, RDMA_NLDEV_ATTR_STAT_HWCOUNTERS, &hw_counters_attr );
    1913           0 :   if( FD_UNLIKELY( hw_counters_found<0 ) ) return -1;
    1914           0 :   if( !hw_counters_found ) return 0;
    1915           0 :   return fd_mlx5_nl_parse_hw_counters( (uchar const *)(hw_counters_attr+1),
    1916           0 :                                        hw_counters_attr->nla_len-sizeof(*hw_counters_attr), stat_find );
    1917           0 : }
    1918             : 
    1919             : static int
    1920             : fd_mlx5_nl_parse_stats( struct nlmsghdr const * nlh,
    1921           0 :                         void *                  parse_arg ) {
    1922           0 :   if( FD_UNLIKELY( nlh->nlmsg_type!=RDMA_NL_GET_TYPE( RDMA_NL_NLDEV, RDMA_NLDEV_CMD_STAT_GET ) ) ) {
    1923           0 :     errno = EPROTO;
    1924           0 :     return -1;
    1925           0 :   }
    1926           0 :   fd_mlx5_nl_stat_find_t * stat_find = parse_arg;
    1927           0 :   struct nlattr const * counters_attr;
    1928           0 :   int counters_found = fd_mlx5_nla_find( nlh+1, nlh->nlmsg_len-sizeof(*nlh),
    1929           0 :                                          RDMA_NLDEV_ATTR_STAT_COUNTER, &counters_attr );
    1930           0 :   if( FD_UNLIKELY( counters_found<0 ) ) return -1;
    1931           0 :   if( !counters_found ) return 0;
    1932           0 :   uchar const * attr_cur = (uchar const *)(counters_attr+1);
    1933           0 :   ulong attr_rem = counters_attr->nla_len-sizeof(*counters_attr);
    1934           0 :   while( attr_rem ) {
    1935           0 :     struct nlattr const * entry_attr;
    1936           0 :     int next_result = fd_mlx5_nla_next( &attr_cur, &attr_rem, &entry_attr );
    1937           0 :     if( FD_UNLIKELY( next_result<=0 ) ) return next_result;
    1938           0 :     if( (entry_attr->nla_type & (ushort)NLA_TYPE_MASK)==RDMA_NLDEV_ATTR_STAT_COUNTER_ENTRY &&
    1939           0 :         FD_UNLIKELY( fd_mlx5_nl_parse_counter_entry( (uchar const *)(entry_attr+1),
    1940           0 :                                                      entry_attr->nla_len-sizeof(*entry_attr), stat_find ) ) ) return -1;
    1941           0 :   }
    1942           0 :   return 0;
    1943           0 : }
    1944             : 
    1945             : fd_netlink_rdma_ctx_t *
    1946             : fd_mlx5_netlink_rdma_init( fd_netlink_rdma_ctx_t * netlink_rdma,
    1947             :                            char const *            rdma_name,
    1948             :                            uint                    port_num,
    1949           0 :                            uint                    qpn ) {
    1950           0 :   if( FD_UNLIKELY( !netlink_rdma ) ) { errno = EINVAL; return NULL; }
    1951           0 :   fd_memset( netlink_rdma, 0, sizeof(*netlink_rdma) );
    1952           0 :   netlink_rdma->fd = -1;
    1953           0 :   int netlink_fd = socket( AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_RDMA );
    1954           0 :   if( FD_UNLIKELY( netlink_fd<0 ) ) return NULL;
    1955           0 :   struct sockaddr_nl local_addr = { .nl_family=AF_NETLINK };
    1956           0 :   if( FD_UNLIKELY( bind( netlink_fd, fd_type_pun( &local_addr ), sizeof(local_addr) ) ) ) goto fail;
    1957           0 :   netlink_rdma->fd = netlink_fd;
    1958             : 
    1959           0 :   fd_mlx5_nl_req_t req[1];
    1960           0 :   uint request_seq = ++netlink_rdma->seq;
    1961           0 :   fd_mlx5_nl_req_init( req, RDMA_NL_GET_TYPE( RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET ),
    1962           0 :                        NLM_F_REQUEST|NLM_F_DUMP, request_seq );
    1963           0 :   fd_mlx5_nl_dev_find_t dev_find = { .rdma_name=rdma_name };
    1964           0 :   if( FD_UNLIKELY( fd_mlx5_nl_send( netlink_fd, req ) ||
    1965           0 :                    fd_mlx5_nl_recv( netlink_fd, request_seq, 1, fd_mlx5_nl_parse_dev, &dev_find ) ) ) {
    1966           0 :     goto fail;
    1967           0 :   }
    1968           0 :   if( FD_UNLIKELY( !dev_find.dev_idx ) ) { errno = ENODEV; goto fail; }
    1969             : 
    1970           0 :   request_seq = ++netlink_rdma->seq;
    1971           0 :   fd_mlx5_nl_req_init( req, RDMA_NL_GET_TYPE( RDMA_NL_NLDEV, RDMA_NLDEV_CMD_STAT_SET ),
    1972           0 :                        NLM_F_REQUEST|NLM_F_ACK, request_seq );
    1973           0 :   if( FD_UNLIKELY( fd_mlx5_nl_req_u32( req, RDMA_NLDEV_ATTR_STAT_MODE, RDMA_COUNTER_MODE_MANUAL ) ||
    1974           0 :                    fd_mlx5_nl_req_u32( req, RDMA_NLDEV_ATTR_STAT_RES,  RDMA_NLDEV_ATTR_RES_QP   ) ||
    1975           0 :                    fd_mlx5_nl_req_u32( req, RDMA_NLDEV_ATTR_DEV_INDEX, dev_find.dev_idx         ) ||
    1976           0 :                    fd_mlx5_nl_req_u32( req, RDMA_NLDEV_ATTR_PORT_INDEX, port_num                ) ||
    1977           0 :                    fd_mlx5_nl_req_u32( req, RDMA_NLDEV_ATTR_RES_LQPN, qpn                       ) ) ) {
    1978           0 :     goto fail;
    1979           0 :   }
    1980             : 
    1981           0 :   fd_mlx5_nl_counter_find_t counter_find = {0};
    1982           0 :   if( FD_UNLIKELY( fd_mlx5_nl_send( netlink_fd, req ) ||
    1983           0 :                    fd_mlx5_nl_recv( netlink_fd, request_seq, 0, fd_mlx5_nl_parse_counter_id, &counter_find ) ) ) {
    1984           0 :     goto fail;
    1985           0 :   }
    1986           0 :   if( FD_UNLIKELY( !counter_find.found ) ) { errno = EPROTO; goto fail; }
    1987             : 
    1988           0 :   netlink_rdma->dev_idx    = dev_find.dev_idx;
    1989           0 :   netlink_rdma->port_num   = port_num;
    1990           0 :   netlink_rdma->counter_id = counter_find.counter_id;
    1991           0 :   return netlink_rdma;
    1992             : 
    1993           0 : fail:
    1994           0 :   {
    1995           0 :     int err = errno;
    1996           0 :     close( netlink_fd );
    1997           0 :     netlink_rdma->fd = -1;
    1998           0 :     errno = err;
    1999           0 :     return NULL;
    2000           0 :   }
    2001           0 : }
    2002             : 
    2003             : int
    2004             : fd_mlx5_netlink_rdma_qp_counter_read( fd_netlink_rdma_ctx_t * netlink_rdma,
    2005           0 :                                       ulong *                   out_of_buffer ) {
    2006           0 :   if( FD_UNLIKELY( !netlink_rdma || netlink_rdma->fd<0 || !netlink_rdma->dev_idx || !out_of_buffer ) ) {
    2007           0 :     errno = EINVAL;
    2008           0 :     return -1;
    2009           0 :   }
    2010           0 :   uint request_seq = ++netlink_rdma->seq;
    2011           0 :   fd_mlx5_nl_req_t req[1];
    2012           0 :   fd_mlx5_nl_req_init( req, RDMA_NL_GET_TYPE( RDMA_NL_NLDEV, RDMA_NLDEV_CMD_STAT_GET ),
    2013           0 :                        NLM_F_REQUEST|NLM_F_DUMP, request_seq );
    2014           0 :   if( FD_UNLIKELY( fd_mlx5_nl_req_u32( req, RDMA_NLDEV_ATTR_DEV_INDEX, netlink_rdma->dev_idx )   ||
    2015           0 :                    fd_mlx5_nl_req_u32( req, RDMA_NLDEV_ATTR_PORT_INDEX, netlink_rdma->port_num ) ||
    2016           0 :                    fd_mlx5_nl_req_u32( req, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP )   ||
    2017           0 :                    fd_mlx5_nl_send( netlink_rdma->fd, req ) ) ) return -1;
    2018             : 
    2019           0 :   fd_mlx5_nl_stat_find_t stat_find = { .counter_id=netlink_rdma->counter_id };
    2020           0 :   if( FD_UNLIKELY( fd_mlx5_nl_recv( netlink_rdma->fd, request_seq, 1, fd_mlx5_nl_parse_stats, &stat_find ) ) ) return -1;
    2021           0 :   if( FD_UNLIKELY( !stat_find.found ) ) {
    2022           0 :     errno = ENOENT;
    2023           0 :     return -1;
    2024           0 :   }
    2025           0 :   *out_of_buffer = stat_find.out_of_buffer;
    2026           0 :   return 0;
    2027           0 : }

Generated by: LCOV version 1.14