From: Eric Davis via B4 Relay <devnull+eric.davis.broadcom.com@kernel.org>
To: Jason Gunthorpe <jgg@ziepe.ca>, Leon Romanovsky <leon@kernel.org>
Cc: linux-rdma@vger.kernel.org, linux-kernel@vger.kernel.org,
Eric Davis <eric.davis@broadcom.com>
Subject: [PATCH rdma-next 07/13] RDMA/core: job and job key objects
Date: Wed, 14 Oct 2026 15:21:16 -0700 [thread overview]
Message-ID: <20261014-uet-ib-core-v1-7-a0e626117113@broadcom.com> (raw)
In-Reply-To: <20261014-uet-ib-core-v1-0-a0e626117113@broadcom.com>
From: Eric Davis <eric.davis@broadcom.com>
An unconnected transport demultiplexes incoming traffic on a JobID rather
than on a connection. Add two objects for it: a job, which owns that ID
and a table of peer addresses, and a job key, which ties a job to a
protection domain and authorizes a queue pair to act within it.
A job can be exported to a file descriptor and imported elsewhere, so an
unprivileged process can join a job that was set up for it.
Signed-off-by: Eric Davis <eric.davis@broadcom.com>
---
drivers/infiniband/core/Makefile | 3 +-
drivers/infiniband/core/device.c | 10 +
drivers/infiniband/core/nldev.c | 2 +
drivers/infiniband/core/rdma_core.h | 2 +
drivers/infiniband/core/restrack.c | 4 +
drivers/infiniband/core/uverbs.h | 7 +
drivers/infiniband/core/uverbs_main.c | 5 +
drivers/infiniband/core/uverbs_std_types_job.c | 763 +++++++++++++++++++++++++
drivers/infiniband/core/uverbs_uapi.c | 2 +
drivers/infiniband/core/verbs.c | 38 ++
include/rdma/ib_verbs.h | 70 +++
include/rdma/restrack.h | 8 +
include/rdma/uverbs_ioctl.h | 2 +-
include/uapi/rdma/ib_user_ioctl_cmds.h | 82 +++
14 files changed, 996 insertions(+), 2 deletions(-)
diff --git a/drivers/infiniband/core/Makefile b/drivers/infiniband/core/Makefile
index 47ef6b0afd29..f169c1bfa8b8 100644
--- a/drivers/infiniband/core/Makefile
+++ b/drivers/infiniband/core/Makefile
@@ -47,7 +47,8 @@ ib_uverbs-y := uverbs_main.o uverbs_cmd.o uverbs_marshall.o \
uverbs_std_types_async_fd.o \
uverbs_std_types_srq.o \
uverbs_std_types_wq.o \
- uverbs_std_types_qp.o
+ uverbs_std_types_qp.o \
+ uverbs_std_types_job.o
ib_uverbs_support-y := rdma_core.o \
ucaps.o \
diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c
index 8b1563de4726..cf2a59afcf76 100644
--- a/drivers/infiniband/core/device.c
+++ b/drivers/infiniband/core/device.c
@@ -2915,6 +2915,14 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops)
SET_DEVICE_OP(dev_ops, counter_unbind_qp);
SET_DEVICE_OP(dev_ops, counter_update_stats);
SET_DEVICE_OP(dev_ops, create_ah);
+ SET_DEVICE_OP(dev_ops, alloc_job);
+ SET_DEVICE_OP(dev_ops, dealloc_job);
+ SET_DEVICE_OP(dev_ops, query_job);
+ SET_DEVICE_OP(dev_ops, job_addr_insert);
+ SET_DEVICE_OP(dev_ops, job_addr_remove);
+ SET_DEVICE_OP(dev_ops, job_addr_query);
+ SET_DEVICE_OP(dev_ops, create_jkey);
+ SET_DEVICE_OP(dev_ops, destroy_jkey);
SET_DEVICE_OP(dev_ops, create_counters);
SET_DEVICE_OP(dev_ops, create_cq);
SET_DEVICE_OP(dev_ops, create_comp_cntr);
@@ -3031,6 +3039,8 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops)
SET_OBJ_SIZE(dev_ops, ib_ah);
SET_OBJ_SIZE(dev_ops, ib_counters);
+ SET_OBJ_SIZE(dev_ops, ib_job);
+ SET_OBJ_SIZE(dev_ops, ib_jkey);
SET_OBJ_SIZE(dev_ops, ib_cq);
SET_OBJ_SIZE(dev_ops, ib_comp_cntr);
SET_OBJ_SIZE(dev_ops, ib_dmah);
diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c
index 91b7be84b15f..5313eb4849c7 100644
--- a/drivers/infiniband/core/nldev.c
+++ b/drivers/infiniband/core/nldev.c
@@ -454,6 +454,8 @@ static int fill_res_info(struct sk_buff *msg, struct ib_device *device,
[RDMA_RESTRACK_CTX] = "ctx",
[RDMA_RESTRACK_SRQ] = "srq",
[RDMA_RESTRACK_COMP_CNTR] = "comp_cntr",
+ [RDMA_RESTRACK_JOB] = "job",
+ [RDMA_RESTRACK_JKEY] = "jkey",
};
struct ib_comp_cntr_caps comp_cntr_caps = {};
diff --git a/drivers/infiniband/core/rdma_core.h b/drivers/infiniband/core/rdma_core.h
index 9de14e625dd3..523a6f7d4439 100644
--- a/drivers/infiniband/core/rdma_core.h
+++ b/drivers/infiniband/core/rdma_core.h
@@ -164,6 +164,8 @@ void uverbs_user_mmap_disassociate(struct ib_uverbs_file *ufile);
extern const struct uapi_definition uverbs_def_obj_async_fd[];
extern const struct uapi_definition uverbs_def_obj_counters[];
+extern const struct uapi_definition uverbs_def_obj_job[];
+extern const struct uapi_definition uverbs_def_obj_jkey[];
extern const struct uapi_definition uverbs_def_obj_comp_cntr[];
extern const struct uapi_definition uverbs_def_obj_cq[];
extern const struct uapi_definition uverbs_def_obj_device[];
diff --git a/drivers/infiniband/core/restrack.c b/drivers/infiniband/core/restrack.c
index ddb09b17b733..53217f0ad4c8 100644
--- a/drivers/infiniband/core/restrack.c
+++ b/drivers/infiniband/core/restrack.c
@@ -106,6 +106,10 @@ static struct ib_device *res_to_dev(struct rdma_restrack_entry *res)
return container_of(res, struct ib_dmah, res)->device;
case RDMA_RESTRACK_COMP_CNTR:
return container_of(res, struct ib_comp_cntr, res)->device;
+ case RDMA_RESTRACK_JOB:
+ return container_of(res, struct ib_job, res)->device;
+ case RDMA_RESTRACK_JKEY:
+ return container_of(res, struct ib_jkey, res)->device;
default:
WARN_ONCE(true, "Wrong resource tracking type %u\n", res->type);
return NULL;
diff --git a/drivers/infiniband/core/uverbs.h b/drivers/infiniband/core/uverbs.h
index e08c41b8f45b..cecb10ffd635 100644
--- a/drivers/infiniband/core/uverbs.h
+++ b/drivers/infiniband/core/uverbs.h
@@ -174,6 +174,12 @@ struct ib_uxrcd_object {
atomic_t refcnt;
};
+struct ib_ujob_object {
+ struct ib_uobject uobject;
+ /* job keys created through this handle */
+ atomic_t refcnt;
+};
+
struct ib_usrq_object {
struct ib_uevent_object uevent;
struct ib_uxrcd_object *uxrcd;
@@ -230,6 +236,7 @@ void ib_uverbs_detach_umcast(struct ib_qp *qp,
int copy_ah_attr_from_uverbs_ah(struct ib_device *dev,
struct rdma_ah_attr *rdma_attr,
const struct ib_uverbs_ah_attr *uverb_attr);
+void ib_uverbs_revoke_jobs(struct ib_device *device);
struct bundle_alloc_head {
struct_group_tagged(bundle_alloc_head_hdr, hdr,
diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c
index 2a046c888dfa..3d1da3083132 100644
--- a/drivers/infiniband/core/uverbs_main.c
+++ b/drivers/infiniband/core/uverbs_main.c
@@ -1282,6 +1282,11 @@ static void ib_uverbs_remove_one(struct ib_device *device, void *client_data)
if (wait_clients)
wait_for_completion(&uverbs_dev->comp);
+ /* Every handle is gone, only an exported job can still hold the
+ * device.
+ */
+ ib_uverbs_revoke_jobs(device);
+
put_device(&uverbs_dev->dev);
}
diff --git a/drivers/infiniband/core/uverbs_std_types_job.c b/drivers/infiniband/core/uverbs_std_types_job.c
new file mode 100644
index 000000000000..8af3e7653e2d
--- /dev/null
+++ b/drivers/infiniband/core/uverbs_std_types_job.c
@@ -0,0 +1,763 @@
+// SPDX-License-Identifier: GPL-2.0-only OR Linux-OpenIB
+
+/*
+ * Copyright (c) 2026 Broadcom. All rights reserved. The term Broadcom
+ * refers to Broadcom Inc. and/or its subsidiaries.
+ */
+
+/*
+ * The job object of a reliable unordered transport.
+ */
+
+#include <linux/anon_inodes.h>
+#include <linux/file.h>
+
+#include <rdma/ib_marshall.h>
+#include <rdma/uverbs_std_types.h>
+
+#include "rdma_core.h"
+#include "restrack.h"
+#include "uverbs.h"
+
+/* Release the job once, whether from its last reference or from a revoke
+ * at device unregister. Called with job->lock held.
+ */
+static void ib_job_teardown(struct ib_job *job)
+{
+ if (job->revoked)
+ return;
+
+ rdma_restrack_del(&job->res);
+ job->device->ops.dealloc_job(job);
+ WRITE_ONCE(job->revoked, true);
+}
+
+static void ib_job_release(struct kref *ref)
+{
+ struct ib_job *job = container_of(ref, struct ib_job, ref);
+ struct ib_device *ib_dev = job->device;
+
+ mutex_lock(&job->lock);
+ ib_job_teardown(job);
+ mutex_unlock(&job->lock);
+
+ mutex_destroy(&job->lock);
+ kfree(job);
+ put_device(&ib_dev->dev);
+}
+
+/* An exported job is a file that no ufile tracks, so it can outlive the
+ * device. Once every handle is gone, release what is left while the driver
+ * is still here; the job then holds only its memory and the device's, until
+ * its last file is closed.
+ */
+void ib_uverbs_revoke_jobs(struct ib_device *ib_dev)
+{
+ struct rdma_restrack_root *rt = &ib_dev->res[RDMA_RESTRACK_JOB];
+ struct rdma_restrack_entry *res;
+ unsigned long id = 0;
+ struct ib_job *job;
+ bool live;
+
+ for (;;) {
+ xa_lock(&rt->xa);
+
+ res = xa_find(&rt->xa, &id, U32_MAX, XA_PRESENT);
+ if (!res) {
+ xa_unlock(&rt->xa);
+ break;
+ }
+
+ /* a job already on its way out removes itself */
+ job = container_of(res, struct ib_job, res);
+ live = kref_get_unless_zero(&job->ref);
+
+ xa_unlock(&rt->xa);
+
+ id++;
+
+ if (!live)
+ continue;
+
+ mutex_lock(&job->lock);
+ ib_job_teardown(job);
+ mutex_unlock(&job->lock);
+ kref_put(&job->ref, ib_job_release);
+ }
+}
+
+static int ib_job_fd_release(struct inode *inode, struct file *filp)
+{
+ kref_put(&((struct ib_job *)filp->private_data)->ref, ib_job_release);
+
+ return 0;
+}
+
+static const struct file_operations ib_job_fops = {
+ .owner = THIS_MODULE,
+ .release = ib_job_fd_release,
+};
+
+static int UVERBS_HANDLER(UVERBS_METHOD_JOB_EXPORT)(
+ struct uverbs_attr_bundle *attrs)
+{
+ struct ib_job *job =
+ uverbs_attr_get_obj(attrs, UVERBS_ATTR_EXPORT_JOB_HANDLE);
+ struct file *filp;
+ int fd, ret;
+
+ if (IS_ERR(job))
+ return PTR_ERR(job);
+
+ fd = get_unused_fd_flags(O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ /* an exported job is an anonymous file holding one reference */
+ kref_get(&job->ref);
+
+ filp = anon_inode_getfile("[ib_job]", &ib_job_fops, job,
+ (O_RDWR | O_CLOEXEC));
+ if (IS_ERR(filp)) {
+ ret = PTR_ERR(filp);
+ goto err;
+ }
+
+ ret = uverbs_copy_to(attrs, UVERBS_ATTR_EXPORT_JOB_FD, &fd,
+ sizeof(fd));
+ if (ret) {
+ fput(filp);
+ put_unused_fd(fd);
+ return ret;
+ }
+
+ fd_install(fd, filp);
+
+ return 0;
+
+err:
+ kref_put(&job->ref, ib_job_release);
+ put_unused_fd(fd);
+
+ return ret;
+}
+
+static int UVERBS_HANDLER(UVERBS_METHOD_JOB_IMPORT)(
+ struct uverbs_attr_bundle *attrs)
+{
+ struct ib_uobject *uobj =
+ uverbs_attr_get_uobject(attrs, UVERBS_ATTR_IMPORT_JOB_HANDLE);
+ struct ib_device *ib_dev = attrs->context->device;
+ struct ib_job *job;
+ struct fd f;
+ s32 fd;
+ int ret;
+
+ ret = uverbs_copy_from(&fd, attrs, UVERBS_ATTR_IMPORT_JOB_FD);
+ if (ret)
+ return ret;
+
+ f = fdget(fd);
+ if (!fd_file(f))
+ return -EBADF;
+
+ /* refuse anything that is not an exported job */
+ if (fd_file(f)->f_op != &ib_job_fops) {
+ fdput(f);
+ return -EINVAL;
+ }
+
+ job = fd_file(f)->private_data;
+
+ /* a handle names a job on the device it was allocated on */
+ if (job->device != ib_dev) {
+ fdput(f);
+ return -EINVAL;
+ }
+
+ if (READ_ONCE(job->revoked)) {
+ fdput(f);
+ return -EIO;
+ }
+
+ /* an imported job holds one reference */
+ kref_get(&job->ref);
+ fdput(f);
+
+ uobj->object = job;
+
+ return 0;
+}
+
+static int uverbs_free_job(struct ib_uobject *uobject,
+ enum rdma_remove_reason why,
+ struct uverbs_attr_bundle *attrs)
+{
+ struct ib_ujob_object *ujob =
+ container_of(uobject, struct ib_ujob_object, uobject);
+ struct ib_job *job = uobject->object;
+
+ /* don't free if there are still references to this job */
+ if (atomic_read(&ujob->refcnt))
+ return -EBUSY;
+
+ kref_put(&job->ref, ib_job_release);
+
+ return 0;
+}
+
+static int UVERBS_HANDLER(UVERBS_METHOD_JOB_ALLOC)(
+ struct uverbs_attr_bundle *attrs)
+{
+ struct ib_uobject *uobj =
+ uverbs_attr_get_uobject(attrs, UVERBS_ATTR_ALLOC_JOB_HANDLE);
+ struct ib_device *ib_dev = attrs->context->device;
+ struct ib_job_attr attr = {};
+ struct ib_job *job;
+ int ret;
+
+ if (!ib_dev->ops.alloc_job)
+ return -EOPNOTSUPP;
+
+ /* A JobID names a domain across the fabric rather than anything
+ * local to this process, so claiming one is an administrative act.
+ */
+ if (!rdma_uattrs_has_raw_cap(attrs))
+ return -EPERM;
+
+ ret = uverbs_copy_from(&attr.job_id, attrs, UVERBS_ATTR_ALLOC_JOB_ID);
+ if (ret)
+ return ret;
+
+ /* The rest are optional and an absent attribute reads as -ENOENT
+ * which IS_UVERBS_COPY_ERR() checks.
+ */
+ ret = uverbs_copy_from(&attr.max_addr_entries, attrs,
+ UVERBS_ATTR_ALLOC_JOB_MAX_ADDR_ENTRIES);
+ if (IS_UVERBS_COPY_ERR(ret))
+ return ret;
+
+ ret = uverbs_copy_from(&attr.flags, attrs,
+ UVERBS_ATTR_ALLOC_JOB_FLAGS);
+ if (IS_UVERBS_COPY_ERR(ret))
+ return ret;
+
+ ret = uverbs_copy_from(&attr.port_num, attrs,
+ UVERBS_ATTR_ALLOC_JOB_PORT_NUM);
+ if (IS_UVERBS_COPY_ERR(ret))
+ return ret;
+
+ ret = uverbs_copy_from(&attr.sgid_index, attrs,
+ UVERBS_ATTR_ALLOC_JOB_SGID_INDEX);
+ if (IS_UVERBS_COPY_ERR(ret))
+ return ret;
+
+ /* no flag is defined yet */
+ if (attr.flags)
+ return -EINVAL;
+
+ if (attr.max_addr_entries > ib_dev->attrs.max_addr_entries)
+ return -EINVAL;
+
+ /* the port carries the job's traffic, so it has to be one we have */
+ if (!attr.port_num)
+ attr.port_num = rdma_start_port(ib_dev);
+ if (!rdma_is_port_valid(ib_dev, attr.port_num))
+ return -EINVAL;
+
+ if (attr.sgid_index >=
+ ib_dev->port_data[attr.port_num].immutable.gid_tbl_len)
+ return -EINVAL;
+
+ job = rdma_zalloc_drv_obj(ib_dev, ib_job);
+ if (!job)
+ return -ENOMEM;
+
+ job->device = ib_dev;
+ job->job_id = attr.job_id;
+ kref_init(&job->ref);
+ mutex_init(&job->lock);
+ uobj->object = job;
+
+ rdma_restrack_new(&job->res, RDMA_RESTRACK_JOB);
+ rdma_restrack_set_name(&job->res, NULL);
+
+ ret = ib_dev->ops.alloc_job(job, &attr, attrs);
+ if (ret)
+ goto err_free;
+
+ /* revoke finds a job through restrack, an untracked one can't exist */
+ rdma_restrack_add(&job->res);
+ if (!job->res.valid) {
+ ib_dev->ops.dealloc_job(job);
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ /* an exported job can outlive unregistration, so pin the device's
+ * memory
+ */
+ get_device(&ib_dev->dev);
+
+ return 0;
+
+err_free:
+ rdma_restrack_put(&job->res);
+ mutex_destroy(&job->lock);
+ kfree(job);
+ return ret;
+}
+
+static int UVERBS_HANDLER(UVERBS_METHOD_JOB_QUERY)(
+ struct uverbs_attr_bundle *attrs)
+{
+ struct ib_job *job =
+ uverbs_attr_get_obj(attrs, UVERBS_ATTR_QUERY_JOB_HANDLE);
+ struct ib_job_attr attr = {};
+ int ret;
+
+ if (!job->device->ops.query_job)
+ return -EOPNOTSUPP;
+
+ ret = job->device->ops.query_job(job, &attr);
+ if (ret)
+ return ret;
+
+ ret = uverbs_copy_to(attrs, UVERBS_ATTR_QUERY_JOB_ID, &attr.job_id,
+ sizeof(attr.job_id));
+ if (ret)
+ return ret;
+
+ ret = uverbs_copy_to(attrs, UVERBS_ATTR_QUERY_JOB_MAX_ADDR_ENTRIES,
+ &attr.max_addr_entries,
+ sizeof(attr.max_addr_entries));
+ if (ret)
+ return ret;
+
+ ret = uverbs_copy_to(attrs, UVERBS_ATTR_QUERY_JOB_FLAGS, &attr.flags,
+ sizeof(attr.flags));
+ if (ret)
+ return ret;
+
+ ret = uverbs_copy_to(attrs, UVERBS_ATTR_QUERY_JOB_PORT_NUM,
+ &attr.port_num, sizeof(attr.port_num));
+ if (ret)
+ return ret;
+
+ return uverbs_copy_to(attrs, UVERBS_ATTR_QUERY_JOB_SGID_INDEX,
+ &attr.sgid_index, sizeof(attr.sgid_index));
+}
+
+static int UVERBS_HANDLER(UVERBS_METHOD_JOB_ADDR_INSERT)(
+ struct uverbs_attr_bundle *attrs)
+{
+ struct ib_job *job = uverbs_attr_get_obj(
+ attrs, UVERBS_ATTR_JOB_ADDR_INSERT_HANDLE);
+ struct ib_uverbs_ah_attr_ex uah;
+ struct rdma_ah_attr_ex ah_attr = {};
+ u32 index, flags = 0;
+ int ret;
+
+ if (IS_ERR(job))
+ return PTR_ERR(job);
+
+ if (!job->device->ops.job_addr_insert)
+ return -EOPNOTSUPP;
+
+ /* An address table entry names a peer on the fabric for every process
+ * in the job, so writing one redirects their traffic. An address
+ * handle, by contrast, serves only the process that made it.
+ */
+ if (!rdma_uattrs_has_raw_cap(attrs))
+ return -EPERM;
+
+ ret = uverbs_copy_from(&index, attrs,
+ UVERBS_ATTR_JOB_ADDR_INSERT_INDEX);
+ if (ret)
+ return ret;
+
+ ret = uverbs_copy_from(&uah, attrs,
+ UVERBS_ATTR_JOB_ADDR_INSERT_AH_ATTR_EX);
+ if (ret)
+ return ret;
+
+ ret = uverbs_copy_from(&flags, attrs,
+ UVERBS_ATTR_JOB_ADDR_INSERT_FLAGS);
+ if (IS_UVERBS_COPY_ERR(ret))
+ return ret;
+
+ if (flags || index >= job->device->attrs.max_addr_entries)
+ return -EINVAL;
+
+ ret = copy_ah_attr_from_uverbs_ah(job->device, &ah_attr.ah_attr,
+ &uah.ah_attr);
+ if (ret)
+ return ret;
+
+ ah_attr.remote_qpn = uah.remote_qpn;
+
+ return rdma_job_addr_insert(job, index, &ah_attr, flags);
+}
+
+static int UVERBS_HANDLER(UVERBS_METHOD_JOB_ADDR_REMOVE)(
+ struct uverbs_attr_bundle *attrs)
+{
+ struct ib_job *job = uverbs_attr_get_obj(
+ attrs, UVERBS_ATTR_JOB_ADDR_REMOVE_HANDLE);
+ u32 index, flags = 0;
+ int ret;
+
+ if (IS_ERR(job))
+ return PTR_ERR(job);
+
+ if (!job->device->ops.job_addr_remove)
+ return -EOPNOTSUPP;
+
+ if (!rdma_uattrs_has_raw_cap(attrs))
+ return -EPERM;
+
+ ret = uverbs_copy_from(&index, attrs,
+ UVERBS_ATTR_JOB_ADDR_REMOVE_INDEX);
+ if (ret)
+ return ret;
+
+ ret = uverbs_copy_from(&flags, attrs,
+ UVERBS_ATTR_JOB_ADDR_REMOVE_FLAGS);
+ if (IS_UVERBS_COPY_ERR(ret))
+ return ret;
+
+ if (flags || index >= job->device->attrs.max_addr_entries)
+ return -EINVAL;
+
+ return job->device->ops.job_addr_remove(job, index, flags);
+}
+
+static int UVERBS_HANDLER(UVERBS_METHOD_JOB_ADDR_QUERY)(
+ struct uverbs_attr_bundle *attrs)
+{
+ struct ib_job *job = uverbs_attr_get_obj(
+ attrs, UVERBS_ATTR_JOB_ADDR_QUERY_HANDLE);
+ struct ib_uverbs_ah_attr_ex uah = {};
+ struct rdma_ah_attr_ex ah_attr = {};
+ u32 index, flags = 0;
+ int ret;
+
+ if (IS_ERR(job))
+ return PTR_ERR(job);
+
+ if (!job->device->ops.job_addr_query)
+ return -EOPNOTSUPP;
+
+ ret = uverbs_copy_from(&index, attrs,
+ UVERBS_ATTR_JOB_ADDR_QUERY_INDEX);
+ if (ret)
+ return ret;
+
+ ret = uverbs_copy_from(&flags, attrs,
+ UVERBS_ATTR_JOB_ADDR_QUERY_FLAGS);
+ if (IS_UVERBS_COPY_ERR(ret))
+ return ret;
+
+ if (flags || index >= job->device->attrs.max_addr_entries)
+ return -EINVAL;
+
+ ret = job->device->ops.job_addr_query(job, index, &ah_attr, flags);
+ if (ret)
+ return ret;
+
+ ib_copy_ah_attr_to_user(job->device, &uah.ah_attr, &ah_attr.ah_attr);
+ uah.remote_qpn = ah_attr.remote_qpn;
+
+ return uverbs_copy_to(attrs, UVERBS_ATTR_JOB_ADDR_QUERY_AH_ATTR_EX,
+ &uah, sizeof(uah));
+}
+
+DECLARE_UVERBS_NAMED_METHOD(
+ UVERBS_METHOD_JOB_ADDR_INSERT,
+ UVERBS_ATTR_IDR(UVERBS_ATTR_JOB_ADDR_INSERT_HANDLE,
+ UVERBS_OBJECT_JOB,
+ UVERBS_ACCESS_READ,
+ UA_MANDATORY),
+ UVERBS_ATTR_PTR_IN(UVERBS_ATTR_JOB_ADDR_INSERT_INDEX,
+ UVERBS_ATTR_TYPE(u32),
+ UA_MANDATORY),
+ UVERBS_ATTR_PTR_IN(UVERBS_ATTR_JOB_ADDR_INSERT_AH_ATTR_EX,
+ UVERBS_ATTR_STRUCT(struct ib_uverbs_ah_attr_ex,
+ remote_qpn),
+ UA_MANDATORY),
+ UVERBS_ATTR_PTR_IN(UVERBS_ATTR_JOB_ADDR_INSERT_FLAGS,
+ UVERBS_ATTR_TYPE(u32),
+ UA_OPTIONAL));
+
+DECLARE_UVERBS_NAMED_METHOD(
+ UVERBS_METHOD_JOB_ADDR_REMOVE,
+ UVERBS_ATTR_IDR(UVERBS_ATTR_JOB_ADDR_REMOVE_HANDLE,
+ UVERBS_OBJECT_JOB,
+ UVERBS_ACCESS_READ,
+ UA_MANDATORY),
+ UVERBS_ATTR_PTR_IN(UVERBS_ATTR_JOB_ADDR_REMOVE_INDEX,
+ UVERBS_ATTR_TYPE(u32),
+ UA_MANDATORY),
+ UVERBS_ATTR_PTR_IN(UVERBS_ATTR_JOB_ADDR_REMOVE_FLAGS,
+ UVERBS_ATTR_TYPE(u32),
+ UA_OPTIONAL));
+
+DECLARE_UVERBS_NAMED_METHOD(
+ UVERBS_METHOD_JOB_ADDR_QUERY,
+ UVERBS_ATTR_IDR(UVERBS_ATTR_JOB_ADDR_QUERY_HANDLE,
+ UVERBS_OBJECT_JOB,
+ UVERBS_ACCESS_READ,
+ UA_MANDATORY),
+ UVERBS_ATTR_PTR_IN(UVERBS_ATTR_JOB_ADDR_QUERY_INDEX,
+ UVERBS_ATTR_TYPE(u32),
+ UA_MANDATORY),
+ UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_JOB_ADDR_QUERY_AH_ATTR_EX,
+ UVERBS_ATTR_STRUCT(struct ib_uverbs_ah_attr_ex,
+ remote_qpn),
+ UA_MANDATORY),
+ UVERBS_ATTR_PTR_IN(UVERBS_ATTR_JOB_ADDR_QUERY_FLAGS,
+ UVERBS_ATTR_TYPE(u32),
+ UA_OPTIONAL));
+
+DECLARE_UVERBS_NAMED_METHOD(
+ UVERBS_METHOD_JOB_ALLOC,
+ UVERBS_ATTR_IDR(UVERBS_ATTR_ALLOC_JOB_HANDLE,
+ UVERBS_OBJECT_JOB,
+ UVERBS_ACCESS_NEW,
+ UA_MANDATORY),
+ UVERBS_ATTR_PTR_IN(UVERBS_ATTR_ALLOC_JOB_ID,
+ UVERBS_ATTR_TYPE(u32),
+ UA_MANDATORY),
+ UVERBS_ATTR_PTR_IN(UVERBS_ATTR_ALLOC_JOB_MAX_ADDR_ENTRIES,
+ UVERBS_ATTR_TYPE(u32),
+ UA_OPTIONAL),
+ UVERBS_ATTR_PTR_IN(UVERBS_ATTR_ALLOC_JOB_FLAGS,
+ UVERBS_ATTR_TYPE(u32),
+ UA_OPTIONAL),
+ UVERBS_ATTR_PTR_IN(UVERBS_ATTR_ALLOC_JOB_PORT_NUM,
+ UVERBS_ATTR_TYPE(u32),
+ UA_OPTIONAL),
+ UVERBS_ATTR_PTR_IN(UVERBS_ATTR_ALLOC_JOB_SGID_INDEX,
+ UVERBS_ATTR_TYPE(u8),
+ UA_OPTIONAL),
+ UVERBS_ATTR_UHW());
+
+DECLARE_UVERBS_NAMED_METHOD(
+ UVERBS_METHOD_JOB_EXPORT,
+ UVERBS_ATTR_IDR(UVERBS_ATTR_EXPORT_JOB_HANDLE,
+ UVERBS_OBJECT_JOB,
+ UVERBS_ACCESS_READ,
+ UA_MANDATORY),
+ UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_EXPORT_JOB_FD,
+ UVERBS_ATTR_TYPE(s32),
+ UA_MANDATORY));
+
+DECLARE_UVERBS_NAMED_METHOD(
+ UVERBS_METHOD_JOB_IMPORT,
+ UVERBS_ATTR_IDR(UVERBS_ATTR_IMPORT_JOB_HANDLE,
+ UVERBS_OBJECT_JOB,
+ UVERBS_ACCESS_NEW,
+ UA_MANDATORY),
+ UVERBS_ATTR_PTR_IN(UVERBS_ATTR_IMPORT_JOB_FD,
+ UVERBS_ATTR_TYPE(s32),
+ UA_MANDATORY));
+
+DECLARE_UVERBS_NAMED_METHOD_DESTROY(
+ UVERBS_METHOD_JOB_DEALLOC,
+ UVERBS_ATTR_IDR(UVERBS_ATTR_DEALLOC_JOB_HANDLE,
+ UVERBS_OBJECT_JOB,
+ UVERBS_ACCESS_DESTROY,
+ UA_MANDATORY));
+
+DECLARE_UVERBS_NAMED_METHOD(
+ UVERBS_METHOD_JOB_QUERY,
+ UVERBS_ATTR_IDR(UVERBS_ATTR_QUERY_JOB_HANDLE,
+ UVERBS_OBJECT_JOB,
+ UVERBS_ACCESS_READ,
+ UA_MANDATORY),
+ UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_QUERY_JOB_ID,
+ UVERBS_ATTR_TYPE(u32),
+ UA_MANDATORY),
+ UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_QUERY_JOB_MAX_ADDR_ENTRIES,
+ UVERBS_ATTR_TYPE(u32),
+ UA_MANDATORY),
+ UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_QUERY_JOB_FLAGS,
+ UVERBS_ATTR_TYPE(u32),
+ UA_MANDATORY),
+ UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_QUERY_JOB_PORT_NUM,
+ UVERBS_ATTR_TYPE(u32),
+ UA_MANDATORY),
+ UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_QUERY_JOB_SGID_INDEX,
+ UVERBS_ATTR_TYPE(u8),
+ UA_MANDATORY));
+
+DECLARE_UVERBS_NAMED_OBJECT(UVERBS_OBJECT_JOB,
+ UVERBS_TYPE_ALLOC_IDR_SZ(
+ sizeof(struct ib_ujob_object),
+ uverbs_free_job),
+ &UVERBS_METHOD(UVERBS_METHOD_JOB_ALLOC),
+ &UVERBS_METHOD(UVERBS_METHOD_JOB_DEALLOC),
+ &UVERBS_METHOD(UVERBS_METHOD_JOB_QUERY),
+ &UVERBS_METHOD(UVERBS_METHOD_JOB_ADDR_INSERT),
+ &UVERBS_METHOD(UVERBS_METHOD_JOB_ADDR_REMOVE),
+ &UVERBS_METHOD(UVERBS_METHOD_JOB_ADDR_QUERY),
+ &UVERBS_METHOD(UVERBS_METHOD_JOB_EXPORT),
+ &UVERBS_METHOD(UVERBS_METHOD_JOB_IMPORT));
+
+const struct uapi_definition uverbs_def_obj_job[] = {
+ UAPI_DEF_CHAIN_OBJ_TREE_NAMED(UVERBS_OBJECT_JOB,
+ UAPI_DEF_OBJ_NEEDS_FN(dealloc_job)),
+ {}
+};
+
+/* drop what JKEY_CREATE took on the key's job and PD */
+static void ib_jkey_put_refs(struct ib_jkey *jkey)
+{
+ atomic_dec(&jkey->pd->usecnt);
+ atomic_dec(&container_of(jkey->job_uobject, struct ib_ujob_object,
+ uobject)->refcnt);
+ kref_put(&jkey->job->ref, ib_job_release);
+}
+
+static int uverbs_free_jkey(struct ib_uobject *uobject,
+ enum rdma_remove_reason why,
+ struct uverbs_attr_bundle *attrs)
+{
+ struct ib_jkey *jkey = uobject->object;
+ int ret;
+
+ /* queue pairs created with this key still hold it */
+ if (atomic_read(&jkey->usecnt))
+ return -EBUSY;
+
+ rdma_restrack_begin_del(&jkey->res);
+
+ ret = jkey->device->ops.destroy_jkey(jkey, attrs);
+ if (ret) {
+ rdma_restrack_abort_del(&jkey->res);
+ return ret;
+ }
+
+ rdma_restrack_commit_del(&jkey->res);
+
+ ib_jkey_put_refs(jkey);
+ kfree(jkey);
+
+ return 0;
+}
+
+static int UVERBS_HANDLER(UVERBS_METHOD_JKEY_CREATE)(
+ struct uverbs_attr_bundle *attrs)
+{
+ struct ib_uobject *uobj =
+ uverbs_attr_get_uobject(attrs, UVERBS_ATTR_CREATE_JKEY_HANDLE);
+ struct ib_uobject *job_uobj = uverbs_attr_get_uobject(
+ attrs, UVERBS_ATTR_CREATE_JKEY_JOB_HANDLE);
+ struct ib_pd *pd =
+ uverbs_attr_get_obj(attrs, UVERBS_ATTR_CREATE_JKEY_PD_HANDLE);
+ struct ib_job *job;
+ struct ib_device *ib_dev = attrs->context->device;
+ struct ib_jkey *jkey;
+ u32 flags = 0;
+ int ret;
+
+ if (!ib_dev->ops.create_jkey)
+ return -EOPNOTSUPP;
+
+ if (IS_ERR(job_uobj))
+ return PTR_ERR(job_uobj);
+
+ if (IS_ERR(pd))
+ return PTR_ERR(pd);
+
+ job = job_uobj->object;
+
+ ret = uverbs_copy_from(&flags, attrs, UVERBS_ATTR_CREATE_JKEY_FLAGS);
+ if (IS_UVERBS_COPY_ERR(ret))
+ return ret;
+
+ if (flags)
+ return -EINVAL;
+
+ jkey = rdma_zalloc_drv_obj(ib_dev, ib_jkey);
+ if (!jkey)
+ return -ENOMEM;
+
+ jkey->device = ib_dev;
+ jkey->uobject = uobj;
+ jkey->job = job;
+ jkey->job_uobject = job_uobj;
+ jkey->pd = pd;
+ atomic_set(&jkey->usecnt, 0);
+ uobj->object = jkey;
+
+ rdma_restrack_new(&jkey->res, RDMA_RESTRACK_JKEY);
+ rdma_restrack_set_name(&jkey->res, NULL);
+
+ ret = ib_dev->ops.create_jkey(jkey, flags, attrs);
+ if (ret) {
+ rdma_restrack_put(&jkey->res);
+ kfree(jkey);
+ return ret;
+ }
+
+ rdma_restrack_add(&jkey->res);
+
+ /* the job and PD cannot be freed while this key references them */
+ kref_get(&job->ref);
+ atomic_inc(&container_of(job_uobj, struct ib_ujob_object,
+ uobject)->refcnt);
+ atomic_inc(&pd->usecnt);
+
+ ret = uverbs_copy_to(attrs, UVERBS_ATTR_CREATE_JKEY_JKEY, &jkey->jkey,
+ sizeof(jkey->jkey));
+ if (ret) {
+ rdma_restrack_del(&jkey->res);
+ ib_dev->ops.destroy_jkey(jkey, attrs);
+ ib_jkey_put_refs(jkey);
+ kfree(jkey);
+ return ret;
+ }
+
+ return 0;
+}
+
+DECLARE_UVERBS_NAMED_METHOD(
+ UVERBS_METHOD_JKEY_CREATE,
+ UVERBS_ATTR_IDR(UVERBS_ATTR_CREATE_JKEY_HANDLE,
+ UVERBS_OBJECT_JKEY,
+ UVERBS_ACCESS_NEW,
+ UA_MANDATORY),
+ UVERBS_ATTR_IDR(UVERBS_ATTR_CREATE_JKEY_JOB_HANDLE,
+ UVERBS_OBJECT_JOB,
+ UVERBS_ACCESS_READ,
+ UA_MANDATORY),
+ UVERBS_ATTR_IDR(UVERBS_ATTR_CREATE_JKEY_PD_HANDLE,
+ UVERBS_OBJECT_PD,
+ UVERBS_ACCESS_READ,
+ UA_MANDATORY),
+ UVERBS_ATTR_PTR_IN(UVERBS_ATTR_CREATE_JKEY_FLAGS,
+ UVERBS_ATTR_TYPE(u32),
+ UA_OPTIONAL),
+ UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_CREATE_JKEY_JKEY,
+ UVERBS_ATTR_TYPE(u32),
+ UA_MANDATORY),
+ UVERBS_ATTR_UHW());
+
+DECLARE_UVERBS_NAMED_METHOD_DESTROY(
+ UVERBS_METHOD_JKEY_DESTROY,
+ UVERBS_ATTR_IDR(UVERBS_ATTR_DESTROY_JKEY_HANDLE,
+ UVERBS_OBJECT_JKEY,
+ UVERBS_ACCESS_DESTROY,
+ UA_MANDATORY));
+
+DECLARE_UVERBS_NAMED_OBJECT(UVERBS_OBJECT_JKEY,
+ UVERBS_TYPE_ALLOC_IDR(uverbs_free_jkey),
+ &UVERBS_METHOD(UVERBS_METHOD_JKEY_CREATE),
+ &UVERBS_METHOD(UVERBS_METHOD_JKEY_DESTROY));
+
+const struct uapi_definition uverbs_def_obj_jkey[] = {
+ UAPI_DEF_CHAIN_OBJ_TREE_NAMED(UVERBS_OBJECT_JKEY,
+ UAPI_DEF_OBJ_NEEDS_FN(destroy_jkey)),
+ {}
+};
diff --git a/drivers/infiniband/core/uverbs_uapi.c b/drivers/infiniband/core/uverbs_uapi.c
index b67db3790f03..ee84be3061ea 100644
--- a/drivers/infiniband/core/uverbs_uapi.c
+++ b/drivers/infiniband/core/uverbs_uapi.c
@@ -636,6 +636,8 @@ static const struct uapi_definition uverbs_core_api[] = {
UAPI_DEF_CHAIN(uverbs_def_obj_dmah),
UAPI_DEF_CHAIN(uverbs_def_obj_flow_action),
UAPI_DEF_CHAIN(uverbs_def_obj_intf),
+ UAPI_DEF_CHAIN(uverbs_def_obj_job),
+ UAPI_DEF_CHAIN(uverbs_def_obj_jkey),
UAPI_DEF_CHAIN(uverbs_def_obj_mr),
UAPI_DEF_CHAIN(uverbs_def_obj_qp),
UAPI_DEF_CHAIN(uverbs_def_obj_srq),
diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c
index 8ce7e376112c..0a5340642abb 100644
--- a/drivers/infiniband/core/verbs.c
+++ b/drivers/infiniband/core/verbs.c
@@ -690,6 +690,44 @@ struct ib_ah *rdma_create_user_ah(struct ib_pd *pd,
}
EXPORT_SYMBOL(rdma_create_user_ah);
+/**
+ * rdma_job_addr_insert - Insert a peer into a job's address table
+ * @job: The job.
+ * @index: The entry in the job's address table.
+ * @ah_attr: The peer's address and queue pair.
+ * @flags: Insert flags.
+ *
+ * The address is resolved as rdma_create_user_ah() resolves an address
+ * handle's: the source GID attribute is filled in, and for an Ethernet
+ * address so is the destination MAC.
+ *
+ * Return: 0 on success, or a negative errno.
+ */
+int rdma_job_addr_insert(struct ib_job *job, u32 index,
+ struct rdma_ah_attr_ex *ah_attr, u32 flags)
+{
+ struct rdma_ah_attr *attr = &ah_attr->ah_attr;
+ const struct ib_gid_attr *old_sgid_attr;
+ int ret;
+
+ ret = rdma_fill_sgid_attr(job->device, attr, &old_sgid_attr);
+ if (ret)
+ return ret;
+
+ if (attr->type == RDMA_AH_ATTR_TYPE_ROCE) {
+ ret = ib_resolve_eth_dmac(job->device, attr);
+ if (ret)
+ goto out;
+ }
+
+ ret = job->device->ops.job_addr_insert(job, index, ah_attr, flags);
+
+out:
+ rdma_unfill_sgid_attr(attr, old_sgid_attr);
+ return ret;
+}
+EXPORT_SYMBOL(rdma_job_addr_insert);
+
int ib_get_rdma_header_version(const union rdma_network_hdr *hdr)
{
const struct iphdr *ip4h = (struct iphdr *)&hdr->roce4grh;
diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h
index 003b70424429..6aa8e7120e71 100644
--- a/include/rdma/ib_verbs.h
+++ b/include/rdma/ib_verbs.h
@@ -2496,6 +2496,43 @@ struct ib_counters {
atomic_t usecnt;
};
+struct ib_job_attr {
+ u32 job_id;
+ u32 max_addr_entries;
+ u32 flags;
+ u32 port_num;
+ u8 sgid_index;
+};
+
+/* A job is a receive-side demultiplexing domain of an unconnected transport.
+ * A JobID names it on the wire, memory regions bind to it, and job keys
+ * authorize queue pairs within it.
+ */
+struct ib_job {
+ struct ib_device *device;
+ /* exports, job keys, regions, and queue pairs referencing this job */
+ struct kref ref;
+ u32 job_id;
+ struct rdma_restrack_entry res;
+ /* serializes release against revoke at device unregister */
+ struct mutex lock;
+ bool revoked;
+};
+
+/* A job key ties a job to a PD and authorizes a queue pair to act within a
+ * job. It holds a reference to its job and PD.
+ */
+struct ib_jkey {
+ struct ib_device *device;
+ struct ib_uobject *uobject;
+ struct ib_job *job;
+ struct ib_uobject *job_uobject;
+ struct ib_pd *pd;
+ u32 jkey;
+ atomic_t usecnt;
+ struct rdma_restrack_entry res;
+};
+
/* what a queue pair of a given type guarantees (query_qp_semantics) */
struct ib_qp_semantics {
u32 comp_mask; /* enum ib_uverbs_qp_semantics_mask */
@@ -2830,6 +2867,20 @@ struct ib_device_ops {
struct ib_mr *(*reg_dm_mr)(struct ib_pd *pd, struct ib_dm *dm,
struct ib_dm_mr_attr *attr,
struct uverbs_attr_bundle *attrs);
+ /* job_addr_* may run concurrently on one job; the driver serializes */
+ int (*alloc_job)(struct ib_job *job, struct ib_job_attr *attr,
+ struct uverbs_attr_bundle *attrs);
+ void (*dealloc_job)(struct ib_job *job);
+ int (*query_job)(struct ib_job *job, struct ib_job_attr *attr);
+ int (*job_addr_insert)(struct ib_job *job, u32 index,
+ struct rdma_ah_attr_ex *ah_attr, u32 flags);
+ int (*job_addr_remove)(struct ib_job *job, u32 index, u32 flags);
+ int (*job_addr_query)(struct ib_job *job, u32 index,
+ struct rdma_ah_attr_ex *ah_attr, u32 flags);
+ int (*create_jkey)(struct ib_jkey *jkey, u32 flags,
+ struct uverbs_attr_bundle *attrs);
+ int (*destroy_jkey)(struct ib_jkey *jkey,
+ struct uverbs_attr_bundle *attrs);
int (*create_counters)(struct ib_counters *counters,
struct uverbs_attr_bundle *attrs);
int (*destroy_counters)(struct ib_counters *counters);
@@ -2983,6 +3034,8 @@ struct ib_device_ops {
DECLARE_RDMA_OBJ_SIZE(ib_ah);
DECLARE_RDMA_OBJ_SIZE(ib_counters);
+ DECLARE_RDMA_OBJ_SIZE(ib_job);
+ DECLARE_RDMA_OBJ_SIZE(ib_jkey);
DECLARE_RDMA_OBJ_SIZE(ib_cq);
DECLARE_RDMA_OBJ_SIZE(ib_comp_cntr);
DECLARE_RDMA_OBJ_SIZE(ib_dmah);
@@ -3930,6 +3983,23 @@ struct ib_ah *rdma_create_ah(struct ib_pd *pd, struct rdma_ah_attr *ah_attr,
struct ib_ah *rdma_create_user_ah(struct ib_pd *pd,
struct rdma_ah_init_attr *init_attr,
struct ib_udata *udata);
+
+/**
+ * rdma_job_addr_insert - Insert a peer into a job's address table
+ * @job: The job.
+ * @index: The entry in the job's address table.
+ * @ah_attr: The peer's address and queue pair.
+ * @flags: Insert flags.
+ *
+ * The address is resolved as rdma_create_user_ah() resolves an address
+ * handle's: the source GID attribute is filled in, and for an Ethernet
+ * address so is the destination MAC.
+ *
+ * Return: 0 on success, or a negative errno.
+ */
+int rdma_job_addr_insert(struct ib_job *job, u32 index,
+ struct rdma_ah_attr_ex *ah_attr, u32 flags);
+
/**
* ib_get_gids_from_rdma_hdr - Get sgid and dgid from GRH or IPv4 header
* work completion.
diff --git a/include/rdma/restrack.h b/include/rdma/restrack.h
index 47fec3083d28..a7d53540da67 100644
--- a/include/rdma/restrack.h
+++ b/include/rdma/restrack.h
@@ -64,6 +64,14 @@ enum rdma_restrack_type {
* @RDMA_RESTRACK_COMP_CNTR: Completion Counter
*/
RDMA_RESTRACK_COMP_CNTR,
+ /**
+ * @RDMA_RESTRACK_JOB: Transport job
+ */
+ RDMA_RESTRACK_JOB,
+ /**
+ * @RDMA_RESTRACK_JKEY: Job key
+ */
+ RDMA_RESTRACK_JKEY,
/**
* @RDMA_RESTRACK_MAX: Last entry, used for array dclarations
*/
diff --git a/include/rdma/uverbs_ioctl.h b/include/rdma/uverbs_ioctl.h
index f4d93bc3625a..f19ed5e956a6 100644
--- a/include/rdma/uverbs_ioctl.h
+++ b/include/rdma/uverbs_ioctl.h
@@ -147,7 +147,7 @@ enum uapi_radix_data {
UVERBS_API_OBJ_KEY_BITS = 6,
UVERBS_API_OBJ_KEY_SHIFT =
UVERBS_API_METHOD_KEY_BITS + UVERBS_API_METHOD_KEY_SHIFT,
- UVERBS_API_OBJ_KEY_NUM_CORE = 20,
+ UVERBS_API_OBJ_KEY_NUM_CORE = 22,
UVERBS_API_OBJ_KEY_NUM_DRIVER =
(1 << UVERBS_API_OBJ_KEY_BITS) - UVERBS_API_OBJ_KEY_NUM_CORE,
UVERBS_API_OBJ_KEY_MASK = GENMASK(31, UVERBS_API_OBJ_KEY_SHIFT),
diff --git a/include/uapi/rdma/ib_user_ioctl_cmds.h b/include/uapi/rdma/ib_user_ioctl_cmds.h
index fb746257eb2c..29abfbd227bc 100644
--- a/include/uapi/rdma/ib_user_ioctl_cmds.h
+++ b/include/uapi/rdma/ib_user_ioctl_cmds.h
@@ -58,6 +58,8 @@ enum uverbs_default_objects {
UVERBS_OBJECT_DMAH,
UVERBS_OBJECT_DMABUF,
UVERBS_OBJECT_COMP_CNTR,
+ UVERBS_OBJECT_JOB,
+ UVERBS_OBJECT_JKEY,
};
enum {
@@ -495,4 +497,84 @@ enum uverbs_attrs_read_comp_cntr_cmd_attr_ids {
UVERBS_ATTR_READ_COMP_CNTR_RESP_VALUE,
};
+enum uverbs_methods_job {
+ UVERBS_METHOD_JOB_ALLOC,
+ UVERBS_METHOD_JOB_DEALLOC,
+ UVERBS_METHOD_JOB_QUERY,
+ UVERBS_METHOD_JOB_ADDR_INSERT,
+ UVERBS_METHOD_JOB_ADDR_REMOVE,
+ UVERBS_METHOD_JOB_ADDR_QUERY,
+ UVERBS_METHOD_JOB_EXPORT,
+ UVERBS_METHOD_JOB_IMPORT,
+};
+
+enum uverbs_attrs_alloc_job_cmd_attr_ids {
+ UVERBS_ATTR_ALLOC_JOB_HANDLE,
+ UVERBS_ATTR_ALLOC_JOB_ID,
+ UVERBS_ATTR_ALLOC_JOB_MAX_ADDR_ENTRIES,
+ UVERBS_ATTR_ALLOC_JOB_FLAGS,
+ UVERBS_ATTR_ALLOC_JOB_PORT_NUM,
+ UVERBS_ATTR_ALLOC_JOB_SGID_INDEX,
+};
+
+enum uverbs_attrs_dealloc_job_cmd_attr_ids {
+ UVERBS_ATTR_DEALLOC_JOB_HANDLE,
+};
+
+enum uverbs_attrs_query_job_cmd_attr_ids {
+ UVERBS_ATTR_QUERY_JOB_HANDLE,
+ UVERBS_ATTR_QUERY_JOB_ID,
+ UVERBS_ATTR_QUERY_JOB_MAX_ADDR_ENTRIES,
+ UVERBS_ATTR_QUERY_JOB_FLAGS,
+ UVERBS_ATTR_QUERY_JOB_PORT_NUM,
+ UVERBS_ATTR_QUERY_JOB_SGID_INDEX,
+};
+
+enum uverbs_attrs_job_addr_insert_cmd_attr_ids {
+ UVERBS_ATTR_JOB_ADDR_INSERT_HANDLE,
+ UVERBS_ATTR_JOB_ADDR_INSERT_INDEX,
+ UVERBS_ATTR_JOB_ADDR_INSERT_AH_ATTR_EX,
+ UVERBS_ATTR_JOB_ADDR_INSERT_FLAGS,
+};
+
+enum uverbs_attrs_job_addr_remove_cmd_attr_ids {
+ UVERBS_ATTR_JOB_ADDR_REMOVE_HANDLE,
+ UVERBS_ATTR_JOB_ADDR_REMOVE_INDEX,
+ UVERBS_ATTR_JOB_ADDR_REMOVE_FLAGS,
+};
+
+enum uverbs_attrs_job_addr_query_cmd_attr_ids {
+ UVERBS_ATTR_JOB_ADDR_QUERY_HANDLE,
+ UVERBS_ATTR_JOB_ADDR_QUERY_INDEX,
+ UVERBS_ATTR_JOB_ADDR_QUERY_AH_ATTR_EX,
+ UVERBS_ATTR_JOB_ADDR_QUERY_FLAGS,
+};
+
+enum uverbs_attrs_export_job_cmd_attr_ids {
+ UVERBS_ATTR_EXPORT_JOB_HANDLE,
+ UVERBS_ATTR_EXPORT_JOB_FD,
+};
+
+enum uverbs_attrs_import_job_cmd_attr_ids {
+ UVERBS_ATTR_IMPORT_JOB_HANDLE,
+ UVERBS_ATTR_IMPORT_JOB_FD,
+};
+
+enum uverbs_methods_jkey {
+ UVERBS_METHOD_JKEY_CREATE,
+ UVERBS_METHOD_JKEY_DESTROY,
+};
+
+enum uverbs_attrs_create_jkey_cmd_attr_ids {
+ UVERBS_ATTR_CREATE_JKEY_HANDLE,
+ UVERBS_ATTR_CREATE_JKEY_JOB_HANDLE,
+ UVERBS_ATTR_CREATE_JKEY_PD_HANDLE,
+ UVERBS_ATTR_CREATE_JKEY_FLAGS,
+ UVERBS_ATTR_CREATE_JKEY_JKEY,
+};
+
+enum uverbs_attrs_destroy_jkey_cmd_attr_ids {
+ UVERBS_ATTR_DESTROY_JKEY_HANDLE,
+};
+
#endif
--
2.56.0
next prev parent reply other threads:[~2026-10-14 22:21 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-14 22:21 [PATCH rdma-next 00/13] RDMA/core: Ultra Ethernet transport verbs Eric Davis via B4 Relay
2026-10-14 22:21 ` [PATCH rdma-next 01/13] RDMA/core: widen the uverbs object key Eric Davis via B4 Relay
2026-10-14 22:21 ` [PATCH rdma-next 02/13] RDMA/core: add the UET GID types Eric Davis via B4 Relay
2026-10-14 22:21 ` [PATCH rdma-next 03/13] RDMA/core: report the reliable unconnected device capabilities Eric Davis via B4 Relay
2026-10-14 22:21 ` [PATCH rdma-next 04/13] RDMA/core: add the reliable unconnected queue pair type Eric Davis via B4 Relay
2026-10-14 22:21 ` [PATCH rdma-next 05/13] RDMA/core: extended address handle attributes Eric Davis via B4 Relay
2026-10-14 22:21 ` [PATCH rdma-next 06/13] RDMA/core: query the semantics a queue pair type guarantees Eric Davis via B4 Relay
2026-10-14 22:21 ` Eric Davis via B4 Relay [this message]
2026-10-14 22:21 ` [PATCH rdma-next 08/13] RDMA/uverbs: factor out the queue pair attribute copy Eric Davis via B4 Relay
2026-10-14 22:21 ` [PATCH rdma-next 09/13] RDMA/core: create a queue pair ready to send Eric Davis via B4 Relay
2026-10-14 22:21 ` [PATCH rdma-next 10/13] RDMA/core: queue pair attributes for unconnected transports Eric Davis via B4 Relay
2026-10-14 22:21 ` [PATCH rdma-next 11/13] RDMA/core: extended memory registration Eric Davis via B4 Relay
2026-10-14 22:21 ` [PATCH rdma-next 12/13] RDMA/core: 64-bit memory keys Eric Davis via B4 Relay
2026-10-14 22:21 ` [PATCH rdma-next 13/13] RDMA/core: attach a memory region to a queue pair Eric Davis via B4 Relay
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261014-uet-ib-core-v1-7-a0e626117113@broadcom.com \
--to=devnull+eric.davis.broadcom.com@kernel.org \
--cc=eric.davis@broadcom.com \
--cc=jgg@ziepe.ca \
--cc=leon@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-rdma@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®