LCOV - code coverage report
Current view: top level - disco/net/mlx5 - fd_mlx5.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 994 0.0 %
Date: 2026-11-11 12:40:43 Functions: 0 38 0.0 %

          Line data    Source code
       1             : #define _GNU_SOURCE
       2             : #include "../fd_net_tile.h"
       3             : #include "fd_mlx5_private.h"
       4             : #include "../../../util/log/fd_log.h"
       5             : #include "../../../util/net/fd_eth.h"
       6             : #include "../../../util/net/fd_ip4.h"
       7             : #include "../../../util/rng/fd_rng.h"
       8             : #include "../fd_linux_bond.h"
       9             : 
      10             : #include <ctype.h>
      11             : #include <dirent.h>
      12             : #include <errno.h>
      13             : #include <fcntl.h>
      14             : #include <limits.h>
      15             : #include <net/if.h>
      16             : #include <stddef.h>
      17             : #include <stdio.h>
      18             : #include <stdlib.h>
      19             : #include <sys/ioctl.h>
      20             : #include <sys/mman.h>
      21             : #include <sys/stat.h>
      22             : #include <sys/syscall.h>
      23             : #include <unistd.h>
      24             : 
      25             : #include <rdma/ib_user_ioctl_cmds.h>
      26             : #include <rdma/ib_user_ioctl_verbs.h>
      27             : #include <rdma/ib_user_verbs.h>
      28             : #include <rdma/mlx5-abi.h>
      29             : #include <rdma/mlx5_user_ioctl_cmds.h>
      30             : #include <rdma/mlx5_user_ioctl_verbs.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             : 
      86           0 : #define FD_MLX5_ETH_INLINE_HDR_SZ   (18UL)    /* MLX5_ETH_L2_INLINE_HEADER_SIZE */
      87             : #define FD_MLX5_LINK_LAYER_ETHERNET (2U)      /* IB_LINK_LAYER_ETHERNET */
      88           0 : #define FD_UVERBS_NAME_MAX          (32UL)
      89             : 
      90           0 : #define FD_MLX5_QPS_INIT (1U) /* IB_QPS_INIT */
      91             : #define FD_MLX5_QPS_RTR  (2U) /* IB_QPS_RTR */
      92             : #define FD_MLX5_QPS_RTS  (3U) /* IB_QPS_RTS */
      93             : 
      94           0 : #define FD_MLX5_QP_ATTR_STATE (1U)    /* IB_QP_STATE */
      95           0 : #define FD_MLX5_QP_ATTR_PORT  (1U<<5) /* IB_QP_PORT */
      96             : 
      97           0 : #define FD_MLX5_WQS_RESET (0U) /* IB_WQS_RESET */
      98           0 : #define FD_MLX5_WQS_RDY   (1U) /* IB_WQS_RDY */
      99             : 
     100           0 : #define FD_MLX5_WQ_ATTR_STATE     (1U)    /* IB_WQ_STATE */
     101           0 : #define FD_MLX5_WQ_ATTR_CUR_STATE (1U<<1) /* IB_WQ_CUR_STATE */
     102           0 : #define FD_MLX5_WQ_ATTR_FLAGS     (1U<<2) /* IB_WQ_FLAGS */
     103             : 
     104           0 : #define FD_MLX5_WQ_FLAGS_CVLAN_STRIPPING (1U) /* IB_WQ_FLAGS_CVLAN_STRIPPING */
     105             : 
     106           0 : #define FD_MLX5_RX_HASH_FUNC_TOEPLITZ (1U)  /* MLX5_RX_HASH_FUNC_TOEPLITZ */
     107           0 : #define FD_MLX5_RX_HASH_KEY_SZ        (40U) /* MLX5_FLD_SZ_BYTES(tirc, rx_hash_toeplitz_key) */
     108           0 : #define FD_MLX5_RX_HASH_SRC_IPV4      (1UL<<0)  /* MLX5_RX_HASH_SRC_IPV4 */
     109           0 : #define FD_MLX5_RX_HASH_DST_IPV4      (1UL<<1)  /* MLX5_RX_HASH_DST_IPV4 */
     110           0 : #define FD_MLX5_RX_HASH_SRC_PORT_UDP  (1UL<<6)  /* MLX5_RX_HASH_SRC_PORT_UDP */
     111           0 : #define FD_MLX5_RX_HASH_DST_PORT_UDP  (1UL<<7)  /* MLX5_RX_HASH_DST_PORT_UDP */
     112           0 : #define FD_MLX5_RX_HASH_INNER         (1UL<<31) /* MLX5_RX_HASH_INNER */
     113           0 : #define FD_MLX5_RX_HASH_IPV4_UDP      ( FD_MLX5_RX_HASH_SRC_IPV4     | FD_MLX5_RX_HASH_DST_IPV4     | \
     114           0 :                                         FD_MLX5_RX_HASH_SRC_PORT_UDP | FD_MLX5_RX_HASH_DST_PORT_UDP )
     115             : #define FD_MLX5_QP_TUNNEL_OFFLOADS    (1U<<2)   /* MLX5_QP_FLAG_TUNNEL_OFFLOADS */
     116             : #define FD_MLX5_TUNNEL_OFFLOADS_GRE   (1U<<1)   /* MLX5_IB_TUNNELED_OFFLOADS_GRE */
     117           0 : #define FD_MLX5_INDIRECTION_COUNT     (1UL<<8)  /* Spreads RSS more equally across non power of 2 tile counts */
     118             : 
     119             : static int
     120             : fd_mlx5_rdma_port_contains_if( char const * rdma_name,
     121             :                                uint         port,
     122           0 :                                char const * interface_name ) {
     123           0 :   char netdev_dir_path[ PATH_MAX ];
     124           0 :   int path_sz = snprintf( netdev_dir_path, sizeof(netdev_dir_path),
     125           0 :                           "/sys/class/infiniband/%s/ports/%u/gid_attrs/ndevs", rdma_name, port );
     126           0 :   if( FD_UNLIKELY( path_sz<=0 || (ulong)path_sz>=sizeof(netdev_dir_path) ) ) {
     127           0 :     errno = ENAMETOOLONG;
     128           0 :     return -1;
     129           0 :   }
     130             : 
     131           0 :   DIR * netdev_dir = opendir( netdev_dir_path );
     132           0 :   if( FD_UNLIKELY( !netdev_dir ) ) return errno==ENOENT ? 0 : -1;
     133             : 
     134           0 :   int result = 0;
     135           0 :   for(;;) {
     136           0 :     errno = 0;
     137           0 :     struct dirent * netdev_entry = readdir( netdev_dir );
     138           0 :     if( FD_UNLIKELY( !netdev_entry ) ) {
     139           0 :       if( errno ) result = -1;
     140           0 :       break;
     141           0 :     }
     142           0 :     if( netdev_entry->d_name[0]=='.' ) continue;
     143             : 
     144           0 :     char netdev_name_path[ PATH_MAX ];
     145           0 :     int netdev_name_path_sz = snprintf( netdev_name_path, sizeof(netdev_name_path),
     146           0 :                                         "%s/%s", netdev_dir_path, netdev_entry->d_name );
     147           0 :     if( FD_UNLIKELY( netdev_name_path_sz<=0 || (ulong)netdev_name_path_sz>=sizeof(netdev_name_path) ) ) {
     148           0 :       errno  = ENAMETOOLONG;
     149           0 :       result = -1;
     150           0 :       break;
     151           0 :     }
     152             : 
     153           0 :     int netdev_fd = open( netdev_name_path, O_RDONLY );
     154           0 :     if( FD_UNLIKELY( netdev_fd<0 ) ) {
     155           0 :       if( errno==ENOENT ) continue;
     156           0 :       result = -1;
     157           0 :       break;
     158           0 :     }
     159             : 
     160           0 :     char netdev_name[ IF_NAMESIZE+1 ];
     161           0 :     ssize_t read_sz = read( netdev_fd, netdev_name, IF_NAMESIZE );
     162           0 :     int const read_err  = read_sz<0          ? errno : 0;
     163           0 :     int const close_err = close( netdev_fd ) ? errno : 0;
     164           0 :     if( FD_UNLIKELY( (read_err && read_err!=EINVAL) || close_err ) ) {
     165           0 :       errno  = read_err && read_err!=EINVAL ? read_err : close_err;
     166           0 :       result = -1;
     167           0 :       break;
     168           0 :     }
     169             :     /* Empty Linux RDMA GID attribute entries return EINVAL. */
     170           0 :     if( FD_UNLIKELY( read_err==EINVAL ) ) continue;
     171             : 
     172           0 :     if( FD_UNLIKELY( !read_sz ) ) continue;
     173           0 :     while( read_sz>0 && (netdev_name[read_sz-1]=='\n' || netdev_name[read_sz-1]=='\r') ) read_sz--;
     174           0 :     netdev_name[read_sz] = '\0';
     175           0 :     if( !strcmp( netdev_name, interface_name ) ) {
     176           0 :       result = 1;
     177           0 :       break;
     178           0 :     }
     179           0 :   }
     180             : 
     181           0 :   int err = result<0 ? errno : 0;
     182           0 :   if( FD_UNLIKELY( closedir( netdev_dir ) && !err ) ) err = errno;
     183           0 :   if( FD_UNLIKELY( err ) ) {
     184           0 :     errno = err;
     185           0 :     return -1;
     186           0 :   }
     187           0 :   return result;
     188           0 : }
     189             : 
     190             : static int
     191             : fd_mlx5_rdma_port_matches_if( char const * rdma_name,
     192             :                               uint         port,
     193           0 :                               char const * interface_name ) {
     194           0 :   int match = fd_mlx5_rdma_port_contains_if( rdma_name, port, interface_name );
     195           0 :   if( FD_UNLIKELY( match<0 ) ) return -1;
     196           0 :   if( match ) return 1;
     197           0 :   if( !fd_bonding_is_master( interface_name ) ) return 0;
     198             : 
     199           0 :   fd_bonding_slave_iter_t   iter_mem[1];
     200           0 :   fd_bonding_slave_iter_t * iter = fd_bonding_slave_iter_init( iter_mem, interface_name );
     201           0 :   while( !fd_bonding_slave_iter_done( iter ) ) {
     202           0 :     char const * slave_interface_name = fd_bonding_slave_iter_ele( iter );
     203           0 :     match = fd_mlx5_rdma_port_contains_if( rdma_name, port, slave_interface_name );
     204           0 :     if( FD_UNLIKELY( match<0 ) ) return -1;
     205           0 :     if( match ) return 1;
     206           0 :     fd_bonding_slave_iter_next( iter );
     207           0 :   }
     208             : 
     209           0 :   return 0;
     210           0 : }
     211             : 
     212             : int
     213             : fd_mlx5_rdma_dev_find( char         rdma_name[ FD_MLX5_RDMA_NAME_MAX ],
     214             :                        uint *       rdma_port,
     215           0 :                        char const * interface_name ) {
     216           0 :   rdma_name[0] = '\0';
     217           0 :   *rdma_port   = 0U;
     218             : 
     219           0 :   DIR * infiniband_dir = opendir( "/sys/class/infiniband" );
     220           0 :   if( FD_UNLIKELY( !infiniband_dir ) ) return 0;
     221             : 
     222           0 :   ulong match_cnt = 0UL;
     223           0 :   int err = 0;
     224           0 :   for(;;) {
     225           0 :     errno = 0;
     226           0 :     struct dirent * device_entry = readdir( infiniband_dir );
     227           0 :     if( FD_UNLIKELY( !device_entry ) ) {
     228           0 :       err = errno;
     229           0 :       break;
     230           0 :     }
     231           0 :     if( device_entry->d_name[0]=='.' ) continue;
     232             : 
     233           0 :     char device_ports_path[ PATH_MAX ];
     234           0 :     int const device_ports_path_sz = snprintf( device_ports_path, sizeof(device_ports_path),
     235           0 :                                                "/sys/class/infiniband/%s/ports", device_entry->d_name );
     236           0 :     if( FD_UNLIKELY( device_ports_path_sz<=0 || (ulong)device_ports_path_sz>=sizeof(device_ports_path) ) ) {
     237           0 :       err = ENAMETOOLONG;
     238           0 :       break;
     239           0 :     }
     240             : 
     241           0 :     DIR * ports_dir = opendir( device_ports_path );
     242           0 :     if( FD_UNLIKELY( !ports_dir ) ) {
     243           0 :       if( errno==ENOENT ) continue;
     244           0 :       err = errno;
     245           0 :       break;
     246           0 :     }
     247             : 
     248           0 :     for(;;) {
     249           0 :       errno = 0;
     250           0 :       struct dirent * port_entry = readdir( ports_dir );
     251           0 :       if( FD_UNLIKELY( !port_entry ) ) {
     252           0 :         err = errno;
     253           0 :         break;
     254           0 :       }
     255           0 :       char * port_end;
     256           0 :       ulong const port_num = strtoul( port_entry->d_name, &port_end, 10 );
     257           0 :       if( !port_num || *port_end || port_num>(ulong)UCHAR_MAX ) continue;
     258           0 :       int const match = fd_mlx5_rdma_port_matches_if( device_entry->d_name, (uint)port_num, interface_name );
     259           0 :       if( FD_UNLIKELY( match<0 ) ) {
     260           0 :         err = errno;
     261           0 :         break;
     262           0 :       }
     263           0 :       if( !match ) continue;
     264             : 
     265           0 :       match_cnt++;
     266           0 :       if( match_cnt==1UL ) {
     267           0 :         fd_cstr_ncpy( rdma_name, device_entry->d_name, FD_MLX5_RDMA_NAME_MAX );
     268           0 :         *rdma_port = (uint)port_num;
     269           0 :       }
     270           0 :     }
     271           0 :     if( FD_UNLIKELY( closedir( ports_dir ) && !err ) ) err = errno;
     272           0 :     if( FD_UNLIKELY( err ) ) break;
     273           0 :   }
     274             : 
     275           0 :   if( FD_UNLIKELY( closedir( infiniband_dir ) && !err ) ) err = errno;
     276           0 :   if( FD_UNLIKELY( err ) ) {
     277           0 :     rdma_name[0] = '\0';
     278           0 :     *rdma_port   = 0U;
     279           0 :     errno        = err;
     280           0 :     return 0;
     281           0 :   }
     282           0 :   if( FD_UNLIKELY( match_cnt!=1UL ) ) {
     283           0 :     errno = match_cnt ? EEXIST : ENOENT;
     284           0 :     rdma_name[0] = '\0';
     285           0 :     *rdma_port   = 0U;
     286           0 :     return 0;
     287           0 :   }
     288           0 :   return 1;
     289           0 : }
     290             : 
     291             : int
     292           0 : fd_mlx5_uverbs_avail( void ) {
     293           0 :   struct stat class_stat;
     294           0 :   return !stat( "/sys/class/infiniband_verbs", &class_stat ) && S_ISDIR( class_stat.st_mode );
     295           0 : }
     296             : 
     297             : struct fd_mlx5_pd {
     298             :   fd_uverbs_ctx_t * ctx;    /* uverbs context */
     299             :   uint              handle; /* protection domain handle */
     300             : };
     301             : typedef struct fd_mlx5_pd fd_mlx5_pd_t;
     302             : 
     303             : struct fd_mlx5_caps {
     304             :   ulong max_mr_size;
     305             :   uint  max_send_wqe; /* max_send_wqebb, SQ WQE capacity */
     306             :   uint  max_recv_wr;
     307             :   uint  max_cqe;
     308             :   uchar eth_min_inline_hdr_sz;
     309             : };
     310             : typedef struct fd_mlx5_caps fd_mlx5_caps_t;
     311             : 
     312             : /* fd_uverbs_* types define Linux uverbs requests and responses */
     313             : struct fd_uverbs_ex_hdr {
     314             :   struct ib_uverbs_cmd_hdr    cmd;
     315             :   struct ib_uverbs_ex_cmd_hdr ex;
     316             : };
     317             : typedef struct fd_uverbs_ex_hdr fd_uverbs_ex_hdr_t;
     318             : FD_STATIC_ASSERT( sizeof(fd_uverbs_ex_hdr_t)==24UL, uverbs_ex_hdr_sz );
     319             : 
     320             : struct fd_uverbs_get_context_req {
     321             :   struct ib_uverbs_cmd_hdr             hdr;
     322             :   ulong                                response;
     323             :   struct mlx5_ib_alloc_ucontext_req_v2 mlx5;
     324             : };
     325             : typedef struct fd_uverbs_get_context_req fd_uverbs_get_context_req_t;
     326             : FD_STATIC_ASSERT( sizeof(fd_uverbs_get_context_req_t)==48UL, uverbs_get_context_req_sz );
     327             : 
     328             : struct fd_uverbs_get_context_resp {
     329             :   uint                               async_fd;
     330             :   uint                               num_comp_vectors;
     331             :   struct mlx5_ib_alloc_ucontext_resp mlx5;
     332             : };
     333             : typedef struct fd_uverbs_get_context_resp fd_uverbs_get_context_resp_t;
     334             : FD_STATIC_ASSERT( sizeof(fd_uverbs_get_context_resp_t)==80UL, uverbs_get_context_resp_sz );
     335             : 
     336             : struct fd_uverbs_query_device_req {
     337             :   fd_uverbs_ex_hdr_t                 hdr;
     338             :   struct ib_uverbs_ex_query_device   core;
     339             : };
     340             : typedef struct fd_uverbs_query_device_req fd_uverbs_query_device_req_t;
     341             : 
     342             : struct fd_uverbs_query_device_resp {
     343             :   struct ib_uverbs_ex_query_device_resp core;
     344             :   struct mlx5_ib_query_device_resp      mlx5;
     345             : };
     346             : typedef struct fd_uverbs_query_device_resp fd_uverbs_query_device_resp_t;
     347             : 
     348             : struct fd_uverbs_query_port_req {
     349             :   struct ib_uverbs_cmd_hdr hdr;
     350             :   ulong                    response;
     351             :   uchar                    port_num;
     352             :   uchar                    reserved[ 7 ];
     353             : };
     354             : typedef struct fd_uverbs_query_port_req fd_uverbs_query_port_req_t;
     355             : FD_STATIC_ASSERT( sizeof(fd_uverbs_query_port_req_t)==24UL, uverbs_query_port_req_sz );
     356             : 
     357             : struct fd_uverbs_alloc_pd_req {
     358             :   struct ib_uverbs_cmd_hdr hdr;
     359             :   ulong                    response;
     360             : };
     361             : typedef struct fd_uverbs_alloc_pd_req fd_uverbs_alloc_pd_req_t;
     362             : FD_STATIC_ASSERT( sizeof(fd_uverbs_alloc_pd_req_t)==16UL, uverbs_alloc_pd_req_sz );
     363             : 
     364             : struct fd_uverbs_alloc_pd_resp {
     365             :   uint                         pd_handle;
     366             :   struct mlx5_ib_alloc_pd_resp mlx5;
     367             : };
     368             : typedef struct fd_uverbs_alloc_pd_resp fd_uverbs_alloc_pd_resp_t;
     369             : FD_STATIC_ASSERT( sizeof(fd_uverbs_alloc_pd_resp_t)==8UL, uverbs_alloc_pd_resp_sz );
     370             : 
     371             : struct fd_uverbs_reg_mr_req {
     372             :   struct ib_uverbs_cmd_hdr hdr;
     373             :   ulong                    response;
     374             :   ulong                    start;
     375             :   ulong                    length;
     376             :   ulong                    hca_va;
     377             :   uint                     pd_handle;
     378             :   uint                     access_flags;
     379             : };
     380             : typedef struct fd_uverbs_reg_mr_req fd_uverbs_reg_mr_req_t;
     381             : FD_STATIC_ASSERT( sizeof(fd_uverbs_reg_mr_req_t)==48UL, uverbs_reg_mr_req_sz );
     382             : 
     383             : struct fd_uverbs_reg_mr_resp {
     384             :   uint mr_handle;
     385             :   uint lkey;
     386             :   uint rkey;
     387             : };
     388             : typedef struct fd_uverbs_reg_mr_resp fd_uverbs_reg_mr_resp_t;
     389             : FD_STATIC_ASSERT( sizeof(fd_uverbs_reg_mr_resp_t)==12UL, uverbs_reg_mr_resp_sz );
     390             : 
     391             : struct fd_uverbs_ioctl_hdr {
     392             :   ushort length;
     393             :   ushort object_id;
     394             :   ushort method_id;
     395             :   ushort num_attrs;
     396             :   ulong  reserved1;
     397             :   uint   driver_id;
     398             :   uint   reserved2;
     399             : };
     400             : typedef struct fd_uverbs_ioctl_hdr fd_uverbs_ioctl_hdr_t;
     401             : FD_STATIC_ASSERT( sizeof(fd_uverbs_ioctl_hdr_t)==24UL, uverbs_ioctl_hdr_sz );
     402             : 
     403             : struct fd_uverbs_alloc_uar_req {
     404             :   fd_uverbs_ioctl_hdr_t hdr;
     405             :   struct ib_uverbs_attr attrs[ 5 ];
     406             : };
     407             : typedef struct fd_uverbs_alloc_uar_req fd_uverbs_alloc_uar_req_t;
     408             : FD_STATIC_ASSERT( sizeof(fd_uverbs_alloc_uar_req_t)==104UL, uverbs_alloc_uar_req_sz );
     409             : 
     410             : struct fd_uverbs_destroy_uar_req {
     411             :   fd_uverbs_ioctl_hdr_t hdr;
     412             :   struct ib_uverbs_attr attrs[ 1 ];
     413             : };
     414             : typedef struct fd_uverbs_destroy_uar_req fd_uverbs_destroy_uar_req_t;
     415             : FD_STATIC_ASSERT( sizeof(fd_uverbs_destroy_uar_req_t)==40UL, uverbs_destroy_uar_req_sz );
     416             : 
     417             : struct fd_uverbs_create_comp_channel_req {
     418             :   struct ib_uverbs_cmd_hdr hdr;
     419             :   ulong                    response;
     420             : };
     421             : typedef struct fd_uverbs_create_comp_channel_req fd_uverbs_create_comp_channel_req_t;
     422             : FD_STATIC_ASSERT( sizeof(fd_uverbs_create_comp_channel_req_t)==16UL, uverbs_create_comp_channel_req_sz );
     423             : 
     424             : struct fd_uverbs_create_cq_req {
     425             :   struct ib_uverbs_cmd_hdr hdr;
     426             :   ulong                    response;
     427             :   ulong                    user_handle;
     428             :   uint                     cqe;
     429             :   uint                     comp_vector;
     430             :   int                      comp_channel;
     431             :   uint                     reserved;
     432             :   /* mlx5_ib_create_cq gained uar_page_index in Linux 5.7 */
     433             :   union {
     434             :     struct mlx5_ib_create_cq fields;
     435             :     struct {
     436             :       uchar  prefix[ 24 ];
     437             :       ushort uar_page_index;
     438             :     } uar;
     439             :   } mlx5;
     440             : };
     441             : typedef struct fd_uverbs_create_cq_req fd_uverbs_create_cq_req_t;
     442             : 
     443             : struct fd_uverbs_create_cq_resp {
     444             :   uint                          cq_handle;
     445             :   uint                          cqe;
     446             :   struct mlx5_ib_create_cq_resp mlx5;
     447             : };
     448             : typedef struct fd_uverbs_create_cq_resp fd_uverbs_create_cq_resp_t;
     449             : FD_STATIC_ASSERT( sizeof(fd_uverbs_create_cq_resp_t)==16UL, uverbs_create_cq_resp_sz );
     450             : 
     451             : struct fd_uverbs_destroy_cq_req {
     452             :   struct ib_uverbs_cmd_hdr hdr;
     453             :   ulong                    response;
     454             :   uint                     cq_handle;
     455             :   uint                     reserved;
     456             : };
     457             : typedef struct fd_uverbs_destroy_cq_req fd_uverbs_destroy_cq_req_t;
     458             : FD_STATIC_ASSERT( sizeof(fd_uverbs_destroy_cq_req_t)==24UL, uverbs_destroy_cq_req_sz );
     459             : 
     460             : struct fd_uverbs_create_tx_qp_req {
     461             :   struct ib_uverbs_cmd_hdr hdr;
     462             :   ulong                    response;
     463             :   ulong                    user_handle;
     464             :   uint                     pd_handle;
     465             :   uint                     send_cq_handle;
     466             :   uint                     recv_cq_handle;
     467             :   uint                     srq_handle;
     468             :   uint                     max_send_wr;
     469             :   uint                     max_recv_wr;
     470             :   uint                     max_send_sge;
     471             :   uint                     max_recv_sge;
     472             :   uint                     max_inline_data;
     473             :   uchar                    sq_sig_all;
     474             :   uchar                    qp_type;
     475             :   uchar                    is_srq;
     476             :   uchar                    reserved;
     477             :   struct mlx5_ib_create_qp mlx5;
     478             : };
     479             : typedef struct fd_uverbs_create_tx_qp_req fd_uverbs_create_tx_qp_req_t;
     480             : 
     481             : struct fd_uverbs_create_tx_qp_resp {
     482             :   struct ib_uverbs_create_qp_resp core;
     483             :   struct mlx5_ib_create_qp_resp   mlx5;
     484             : };
     485             : typedef struct fd_uverbs_create_tx_qp_resp fd_uverbs_create_tx_qp_resp_t;
     486             : 
     487             : struct fd_uverbs_modify_qp_req {
     488             :   struct ib_uverbs_cmd_hdr   hdr;
     489             :   struct ib_uverbs_modify_qp core;
     490             : };
     491             : typedef struct fd_uverbs_modify_qp_req fd_uverbs_modify_qp_req_t;
     492             : FD_STATIC_ASSERT( sizeof(fd_uverbs_modify_qp_req_t)==120UL, uverbs_modify_qp_req_sz );
     493             : 
     494             : struct fd_uverbs_create_rx_wq_req {
     495             :   fd_uverbs_ex_hdr_t            hdr;
     496             :   struct ib_uverbs_ex_create_wq core;
     497             :   struct mlx5_ib_create_wq      mlx5;
     498             : };
     499             : typedef struct fd_uverbs_create_rx_wq_req fd_uverbs_create_rx_wq_req_t;
     500             : 
     501             : struct fd_uverbs_create_rx_wq_resp {
     502             :   struct ib_uverbs_ex_create_wq_resp core;
     503             :   struct mlx5_ib_create_wq_resp      mlx5;
     504             : };
     505             : typedef struct fd_uverbs_create_rx_wq_resp fd_uverbs_create_rx_wq_resp_t;
     506             : 
     507             : struct fd_uverbs_modify_rx_wq_req {
     508             :   fd_uverbs_ex_hdr_t            hdr;
     509             :   struct ib_uverbs_ex_modify_wq core;
     510             :   struct mlx5_ib_modify_wq      mlx5;
     511             : };
     512             : typedef struct fd_uverbs_modify_rx_wq_req fd_uverbs_modify_rx_wq_req_t;
     513             : 
     514             : struct fd_uverbs_create_rwq_ind_table_req {
     515             :   fd_uverbs_ex_hdr_t hdr;
     516             :   uint               comp_mask;
     517             :   uint               log_ind_tbl_size;
     518             :   uint               wq_handles[];
     519             : };
     520             : typedef struct fd_uverbs_create_rwq_ind_table_req fd_uverbs_create_rwq_ind_table_req_t;
     521             : 
     522             : struct fd_uverbs_create_rss_qp_req {
     523             :   fd_uverbs_ex_hdr_t                 hdr;
     524             :   struct ib_uverbs_ex_create_qp      core;
     525             :   struct mlx5_ib_create_qp_rss       mlx5;
     526             : };
     527             : typedef struct fd_uverbs_create_rss_qp_req fd_uverbs_create_rss_qp_req_t;
     528             : 
     529             : struct fd_uverbs_create_rss_qp_resp {
     530             :   struct ib_uverbs_ex_create_qp_resp core;
     531             :   struct mlx5_ib_create_qp_resp      mlx5;
     532             : };
     533             : typedef struct fd_uverbs_create_rss_qp_resp fd_uverbs_create_rss_qp_resp_t;
     534             : 
     535             : struct fd_uverbs_create_udp_flow_req {
     536             :   fd_uverbs_ex_hdr_t                 hdr;
     537             :   uint                               comp_mask;
     538             :   uint                               qp_handle;
     539             :   uint                               type;
     540             :   ushort                             size;
     541             :   ushort                             priority;
     542             :   uchar                              num_of_specs;
     543             :   uchar                              reserved[2];
     544             :   uchar                              port;
     545             :   uint                               flags;
     546             :   struct ib_uverbs_flow_spec_eth     eth;
     547             :   struct ib_uverbs_flow_spec_ipv4    ipv4;
     548             :   struct ib_uverbs_flow_spec_tcp_udp udp;
     549             :   uint                               mlx5_ncounters_data;
     550             :   uint                               mlx5_reserved;
     551             : };
     552             : typedef struct fd_uverbs_create_udp_flow_req fd_uverbs_create_udp_flow_req_t;
     553             : FD_STATIC_ASSERT( sizeof(fd_uverbs_create_udp_flow_req_t)==144UL, uverbs_create_udp_flow_req_sz );
     554             : 
     555             : struct fd_uverbs_create_gre_flow_req {
     556             :   fd_uverbs_ex_hdr_t                 hdr;
     557             :   uint                               comp_mask;
     558             :   uint                               qp_handle;
     559             :   uint                               type;
     560             :   ushort                             size;
     561             :   ushort                             priority;
     562             :   uchar                              num_of_specs;
     563             :   uchar                              reserved[2];
     564             :   uchar                              port;
     565             :   uint                               flags;
     566             :   struct ib_uverbs_flow_spec_eth     eth;
     567             :   struct ib_uverbs_flow_spec_ipv4    outer_ipv4;
     568             :   struct ib_uverbs_flow_spec_gre     gre;
     569             :   struct ib_uverbs_flow_spec_ipv4    inner_ipv4;
     570             :   struct ib_uverbs_flow_spec_tcp_udp inner_udp;
     571             :   uint                               mlx5_ncounters_data;
     572             :   uint                               mlx5_reserved;
     573             : };
     574             : typedef struct fd_uverbs_create_gre_flow_req fd_uverbs_create_gre_flow_req_t;
     575             : FD_STATIC_ASSERT( sizeof(fd_uverbs_create_gre_flow_req_t)==200UL, uverbs_create_gre_flow_req_sz );
     576             : 
     577             : #define FD_RDMA_PATH_MAX (256UL)
     578             : 
     579           0 : #define FD_MLX5_FLOW_SPEC_ETH   (0x20U)  /* IB_FLOW_SPEC_ETH */
     580           0 : #define FD_MLX5_FLOW_SPEC_IPV4  (0x30U)  /* IB_FLOW_SPEC_IPV4 */
     581           0 : #define FD_MLX5_FLOW_SPEC_UDP   (0x41U)  /* IB_FLOW_SPEC_UDP */
     582           0 : #define FD_MLX5_FLOW_SPEC_GRE   (0x51U)  /* IB_FLOW_SPEC_GRE */
     583           0 : #define FD_MLX5_FLOW_SPEC_INNER (0x100U) /* IB_FLOW_SPEC_INNER */
     584             : 
     585             : /* fd_rdma_* helpers discover and inspect the selected Linux RDMA device */
     586             : static int
     587             : fd_rdma_read_text( char const * path,
     588             :                    char *       buf,
     589           0 :                    ulong        buf_sz ) {
     590           0 :   FD_TEST( buf_sz>1UL );
     591             : 
     592           0 :   int text_fd = open( path, O_RDONLY | O_CLOEXEC );
     593           0 :   if( FD_UNLIKELY( text_fd<0 ) ) return -1;
     594             : 
     595           0 :   long read_sz = read( text_fd, buf, buf_sz );
     596           0 :   int  err     = 0;
     597           0 :   if( FD_UNLIKELY( read_sz<0L ) )                  err = errno;
     598           0 :   else if( FD_UNLIKELY( (ulong)read_sz==buf_sz ) ) err = EOVERFLOW;
     599           0 :   if( FD_UNLIKELY( close( text_fd ) && !err ) )    err = errno;
     600           0 :   if( FD_UNLIKELY( err ) ) {
     601           0 :     errno = err;
     602           0 :     return -1;
     603           0 :   }
     604             : 
     605           0 :   ulong text_sz = (ulong)read_sz;
     606           0 :   while( text_sz && fd_isspace( (int)(uchar)buf[ text_sz-1UL ] ) ) text_sz--;
     607           0 :   if( FD_UNLIKELY( !text_sz ) ) {
     608           0 :     errno = EPROTO;
     609           0 :     return -1;
     610           0 :   }
     611           0 :   buf[ text_sz ] = '\0';
     612           0 :   return 0;
     613           0 : }
     614             : 
     615             : static int
     616             : fd_rdma_read_uint( char const * path,
     617           0 :                    uint *       value ) {
     618           0 :   char buf[ 32 ];
     619           0 :   if( FD_UNLIKELY( fd_rdma_read_text( path, buf, sizeof(buf) ) ) ) return -1;
     620             : 
     621           0 :   char * end;
     622           0 :   ulong parsed_value = strtoul( buf, &end, 10 );
     623           0 :   if( FD_UNLIKELY( *end || parsed_value>UINT_MAX ) ) {
     624           0 :     errno = EPROTO;
     625           0 :     return -1;
     626           0 :   }
     627           0 :   *value = (uint)parsed_value;
     628           0 :   return 0;
     629           0 : }
     630             : 
     631             : static int
     632             : fd_rdma_name_valid( char const * name,
     633           0 :                     ulong        name_max ) {
     634           0 :   if( FD_UNLIKELY( !name || !name[0] ) ) return 0;
     635           0 :   if( FD_UNLIKELY( name[0]=='.' && (!name[1] || (name[1]=='.' && !name[2])) ) ) return 0;
     636           0 :   for( ulong i=0UL; i<name_max; i++ ) {
     637           0 :     uchar c = (uchar)name[ i ];
     638           0 :     if( !c ) return 1;
     639           0 :     if( !((c>=(uchar)'a' && c<=(uchar)'z') ||
     640           0 :           (c>=(uchar)'A' && c<=(uchar)'Z') ||
     641           0 :           (c>=(uchar)'0' && c<=(uchar)'9') ||
     642           0 :           c==(uchar)'_' || c==(uchar)'-' || c==(uchar)'.') ) return 0;
     643           0 :   }
     644           0 :   return 0;
     645           0 : }
     646             : 
     647             : /* fd_uverbs_* helpers build and submit Linux uverbs commands */
     648             : static int
     649           0 : fd_uverbs_name_valid( char const * name ) {
     650           0 :   ulong name_sz = strnlen( name, FD_UVERBS_NAME_MAX );
     651           0 :   if( name_sz==FD_UVERBS_NAME_MAX || name_sz==6UL || strncmp( name, "uverbs", 6UL ) ) return 0;
     652           0 :   for( char const * digit_cur=name+6; *digit_cur; digit_cur++ )
     653           0 :     if( *digit_cur<'0' || *digit_cur>'9' ) return 0;
     654           0 :   return 1;
     655           0 : }
     656             : 
     657             : static int
     658             : fd_uverbs_resolve( char         uverbs_name[ FD_UVERBS_NAME_MAX ],
     659           0 :                    char const * rdma_name ) {
     660           0 :   if( FD_UNLIKELY( !fd_rdma_name_valid( rdma_name, FD_MLX5_RDMA_NAME_MAX ) ) ) {
     661           0 :     errno = EINVAL;
     662           0 :     return -1;
     663           0 :   }
     664             : 
     665           0 :   DIR * uverbs_dir = opendir( "/sys/class/infiniband_verbs" );
     666           0 :   if( FD_UNLIKELY( !uverbs_dir ) ) return -1;
     667             : 
     668           0 :   char matched_uverbs_name[ FD_UVERBS_NAME_MAX ];
     669           0 :   int  err;
     670           0 :   for(;;) {
     671           0 :     errno = 0;
     672           0 :     struct dirent * uverbs_entry = readdir( uverbs_dir );
     673           0 :     if( !uverbs_entry ) {
     674           0 :       err = errno ? errno : ENODEV;
     675           0 :       break;
     676           0 :     }
     677           0 :     if( !fd_uverbs_name_valid( uverbs_entry->d_name ) ) continue;
     678             : 
     679           0 :     char path[ FD_RDMA_PATH_MAX ];
     680           0 :     char entry_rdma_name[ FD_MLX5_RDMA_NAME_MAX ];
     681           0 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL,
     682           0 :                                    "/sys/class/infiniband_verbs/%s/ibdev", uverbs_entry->d_name ) );
     683           0 :     if( FD_UNLIKELY( fd_rdma_read_text( path, entry_rdma_name, sizeof(entry_rdma_name) ) ) ) {
     684           0 :       err = errno;
     685           0 :       break;
     686           0 :     }
     687           0 :     if( strcmp( entry_rdma_name, rdma_name ) ) continue;
     688             : 
     689           0 :     ulong name_sz = strlen( uverbs_entry->d_name );
     690           0 :     fd_memcpy( matched_uverbs_name, uverbs_entry->d_name, name_sz+1UL );
     691           0 :     err = 0;
     692           0 :     break;
     693           0 :   }
     694           0 :   closedir( uverbs_dir );
     695           0 :   if( FD_UNLIKELY( err ) ) {
     696           0 :     errno = err;
     697           0 :     return -1;
     698           0 :   }
     699             : 
     700           0 :   char path[ FD_RDMA_PATH_MAX ];
     701           0 :   uint core_abi;
     702           0 :   uint provider_abi;
     703           0 :   if( FD_UNLIKELY( fd_rdma_read_uint( "/sys/class/infiniband_verbs/abi_version", &core_abi ) ) ) return -1;
     704           0 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL,
     705           0 :                                  "/sys/class/infiniband_verbs/%s/abi_version", matched_uverbs_name ) );
     706           0 :   if( FD_UNLIKELY( fd_rdma_read_uint( path, &provider_abi ) ) ) return -1;
     707           0 :   if( FD_UNLIKELY( core_abi!=IB_USER_VERBS_ABI_VERSION ||
     708           0 :                    provider_abi!=MLX5_IB_UVERBS_ABI_VERSION ) ) {
     709           0 :     FD_LOG_WARNING(( "mlx5 uverbs ABI mismatch (core %u, expected %u, provider %u, expected %u)",
     710           0 :                      core_abi, (uint)IB_USER_VERBS_ABI_VERSION,
     711           0 :                      provider_abi, (uint)MLX5_IB_UVERBS_ABI_VERSION ));
     712           0 :     errno = EPROTONOSUPPORT;
     713           0 :     return -1;
     714           0 :   }
     715             : 
     716           0 :   fd_memcpy( uverbs_name, matched_uverbs_name, strlen(matched_uverbs_name)+1UL );
     717           0 :   return 0;
     718           0 : }
     719             : 
     720             : static int
     721             : fd_uverbs_init_cmd_hdr( struct ib_uverbs_cmd_hdr * hdr,
     722             :                         uint                       command,
     723             :                         ulong                      request_sz,
     724           0 :                         ulong                      response_sz ) {
     725           0 :   if( FD_UNLIKELY( !hdr || command>IB_USER_VERBS_CMD_COMMAND_MASK || request_sz<sizeof(*hdr) ) ) return -1;
     726             : 
     727           0 :   hdr->command   = command;
     728           0 :   hdr->in_words  = (ushort)(request_sz / 4UL);
     729           0 :   hdr->out_words = (ushort)(response_sz / 4UL);
     730           0 :   return 0;
     731           0 : }
     732             : 
     733             : static int
     734             : fd_uverbs_init_ex_hdr( fd_uverbs_ex_hdr_t * hdr,
     735             :                        uint                 command,
     736             :                        ulong                core_request_sz,
     737             :                        ulong                request_sz,
     738             :                        void *               response,
     739             :                        ulong                core_response_sz,
     740           0 :                        ulong                response_sz ) {
     741           0 :   ulong const hdr_sz = sizeof(*hdr);
     742           0 :   if( FD_UNLIKELY( !hdr || command>IB_USER_VERBS_CMD_COMMAND_MASK ||
     743           0 :                    core_request_sz<hdr_sz || request_sz<core_request_sz ||
     744           0 :                    response_sz<core_response_sz || (response_sz && !response) ) ) return -1;
     745             : 
     746           0 :   hdr->cmd.command           = IB_USER_VERBS_CMD_FLAG_EXTENDED | command;
     747           0 :   hdr->cmd.in_words          = (ushort)((core_request_sz-hdr_sz) / 8UL);
     748           0 :   hdr->cmd.out_words         = (ushort)(core_response_sz / 8UL);
     749           0 :   hdr->ex.provider_in_words  = (ushort)((request_sz-core_request_sz) / 8UL);
     750           0 :   hdr->ex.provider_out_words = (ushort)((response_sz-core_response_sz) / 8UL);
     751           0 :   hdr->ex.response           = (ulong)response;
     752           0 :   hdr->ex.cmd_hdr_reserved   = 0U;
     753           0 :   return 0;
     754           0 : }
     755             : 
     756             : static int
     757             : fd_uverbs_write_cmd( int          cmd_fd,
     758             :                      void const * req,
     759           0 :                      ulong        req_sz ) {
     760           0 :   ssize_t write_sz = write( cmd_fd, req, req_sz );
     761           0 :   if( FD_UNLIKELY( write_sz<0 ) ) return -1;
     762           0 :   if( FD_UNLIKELY( (ulong)write_sz!=req_sz ) ) {
     763           0 :     errno = EPROTO;
     764           0 :     return -1;
     765           0 :   }
     766           0 :   return 0;
     767           0 : }
     768             : 
     769             : static int
     770             : fd_uverbs_get_context( fd_uverbs_ctx_t * ctx,
     771           0 :                        fd_mlx5_caps_t *  caps ) {
     772           0 :   fd_uverbs_get_context_req_t req [1];
     773           0 :   fd_uverbs_get_context_resp_t resp[1];
     774           0 :   fd_memset( req,  0, sizeof(req ) );
     775           0 :   fd_memset( resp, 0, sizeof(resp) );
     776             : 
     777           0 :   req->response                    = (ulong)resp;
     778           0 :   req->mlx5.total_num_bfregs       = 16U;
     779           0 :   req->mlx5.num_low_latency_bfregs = 4U;
     780           0 :   req->mlx5.max_cqe_version        = 1U;
     781           0 :   req->mlx5.lib_caps               = MLX5_LIB_CAP_4K_UAR | MLX5_LIB_CAP_DYN_UAR;
     782           0 :   FD_TEST( !fd_uverbs_init_cmd_hdr( &req->hdr, IB_USER_VERBS_CMD_GET_CONTEXT,
     783           0 :                                     sizeof(req), sizeof(resp) ) );
     784           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( ctx->cmd_fd, req, sizeof(req) ) ) ) return -1;
     785           0 :   if( FD_UNLIKELY( resp->async_fd>(uint)INT_MAX ) ) {
     786           0 :     errno = EPROTO;
     787           0 :     return -1;
     788           0 :   }
     789             : 
     790           0 :   ctx->async_fd = (int)resp->async_fd;
     791           0 :   int fd_flags = fcntl( ctx->async_fd, F_GETFD );
     792           0 :   if( FD_UNLIKELY( fd_flags<0 || fcntl( ctx->async_fd, F_SETFD, fd_flags | FD_CLOEXEC ) ) ) return -1;
     793             : 
     794           0 :   ulong const uar_resp_sz = offsetof( struct mlx5_ib_alloc_ucontext_resp, num_uars_per_page )+
     795           0 :                             sizeof(resp->mlx5.num_uars_per_page);
     796           0 :   if( FD_UNLIKELY( resp->mlx5.response_length<uar_resp_sz                              ||
     797           0 :                    resp->mlx5.cqe_version>1U                                           ||
     798           0 :                    resp->mlx5.max_sq_desc_sz<sizeof(fd_mlx5_tx_wqe_t)                  ||
     799           0 :                    resp->mlx5.max_rq_desc_sz<sizeof(fd_mlx5_rx_wqe_t)                  ||
     800           0 :                    resp->mlx5.log_uar_size!=(uint)fd_ulong_find_lsb( FD_MLX5_PAGE_SZ ) ||
     801           0 :                    resp->mlx5.num_uars_per_page!=1U                                    ||
     802           0 :                    resp->mlx5.tot_bfregs ) ) {
     803           0 :     FD_LOG_WARNING(( "unsupported mlx5 context capabilities (response length %u, CQE version %u, "
     804           0 :                      "max SQ descriptor %u, max RQ descriptor %u, log UAR size %u, UARs per page %u, "
     805           0 :                      "legacy BF registers %u)",
     806           0 :                      resp->mlx5.response_length, (uint)resp->mlx5.cqe_version,
     807           0 :                      (uint)resp->mlx5.max_sq_desc_sz, (uint)resp->mlx5.max_rq_desc_sz,
     808           0 :                      resp->mlx5.log_uar_size, resp->mlx5.num_uars_per_page, resp->mlx5.tot_bfregs ));
     809           0 :     errno = EPROTONOSUPPORT;
     810           0 :     return -1;
     811           0 :   }
     812             : 
     813           0 :   caps->max_send_wqe = resp->mlx5.max_send_wqebb;
     814           0 :   caps->max_recv_wr  = resp->mlx5.max_recv_wr;
     815           0 :   switch( resp->mlx5.eth_min_inline ) {
     816           0 :   case MLX5_USER_INLINE_MODE_NONE: caps->eth_min_inline_hdr_sz = 0U;                        break;
     817           0 :   case MLX5_USER_INLINE_MODE_L2:   caps->eth_min_inline_hdr_sz = FD_MLX5_ETH_INLINE_HDR_SZ; break;
     818           0 :   default:
     819           0 :     FD_LOG_WARNING(( "unsupported mlx5 minimum inline mode %u", (uint)resp->mlx5.eth_min_inline ));
     820           0 :     errno = EPROTONOSUPPORT;
     821           0 :     return -1;
     822           0 :   }
     823           0 :   return 0;
     824           0 : }
     825             : 
     826             : static int
     827             : fd_uverbs_query_device( fd_uverbs_ctx_t * ctx,
     828           0 :                         fd_mlx5_caps_t *  caps ) {
     829           0 :   fd_uverbs_query_device_req_t  req [1];
     830           0 :   fd_uverbs_query_device_resp_t resp[1];
     831           0 :   fd_memset( req,  0, sizeof(req ) );
     832           0 :   fd_memset( resp, 0, sizeof(resp) );
     833             : 
     834           0 :   FD_TEST( !fd_uverbs_init_ex_hdr( &req->hdr, IB_USER_VERBS_EX_CMD_QUERY_DEVICE,
     835           0 :                                    sizeof(req), sizeof(req), resp,
     836           0 :                                    sizeof(resp->core), sizeof(resp) ) );
     837           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( ctx->cmd_fd, req, sizeof(req) ) ) ) return -1;
     838             : 
     839           0 :   struct ib_uverbs_query_device_resp const * core = &resp->core.base;
     840           0 :   caps->max_mr_size = core->max_mr_size;
     841           0 :   caps->max_cqe     = core->max_cqe;
     842           0 :   if( FD_UNLIKELY( resp->core.response_length<sizeof(*core) ||
     843           0 :                    !caps->max_mr_size                       ||
     844           0 :                    !core->max_qp_wr                         ||
     845           0 :                    !core->max_sge                           ||
     846           0 :                    !caps->max_cqe                           ||
     847           0 :                    core->phys_port_cnt<ctx->port_num ) ) {
     848           0 :     FD_LOG_WARNING(( "unsupported mlx5 device capabilities (max MR size %lu, max QP WRs %u, max SGEs %u, "
     849           0 :                      "max CQEs %u, physical ports %u, requested port %u)",
     850           0 :                      caps->max_mr_size, core->max_qp_wr, core->max_sge, caps->max_cqe,
     851           0 :                      (uint)core->phys_port_cnt, ctx->port_num ));
     852           0 :     errno = EPROTONOSUPPORT;
     853           0 :     return -1;
     854           0 :   }
     855             : 
     856           0 :   ulong const rss_caps_sz = offsetof( struct ib_uverbs_ex_query_device_resp, rss_caps )+
     857           0 :                             sizeof(resp->core.rss_caps);
     858           0 :   if( FD_UNLIKELY( resp->core.response_length<rss_caps_sz ) ) {
     859           0 :     FD_LOG_WARNING(( "mlx5 device query did not return RSS capabilities (response length %u, required %lu)",
     860           0 :                      resp->core.response_length, rss_caps_sz ));
     861           0 :     errno = EPROTONOSUPPORT;
     862           0 :     return -1;
     863           0 :   }
     864           0 :   if( FD_UNLIKELY( resp->core.rss_caps.max_rwq_indirection_table_size<FD_MLX5_INDIRECTION_COUNT ) ) {
     865           0 :     FD_LOG_WARNING(( "mlx5 RSS indirection table requires %lu entries, device supports only %u",
     866           0 :                      FD_MLX5_INDIRECTION_COUNT, resp->core.rss_caps.max_rwq_indirection_table_size ));
     867           0 :     errno = EPROTONOSUPPORT;
     868           0 :     return -1;
     869           0 :   }
     870             : 
     871           0 :   ulong const tunnel_caps_sz = offsetof( struct mlx5_ib_query_device_resp, tunnel_offloads_caps )+
     872           0 :                                sizeof(resp->mlx5.tunnel_offloads_caps);
     873           0 :   ulong const required_hash_fields = FD_MLX5_RX_HASH_IPV4_UDP | FD_MLX5_RX_HASH_INNER;
     874             : 
     875           0 :   if( FD_UNLIKELY( resp->mlx5.response_length<tunnel_caps_sz                                              ||
     876           0 :                    !(resp->mlx5.rss_caps.rx_hash_function & FD_MLX5_RX_HASH_FUNC_TOEPLITZ)                ||
     877           0 :                    (resp->mlx5.rss_caps.rx_hash_fields_mask & required_hash_fields)!=required_hash_fields ||
     878           0 :                    !(resp->mlx5.tunnel_offloads_caps & FD_MLX5_TUNNEL_OFFLOADS_GRE) ) ) {
     879           0 :     FD_LOG_WARNING(( "unsupported mlx5 GRE RSS capabilities (response length %u, hash functions %#x, "
     880           0 :                      "hash fields %#lx, tunnel offloads %#x)",
     881           0 :                      resp->mlx5.response_length, (uint)resp->mlx5.rss_caps.rx_hash_function,
     882           0 :                      (ulong)resp->mlx5.rss_caps.rx_hash_fields_mask, resp->mlx5.tunnel_offloads_caps ));
     883           0 :     errno = EPROTONOSUPPORT;
     884           0 :     return -1;
     885           0 :   }
     886           0 :   return 0;
     887           0 : }
     888             : 
     889             : static int
     890           0 : fd_uverbs_query_port( fd_uverbs_ctx_t * ctx ) {
     891           0 :   fd_uverbs_query_port_req_t       req [1];
     892           0 :   struct ib_uverbs_query_port_resp resp[1];
     893           0 :   fd_memset( req,  0, sizeof(req ) );
     894           0 :   fd_memset( resp, 0, sizeof(resp) );
     895             : 
     896           0 :   req->response = (ulong)resp;
     897           0 :   req->port_num = (uchar)ctx->port_num;
     898           0 :   FD_TEST( !fd_uverbs_init_cmd_hdr( &req->hdr, IB_USER_VERBS_CMD_QUERY_PORT,
     899           0 :                                     sizeof(req), sizeof(resp) ) );
     900           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( ctx->cmd_fd, req, sizeof(req) ) ) ) return -1;
     901             : 
     902           0 :   if( FD_UNLIKELY( resp->link_layer!=FD_MLX5_LINK_LAYER_ETHERNET ) ) {
     903           0 :     FD_LOG_WARNING(( "unsupported mlx5 link layer %u, expected Ethernet (%u)",
     904           0 :                      (uint)resp->link_layer, FD_MLX5_LINK_LAYER_ETHERNET ));
     905           0 :     errno = EPROTONOSUPPORT;
     906           0 :     return -1;
     907           0 :   }
     908           0 :   return 0;
     909           0 : }
     910             : 
     911             : static fd_uverbs_ctx_t *
     912             : fd_uverbs_open_context( fd_uverbs_ctx_t * ctx,
     913             :                         fd_mlx5_caps_t *  caps,
     914             :                         char const *      rdma_name,
     915           0 :                         uint              port_num ) {
     916           0 :   fd_memset( ctx, 0, sizeof(*ctx) );
     917           0 :   fd_memset( caps, 0, sizeof(*caps) );
     918           0 :   ctx->cmd_fd   = -1;
     919           0 :   ctx->async_fd = -1;
     920           0 :   if( FD_UNLIKELY( !port_num || port_num>(uint)UCHAR_MAX ) ) {
     921           0 :     errno = EINVAL;
     922           0 :     return NULL;
     923           0 :   }
     924           0 :   ctx->port_num = port_num;
     925             : 
     926           0 :   char uverbs_name[ FD_UVERBS_NAME_MAX ];
     927           0 :   if( FD_UNLIKELY( fd_uverbs_resolve( uverbs_name, rdma_name ) ) ) return NULL;
     928             : 
     929           0 :   char path[ FD_RDMA_PATH_MAX ];
     930           0 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/dev/infiniband/%s", uverbs_name ) );
     931           0 :   ctx->cmd_fd = open( path, O_RDWR | O_CLOEXEC );
     932           0 :   if( FD_UNLIKELY( ctx->cmd_fd<0 ) ) return NULL;
     933             : 
     934           0 :   struct stat cmd_fd_stat;
     935           0 :   if( FD_UNLIKELY( fstat( ctx->cmd_fd, &cmd_fd_stat ) ) ) return NULL;
     936           0 :   if( FD_UNLIKELY( !S_ISCHR( cmd_fd_stat.st_mode ) ) ) {
     937           0 :     errno = ENODEV;
     938           0 :     return NULL;
     939           0 :   }
     940             : 
     941           0 :   if( FD_UNLIKELY( fd_uverbs_get_context( ctx, caps )  ||
     942           0 :                    fd_uverbs_query_device( ctx, caps ) ||
     943           0 :                    fd_uverbs_query_port( ctx ) ) ) return NULL;
     944           0 :   return ctx;
     945           0 : }
     946             : 
     947             : static fd_mlx5_pd_t *
     948             : fd_uverbs_alloc_pd( fd_mlx5_pd_t *      pd,
     949           0 :                     fd_uverbs_ctx_t *   ctx ) {
     950           0 :   if( FD_UNLIKELY( !pd ) ) {
     951           0 :     errno = EINVAL;
     952           0 :     return NULL;
     953           0 :   }
     954           0 :   fd_memset( pd, 0, sizeof(*pd) );
     955           0 :   if( FD_UNLIKELY( !ctx || ctx->cmd_fd<0 ) ) {
     956           0 :     errno = EINVAL;
     957           0 :     return NULL;
     958           0 :   }
     959             : 
     960           0 :   fd_uverbs_alloc_pd_req_t  req [1];
     961           0 :   fd_uverbs_alloc_pd_resp_t resp[1];
     962           0 :   fd_memset( req,  0, sizeof(req ) );
     963           0 :   fd_memset( resp, 0, sizeof(resp) );
     964           0 :   req->response = (ulong)resp;
     965           0 :   FD_TEST( !fd_uverbs_init_cmd_hdr( &req->hdr, IB_USER_VERBS_CMD_ALLOC_PD,
     966           0 :                                     sizeof(req), sizeof(resp) ) );
     967           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( ctx->cmd_fd, req, sizeof(req) ) ) ) return NULL;
     968             : 
     969           0 :   pd->ctx    = ctx;
     970           0 :   pd->handle = resp->pd_handle;
     971           0 :   return pd;
     972           0 : }
     973             : 
     974             : static uint *
     975             : fd_uverbs_register_mr( uint *         lkey,
     976             :                        fd_mlx5_pd_t * pd,
     977             :                        void *         memory,
     978             :                        ulong          memory_sz,
     979             :                        ulong          iova,
     980           0 :                        ulong          max_mr_size ) {
     981           0 :   ulong memory_addr = (ulong)memory;
     982           0 :   if( FD_UNLIKELY( !memory || !memory_sz || memory_sz>ULONG_MAX-memory_addr || memory_sz>ULONG_MAX-iova ||
     983           0 :                    memory_sz>max_mr_size ||
     984           0 :                    (memory_addr & (FD_MLX5_PAGE_SZ-1UL))!=(iova & (FD_MLX5_PAGE_SZ-1UL)) ) ) {
     985           0 :     errno = EINVAL;
     986           0 :     return NULL;
     987           0 :   }
     988             : 
     989           0 :   fd_uverbs_reg_mr_req_t  req [1];
     990           0 :   fd_uverbs_reg_mr_resp_t resp[1];
     991           0 :   fd_memset( req,  0, sizeof(req ) );
     992           0 :   fd_memset( resp, 0, sizeof(resp) );
     993           0 :   req->response     = (ulong)resp;
     994           0 :   req->start        = memory_addr;
     995           0 :   req->length       = memory_sz;
     996           0 :   req->hca_va       = iova;
     997           0 :   req->pd_handle    = pd->handle;
     998           0 :   req->access_flags = IB_UVERBS_ACCESS_LOCAL_WRITE;
     999           0 :   FD_TEST( !fd_uverbs_init_cmd_hdr( &req->hdr, IB_USER_VERBS_CMD_REG_MR,
    1000           0 :                                     sizeof(req), sizeof(resp) ) );
    1001           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( pd->ctx->cmd_fd, req, sizeof(req) ) ) ) return NULL;
    1002             : 
    1003           0 :   *lkey = resp->lkey;
    1004           0 :   return lkey;
    1005           0 : }
    1006             : 
    1007             : static void
    1008             : fd_uverbs_init_ioctl_attr( struct ib_uverbs_attr * attr,
    1009             :                            ushort                  attr_id,
    1010             :                            ushort                  len,
    1011           0 :                            ulong                   data ) {
    1012           0 :   fd_memset( attr, 0, sizeof(*attr) );
    1013           0 :   attr->attr_id = attr_id;
    1014           0 :   attr->len     = len;
    1015           0 :   attr->flags   = UVERBS_ATTR_F_MANDATORY;
    1016           0 :   attr->data    = data;
    1017           0 : }
    1018             : 
    1019             : static int
    1020             : fd_uverbs_init_alloc_uar_req( fd_uverbs_alloc_uar_req_t * req,
    1021             :                               ulong *                     mmap_offset,
    1022             :                               uint *                      mmap_sz,
    1023           0 :                               uint *                      page_id ) {
    1024           0 :   if( FD_UNLIKELY( !req || !mmap_offset || !mmap_sz || !page_id ) ) return -1;
    1025           0 :   fd_memset( req, 0, sizeof(*req) );
    1026           0 :   req->hdr.length    = (ushort)sizeof(*req);
    1027           0 :   req->hdr.object_id = MLX5_IB_OBJECT_UAR;
    1028           0 :   req->hdr.method_id = MLX5_IB_METHOD_UAR_OBJ_ALLOC;
    1029           0 :   req->hdr.num_attrs = 5U;
    1030           0 :   req->hdr.driver_id = RDMA_DRIVER_MLX5;
    1031           0 :   fd_uverbs_init_ioctl_attr( req->attrs+0, MLX5_IB_ATTR_UAR_OBJ_ALLOC_HANDLE,      0U, 0UL );
    1032           0 :   fd_uverbs_init_ioctl_attr( req->attrs+1, MLX5_IB_ATTR_UAR_OBJ_ALLOC_TYPE,        8U, MLX5_IB_UAPI_UAR_ALLOC_TYPE_NC );
    1033           0 :   fd_uverbs_init_ioctl_attr( req->attrs+2, MLX5_IB_ATTR_UAR_OBJ_ALLOC_MMAP_OFFSET, 8U, (ulong)mmap_offset );
    1034           0 :   fd_uverbs_init_ioctl_attr( req->attrs+3, MLX5_IB_ATTR_UAR_OBJ_ALLOC_MMAP_LENGTH, 4U, (ulong)mmap_sz );
    1035           0 :   fd_uverbs_init_ioctl_attr( req->attrs+4, MLX5_IB_ATTR_UAR_OBJ_ALLOC_PAGE_ID,     4U, (ulong)page_id );
    1036           0 :   return 0;
    1037           0 : }
    1038             : 
    1039             : static int
    1040             : fd_uverbs_init_destroy_uar_req( fd_uverbs_destroy_uar_req_t * req,
    1041           0 :                                 uint                          handle ) {
    1042           0 :   if( FD_UNLIKELY( !req ) ) return -1;
    1043           0 :   fd_memset( req, 0, sizeof(*req) );
    1044           0 :   req->hdr.length    = (ushort)sizeof(*req);
    1045           0 :   req->hdr.object_id = MLX5_IB_OBJECT_UAR;
    1046           0 :   req->hdr.method_id = MLX5_IB_METHOD_UAR_OBJ_DESTROY;
    1047           0 :   req->hdr.num_attrs = 1U;
    1048           0 :   req->hdr.driver_id = RDMA_DRIVER_MLX5;
    1049           0 :   fd_uverbs_init_ioctl_attr( req->attrs, MLX5_IB_ATTR_UAR_OBJ_DESTROY_HANDLE, 0U, handle );
    1050           0 :   return 0;
    1051           0 : }
    1052             : 
    1053             : static int
    1054             : fd_uverbs_destroy_uar( fd_uverbs_ctx_t * ctx,
    1055           0 :                        uint              handle ) {
    1056           0 :   fd_uverbs_destroy_uar_req_t req[1];
    1057           0 :   if( FD_UNLIKELY( fd_uverbs_init_destroy_uar_req( req, handle ) ) ) {
    1058           0 :     errno = EINVAL;
    1059           0 :     return -1;
    1060           0 :   }
    1061           0 :   return (int)syscall( SYS_ioctl, ctx->cmd_fd, RDMA_VERBS_IOCTL, &req->hdr );
    1062           0 : }
    1063             : 
    1064             : static ulong *
    1065             : fd_uverbs_alloc_uar( fd_uverbs_ctx_t * ctx,
    1066             :                      uint *            page_id,
    1067           0 :                      ulong *           mmap_offset_out ) {
    1068           0 :   ulong mmap_offset = 0UL;
    1069           0 :   uint  mmap_sz     = 0U;
    1070           0 :   uint  uar_page_id = 0U;
    1071           0 :   fd_uverbs_alloc_uar_req_t req[1];
    1072           0 :   if( FD_UNLIKELY( fd_uverbs_init_alloc_uar_req( req, &mmap_offset, &mmap_sz, &uar_page_id ) ) ) {
    1073           0 :     errno = EINVAL;
    1074           0 :     return NULL;
    1075           0 :   }
    1076           0 :   if( FD_UNLIKELY( syscall( SYS_ioctl, ctx->cmd_fd, RDMA_VERBS_IOCTL, &req->hdr ) ) ) return NULL;
    1077             : 
    1078           0 :   ulong handle_raw = req->attrs[0].data;
    1079           0 :   if( FD_UNLIKELY( handle_raw>UINT_MAX || mmap_sz!=FD_MLX5_PAGE_SZ ||
    1080           0 :                    (mmap_offset & (FD_MLX5_PAGE_SZ-1UL)) || mmap_offset>(ulong)LONG_MAX ) ) {
    1081           0 :     FD_LOG_WARNING(( "invalid mlx5 UAR response (handle %lu, mapping size %u, mapping offset %#lx)",
    1082           0 :                      handle_raw, mmap_sz, mmap_offset ));
    1083           0 :     int err = EPROTO;
    1084           0 :     if( handle_raw<=UINT_MAX ) fd_uverbs_destroy_uar( ctx, (uint)handle_raw );
    1085           0 :     errno = err;
    1086           0 :     return NULL;
    1087           0 :   }
    1088             : 
    1089           0 :   *page_id         = uar_page_id;
    1090           0 :   *mmap_offset_out = mmap_offset;
    1091           0 :   return mmap_offset_out;
    1092           0 : }
    1093             : 
    1094             : volatile uchar *
    1095             : fd_uverbs_map_uar( fd_uverbs_ctx_t * ctx,
    1096           0 :                    ulong             mmap_offset ) {
    1097           0 :   if( FD_UNLIKELY( !ctx || ctx->cmd_fd<0 ||
    1098           0 :                    (mmap_offset & (FD_MLX5_PAGE_SZ-1UL)) || mmap_offset>(ulong)LONG_MAX ) ) {
    1099           0 :     errno = EINVAL;
    1100           0 :     return NULL;
    1101           0 :   }
    1102           0 :   void * uar_mapping = mmap( NULL, FD_MLX5_PAGE_SZ, PROT_WRITE, MAP_SHARED,
    1103           0 :                              ctx->cmd_fd, (off_t)mmap_offset );
    1104           0 :   if( FD_UNLIKELY( uar_mapping==MAP_FAILED ) ) return NULL;
    1105           0 :   return (volatile uchar *)uar_mapping;
    1106           0 : }
    1107             : 
    1108             : static int
    1109           0 : fd_uverbs_create_comp_channel( fd_uverbs_ctx_t * ctx ) {
    1110           0 :   fd_uverbs_create_comp_channel_req_t       req [1];
    1111           0 :   struct ib_uverbs_create_comp_channel_resp resp[1];
    1112           0 :   fd_memset( req,  0, sizeof(req ) );
    1113           0 :   fd_memset( resp, 0, sizeof(resp) );
    1114             : 
    1115           0 :   req->response = (ulong)resp;
    1116           0 :   FD_TEST( !fd_uverbs_init_cmd_hdr( &req->hdr, IB_USER_VERBS_CMD_CREATE_COMP_CHANNEL,
    1117           0 :                                     sizeof(req), sizeof(resp) ) );
    1118           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( ctx->cmd_fd, req, sizeof(req) ) ) ) return -1;
    1119           0 :   return (int)resp->fd;
    1120           0 : }
    1121             : 
    1122             : static uint *
    1123             : fd_uverbs_create_cq( uint *            handle,
    1124             :                      fd_uverbs_ctx_t * ctx,
    1125             :                      fd_mlx5_cq_t *    cq,
    1126             :                      int               comp_channel_fd,
    1127             :                      uint              page_id,
    1128           0 :                      uint              max_cqe ) {
    1129           0 :   if( FD_UNLIKELY( !handle ) ) {
    1130           0 :     errno = EINVAL;
    1131           0 :     return NULL;
    1132           0 :   }
    1133           0 :   *handle = 0U;
    1134           0 :   if( FD_UNLIKELY( !ctx || ctx->cmd_fd<0 || !cq || !cq->entries || !cq->control ||
    1135           0 :                    !fd_uint_is_pow2( cq->depth ) || cq->depth-1U>max_cqe || page_id>(uint)USHRT_MAX ) ) {
    1136           0 :     errno = EINVAL;
    1137           0 :     return NULL;
    1138           0 :   }
    1139             : 
    1140           0 :   fd_uverbs_create_cq_req_t  req [1];
    1141           0 :   fd_uverbs_create_cq_resp_t resp[1];
    1142           0 :   fd_memset( req,  0, sizeof(req ) );
    1143           0 :   fd_memset( resp, 0, sizeof(resp) );
    1144             : 
    1145           0 :   req->response                = (ulong)resp;
    1146           0 :   req->user_handle             = (ulong)cq;
    1147           0 :   req->cqe                     = cq->depth-1U;
    1148           0 :   req->comp_channel            = comp_channel_fd;
    1149           0 :   req->mlx5.fields.buf_addr    = (ulong)cq->entries;
    1150           0 :   req->mlx5.fields.db_addr     = (ulong)cq->control;
    1151           0 :   req->mlx5.fields.cqe_size    = sizeof(fd_mlx5_cqe_t);
    1152           0 :   req->mlx5.fields.flags       = MLX5_IB_CREATE_CQ_FLAGS_UAR_PAGE_INDEX;
    1153           0 :   req->mlx5.uar.uar_page_index = (ushort)page_id;
    1154           0 :   FD_TEST( !fd_uverbs_init_cmd_hdr( &req->hdr, IB_USER_VERBS_CMD_CREATE_CQ,
    1155           0 :                                     sizeof(req), sizeof(resp) ) );
    1156           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( ctx->cmd_fd, req, sizeof(req) ) ) ) return NULL;
    1157           0 :   if( FD_UNLIKELY( resp->cqe!=cq->depth-1U ) ) {
    1158           0 :     fd_uverbs_destroy_cq_req_t       destroy[1];
    1159           0 :     struct ib_uverbs_destroy_cq_resp destroy_resp[1];
    1160           0 :     fd_memset( destroy,      0, sizeof(destroy)      );
    1161           0 :     fd_memset( destroy_resp, 0, sizeof(destroy_resp) );
    1162             : 
    1163           0 :     destroy->response  = (ulong)destroy_resp;
    1164           0 :     destroy->cq_handle = resp->cq_handle;
    1165           0 :     FD_TEST( !fd_uverbs_init_cmd_hdr( &destroy->hdr, IB_USER_VERBS_CMD_DESTROY_CQ,
    1166           0 :                                       sizeof(destroy), sizeof(destroy_resp) ) );
    1167           0 :     fd_uverbs_write_cmd( ctx->cmd_fd, destroy, sizeof(destroy) );
    1168           0 :     FD_LOG_WARNING(( "invalid mlx5 CQ response (CQEs %u, requested %u)", resp->cqe, cq->depth-1U ));
    1169           0 :     errno = EPROTO;
    1170           0 :     return NULL;
    1171           0 :   }
    1172             : 
    1173           0 :   cq->cqn = resp->mlx5.cqn;
    1174           0 :   *handle = resp->cq_handle;
    1175           0 :   return handle;
    1176           0 : }
    1177             : 
    1178             : static int
    1179             : fd_uverbs_modify_qp( fd_uverbs_ctx_t * uverbs,
    1180             :                      fd_mlx5_tx_qp_t * tx_qp,
    1181           0 :                      uint              state ) {
    1182           0 :   fd_uverbs_modify_qp_req_t req[1];
    1183           0 :   fd_memset( req, 0, sizeof(req) );
    1184           0 :   req->core.qp_handle = tx_qp->handle;
    1185           0 :   req->core.attr_mask = FD_MLX5_QP_ATTR_STATE;
    1186           0 :   req->core.qp_state  = (uchar)state;
    1187           0 :   if( state==FD_MLX5_QPS_INIT ) {
    1188           0 :     req->core.attr_mask |= FD_MLX5_QP_ATTR_PORT;
    1189           0 :     req->core.port_num   = (uchar)uverbs->port_num;
    1190           0 :   }
    1191           0 :   FD_TEST( !fd_uverbs_init_cmd_hdr( &req->hdr, IB_USER_VERBS_CMD_MODIFY_QP,
    1192           0 :                                     sizeof(req), 0UL ) );
    1193           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( uverbs->cmd_fd, req, sizeof(req) ) ) ) return -1;
    1194           0 :   return 0;
    1195           0 : }
    1196             : 
    1197             : static fd_mlx5_tx_qp_t *
    1198             : fd_uverbs_create_tx_qp( fd_uverbs_ctx_t * uverbs,
    1199             :                         fd_mlx5_tx_qp_t * tx_qp,
    1200             :                         fd_mlx5_pd_t *    pd,
    1201             :                         uint              tx_cq_handle,
    1202           0 :                         uint              uar_page_id ) {
    1203           0 :   if( FD_UNLIKELY( !uverbs || !tx_qp || !tx_qp->tx_cq || !tx_qp->sq ||
    1204           0 :                    !tx_qp->control || !pd || pd->ctx!=uverbs ) ) {
    1205           0 :     errno = EINVAL;
    1206           0 :     return NULL;
    1207           0 :   }
    1208             : 
    1209           0 :   fd_uverbs_create_tx_qp_req_t  req [1];
    1210           0 :   fd_uverbs_create_tx_qp_resp_t resp[1];
    1211           0 :   fd_memset( req,  0, sizeof(req ) );
    1212           0 :   fd_memset( resp, 0, sizeof(resp) );
    1213             : 
    1214           0 :   req->response          = (ulong)resp;
    1215           0 :   req->user_handle       = (ulong)tx_qp;
    1216           0 :   req->pd_handle         = pd->handle;
    1217           0 :   req->send_cq_handle    = tx_cq_handle;
    1218             :   /* Linux requires both CQ handles even when max_recv_wr is zero. */
    1219           0 :   req->recv_cq_handle    = tx_cq_handle;
    1220           0 :   req->max_send_wr       = tx_qp->tx_depth;
    1221           0 :   req->max_send_sge      = 1U;
    1222           0 :   req->qp_type           = IB_UVERBS_QPT_RAW_PACKET;
    1223           0 :   req->mlx5.db_addr      = (ulong)tx_qp->control;
    1224           0 :   req->mlx5.sq_wqe_count = tx_qp->tx_depth;
    1225           0 :   req->mlx5.flags        = MLX5_QP_FLAG_UAR_PAGE_INDEX;
    1226           0 :   req->mlx5.uidx         = 0U;
    1227           0 :   req->mlx5.bfreg_index  = uar_page_id;
    1228           0 :   req->mlx5.sq_buf_addr  = (ulong)tx_qp->sq;
    1229           0 :   FD_TEST( !fd_uverbs_init_cmd_hdr( &req->hdr, IB_USER_VERBS_CMD_CREATE_QP,
    1230           0 :                                     sizeof(req), sizeof(resp) ) );
    1231           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( uverbs->cmd_fd, req, sizeof(req) ) ) ) return NULL;
    1232             : 
    1233           0 :   if( FD_UNLIKELY( resp->core.max_send_wr<tx_qp->tx_depth ||
    1234           0 :                    resp->core.max_recv_wr                 ||
    1235           0 :                    resp->core.max_send_sge<1U             ||
    1236           0 :                    resp->core.max_recv_sge                ||
    1237           0 :                    resp->core.qpn>0xffffffU ) ) {
    1238           0 :     FD_LOG_WARNING(( "invalid mlx5 QP response (max send WRs %u, max receive WRs %u, max send SGEs %u, "
    1239           0 :                      "max receive SGEs %u, QPN %u)",
    1240           0 :                      resp->core.max_send_wr, resp->core.max_recv_wr, resp->core.max_send_sge,
    1241           0 :                      resp->core.max_recv_sge, resp->core.qpn ));
    1242           0 :     errno = EPROTO;
    1243           0 :     return NULL;
    1244           0 :   }
    1245             : 
    1246           0 :   tx_qp->handle = resp->core.qp_handle;
    1247           0 :   tx_qp->qpn    = resp->core.qpn;
    1248           0 :   return tx_qp;
    1249           0 : }
    1250             : 
    1251             : static fd_mlx5_tx_qp_t *
    1252             : fd_uverbs_start_tx_qp( fd_uverbs_ctx_t * uverbs,
    1253           0 :                        fd_mlx5_tx_qp_t * tx_qp ) {
    1254           0 :   if( FD_UNLIKELY( fd_uverbs_modify_qp( uverbs, tx_qp, FD_MLX5_QPS_INIT ) ||
    1255           0 :                    fd_uverbs_modify_qp( uverbs, tx_qp, FD_MLX5_QPS_RTR  ) ||
    1256           0 :                    fd_uverbs_modify_qp( uverbs, tx_qp, FD_MLX5_QPS_RTS  ) ) ) return NULL;
    1257           0 :   return tx_qp;
    1258           0 : }
    1259             : 
    1260             : static fd_mlx5_rx_wq_t *
    1261             : fd_uverbs_create_rx_wq( fd_uverbs_ctx_t * uverbs,
    1262             :                         fd_mlx5_rx_wq_t * rx_wq,
    1263             :                         fd_mlx5_pd_t *    pd,
    1264           0 :                         uint              rx_cq_handle ) {
    1265           0 :   fd_uverbs_create_rx_wq_req_t  req [1];
    1266           0 :   fd_uverbs_create_rx_wq_resp_t resp[1];
    1267           0 :   fd_memset( req,  0, sizeof(req ) );
    1268           0 :   fd_memset( resp, 0, sizeof(resp) );
    1269             : 
    1270           0 :   req->core.wq_type      = IB_UVERBS_WQT_RQ;
    1271           0 :   req->core.user_handle  = (ulong)rx_wq;
    1272           0 :   req->core.pd_handle    = pd->handle;
    1273           0 :   req->core.cq_handle    = rx_cq_handle;
    1274           0 :   req->core.max_wr       = rx_wq->rx_depth;
    1275           0 :   req->core.max_sge      = 1U;
    1276           0 :   req->mlx5.buf_addr     = (ulong)rx_wq->rq;
    1277           0 :   req->mlx5.db_addr      = (ulong)rx_wq->control;
    1278           0 :   req->mlx5.rq_wqe_count = rx_wq->rx_depth;
    1279           0 :   req->mlx5.rq_wqe_shift = (uint)fd_ulong_find_lsb( sizeof(fd_mlx5_rx_wqe_t) );
    1280           0 :   ulong const core_req_sz  = offsetof( fd_uverbs_create_rx_wq_req_t,  mlx5 );
    1281           0 :   ulong const core_resp_sz = offsetof( fd_uverbs_create_rx_wq_resp_t, mlx5 );
    1282           0 :   FD_TEST( !fd_uverbs_init_ex_hdr( &req->hdr, IB_USER_VERBS_EX_CMD_CREATE_WQ,
    1283           0 :                                    core_req_sz, sizeof(req), resp,
    1284           0 :                                    core_resp_sz, sizeof(resp) ) );
    1285           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( uverbs->cmd_fd, req, sizeof(req) ) ) ) return NULL;
    1286           0 :   if( FD_UNLIKELY( resp->core.max_wr<rx_wq->rx_depth ||
    1287           0 :                    resp->core.max_sge<1U             ||
    1288           0 :                    resp->core.wqn>0xffffffU ) ) {
    1289           0 :     FD_LOG_WARNING(( "invalid mlx5 receive WQ response (max WRs %u, max SGEs %u, WQN %u)",
    1290           0 :                      resp->core.max_wr, resp->core.max_sge, resp->core.wqn ));
    1291           0 :     errno = EPROTO;
    1292           0 :     return NULL;
    1293           0 :   }
    1294             : 
    1295           0 :   rx_wq->handle = resp->core.wq_handle;
    1296           0 :   rx_wq->wqn    = resp->core.wqn;
    1297           0 :   return rx_wq;
    1298           0 : }
    1299             : 
    1300             : static fd_mlx5_rx_wq_t *
    1301             : fd_uverbs_start_rx_wq( fd_uverbs_ctx_t * uverbs,
    1302           0 :                        fd_mlx5_rx_wq_t * rx_wq ) {
    1303           0 :   fd_uverbs_modify_rx_wq_req_t req[1];
    1304           0 :   fd_memset( req, 0, sizeof(req) );
    1305           0 :   req->core.attr_mask     = FD_MLX5_WQ_ATTR_STATE | FD_MLX5_WQ_ATTR_CUR_STATE;
    1306           0 :   req->core.wq_handle     = rx_wq->handle;
    1307           0 :   req->core.wq_state      = FD_MLX5_WQS_RDY;
    1308           0 :   req->core.curr_wq_state = FD_MLX5_WQS_RESET;
    1309           0 :   ulong const core_req_sz = offsetof( fd_uverbs_modify_rx_wq_req_t, mlx5 );
    1310           0 :   FD_TEST( !fd_uverbs_init_ex_hdr( &req->hdr, IB_USER_VERBS_EX_CMD_MODIFY_WQ,
    1311           0 :                                    core_req_sz, sizeof(req), NULL, 0UL, 0UL ) );
    1312           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( uverbs->cmd_fd, req, sizeof(req) ) ) ) return NULL;
    1313             : 
    1314             :   /* Explicitly disable CVLAN stripping. Some kernel configurations will
    1315             :     still strip it anyway so we explicitly disable it so all providers
    1316             :     follow the same software path. */
    1317           0 :   fd_memset( req, 0, sizeof(req) );
    1318           0 :   req->core.attr_mask     = FD_MLX5_WQ_ATTR_STATE | FD_MLX5_WQ_ATTR_CUR_STATE | FD_MLX5_WQ_ATTR_FLAGS;
    1319           0 :   req->core.wq_handle     = rx_wq->handle;
    1320           0 :   req->core.wq_state      = FD_MLX5_WQS_RDY;
    1321           0 :   req->core.curr_wq_state = FD_MLX5_WQS_RDY;
    1322           0 :   req->core.flags         = 0U;
    1323           0 :   req->core.flags_mask    = FD_MLX5_WQ_FLAGS_CVLAN_STRIPPING;
    1324           0 :   FD_TEST( !fd_uverbs_init_ex_hdr( &req->hdr, IB_USER_VERBS_EX_CMD_MODIFY_WQ,
    1325           0 :                                    core_req_sz, sizeof(req), NULL, 0UL, 0UL ) );
    1326           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( uverbs->cmd_fd, req, sizeof(req) ) ) ) {
    1327           0 :     FD_LOG_WARNING(( "failed to disable mlx5 CVLAN stripping on WQ %u (%i-%s)",
    1328           0 :                      rx_wq->wqn, errno, fd_io_strerror( errno ) ));
    1329           0 :   }
    1330           0 :   return rx_wq;
    1331           0 : }
    1332             : 
    1333             : static uint *
    1334             : fd_uverbs_create_rwq_ind_table( uint *                        handle,
    1335             :                                 fd_uverbs_ctx_t *             uverbs,
    1336             :                                 fd_mlx5_uverbs_tile_t const * tiles,
    1337           0 :                                 ulong                         tile_cnt ) {
    1338           0 :   if( FD_UNLIKELY( 0==tile_cnt || tile_cnt>FD_MLX5_TILE_MAX ) ) {
    1339           0 :     errno = EINVAL;
    1340           0 :     return NULL;
    1341           0 :   }
    1342             : 
    1343           0 :   ulong const req_sz = fd_ulong_align_up( sizeof(fd_uverbs_create_rwq_ind_table_req_t)+FD_MLX5_INDIRECTION_COUNT*sizeof(uint), 8UL );
    1344           0 :   fd_uverbs_create_rwq_ind_table_req_t * req = aligned_alloc( 8UL, req_sz );
    1345           0 :   if( FD_UNLIKELY( !req ) ) return NULL;
    1346           0 :   struct ib_uverbs_ex_create_rwq_ind_table_resp resp[1];
    1347           0 :   fd_memset( req,  0, req_sz );
    1348           0 :   fd_memset( resp, 0, sizeof(resp) );
    1349           0 :   req->log_ind_tbl_size = (uint)fd_ulong_find_lsb( FD_MLX5_INDIRECTION_COUNT );
    1350           0 :   for( ulong i=0UL; i<FD_MLX5_INDIRECTION_COUNT; i++ ) req->wq_handles[ i ] = tiles[ i%tile_cnt ].rx_wq->handle;
    1351           0 :   FD_TEST( !fd_uverbs_init_ex_hdr( &req->hdr, IB_USER_VERBS_EX_CMD_CREATE_RWQ_IND_TBL,
    1352           0 :                                    req_sz, req_sz, resp,
    1353           0 :                                    sizeof(resp), sizeof(resp) ) );
    1354           0 :   int err = fd_uverbs_write_cmd( uverbs->cmd_fd, req, req_sz );
    1355           0 :   int saved_errno = errno;
    1356           0 :   free( req );
    1357           0 :   if( FD_UNLIKELY( err ) ) {
    1358           0 :     errno = saved_errno;
    1359           0 :     return NULL;
    1360           0 :   }
    1361           0 :   *handle = resp->ind_tbl_handle;
    1362           0 :   return handle;
    1363           0 : }
    1364             : 
    1365             : static fd_mlx5_rss_qp_t *
    1366             : fd_uverbs_create_rss_qp( fd_uverbs_ctx_t *  uverbs,
    1367             :                          fd_mlx5_rss_qp_t * rss_qp,
    1368             :                          fd_mlx5_pd_t *     pd,
    1369             :                          uint               rwq_ind_table_handle,
    1370             :                          ulong              hash_fields,
    1371           0 :                          uint               flags ) {
    1372           0 :   fd_uverbs_create_rss_qp_req_t  req [1];
    1373           0 :   fd_uverbs_create_rss_qp_resp_t resp[1];
    1374           0 :   fd_memset( req,  0, sizeof(req ) );
    1375           0 :   fd_memset( resp, 0, sizeof(resp) );
    1376           0 :   req->core.user_handle         = (ulong)rss_qp;
    1377           0 :   req->core.pd_handle           = pd->handle;
    1378           0 :   req->core.qp_type             = IB_UVERBS_QPT_RAW_PACKET;
    1379           0 :   req->core.comp_mask           = IB_UVERBS_CREATE_QP_MASK_IND_TABLE;
    1380           0 :   req->core.rwq_ind_tbl_handle  = rwq_ind_table_handle;
    1381           0 :   req->mlx5.rx_hash_fields_mask = hash_fields;
    1382           0 :   req->mlx5.rx_hash_function    = FD_MLX5_RX_HASH_FUNC_TOEPLITZ;
    1383           0 :   req->mlx5.rx_key_len          = FD_MLX5_RX_HASH_KEY_SZ;
    1384           0 :   req->mlx5.flags               = flags;
    1385             : 
    1386           0 :   if( FD_UNLIKELY( !fd_rng_secure( req->mlx5.rx_hash_key, FD_MLX5_RX_HASH_KEY_SZ ) ) ) return NULL;
    1387             : 
    1388           0 :   ulong const core_req_sz  = offsetof( fd_uverbs_create_rss_qp_req_t,  mlx5 );
    1389           0 :   ulong const core_resp_sz = offsetof( fd_uverbs_create_rss_qp_resp_t, mlx5 );
    1390             : 
    1391           0 :   FD_TEST( !fd_uverbs_init_ex_hdr( &req->hdr, IB_USER_VERBS_EX_CMD_CREATE_QP,
    1392           0 :                                    core_req_sz, sizeof(req), resp,
    1393           0 :                                    core_resp_sz, sizeof(resp) ) );
    1394           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( uverbs->cmd_fd, req, sizeof(req) ) ) ) return NULL;
    1395           0 :   rss_qp->handle = resp->core.base.qp_handle;
    1396           0 :   return rss_qp;
    1397           0 : }
    1398             : 
    1399             : static int
    1400             : fd_uverbs_init_udp_flow_req( fd_uverbs_create_udp_flow_req_t *   req,
    1401             :                              struct ib_uverbs_create_flow_resp * resp,
    1402             :                              uint                                qp_handle,
    1403             :                              uint                                port_num,
    1404             :                              uint                                dst_ip,
    1405           0 :                              ushort                              dst_port ) {
    1406           0 :   if( FD_UNLIKELY( !req || !resp || !port_num || port_num>UCHAR_MAX || !dst_port ) ) return -1;
    1407             : 
    1408           0 :   fd_memset( req,  0, sizeof(*req ) );
    1409           0 :   fd_memset( resp, 0, sizeof(*resp) );
    1410           0 :   ulong const core_req_sz = offsetof( fd_uverbs_create_udp_flow_req_t, mlx5_ncounters_data );
    1411           0 :   FD_TEST( !fd_uverbs_init_ex_hdr( &req->hdr, IB_USER_VERBS_EX_CMD_CREATE_FLOW,
    1412           0 :                                    core_req_sz, sizeof(*req), resp,
    1413           0 :                                    sizeof(*resp), sizeof(*resp) ) );
    1414             : 
    1415           0 :   req->qp_handle           = qp_handle;
    1416           0 :   req->size                = sizeof(req->eth)+sizeof(req->ipv4)+sizeof(req->udp);
    1417           0 :   req->num_of_specs        = 3U;
    1418           0 :   req->port                = (uchar)port_num;
    1419           0 :   req->eth.type            = FD_MLX5_FLOW_SPEC_ETH;
    1420           0 :   req->eth.size            = sizeof(req->eth);
    1421           0 :   req->eth.val.ether_type  = fd_ushort_bswap( FD_ETH_HDR_TYPE_IP );
    1422           0 :   req->eth.mask.ether_type = USHORT_MAX;
    1423           0 :   req->ipv4.type           = FD_MLX5_FLOW_SPEC_IPV4;
    1424           0 :   req->ipv4.size           = sizeof(req->ipv4);
    1425           0 :   req->ipv4.val.dst_ip     = dst_ip;
    1426           0 :   req->ipv4.mask.dst_ip    = dst_ip ? UINT_MAX : 0U;
    1427           0 :   req->udp.type            = FD_MLX5_FLOW_SPEC_UDP;
    1428           0 :   req->udp.size            = sizeof(req->udp);
    1429           0 :   req->udp.val.dst_port    = fd_ushort_bswap( dst_port );
    1430           0 :   req->udp.mask.dst_port   = USHORT_MAX;
    1431           0 :   return 0;
    1432           0 : }
    1433             : 
    1434             : int
    1435             : fd_uverbs_create_udp_flow( fd_uverbs_ctx_t *        uverbs,
    1436             :                            fd_mlx5_rss_qp_t const * rss_qp,
    1437             :                            uint                     dst_ip,
    1438           0 :                            ushort                   dst_port ) {
    1439           0 :   if( FD_UNLIKELY( !uverbs || uverbs->cmd_fd<0 || !rss_qp || !dst_port ) ) {
    1440           0 :     errno = EINVAL;
    1441           0 :     return -1;
    1442           0 :   }
    1443             : 
    1444           0 :   fd_uverbs_create_udp_flow_req_t   req [1];
    1445           0 :   struct ib_uverbs_create_flow_resp resp[1];
    1446           0 :   if( FD_UNLIKELY( fd_uverbs_init_udp_flow_req( req, resp, rss_qp->handle,
    1447           0 :                                                 uverbs->port_num, dst_ip,
    1448           0 :                                                 dst_port ) ) ) {
    1449           0 :     errno = EINVAL;
    1450           0 :     return -1;
    1451           0 :   }
    1452           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( uverbs->cmd_fd, req, sizeof(req) ) ) ) return -1;
    1453           0 :   return 0;
    1454           0 : }
    1455             : 
    1456             : static int
    1457             : fd_uverbs_init_gre_flow_req( fd_uverbs_create_gre_flow_req_t *   req,
    1458             :                              struct ib_uverbs_create_flow_resp * resp,
    1459             :                              uint                                qp_handle,
    1460             :                              uint                                port_num,
    1461             :                              uint                                inner_dst_ip,
    1462           0 :                              ushort                              inner_dst_port ) {
    1463           0 :   if( FD_UNLIKELY( !req || !resp || !port_num || port_num>UCHAR_MAX || !inner_dst_port ) ) return -1;
    1464             : 
    1465           0 :   fd_memset( req,  0, sizeof(*req ) );
    1466           0 :   fd_memset( resp, 0, sizeof(*resp) );
    1467           0 :   ulong const core_req_sz = offsetof( fd_uverbs_create_gre_flow_req_t, mlx5_ncounters_data );
    1468           0 :   FD_TEST( !fd_uverbs_init_ex_hdr( &req->hdr, IB_USER_VERBS_EX_CMD_CREATE_FLOW,
    1469           0 :                                    core_req_sz, sizeof(*req), resp,
    1470           0 :                                    sizeof(*resp), sizeof(*resp) ) );
    1471             : 
    1472           0 :   req->qp_handle    = qp_handle;
    1473           0 :   req->size         = sizeof(req->eth)+sizeof(req->outer_ipv4)+sizeof(req->gre)+
    1474           0 :                       sizeof(req->inner_ipv4)+sizeof(req->inner_udp);
    1475           0 :   req->num_of_specs = 5U;
    1476           0 :   req->port         = (uchar)port_num;
    1477           0 :   req->eth = (struct ib_uverbs_flow_spec_eth) {
    1478           0 :     .type = FD_MLX5_FLOW_SPEC_ETH,
    1479           0 :     .size = sizeof(req->eth),
    1480           0 :     .val  = { .ether_type = fd_ushort_bswap( FD_ETH_HDR_TYPE_IP ) },
    1481           0 :     .mask = { .ether_type = USHORT_MAX },
    1482           0 :   };
    1483           0 :   req->outer_ipv4 = (struct ib_uverbs_flow_spec_ipv4) {
    1484           0 :     .type = FD_MLX5_FLOW_SPEC_IPV4,
    1485           0 :     .size = sizeof(req->outer_ipv4),
    1486           0 :     .val  = { .proto = FD_IP4_HDR_PROTOCOL_GRE },
    1487           0 :     .mask = { .proto = UCHAR_MAX },
    1488           0 :   };
    1489           0 :   req->gre = (struct ib_uverbs_flow_spec_gre) {
    1490           0 :     .type = FD_MLX5_FLOW_SPEC_GRE,
    1491           0 :     .size = sizeof(req->gre),
    1492           0 :     .val  = { .protocol = fd_ushort_bswap( FD_ETH_HDR_TYPE_IP ) },
    1493           0 :     .mask = { .protocol = USHORT_MAX },
    1494           0 :   };
    1495           0 :   req->inner_ipv4 = (struct ib_uverbs_flow_spec_ipv4) {
    1496           0 :     .type = FD_MLX5_FLOW_SPEC_INNER | FD_MLX5_FLOW_SPEC_IPV4,
    1497           0 :     .size = sizeof(req->inner_ipv4),
    1498           0 :     .val  = { .dst_ip = inner_dst_ip, .proto = FD_IP4_HDR_PROTOCOL_UDP },
    1499           0 :     .mask = { .dst_ip = inner_dst_ip ? UINT_MAX : 0U, .proto = UCHAR_MAX },
    1500           0 :   };
    1501           0 :   req->inner_udp = (struct ib_uverbs_flow_spec_tcp_udp) {
    1502           0 :     .type = FD_MLX5_FLOW_SPEC_INNER | FD_MLX5_FLOW_SPEC_UDP,
    1503           0 :     .size = sizeof(req->inner_udp),
    1504           0 :     .val  = { .dst_port = fd_ushort_bswap( inner_dst_port ) },
    1505           0 :     .mask = { .dst_port = USHORT_MAX },
    1506           0 :   };
    1507           0 :   return 0;
    1508           0 : }
    1509             : 
    1510             : int
    1511             : fd_uverbs_create_gre_udp_flow( fd_uverbs_ctx_t *        uverbs,
    1512             :                                fd_mlx5_rss_qp_t const * rss_qp,
    1513             :                                uint                     inner_dst_ip,
    1514           0 :                                ushort                   inner_dst_port ) {
    1515           0 :   if( FD_UNLIKELY( !uverbs || uverbs->cmd_fd<0 || !rss_qp || !inner_dst_port ) ) {
    1516           0 :     errno = EINVAL;
    1517           0 :     return -1;
    1518           0 :   }
    1519             : 
    1520           0 :   fd_uverbs_create_gre_flow_req_t   req [1];
    1521           0 :   struct ib_uverbs_create_flow_resp resp[1];
    1522           0 :   if( FD_UNLIKELY( fd_uverbs_init_gre_flow_req( req, resp, rss_qp->handle,
    1523           0 :                                                 uverbs->port_num, inner_dst_ip,
    1524           0 :                                                 inner_dst_port ) ) ) {
    1525           0 :     errno = EINVAL;
    1526           0 :     return -1;
    1527           0 :   }
    1528           0 :   if( FD_UNLIKELY( fd_uverbs_write_cmd( uverbs->cmd_fd, req, sizeof(req) ) ) ) return -1;
    1529           0 :   return 0;
    1530           0 : }
    1531             : 
    1532             : fd_mlx5_rss_qp_t *
    1533             : fd_uverbs_init( fd_uverbs_ctx_t *       uverbs,
    1534             :                 fd_mlx5_uverbs_tile_t * tiles,
    1535             :                 ulong                   tile_cnt,
    1536             :                 fd_mlx5_rss_qp_t *      outer_rss_qp,
    1537             :                 fd_mlx5_rss_qp_t *      gre_rss_qp,
    1538             :                 char const *            rdma_name,
    1539           0 :                 uint                    port_num ) {
    1540           0 :   if( FD_UNLIKELY( !uverbs || !tiles || 0==tile_cnt || tile_cnt>FD_MLX5_TILE_MAX || !outer_rss_qp || !gre_rss_qp ) ) {
    1541           0 :     errno = EINVAL;
    1542           0 :     return NULL;
    1543           0 :   }
    1544           0 :   for( ulong i=0UL; i<tile_cnt; i++ ) {
    1545           0 :     fd_mlx5_uverbs_tile_t const * tile = tiles+i;
    1546           0 :     if( FD_UNLIKELY( !tile->rx_cq || !tile->tx_cq || !tile->rx_wq || !tile->tx_qp || !tile->lkey ||
    1547           0 :                      !tile->packet_memory || !tile->packet_memory_sz || !tile->rx_wq->rq ||
    1548           0 :                      !tile->rx_wq->control || tile->rx_wq->rx_cq!=tile->rx_cq || tile->tx_qp->tx_cq!=tile->tx_cq ||
    1549           0 :                      tile->rx_wq->rx_depth!=tile->rx_cq->depth || tile->tx_qp->tx_depth!=tile->tx_cq->depth ) ) {
    1550           0 :       errno = EINVAL;
    1551           0 :       return NULL;
    1552           0 :     }
    1553           0 :   }
    1554             : 
    1555           0 :   fd_mlx5_caps_t caps[1];
    1556           0 :   fd_mlx5_pd_t   pd[1];
    1557           0 :   uint           rwq_ind_table_handle;
    1558           0 :   if( FD_UNLIKELY( !fd_uverbs_open_context( uverbs, caps, rdma_name, port_num ) ) ) return NULL;
    1559           0 :   if( FD_UNLIKELY( !fd_uverbs_alloc_pd( pd, uverbs ) ) ) return NULL;
    1560             : 
    1561           0 :   for( ulong i=0UL; i<tile_cnt; i++ ) {
    1562           0 :     fd_mlx5_uverbs_tile_t * tile = tiles+i;
    1563           0 :     fd_mlx5_rx_wq_t * rx_wq = tile->rx_wq;
    1564           0 :     fd_mlx5_tx_qp_t * tx_qp = tile->tx_qp;
    1565           0 :     if( FD_UNLIKELY( !fd_uint_is_pow2( rx_wq->rx_depth ) ||
    1566           0 :                      !fd_uint_is_pow2( tx_qp->tx_depth ) ||
    1567           0 :                      rx_wq->rx_depth>caps->max_recv_wr   ||
    1568           0 :                      tx_qp->tx_depth>caps->max_send_wqe  ||
    1569           0 :                      rx_wq->rx_depth-1U>caps->max_cqe    ||
    1570           0 :                      tx_qp->tx_depth-1U>caps->max_cqe ) ) {
    1571           0 :       errno = EINVAL;
    1572           0 :       return NULL;
    1573           0 :     }
    1574             : 
    1575           0 :     int rx_comp_channel_fd = -1;
    1576           0 :     if( tile->rx_comp_channel_fd ) {
    1577           0 :       rx_comp_channel_fd = fd_uverbs_create_comp_channel( uverbs );
    1578           0 :       if( FD_UNLIKELY( rx_comp_channel_fd<0 ) ) return NULL;
    1579           0 :       *tile->rx_comp_channel_fd = rx_comp_channel_fd;
    1580           0 :     }
    1581             : 
    1582           0 :     uint uar_page_id;
    1583           0 :     uint rx_cq_handle;
    1584           0 :     uint tx_cq_handle;
    1585           0 :     if( FD_UNLIKELY( !fd_uverbs_alloc_uar( uverbs, &uar_page_id, &tx_qp->uar_mmap_offset )                                                       ||
    1586           0 :                      !fd_uverbs_create_cq( &rx_cq_handle, uverbs, tile->rx_cq, rx_comp_channel_fd, uar_page_id, caps->max_cqe )                  ||
    1587           0 :                      !fd_uverbs_create_cq( &tx_cq_handle, uverbs, tile->tx_cq, -1, uar_page_id, caps->max_cqe )                                  ||
    1588           0 :                      !fd_uverbs_register_mr( tile->lkey, pd, tile->packet_memory, tile->packet_memory_sz, tile->packet_iova, caps->max_mr_size ) ||
    1589           0 :                      !fd_uverbs_create_tx_qp( uverbs, tx_qp, pd, tx_cq_handle, uar_page_id )                                                     ||
    1590           0 :                      !fd_uverbs_start_tx_qp( uverbs, tx_qp )                                                                                     ||
    1591           0 :                      !fd_uverbs_create_rx_wq( uverbs, rx_wq, pd, rx_cq_handle )                                                                  ||
    1592           0 :                      !fd_uverbs_start_rx_wq( uverbs, rx_wq ) ) ) {
    1593           0 :       return NULL;
    1594           0 :     }
    1595           0 :     tx_qp->tx_inline_hdr_sz = caps->eth_min_inline_hdr_sz;
    1596           0 :   }
    1597             : 
    1598           0 :   if( FD_UNLIKELY( !fd_uverbs_create_rwq_ind_table( &rwq_ind_table_handle, uverbs, tiles, tile_cnt ) ||
    1599           0 :                    !fd_uverbs_create_rss_qp( uverbs, outer_rss_qp, pd, rwq_ind_table_handle,
    1600           0 :                                              FD_MLX5_RX_HASH_IPV4_UDP, 0U ) ||
    1601           0 :                    !fd_uverbs_create_rss_qp( uverbs, gre_rss_qp, pd, rwq_ind_table_handle,
    1602           0 :                                              FD_MLX5_RX_HASH_IPV4_UDP | FD_MLX5_RX_HASH_INNER,
    1603           0 :                                              FD_MLX5_QP_TUNNEL_OFFLOADS ) ) ) return NULL;
    1604           0 :   return outer_rss_qp;
    1605           0 : }

Generated by: LCOV version 1.14