uverbs的交互方式——ioctl和write
verbs是RDMA设备的标准使用接口,verbs接口中控制面相关的接口实现由用户态的libibverbs.so和内核态的ib_uverbs.ko配合完成。用户态的libibverbs负责将RDMA控制命令信息封装成特定的数据结构,之后通过系统调用传递到内核中;ib_uverbs.ko负责实际实现这些系统调用,根据系统调用传入的参数获取RDMA控制命令的信息,并执行相应的命令。
libibverbs与ib_uverbs间传递命令信息的syscall有两种:ioctl和write。verbs实现中使用ioctl和write分别实现了各类命令信息的传递,同一个命令既可以通过ioctl,也可以通过write来实现。
在早期的实现中,libibverbs是通过write syscall来传递信息的,但在cve-2016-4565中发现了这种实现存在安全漏洞,因此之后在[rdma-core] verbs: Allow all commands to be invoked by ioctl - Patchwork中使用ioctl重新实现了控制命令的传递。为了保持向后兼容,libibverbs和ib_uverbs中都保留了基于write的实现,但在绝大部分情况下,都应该使用ioctl版本的实现来保证内核的安全性。
libibverbs中通过_execute_cmd_write函数实现了对ioctl和write接口的兼容性封装和配置选择。在未指定VERBS_WRITE_ONLY的情况下,会使用ioctl_write函数来实现之前通过write系统调用实现的功能。在ioctl_write中,会将原本用于write的ib_uverbs_cmd_hdr结构转换为ibv_command_buffer结构,最后调用ioctl来下发命令。这种情况下,ibv_command_buffer的object_id和method_id分别设置为UVERBS_OBJECT_DEVICE和UVERBS_METHOD_INVOKE_WRITE,值都为0。
在当前的libibverbs实现中,_execute_cmd_write函数本身只会在libibverbs或者ib_uverbs不直接支持命令通过ioctl实现时才会调用。原本在这种情况下,会使用write实现命令,而现在则会使用ioctl_write实现。这个逻辑在_execute_ioctl_fallback中实现。
这里比较奇怪的一点是为什么不为这些命令也直接支持ioctl操作模式,而是要用fallback到一个特殊的ioctl操作的方式实现。原因是部分命令的内核处理逻辑是对数据结构的用户态内存空间布局有依赖的,因此必须通过一种类似于write的方式去处理。(这块不是很理解,没有细看相关实现。参见https://lpc.events/event/2/contributions/144/attachments/125/157/plumbers-2018.pdf)