简介
IB设备通过ib_core驱动注册后(lsmod|grep ib), 在sysfs下生成的目录树参考如下:
代码语言:bash复制mlx:
[root@s11 mlx5_0]# tree -L 10
.
├── board_id
├── device -> ../../../0000:08:00.0
├── fw_pages
├── fw_ver
├── hca_type
├── hw_rev
├── mad_sa_cc
│ └── 1
│ ├── drops
│ ├── max_outstanding
│ ├── queue_size
│ └── time_sa_mad
├── mr_cache
│ ├── 10
│ │ ├── cur
│ │ ├── limit
│ │ ├── miss
│ │ └── size
│ ├── 11
│ │ ├── cur
│ │ ├── limit
│ │ ├── miss
│ │ └── size
│ ├── 12
│ │ ├── cur
│ │ ├── limit
│ │ ├── miss
│ │ └── size
│ ├── 13
│ │ ├── cur
│ │ ├── limit
│ │ ├── miss
│ │ └── size
│ ├── 14
│ │ ├── cur
│ │ ├── limit
│ │ ├── miss
│ │ └── size
│ ├── 15
│ │ ├── cur
│ │ ├── limit
│ │ ├── miss
│ │ └── size
│ ├── 16
│ │ ├── cur
│ │ ├── limit
│ │ ├── miss
│ │ └── size
│ ├── 17
│ │ ├── cur
│ │ ├── limit
│ │ ├── miss
│ │ └── size
│ ├── 18
│ │ ├── cur
│ │ ├── limit
│ │ ├── miss
│ │ └── size
│ ├── 19
│ │ ├── cur
│ │ ├── limit
│ │ ├── miss
│ │ └── size
│ ├── 2
│ │ ├── cur
│ │ ├── limit
│ │ ├── miss
│ │ └── size
│ ├── 20
│ │ ├── cur
│ │ ├── limit
│ │ ├── miss
│ │ └── size
│ ├── 21
│ │ ├── cur
│ │ ├── limit
│ │ ├── miss
│ │ └── size
│ ├── 22
│ │ ├── cur
│ │ ├── limit
│ │ ├── miss
│ │ └── size
│ ├── 23
│ │ ├── cur
│ │ ├── limit
│ │ ├── miss
│ │ └── size
│ ├── 3
│ │ ├── cur
│ │ ├── limit
│ │ ├── miss
│ │ └── size
│ ├── 4
│ │ ├── cur
│ │ ├── limit
│ │ ├── miss
│ │ └── size
│ ├── 5
│ │ ├── cur
│ │ ├── limit
│ │ ├── miss
│ │ └── size
│ ├── 6
│ │ ├── cur
│ │ ├── limit
│ │ ├── miss
│ │ └── size
│ ├── 7
│ │ ├── cur
│ │ ├── limit
│ │ ├── miss
│ │ └── size
│ ├── 8
│ │ ├── cur
│ │ ├── limit
│ │ ├── miss
│ │ └── size
│ ├── 9
│ │ ├── cur
│ │ ├── limit
│ │ ├── miss
│ │ └── size
│ ├── rel_imm
│ └── rel_timeout
├── node_desc
├── node_guid
├── node_type
├── ports
│ └── 1
│ ├── cap_mask
│ ├── cm_rx_duplicates
│ │ ├── apr
│ │ ├── drep
│ │ ├── dreq
│ │ ├── lap
│ │ ├── mra
│ │ ├── rej
│ │ ├── rep
│ │ ├── req
│ │ ├── rtu
│ │ ├── sidr_rep
│ │ └── sidr_req
│ ├── cm_rx_msgs
│ │ ├── apr
│ │ ├── drep
│ │ ├── dreq
│ │ ├── lap
│ │ ├── mra
│ │ ├── rej
│ │ ├── rep
│ │ ├── req
│ │ ├── rtu
│ │ ├── sidr_rep
│ │ └── sidr_req
│ ├── cm_tx_msgs
│ │ ├── apr
│ │ ├── drep
│ │ ├── dreq
│ │ ├── lap
│ │ ├── mra
│ │ ├── rej
│ │ ├── rep
│ │ ├── req
│ │ ├── rtu
│ │ ├── sidr_rep
│ │ └── sidr_req
│ ├── cm_tx_retries
│ │ ├── apr
│ │ ├── drep
│ │ ├── dreq
│ │ ├── lap
│ │ ├── mra
│ │ ├── rej
│ │ ├── rep
│ │ ├── req
│ │ ├── rtu
│ │ ├── sidr_rep
│ │ └── sidr_req
│ ├── counters
│ │ ├── excessive_buffer_overrun_errors
│ │ ├── link_downed
│ │ ├── link_error_recovery
│ │ ├── local_link_integrity_errors
│ │ ├── multicast_rcv_packets
│ │ ├── multicast_xmit_packets
│ │ ├── port_rcv_constraint_errors
│ │ ├── port_rcv_data
│ │ ├── port_rcv_errors
│ │ ├── port_rcv_packets
│ │ ├── port_rcv_remote_physical_errors
│ │ ├── port_rcv_switch_relay_errors
│ │ ├── port_xmit_constraint_errors
│ │ ├── port_xmit_data
│ │ ├── port_xmit_discards
│ │ ├── port_xmit_packets
│ │ ├── port_xmit_wait
│ │ ├── symbol_error
│ │ ├── unicast_rcv_packets
│ │ ├── unicast_xmit_packets
│ │ └── VL15_dropped
│ ├── gid_attrs
│ │ ├── ndevs
│ │ │ ├── 0
│ │ │ ├── 1
...
│ │ │ ├── 98
│ │ │ └── 99
│ │ └── types
│ │ ├── 0
│ │ ├── 1
...
│ │ └── 99
│ ├── gids
│ │ ├── 0
│ │ ├── 1
...
│ │ └── 99
│ ├── has_smi
│ ├── hw_counters
│ │ ├── duplicate_request
│ │ ├── implied_nak_seq_err
│ │ ├── lifespan
│ │ ├── local_ack_timeout_err
│ │ ├── np_cnp_sent
│ │ ├── np_ecn_marked_roce_packets
│ │ ├── out_of_buffer
│ │ ├── out_of_sequence
│ │ ├── packet_seq_err
│ │ ├── req_cqe_error
│ │ ├── req_cqe_flush_error
│ │ ├── req_remote_access_errors
│ │ ├── req_remote_invalid_request
│ │ ├── resp_cqe_error
│ │ ├── resp_cqe_flush_error
│ │ ├── resp_local_length_error
│ │ ├── resp_remote_access_errors
│ │ ├── rnr_nak_retry_err
│ │ ├── roce_adp_retrans
│ │ ├── roce_adp_retrans_to
│ │ ├── roce_slow_restart
│ │ ├── roce_slow_restart_cnps
│ │ ├── roce_slow_restart_trans
│ │ ├── rp_cnp_handled
│ │ ├── rp_cnp_ignored
│ │ ├── rx_atomic_requests
│ │ ├── rx_dct_connect
│ │ ├── rx_icrc_encapsulated
│ │ ├── rx_read_requests
│ │ └── rx_write_requests
│ ├── lid
│ ├── lid_mask_count
│ ├── link_layer
│ ├── phys_state
│ ├── pkeys
│ │ └── 0
│ ├── rate
│ ├── sm_lid
│ ├── sm_sl
│ └── state
├── power
│ ├── async
│ ├── autosuspend_delay_ms
│ ├── control
│ ├── runtime_active_kids
│ ├── runtime_active_time
│ ├── runtime_enabled
│ ├── runtime_status
│ ├── runtime_suspended_time
│ └── runtime_usage
├── reg_pages
├── subsystem -> ../../../../../../class/infiniband
├── sys_image_guid
├── tc
│ └── 1
│ └── traffic_class
├── ttl
│ └── 1
│ └── ttl
└── uevent
45 directories, 987 files
[root@s11 mlx5_0]#
rdma verbs编程中会使用到一些verbs接口, 如下:
代码语言:c复制ibv_advise_mr
ibv_alloc_dm
ibv_open_device
ibv_get_device_list
ibv_query_device_ex
ibv_get_device_name
ibv_req_notify_cq
ibv_query_gid
ibv_memcpy_to_dm
ibv_get_cq_event
ibv_start_poll
ibv_end_poll
ibv_next_poll
ibv_wc_read_completion_ts
ibv_ack_cq_events
ibv_free_device_list
ibv_cq_ex_to_cq
ibv_modify_qp
...
首先使用的就是查询设备列表和打开设备, 下文以ceph和mlx5用户态和内核态驱动为例, 详解该调用
ceph示例
以ceph server的rdma编程为例, 服务端监听客户端连接前, 首先就查询设备列表(ibv_get_device_list)以及打开设备(ibv_open_device), 获得上下文
代码语言:c复制server.start()
msgr->bind(addr)
AsyncMessenger::bind
bindv -> int r = p->bind
int Processor::bind
listen_sockets.resize
conf->ms_bind_retry_count 3次重试
worker->center.submit_to lambda []()->void 匿名函数
c->in_thread()
pthread_equal(pthread_self(), owner) 本线程
C_submit_event<func> event(std::move(f), false) f=listen
void do_request -> f() -> listen -> worker->listen(listen_addr, k, opts, &listen_sockets[k]) -> int RDMAWorker::listen 由事件触发执行
ib->init() -> void Infiniband::init
new DeviceList(cct)
ibv_get_device_list 4网口
if (cct->_conf->ms_async_rdma_cm)
new Device(cct, device_list[i]) -> Device::Device
ibv_open_device
ibv_get_device_name
ibv_query_device 参考设备属性: device_attr
get_device 根据配置的设备名在设备列表中查询, 默认取第一个, 如: mlx5_0
binding_port -> void Device::binding_port
new Port(cct, ctxt, port_id) 端口ID从1开始 -> Port::Port
ibv_query_port(ctxt, port_num, &port_attr)
ibv_query_gid(ctxt, port_num, gid_idx, &gid)
ib_physical_port = device->active_port->get_port_num() 获取物理端口
new ProtectionDomain(cct, device) -> Infiniband::ProtectionDomain::ProtectionDomain -> ibv_alloc_pd(device->ctxt)
support_srq = cct->_conf->ms_async_rdma_support_srq 共享接收队列srq
rx_queue_len = device->device_attr.max_srq_wr 最终为4096
tx_queue_len = device->device_attr.max_qp_wr - 1 发送队列为beacon保留1个WR, 如:1024 1_K 重载操作符
device->device_attr.max_cqe 设备允许 4194303 完成事件
memory_manager = new MemoryManager(cct, device, pd) -> Infiniband::MemoryManager::MemoryManager 128K -> mem_pool -> boost::pool
memory_manager->create_tx_pool(cct->_conf->ms_async_rdma_buffer_size, tx_queue_len) -> void Infiniband::MemoryManager::create_tx_pool
send = new Cluster(*this, size)
send->fill(tx_num) -> int Infiniband::MemoryManager::Cluster::fill
base = (char*)manager.malloc(bytes) -> void* Infiniband::MemoryManager::malloc -> std::malloc(size) 标准分配或分配大页(huge_pages_malloc)
ibv_reg_mr 注册内存
new(chunk) Chunk
free_chunks.push_back(chunk)
create_shared_receive_queue
ibv_create_srq
post_chunks_to_rq -> int Infiniband::post_chunks_to_rq
chunk = get_memory_manager()->get_rx_buffer() -> return reinterpret_cast<Chunk *>(rxbuf_pool.malloc())
ibv_post_srq_recv
dispatcher->polling_start() -> void RDMADispatcher::polling_start
ib->get_memory_manager()->set_rx_stat_logger(perf_logger) -> void PerfCounters::set
tx_cc = ib->create_comp_channel(cct) -> Infiniband::CompletionChannel* Infiniband::create_comp_channel -> new Infiniband::CompletionChannel
tx_cq = ib->create_comp_queue(cct, tx_cc)
cq->init() -> int Infiniband::CompletionChannel::init
ibv_create_comp_channel 创建完成通道 -> NetHandler(cct).set_nonblock(channel->fd) 设置非阻塞
t = std::thread(&RDMADispatcher::polling, this) 启动polling线程 rdma-polling -> void RDMADispatcher::polling
tx_cq->poll_cq(MAX_COMPLETIONS, wc)
handle_tx_event -> tx_chunks.push_back(chunk) -> post_tx_buffer
tx -> void RDMAWorker::handle_pending_message()
handle_rx_event -> void RDMADispatcher::handle_rx_event
conn->post_chunks_to_rq(1) 向接收队列补一个内存块(WR) -> int Infiniband::post_chunks_to_rq
ibv_post_srq_recv | ibv_post_recv
polled[conn].push_back(*response)
qp->remove_rq_wr(chunk)
chunk->clear_qp()
pass_wc -> void RDMAConnectedSocketImpl::pass_wc(std::vector<ibv_wc> &&v) -> notify() -> void RDMAConnectedSocketImpl::notify
eventfd_write(notify_fd, event_val) -> eventfd_read(notify_fd, &event_val) <- ssize_t RDMAConnectedSocketImpl::read <- process
new RDMAServerSocketImpl(cct, ib, dispatcher, this, sa, addr_slot)
int r = p->listen(sa, opt) -> int RDMAServerSocketImpl::listen
server_setup_socket = net.create_socket(sa.get_family(), true) -> socket_cloexec
net.set_nonblock
net.set_socket_options
::bind(server_setup_socket, sa.get_sockaddr(), sa.get_sockaddr_len()) 系统调用
::listen backlog=512
*sock = ServerSocket(std::unique_ptr<ServerSocketImpl>(p))
cond.notify_all() -> 通知等待的线程
dispatch_event_external -> void EventCenter::dispatch_event_external
external_events.push_back(e)
wakeup()
write(notify_send_fd, &buf, sizeof(buf)) buf=c -> notify_receive_fd, 唤醒 epoll_wait
event.wait()
msgr->add_dispatcher_head(&dispatcher)
ready()
p->start() -> void Processor::start()
worker->center.create_file_event listen_handler -> pro->accept() -> void Processor::accept()
msgr->start() -> int AsyncMessenger::start()
msgr->wait() -> void AsyncMessenger::wait()
获取设备列表(ibv_get_device_list)
代码路径: rdma-core, libibverbs/device.c
代码语言:c复制调用栈
ibv_get_device_list -> 返回设备列表
ibverbs_get_device_list -> verbs: 刷新缓存的 ibv_device 列表 问题 ======== 目前,libibverbs 仅在第一次调用 ibv_get_device_list 时构建缓存的 ibv_device 列表,因此无论硬件是否发生变化,该列表都不会更新。 系统。 解决方案======== 修改 ibv_get_device_list() 的实现,以便连续的调用将以与今天相同的方式重新扫描 sysfs,以便每次创建一个新的 ibv_device 列表。 为此,将缓存的设备列表更改为真正的链表而不是动态数组。 我们如何识别新设备•============================= 根据 /sys/class/infiniband_verbs/ 的时间戳创建来识别同一设备 uverbs%d/ibdev。 我们使用 stat 系统调用获取文件状态,并使用 st_mtime 字段来实现此目的。 当我们重新扫描 sysfs 设备时,我们会检查每个 sysfs 设备是否已经在上次扫描中,如果没有,则分配新的 ibv_device 并将其添加到缓存设备列表中。 本系列的下一个补丁处理设备不再使用的情况。 注意:此补丁根据上面 verbs_device 结构体注释中的要求更改了 IBVERBS_PRIVATE 符号
find_sysfs_devs_nl -> verbs:使用 netlink 来发现 uverbs 设备而不是 sysfs,netlink 查询为我们提供了 ibdev idx,它对于设备来说大多是唯一的,并且在设备重命名时充当稳定的 id。 如果在 verbs 用户操作期间重命名设备,这会使 verbs 更加健壮。 此外,netlink 仅返回在进程的网络命名空间中实际可见的设备,从而简化了发现过程
rdmanl_socket_alloc
nl_socket_alloc
nl_socket_disable_auto_ack
nl_socket_disable_msg_peek
nl_connect(nl, NETLINK_RDMA)
rdmanl_get_devices find_sysfs_devs_nl_cb
nl_send_simple RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET) NLM_F_DUMP
nl_socket_modify_err_cb
nl_socket_modify_cb
nl_recvmsgs_default
find_uverbs_nl find_uverbs_sysfs try_access_device
rdmanl_get_chardev(nl, sysfs_dev->ibdev_idx, "uverbs", find_uverbs_nl_cb
nlmsg_alloc_simple RDMA_NLDEV_CMD_GET_CHARDEV
...
check_snprintf(path, sizeof(path), "%s/device/infiniband_verbs",
setup_sysfs_uverbs
abi_version
...
stat(devpath, &cdev_stat)
nl_socket_free
find_sysfs_devs
%s/class/infiniband_verbs
ibv_read_sysfs_file_at(uv_dirfd, "ibdev",
check_abi_version
"class/infiniband_verbs/abi_version"
try_all_drivers
try_drivers
match_driver_id -> VERBS_MATCH_SENTINEL -> 动词:提供通用代码以将提供程序与内核设备进行匹配 根据表检查 PCI 设备基本上在每个驱动程序中都是重复的。 遵循内核的模式,并将匹配表附加到 verbs_device_ops 驱动程序入口点,该入口点描述提供程序可以处理的所有内核设备,并使核心代码与该表匹配。 驱动程序获取一个指向与分配函数中匹配的表条目的指针。 此实现基于模式别名,而不是读取 PCI 特定供应商和设备文件。 modalias 让我们支持 ACPI 和 OF 提供程序,并提供了一个简单的路径,使提供程序根据其支持的 modalias 字符串(如内核)进行需求加载
try_driver
match_device
alloc_device -> mlx5_device_alloc
dev->transport_type = IBV_TRANSPORT_IB -> 传输类型
...
load_drivers
dlhandle = dlopen(so_name, RTLD_NOW)
ibverbs_device_hold
该调用利用NetLink通信机制, 查询过滤IB设备, 并返回给用户态
内核态参考流程(将ib_core注册的设备(devices)返回用户态)
代码语言:c复制static const struct rdma_nl_cbs nldev_cb_table[RDMA_NLDEV_NUM_OPS] = {
[RDMA_NLDEV_CMD_GET] = {
.doit = nldev_get_doit,
.dump = nldev_get_dumpit,
},
nldev_get_doit
nlmsg_parse_deprecated
nla_get_u32
ib_device_get_by_index -> RDMA/core:扩展 ib_device_get_by_index 以获取网络命名空间,扩展 ib_device_get_by_index() API 以检查设备对网络命名空间的访问,以提供 netlink 命令。 还对 dumpit 命令强制执行 net ns 检查,这些命令迭代所有注册的 rdma 设备并且不调用 ib_device_get_by_index()
device = xa_load(&devices, index) -> 三个 rwsem 锁(devices、clients、client_data)中的每一个都保护同名的 xarray。 具体来说,它允许调用者断言 MARK 在锁定下将/不会改变,并且对于设备和客户端而言,xarray 中的值仍然是有效的指针。 MARK 的更改与对象状态相关联,因此持有锁并测试 MARK 也断言所包含的对象处于某种状态。 这用于构建两阶段注册/取消注册流程,其中对象可以继续位于 xarray 中,即使它们仍在注册/取消注册过程中。 xarray本身提供了额外的锁定,以及可重启的迭代,这也是值得依赖的。 锁不应该嵌套,但 client_data 除外,它允许嵌套在其他两个锁的读取端下方。 devices_rwsem 还保护设备名称列表,设备名称的任何更改或分配也必须保留写入端以保证名称唯一。 */ devices 包含已分配名称的设备。 设备可能未注册。 关心注册状态的用户需要在设备上调用 ib_device_try_get() 以确保其已注册,并在所需的时间内保持注册状态 -> this devices from register
rdma_dev_access_netns
net_eq(read_pnet(&dev->coredev.rdma_net), net)
ib_device_try_get -> RDMA/device:公开 ib_device_try_get((),事实证明,未来的补丁现在非常广泛地需要此功能,而不仅仅是 netlink,因此提供两个全局函数来管理注册锁引用计数。这也将锁变为 1 的点移至以内 ib_register_device() 使得公共 API 的语义非常健全和清晰,在仅分配但尚未注册的设备上调用 ib_device_try_get() 将失败。 -> ib_device_try_get:持有注册锁,设备:要锁定的设备处于活动注册锁下的设备无法取消注册。 只有在完全注册的设备上才能获得注册锁,否则该函数返回 false。 仅当需要设备仍处于注册状态时才需要注册锁。 仅要求设备指针有效的用途应使用 get_device(&ibdev->dev) 来保存内存
refcount_inc_not_zero(&dev->refcount)
nlmsg_new
rdma_nl_unicast(sock_net(skb->sk), msg, NETLINK_CB(skb).portid)
初始化ibverbs, 与ibv_fork_init, 红黑树等结合, 设置日志调试等
代码语言:c复制ibv_get_device_list
ibverbs_init -> verbs:修改 init 的排序方式,将检查 uverbs ABI 移至从内核加载设备列表之后。 当通过 netlink 加载时,我们可以假设 ABI 是 6,而无需转到 sysfs。 这允许我们使用内核依赖项来初始化库,并且错误(例如缺少内核支持)是从 ibverbs_get_device_list() 而不是 ibverbs_init() 返回的。 如果内核支持 netlink,ibverbs 在启动期间不再读取 /sys/ 路径
check_env("RDMAV_FORK_SAFE") || check_env("IBV_FORK_SAFE")
ibv_fork_init -> libibverbs/memory.c
getenv("RDMAV_HUGEPAGES_SAFE") -> 允许在多次调用 ibv_fork_init 中使用大页,设置环境变量 RDMAV_HUGEPAGES_SAFE 告诉库检查内核用于内存区域的基础页大小。 如果应用程序直接或通过 libhugetlbfs 等库间接使用大页,则这是必需的。 该变量的检查是在第一次调用 ibv_fork_init 时执行的。 这会导致具有多个底层库的复杂应用程序出现不可预测的行为。 提议的更改将允许支持大页面,而不依赖于 ibv_fork_init 调用顺序
if (mm_root)
get_page_size
"/proc/%d/smaps", pid
smaps_page_size
KernelPageSize
madvise(tmp_aligned, size, MADV_DONTFORK) -> 在 ibv_fork_init() 和 madvise 跟踪中处理大页,当在 libibverbs 中启用 fork 支持时,将为注册为内存区域的每个内存页调用 madvise() 。 传递给 madvise() 的内存范围必须是页对齐的,并且大小必须是页大小的倍数。 libibverbs 使用 sysconf(_SC_PAGESIZE) 找出系统页面大小,并根据此页面大小对传递给 reg_mr() 的所有范围进行舍入。 当 libhugetlbfs 中的内存传递给 reg_mr() 时,这不起作用,因为该内存范围的页面大小可能不同(例如 16MB)。 因此 libibverbs 必须使用巨大的页面大小来计算 madvise 的页面对齐范围。 由于在预加载 libhugetlbfs 时向应用程序“在后台”提供大页面,因此应用程序不知道何时注册大页面或普通页面。 要解决此问题,请检测 libibverbs 中大页面的使用,并根据大页面大小调整传递给 madvise 的内存范围。 通过观察 madvise() 失败来确定给定内存范围的页面大小已被证明是不可靠的。 因此,我们引入 RDMAV_HUGEPAGES_SAFE 环境变量,让用户决定是否应在每次 reg_mr() 调用时检查页面大小。 这要求用户了解正在运行的应用程序是否使用大页面。 我没有添加额外的 API 调用来启用此功能,因为应用程序可以使用 setenv() ibv_fork_init() 来启用检查代码中的大页面
mm_root->color = IBV_BLACK -> 初始化红黑树(会影响性能)
...
verbs_allow_disassociate_destroy -> verbs:引入ENV来控制销毁命令时的EIO,引入环境变量(即RDMAV_ALLOW_DISASSOC_DESTROY)来控制销毁命令返回的代码。 一旦设置完毕,任何将从内核获取 EIO 的销毁命令都将被视为成功。 在这种情况下,该对象的底层内核资源必须已通过分离机制销毁,并且用户空间驱动程序和应用程序也可以安全地清理其资源。 这是为了防止用户空间区域的内存泄漏
ibv_get_sysfs_path
check_memlock_limit
rlim.rlim_cur <= 32768
verbs_set_log_level -> verbs:添加通用日志记录 API,调试打印机制在调试应用程序故障时非常有用。 此补丁添加了一个通用 API,可供所有提供商使用并替换特定于提供商的对应项。 调试消息通过名为 VERBS_LOG_LEVEL 的环境变量进行控制,其中值指示应启用哪些打印: enum { VERBS_LOG_LEVEL_NONE, VERBS_LOG_ERR, VERBS_LOG_WARN, VERBS_LOG_INFO, VERBS_LOG_DEBUG, }; 例如,要启用警告级别或更高级别的打印,VERBS_LOG_LEVEL 应设置为 2。输出应写入 VERBS_LOG_FILE 环境变量中提供的文件。 当在调试模式下编译库并且未提供文件时,输出应写入 stderr。 对于数据路径流,附加 if 语句的开销很重要,可以使用 verbs_*_datapath() 宏,该宏将在编译库以供发布时编译出来, 参考: https://github.com/ssbandjl/rdma-core/commit/5c3514eb87ca86e817a8b610ada3200bbcdde6f4, 编译开关: Enabling debug prints, 可作为日志实现的一个参考
verbs_set_log_file
ibverbs_get_device_list -> verbs: 刷新缓存的 ibv_device 列表,问题 ======== 目前,libibverbs 仅在第一次调用 ibv_get_device_list 时构建缓存的 ibv_device 列表,因此无论是否有硬件更改,该列表都不会更新 在系统中。 解决方案======== 修改 ibv_get_device_list() 的实现,以便连续的调用将以与今天相同的方式重新扫描 sysfs,以便每次创建一个新的 ibv_device 列表。 为此,将缓存的设备列表更改为真正的链表而不是动态数组。 我们如何识别新设备============================= 根据 /sys/class/infiniband_verbs/ 的时间戳创建来识别同一设备 uverbs%d/ibdev。 我们使用 stat 系统调用获取文件状态,并使用 st_mtime 字段来实现此目的。 当我们重新扫描 sysfs 设备时,我们会检查每个 sysfs 设备是否已经在上次扫描中,如果没有,则分配新的 ibv_device 并将其添加到缓存设备列表中。 本系列的下一个补丁处理设备不再使用的情况。 注意:此补丁根据上面 verbs_device 结构体注释中的要求更改了 IBVERBS_PRIVATE 符号 -> verbs: 整理 ibverbs_get_device_list ,现在我们有了 ccan 列表,这里的逻辑可以大大简化。 消除令人困惑的used和have_driver值,而只是在我们运行进程时从sysfs列表中删除项目。 这直接保证了发现的 sysfs 项仅处理一次,并使 sysfs 指针的生命周期更加清晰
find_sysfs_devs_nl(&sysfs_list) -> verbs:使用 netlink 来发现 uverbs 设备而不是 sysfs,netlink 查询为我们提供了 ibdev idx,它对于设备来说大多是唯一的,并且在设备重命名时充当稳定的 id。 如果在 verbs 用户操作期间重命名设备,这会使 verbs 更加健壮。 此外,netlink 仅返回在进程的网络命名空间中实际可见的设备,从而简化了发现过程
rdmanl_socket_alloc
rdmanl_get_devices(nl, find_sysfs_devs_nl_cb, tmp_sysfs_dev_list)
list_for_each_safe (tmp_sysfs_dev_list
find_uverbs_nl(nl, dev) && find_uverbs_sysfs(dev)
try_access_device(dev)
stat(devpath, &cdev_stat)
nl_socket_free(nl)
find_sysfs_devs(&sysfs_list)
check_abi_version
list_for_each_safe(device_list
same_sysfs_dev
ibverbs_device_put
try_all_drivers
load_drivers()
try_all_drivers
return num_devices
ibverbs_device_hold(l[i]) -> verbs: 避免 ibv_device 内存泄漏,现在,每次调用 ibv_get_device_list 时都会刷新 ibv_device 列表,因此我们需要从之前的扫描中释放不再绑定的设备,否则可能会导致 ibv_device 结构的内存泄漏。 仅当用户不再使用 ibv_device 的内存时,我们才能释放它。 我们如何识别设备是否仍在使用============================================ 我们 将引用计数添加到动词设备结构中。 在以下情况下,该引用计数会增加: 设置为 1 以使该设备位于列表中,直到应将其删除为止。 b. 用户调用 ibv_get_device_list。 C。 用户调用 ibv_open_device。 在以下情况下,引用计数会减少: 用户调用 ibv_free_device_list。 b. 用户调用 ibv_close_device。 C。 设备不再存在于 sysfs 中。 当引用计数减少到零时,设备将被释放。 为了释放 ibv_device 结构,我们将 uninit_device 回调函数添加到 verbs_device_ops
verbs_get_device
container_of(dev, struct verbs_device, device)
atomic_fetch_add(&verbs_device->refcount, 1) -> 加引用
*num = num_devices
打开设备(ibv_open_device)
代码语言:c复制调用栈:
ibv_open_device -> LATEST_SYMVER_FUNC(ibv_open_device -> verbs_open_device
verbs_get_device
cmd_fd = open_cdev -> verbs:启用 verbs_open_device() 以在非 sysfs 设备上工作,从 mlx5 开始,启用 verbs_open_device() 通过 VFIO 在非 sysfs 设备上工作。 verbs_sysfs_dev 上的任何其他 API 都应该彻底失败
verbs_device->ops->alloc_context -> mlx5_alloc_context -> verbs:始终分配 verbs_context,现在所有内容都在一棵树中,我们可以修改旧版 init_context 路径,通过在所有提供程序的包装结构中将 ibv_context 交换为 verbs_context 来始终分配 verbs_context。 为了保持提供者差异最小,这个补丁同时做了几件事: - 引入 verbs_init_and_alloc_context() 宏。 这会为每个驱动程序分配、清零并初始化 verbs_context。 值得注意的是,这个新宏在失败时根据需要正确设置 errno。 - 从所有驱动程序、calloc、malloc、memset、cmd_fd 和设备分配中删除样板文件 - 与 verbs_init 方案一起必然出现 verbs_uninit 方案,该方案将 uninit 调用降低到提供者而不是公共代码中。 这使我们能够在 init 错误路径上正确地 uninit。 总之,这遵循我们在内核中看到的相当成功的模式,用于对子系统进行驱动程序初始化。 此外,这会将 ibv_cmd_get_context 更改为接受 verbs_context,因为大多数调用者现在都提供该内容,这使得差异较小。 这使得整个流程更加一致,并且可以让我们消除 init_context 流程
mlx5_init_context
verbs_init_and_alloc_context -> _verbs_init_and_alloc_context
verbs_init_context
ibverbs_device_hold
verbs_set_ops(context_ex, &verbs_dummy_ops) -> rdma verbs操作 -> 在上下文中设置 -> 如果更改,则必须更改 PRIVATE IBVERBS_PRIVATE_ 符号。 这是驱动程序可以支持的每个操作的联合。 如果向此结构添加新元素,则 verbs_dummy_ops 也必须更新。 保持排序
SET_OP -> 设置一系列操作
...
use_ioctl_write = has_ioctl_write(context)
mlx5_open_debug_file
mlx5_set_debug_mask
single_threaded_app
get_uar_info
get_total_uuars
get_num_low_lat_uuars
mlx5_cmd_get_context
ibv_cmd_get_context
...
execute_write_bufs(context, IB_USER_VERBS_CMD_GET_CONTEXT
mlx5_set_context
adjust_uar_info
cl_qmap_init
mlx5_mmap
mlx5_read_env
verbs_set_ops(v_ctx, &mlx5_ctx_common_ops)
mlx5_query_device_ctx
get_hca_general_caps
mlx5dv_devx_general_cmd MLX5_CMD_OP_QUERY_HCA_CAP
ibv_cmd_query_device_any
execute_cmd_write_ex IB_USER_VERBS_EX_CMD_QUERY_DEVICE
execute_cmd_write(context, IB_USER_VERBS_CMD_QUERY_DEVICE -> 通过命令执行框架, 转到内核态
mlx5_set_singleton_nc_uar
set_lib_ops
ibv_cmd_alloc_async_fd
打开设备时, 通过SET_OP给上下文设置(绑定)一些列verbs操作(如下), 设置调试文件, 掩码, 通过查询寄存器(如下:MLX5_CMD_OP_QUERY_HCA_CAP)获取设备硬件能力(集), 赋值并返回给用户态
mlx5上下文通用操作函数:
代码语言:c复制设置rdma verbs操作:
static const struct verbs_context_ops mlx5_ctx_common_ops = {
.query_port = mlx5_query_port,
.alloc_pd = mlx5_alloc_pd,
.async_event = mlx5_async_event,
.dealloc_pd = mlx5_free_pd,
.reg_mr = mlx5_reg_mr,
.reg_dmabuf_mr = mlx5_reg_dmabuf_mr,
.rereg_mr = mlx5_rereg_mr,
.dereg_mr = mlx5_dereg_mr,
.alloc_mw = mlx5_alloc_mw,
.dealloc_mw = mlx5_dealloc_mw,
.bind_mw = mlx5_bind_mw,
.create_cq = mlx5_create_cq,
.poll_cq = mlx5_poll_cq,
.req_notify_cq = mlx5_arm_cq,
.cq_event = mlx5_cq_event,
.resize_cq = mlx5_resize_cq,
.destroy_cq = mlx5_destroy_cq,
.create_srq = mlx5_create_srq,
.modify_srq = mlx5_modify_srq,
.query_srq = mlx5_query_srq,
.destroy_srq = mlx5_destroy_srq,
.post_srq_recv = mlx5_post_srq_recv,
.create_qp = mlx5_create_qp,
.query_qp = mlx5_query_qp,
.modify_qp = mlx5_modify_qp,
.destroy_qp = mlx5_destroy_qp,
.post_send = mlx5_post_send,
.post_recv = mlx5_post_recv,
.create_ah = mlx5_create_ah,
.destroy_ah = mlx5_destroy_ah,
.attach_mcast = mlx5_attach_mcast,
.detach_mcast = mlx5_detach_mcast,
.advise_mr = mlx5_advise_mr,
.alloc_dm = mlx5_alloc_dm,
.alloc_parent_domain = mlx5_alloc_parent_domain,
.alloc_td = mlx5_alloc_td,
.attach_counters_point_flow = mlx5_attach_counters_point_flow,
.close_xrcd = mlx5_close_xrcd,
.create_counters = mlx5_create_counters,
.create_cq_ex = mlx5_create_cq_ex,
.create_flow = mlx5_create_flow,
.create_flow_action_esp = mlx5_create_flow_action_esp,
.create_qp_ex = mlx5_create_qp_ex,
.create_rwq_ind_table = mlx5_create_rwq_ind_table,
.create_srq_ex = mlx5_create_srq_ex,
.create_wq = mlx5_create_wq,
.dealloc_td = mlx5_dealloc_td,
.destroy_counters = mlx5_destroy_counters,
.destroy_flow = mlx5_destroy_flow,
.destroy_flow_action = mlx5_destroy_flow_action,
.destroy_rwq_ind_table = mlx5_destroy_rwq_ind_table,
.destroy_wq = mlx5_destroy_wq,
.free_dm = mlx5_free_dm,
.get_srq_num = mlx5_get_srq_num,
.import_dm = mlx5_import_dm,
.import_mr = mlx5_import_mr,
.import_pd = mlx5_import_pd,
.modify_cq = mlx5_modify_cq,
.modify_flow_action_esp = mlx5_modify_flow_action_esp,
.modify_qp_rate_limit = mlx5_modify_qp_rate_limit,
.modify_wq = mlx5_modify_wq,
.open_qp = mlx5_open_qp,
.open_xrcd = mlx5_open_xrcd,
.post_srq_ops = mlx5_post_srq_ops,
.query_device_ex = mlx5_query_device_ex,
.query_ece = mlx5_query_ece,
.query_rt_values = mlx5_query_rt_values,
.read_counters = mlx5_read_counters,
.reg_dm_mr = mlx5_reg_dm_mr,
.alloc_null_mr = mlx5_alloc_null_mr,
.free_context = mlx5_free_context,
.set_ece = mlx5_set_ece,
.unimport_dm = mlx5_unimport_dm,
.unimport_mr = mlx5_unimport_mr,
.unimport_pd = mlx5_unimport_pd,
.query_qp_data_in_order = mlx5_query_qp_data_in_order,
};
设备寄存器, net/mlx5_core:使用硬件寄存器描述头文件,添加自动生成的头文件来描述硬件寄存器以及设置/获取值的宏集。 这些宏进行静态检查以避免溢出、处理字节顺序,并总体上提供了一种干净的命令编码方式。 目前头文件很小,我们将在使用宏时添加结构。 一些命令已从命令枚举中删除,因为当前不支持它们,将在支持可用时添加
代码语言:c复制mellanox 命令操作码, 寄存器:
enum {
MLX5_CMD_OP_QUERY_HCA_CAP = 0x100,
MLX5_CMD_OP_QUERY_ADAPTER = 0x101,
MLX5_CMD_OP_INIT_HCA = 0x102,
MLX5_CMD_OP_TEARDOWN_HCA = 0x103,
MLX5_CMD_OP_ENABLE_HCA = 0x104,
MLX5_CMD_OP_DISABLE_HCA = 0x105,
MLX5_CMD_OP_QUERY_PAGES = 0x107,
MLX5_CMD_OP_MANAGE_PAGES = 0x108,
MLX5_CMD_OP_SET_HCA_CAP = 0x109,
MLX5_CMD_OP_QUERY_ISSI = 0x10a,
MLX5_CMD_OP_SET_ISSI = 0x10b,
MLX5_CMD_OP_SET_DRIVER_VERSION = 0x10d,
MLX5_CMD_OP_QUERY_SF_PARTITION = 0x111,
MLX5_CMD_OP_ALLOC_SF = 0x113,
MLX5_CMD_OP_DEALLOC_SF = 0x114,
MLX5_CMD_OP_SUSPEND_VHCA = 0x115,
MLX5_CMD_OP_RESUME_VHCA = 0x116,
MLX5_CMD_OP_QUERY_VHCA_MIGRATION_STATE = 0x117,
MLX5_CMD_OP_SAVE_VHCA_STATE = 0x118,
MLX5_CMD_OP_LOAD_VHCA_STATE = 0x119,
MLX5_CMD_OP_CREATE_MKEY = 0x200,
MLX5_CMD_OP_QUERY_MKEY = 0x201,
MLX5_CMD_OP_DESTROY_MKEY = 0x202,
MLX5_CMD_OP_QUERY_SPECIAL_CONTEXTS = 0x203,
MLX5_CMD_OP_PAGE_FAULT_RESUME = 0x204,
MLX5_CMD_OP_ALLOC_MEMIC = 0x205,
MLX5_CMD_OP_DEALLOC_MEMIC = 0x206,
MLX5_CMD_OP_MODIFY_MEMIC = 0x207,
MLX5_CMD_OP_CREATE_EQ = 0x301,
MLX5_CMD_OP_DESTROY_EQ = 0x302,
MLX5_CMD_OP_QUERY_EQ = 0x303,
MLX5_CMD_OP_GEN_EQE = 0x304,
MLX5_CMD_OP_CREATE_CQ = 0x400,
MLX5_CMD_OP_DESTROY_CQ = 0x401,
MLX5_CMD_OP_QUERY_CQ = 0x402,
MLX5_CMD_OP_MODIFY_CQ = 0x403,
MLX5_CMD_OP_CREATE_QP = 0x500,
MLX5_CMD_OP_DESTROY_QP = 0x501,
MLX5_CMD_OP_RST2INIT_QP = 0x502,
MLX5_CMD_OP_INIT2RTR_QP = 0x503,
MLX5_CMD_OP_RTR2RTS_QP = 0x504,
MLX5_CMD_OP_RTS2RTS_QP = 0x505,
MLX5_CMD_OP_SQERR2RTS_QP = 0x506,
MLX5_CMD_OP_2ERR_QP = 0x507,
MLX5_CMD_OP_2RST_QP = 0x50a,
MLX5_CMD_OP_QUERY_QP = 0x50b,
MLX5_CMD_OP_SQD_RTS_QP = 0x50c,
MLX5_CMD_OP_INIT2INIT_QP = 0x50e,
MLX5_CMD_OP_CREATE_PSV = 0x600,
MLX5_CMD_OP_DESTROY_PSV = 0x601,
MLX5_CMD_OP_CREATE_SRQ = 0x700,
MLX5_CMD_OP_DESTROY_SRQ = 0x701,
MLX5_CMD_OP_QUERY_SRQ = 0x702,
MLX5_CMD_OP_ARM_RQ = 0x703,
MLX5_CMD_OP_CREATE_XRC_SRQ = 0x705,
MLX5_CMD_OP_DESTROY_XRC_SRQ = 0x706,
MLX5_CMD_OP_QUERY_XRC_SRQ = 0x707,
MLX5_CMD_OP_ARM_XRC_SRQ = 0x708,
MLX5_CMD_OP_CREATE_DCT = 0x710,
MLX5_CMD_OP_DESTROY_DCT = 0x711,
MLX5_CMD_OP_DRAIN_DCT = 0x712,
MLX5_CMD_OP_QUERY_DCT = 0x713,
MLX5_CMD_OP_ARM_DCT_FOR_KEY_VIOLATION = 0x714,
MLX5_CMD_OP_CREATE_XRQ = 0x717,
MLX5_CMD_OP_DESTROY_XRQ = 0x718,
MLX5_CMD_OP_QUERY_XRQ = 0x719,
MLX5_CMD_OP_ARM_XRQ = 0x71a,
MLX5_CMD_OP_QUERY_XRQ_DC_PARAMS_ENTRY = 0x725,
MLX5_CMD_OP_SET_XRQ_DC_PARAMS_ENTRY = 0x726,
MLX5_CMD_OP_QUERY_XRQ_ERROR_PARAMS = 0x727,
MLX5_CMD_OP_RELEASE_XRQ_ERROR = 0x729,
MLX5_CMD_OP_MODIFY_XRQ = 0x72a,
MLX5_CMD_OP_QUERY_ESW_FUNCTIONS = 0x740,
MLX5_CMD_OP_QUERY_VPORT_STATE = 0x750,
MLX5_CMD_OP_MODIFY_VPORT_STATE = 0x751,
MLX5_CMD_OP_QUERY_ESW_VPORT_CONTEXT = 0x752,
MLX5_CMD_OP_MODIFY_ESW_VPORT_CONTEXT = 0x753,
MLX5_CMD_OP_QUERY_NIC_VPORT_CONTEXT = 0x754,
MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT = 0x755,
MLX5_CMD_OP_QUERY_ROCE_ADDRESS = 0x760,
MLX5_CMD_OP_SET_ROCE_ADDRESS = 0x761,
MLX5_CMD_OP_QUERY_HCA_VPORT_CONTEXT = 0x762,
MLX5_CMD_OP_MODIFY_HCA_VPORT_CONTEXT = 0x763,
MLX5_CMD_OP_QUERY_HCA_VPORT_GID = 0x764,
MLX5_CMD_OP_QUERY_HCA_VPORT_PKEY = 0x765,
MLX5_CMD_OP_QUERY_VNIC_ENV = 0x76f,
MLX5_CMD_OP_QUERY_VPORT_COUNTER = 0x770,
MLX5_CMD_OP_ALLOC_Q_COUNTER = 0x771,
MLX5_CMD_OP_DEALLOC_Q_COUNTER = 0x772,
MLX5_CMD_OP_QUERY_Q_COUNTER = 0x773,
MLX5_CMD_OP_SET_MONITOR_COUNTER = 0x774,
MLX5_CMD_OP_ARM_MONITOR_COUNTER = 0x775,
MLX5_CMD_OP_SET_PP_RATE_LIMIT = 0x780,
MLX5_CMD_OP_QUERY_RATE_LIMIT = 0x781,
MLX5_CMD_OP_CREATE_SCHEDULING_ELEMENT = 0x782,
MLX5_CMD_OP_DESTROY_SCHEDULING_ELEMENT = 0x783,
MLX5_CMD_OP_QUERY_SCHEDULING_ELEMENT = 0x784,
MLX5_CMD_OP_MODIFY_SCHEDULING_ELEMENT = 0x785,
MLX5_CMD_OP_CREATE_QOS_PARA_VPORT = 0x786,
MLX5_CMD_OP_DESTROY_QOS_PARA_VPORT = 0x787,
MLX5_CMD_OP_ALLOC_PD = 0x800,
MLX5_CMD_OP_DEALLOC_PD = 0x801,
MLX5_CMD_OP_ALLOC_UAR = 0x802,
MLX5_CMD_OP_DEALLOC_UAR = 0x803,
MLX5_CMD_OP_CONFIG_INT_MODERATION = 0x804,
MLX5_CMD_OP_ACCESS_REG = 0x805,
MLX5_CMD_OP_ATTACH_TO_MCG = 0x806,
MLX5_CMD_OP_DETACH_FROM_MCG = 0x807,
MLX5_CMD_OP_GET_DROPPED_PACKET_LOG = 0x80a,
MLX5_CMD_OP_MAD_IFC = 0x50d,
MLX5_CMD_OP_QUERY_MAD_DEMUX = 0x80b,
MLX5_CMD_OP_SET_MAD_DEMUX = 0x80c,
MLX5_CMD_OP_NOP = 0x80d,
MLX5_CMD_OP_ALLOC_XRCD = 0x80e,
MLX5_CMD_OP_DEALLOC_XRCD = 0x80f,
MLX5_CMD_OP_ALLOC_TRANSPORT_DOMAIN = 0x816,
MLX5_CMD_OP_DEALLOC_TRANSPORT_DOMAIN = 0x817,
MLX5_CMD_OP_QUERY_CONG_STATUS = 0x822,
MLX5_CMD_OP_MODIFY_CONG_STATUS = 0x823,
MLX5_CMD_OP_QUERY_CONG_PARAMS = 0x824,
MLX5_CMD_OP_MODIFY_CONG_PARAMS = 0x825,
MLX5_CMD_OP_QUERY_CONG_STATISTICS = 0x826,
MLX5_CMD_OP_ADD_VXLAN_UDP_DPORT = 0x827,
MLX5_CMD_OP_DELETE_VXLAN_UDP_DPORT = 0x828,
MLX5_CMD_OP_SET_L2_TABLE_ENTRY = 0x829,
MLX5_CMD_OP_QUERY_L2_TABLE_ENTRY = 0x82a,
MLX5_CMD_OP_DELETE_L2_TABLE_ENTRY = 0x82b,
MLX5_CMD_OP_SET_WOL_ROL = 0x830,
MLX5_CMD_OP_QUERY_WOL_ROL = 0x831,
MLX5_CMD_OP_CREATE_LAG = 0x840,
MLX5_CMD_OP_MODIFY_LAG = 0x841,
MLX5_CMD_OP_QUERY_LAG = 0x842,
MLX5_CMD_OP_DESTROY_LAG = 0x843,
MLX5_CMD_OP_CREATE_VPORT_LAG = 0x844,
MLX5_CMD_OP_DESTROY_VPORT_LAG = 0x845,
MLX5_CMD_OP_CREATE_TIR = 0x900,
MLX5_CMD_OP_MODIFY_TIR = 0x901,
MLX5_CMD_OP_DESTROY_TIR = 0x902,
MLX5_CMD_OP_QUERY_TIR = 0x903,
MLX5_CMD_OP_CREATE_SQ = 0x904,
MLX5_CMD_OP_MODIFY_SQ = 0x905,
MLX5_CMD_OP_DESTROY_SQ = 0x906,
MLX5_CMD_OP_QUERY_SQ = 0x907,
MLX5_CMD_OP_CREATE_RQ = 0x908,
MLX5_CMD_OP_MODIFY_RQ = 0x909,
MLX5_CMD_OP_SET_DELAY_DROP_PARAMS = 0x910,
MLX5_CMD_OP_DESTROY_RQ = 0x90a,
MLX5_CMD_OP_QUERY_RQ = 0x90b,
MLX5_CMD_OP_CREATE_RMP = 0x90c,
MLX5_CMD_OP_MODIFY_RMP = 0x90d,
MLX5_CMD_OP_DESTROY_RMP = 0x90e,
MLX5_CMD_OP_QUERY_RMP = 0x90f,
MLX5_CMD_OP_CREATE_TIS = 0x912,
MLX5_CMD_OP_MODIFY_TIS = 0x913,
MLX5_CMD_OP_DESTROY_TIS = 0x914,
MLX5_CMD_OP_QUERY_TIS = 0x915,
MLX5_CMD_OP_CREATE_RQT = 0x916,
MLX5_CMD_OP_MODIFY_RQT = 0x917,
MLX5_CMD_OP_DESTROY_RQT = 0x918,
MLX5_CMD_OP_QUERY_RQT = 0x919,
MLX5_CMD_OP_SET_FLOW_TABLE_ROOT = 0x92f,
MLX5_CMD_OP_CREATE_FLOW_TABLE = 0x930,
MLX5_CMD_OP_DESTROY_FLOW_TABLE = 0x931,
MLX5_CMD_OP_QUERY_FLOW_TABLE = 0x932,
MLX5_CMD_OP_CREATE_FLOW_GROUP = 0x933,
MLX5_CMD_OP_DESTROY_FLOW_GROUP = 0x934,
MLX5_CMD_OP_QUERY_FLOW_GROUP = 0x935,
MLX5_CMD_OP_SET_FLOW_TABLE_ENTRY = 0x936,
MLX5_CMD_OP_QUERY_FLOW_TABLE_ENTRY = 0x937,
MLX5_CMD_OP_DELETE_FLOW_TABLE_ENTRY = 0x938,
MLX5_CMD_OP_ALLOC_FLOW_COUNTER = 0x939,
MLX5_CMD_OP_DEALLOC_FLOW_COUNTER = 0x93a,
MLX5_CMD_OP_QUERY_FLOW_COUNTER = 0x93b,
MLX5_CMD_OP_MODIFY_FLOW_TABLE = 0x93c,
MLX5_CMD_OP_ALLOC_PACKET_REFORMAT_CONTEXT = 0x93d,
MLX5_CMD_OP_DEALLOC_PACKET_REFORMAT_CONTEXT = 0x93e,
MLX5_CMD_OP_QUERY_PACKET_REFORMAT_CONTEXT = 0x93f,
MLX5_CMD_OP_ALLOC_MODIFY_HEADER_CONTEXT = 0x940,
MLX5_CMD_OP_DEALLOC_MODIFY_HEADER_CONTEXT = 0x941,
MLX5_CMD_OP_QUERY_MODIFY_HEADER_CONTEXT = 0x942,
MLX5_CMD_OP_FPGA_CREATE_QP = 0x960,
MLX5_CMD_OP_FPGA_MODIFY_QP = 0x961,
MLX5_CMD_OP_FPGA_QUERY_QP = 0x962,
MLX5_CMD_OP_FPGA_DESTROY_QP = 0x963,
MLX5_CMD_OP_FPGA_QUERY_QP_COUNTERS = 0x964,
MLX5_CMD_OP_CREATE_GENERAL_OBJECT = 0xa00,
MLX5_CMD_OP_MODIFY_GENERAL_OBJECT = 0xa01,
MLX5_CMD_OP_QUERY_GENERAL_OBJECT = 0xa02,
MLX5_CMD_OP_DESTROY_GENERAL_OBJECT = 0xa03,
MLX5_CMD_OP_CREATE_UCTX = 0xa04,
MLX5_CMD_OP_DESTROY_UCTX = 0xa06,
MLX5_CMD_OP_CREATE_UMEM = 0xa08,
MLX5_CMD_OP_DESTROY_UMEM = 0xa0a,
MLX5_CMD_OP_SYNC_STEERING = 0xb00,
MLX5_CMD_OP_QUERY_VHCA_STATE = 0xb0d,
MLX5_CMD_OP_MODIFY_VHCA_STATE = 0xb0e,
MLX5_CMD_OP_SYNC_CRYPTO = 0xb12,
MLX5_CMD_OP_ALLOW_OTHER_VHCA_ACCESS = 0xb16,
MLX5_CMD_OP_MAX
};
总结
- 查询设备列表, 利用内核通信机制NetLink进行通信, 完成设备查询,过滤
- 检查是否需要执行fork_init, 检查大页, 用红黑树记录父子进程的内存注册与分配(会有性能开销)
- 可借鉴的通用简单的日志系统设计与实现
- 打开设备, 利用更安全的ioctl, 读取设备寄存器, 得到设备能力集, 提示应用进行决策(hints)
- 驱动也利用动态加载(dlopen)so机制
- 关于实现细节的代码量较大, 也有一定的历史(很多代码都是10年或5年前已经成型), 不得不敬畏这些技术以及背后的设计逻辑, 我辈仍需努力追赶
参考
mojo打开设备: https://www.rdmamojo.com/2012/06/29/ibv_open_device/
rdma-core以及linux内核源码
rdma用户态与内核态交互: https://zhuanlan.zhihu.com/p/346708569
晓兵(ssbandjl)
博客: https://cloud.tencent.com/developer/user/5060293/articles | https://logread.cn | https://blog.csdn.net/ssbandjl | https://www.zhihu.com/people/ssbandjl/posts
DPU专栏
https://cloud.tencent.com/developer/column/101987