Using the new UVERBS_METHOD_INVOKE_WRITE interface all of the existing write() commands can be immediately converted to execute over ioctl. If -DIOCTL_MODE=ioctl is set then only the IOCTL interface is supported, and write will not be called. Otherwise the 'both' mode will auto detect the kernel support and use it. Signed-off-by: Jason Gunthorpe <jgg@xxxxxxxxxxxx> --- libibverbs/cmd_fallback.c | 44 ++++++++++++++++++++++++++++++++++++++- libibverbs/device.c | 27 ++++++++++++++++++++++++ libibverbs/ibverbs.h | 1 + 3 files changed, 71 insertions(+), 1 deletion(-) This needs the matching kernel patch: https://patchwork.kernel.org/patch/10706127/ a kernel headers update and the series at: https://github.com/linux-rdma/rdma-core/pull/434 To apply. diff --git a/libibverbs/cmd_fallback.c b/libibverbs/cmd_fallback.c index 0e50503e003484..38193a186a8ccb 100644 --- a/libibverbs/cmd_fallback.c +++ b/libibverbs/cmd_fallback.c @@ -206,11 +206,43 @@ void *_write_get_resp_ex(struct ibv_command_buffer *link, return resp_start; } +static int ioctl_write(struct ibv_context *ctx, unsigned int write_method, + const void *req, size_t core_req_size, size_t req_size, + void *resp, size_t core_resp_size, size_t resp_size) +{ + DECLARE_COMMAND_BUFFER(cmdb, UVERBS_OBJECT_DEVICE, + UVERBS_METHOD_INVOKE_WRITE, 5); + + fill_attr_const_in(cmdb, UVERBS_ATTR_WRITE_CMD, write_method); + + if (core_req_size) + fill_attr_in(cmdb, UVERBS_ATTR_CORE_IN, req, core_req_size); + if (core_resp_size) + fill_attr_out(cmdb, UVERBS_ATTR_CORE_OUT, resp, core_resp_size); + + if (req_size - core_req_size) + fill_attr_in(cmdb, UVERBS_ATTR_UHW_IN, req + core_req_size, + req_size - core_req_size); + if (resp_size - core_resp_size) + fill_attr_out(cmdb, UVERBS_ATTR_UHW_OUT, resp + core_resp_size, + resp_size - core_resp_size); + + return execute_ioctl(ctx, cmdb); +} + int _execute_cmd_write(struct ibv_context *ctx, unsigned int write_method, struct ib_uverbs_cmd_hdr *req, size_t core_req_size, size_t req_size, void *resp, size_t core_resp_size, size_t resp_size) { + struct verbs_ex_private *priv = get_priv(ctx); + + if (!VERBS_WRITE_ONLY && (VERBS_IOCTL_ONLY || priv->use_ioctl_write)) + return ioctl_write(ctx, write_method, req + 1, + core_req_size - sizeof(*req), + req_size - sizeof(*req), resp, + core_resp_size, resp_size); + req->command = write_method; req->in_words = __check_divide(req_size, 4); req->out_words = __check_divide(resp_size, 4); @@ -232,6 +264,15 @@ int _execute_cmd_write_ex(struct ibv_context *ctx, unsigned int write_method, size_t req_size, void *resp, size_t core_resp_size, size_t resp_size) { + struct verbs_ex_private *priv = get_priv(ctx); + + if (!VERBS_WRITE_ONLY && (VERBS_IOCTL_ONLY || priv->use_ioctl_write)) + return ioctl_write( + ctx, IB_USER_VERBS_CMD_FLAG_EXTENDED | write_method, + req + 1, core_req_size - sizeof(*req), + req_size - sizeof(*req), resp, core_resp_size, + resp_size); + req->hdr.command = IB_USER_VERBS_CMD_FLAG_EXTENDED | write_method; req->hdr.in_words = __check_divide(core_req_size - sizeof(struct ex_hdr), 8); @@ -245,7 +286,8 @@ int _execute_cmd_write_ex(struct ibv_context *ctx, unsigned int write_method, /* * Users assumes the stack buffer is zeroed before passing to the - * kernel for writing. + * kernel for writing. New kernels with the ioctl path do this + * automatically for us. */ if (resp) memset(resp, 0, resp_size); diff --git a/libibverbs/device.c b/libibverbs/device.c index e6e23e718d9fca..1fed29b17ec31d 100644 --- a/libibverbs/device.c +++ b/libibverbs/device.c @@ -43,6 +43,7 @@ #include <alloca.h> #include <errno.h> +#include <rdma/ib_user_ioctl_cmds.h> #include <util/symver.h> #include "ibverbs.h" @@ -193,6 +194,31 @@ __lib_ibv_create_cq_ex(struct ibv_context *context, return cq; } +static bool has_ioctl_write(struct ibv_context *ctx) +{ + int rc; + DECLARE_COMMAND_BUFFER(cmdb, UVERBS_OBJECT_DEVICE, + UVERBS_METHOD_INVOKE_WRITE, 1); + + if (VERBS_IOCTL_ONLY) + return true; + if (VERBS_WRITE_ONLY) + return false; + + /* + * This command should return ENOSPC since the request length is too + * small. + */ + fill_attr_const_in(cmdb, UVERBS_ATTR_WRITE_CMD, + IB_USER_VERBS_CMD_QUERY_DEVICE); + rc = execute_ioctl(ctx, cmdb); + if (rc == EPROTONOSUPPORT) + return false; + if (rc == ENOTTY) + return false; + return true; +} + /* * Ownership of cmd_fd is transferred into this function, and it will either * be released during the matching call to verbs_uninit_contxt or during the @@ -240,6 +266,7 @@ int verbs_init_context(struct verbs_context *context_ex, context_ex->priv->driver_id = driver_id; verbs_set_ops(context_ex, &verbs_dummy_ops); + context_ex->priv->use_ioctl_write = has_ioctl_write(context); return 0; } diff --git a/libibverbs/ibverbs.h b/libibverbs/ibverbs.h index 24843b49fdb3c7..36e29a6b14d87b 100644 --- a/libibverbs/ibverbs.h +++ b/libibverbs/ibverbs.h @@ -70,6 +70,7 @@ void load_drivers(void); struct verbs_ex_private { BITMAP_DECLARE(unsupported_ioctls, VERBS_OPS_NUM); uint32_t driver_id; + bool use_ioctl_write; struct verbs_context_ops ops; }; -- 2.19.2