mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge tag 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rdma/rdma
Pull rdma updates from Jason Gunthorpe:
"Many AI driven bug fixes, and several big driver API cleanups
- Driver bug fixes and minor cleanups in mlx5, hns, rxe, efa, siw,
rtrs, mana, irdma, mlx4. Commonly error path flows, integer
arithmetic overflows on unsafe data, out of bounds access, and use
after free issues under races.
- Second half of the new udata API for drivers focusing on uAPI
response
- bnxt_re supports more options for QP creation that will allow a dv
path in rdma-core
- Untangle the module dependencies so drivers don't link to
ib_uverbs.ko as was originall intended
- Provide a new way to handle umems with a consistent simplified uAPI
and update several drivers to use it. This brings dmabuf support to
more places and more drivers
- Support for mlx5 rate limit and packet pacing for UD and UC
- A batch of fixes for the new shared FRMR pools infrastructure"
* tag 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rdma/rdma: (148 commits)
RDMA/irdma: Replace waitqueue and flag with completion
RDMA/hns: Fix memory leak of bonding resources
RDMA/rtrs-srv: Bound RDMA-Write length to chunk size in rdma_write_sg
docs: infiniband: correct name of option to enable the ib_uverbs module
RDMA/bnxt_re: Reject GET_TOGGLE_MEM when toggle page was not allocated
RDMA/bnxt_re: Fail DBR related page allocation UAPIs if the feature is disabled
RDMA/bnxt_re: Avoid repeated requests to allocate WC pages
RDMA/bnxt_re: Proper rollback if the ioremap fails
RDMA/bnxt_re: Add a max slot check for SQ
RDMA/bnxt_re: Avoid displaying the kernel pointer
RDMA/bnxt_re: Free CQ toggle page after firmware teardown
RDMA/bnxt_re: Free SRQ toggle page after firmware teardown
RDMA/bnxt_re: Initialize dpi variable to zero
ABI: sysfs-class-infiniband: minor cleanup
RDMA/mlx5: Release the HW‑provided UAR index rather than the SW one
RDMA/mlx5: Fix undefined shift of user RQ WQE size
RDMA/mlx5: Remove raw RSS QP restrack tracking
RDMA/mlx5: Remove DCT restrack tracking
RDMA/mlx5: Drop FRMR pool handle on UMR revoke failure
RDMA/core: Add ib_frmr_pool_drop for unrecoverable handles
...
This commit is contained in:
@@ -148,17 +148,17 @@ Description:
|
||||
**Data info**:
|
||||
|
||||
port_xmit_data: (RO) Total number of data octets, divided by 4
|
||||
(lanes), transmitted on all VLs. This is 64 bit counter
|
||||
(lanes), transmitted on all VLs. This is a 64-bit counter
|
||||
|
||||
port_rcv_data: (RO) Total number of data octets, divided by 4
|
||||
(lanes), received on all VLs. This is 64 bit counter.
|
||||
(lanes), received on all VLs. This is a 64-bit counter.
|
||||
|
||||
port_xmit_packets: (RO) Total number of packets transmitted on
|
||||
all VLs from this port. This may include packets with errors.
|
||||
This is 64 bit counter.
|
||||
This is a 64-bit counter.
|
||||
|
||||
port_rcv_packets: (RO) Total number of packets (this may include
|
||||
packets containing Errors. This is 64 bit counter.
|
||||
packets containing Errors). This is a 64-bit counter.
|
||||
|
||||
link_downed: (RO) Total number of times the Port Training state
|
||||
machine has failed the link error recovery process and downed
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Userspace verbs access
|
||||
======================
|
||||
|
||||
The ib_uverbs module, built by enabling CONFIG_INFINIBAND_USER_VERBS,
|
||||
The ib_uverbs module, built by enabling CONFIG_INFINIBAND_USER_ACCESS,
|
||||
enables direct userspace access to IB hardware via "verbs," as
|
||||
described in chapter 11 of the InfiniBand Architecture Specification.
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
用户空间verbs访问
|
||||
=================
|
||||
|
||||
ib_uverbs模块,通过启用CONFIG_INFINIBAND_USER_VERBS构建,使用户空间
|
||||
ib_uverbs模块,通过启用CONFIG_INFINIBAND_USER_ACCESS构建,使用户空间
|
||||
通过“verbs”直接访问IB硬件,如InfiniBand架构规范第11章所述。
|
||||
|
||||
要使用verbs,需要libibverbs库,可从https://github.com/linux-rdma/rdma-core。
|
||||
|
||||
@@ -12937,7 +12937,6 @@ F: include/linux/avf/virtchnl.h
|
||||
F: include/linux/net/intel/*/
|
||||
|
||||
INTEL ETHERNET PROTOCOL DRIVER FOR RDMA
|
||||
M: Krzysztof Czurylo <krzysztof.czurylo@intel.com>
|
||||
M: Tatyana Nikolova <tatyana.e.nikolova@intel.com>
|
||||
L: linux-rdma@vger.kernel.org
|
||||
S: Supported
|
||||
|
||||
@@ -37,6 +37,10 @@ config INFINIBAND_USER_ACCESS
|
||||
libibverbs, libibcm and a hardware driver library from
|
||||
rdma-core <https://github.com/linux-rdma/rdma-core>.
|
||||
|
||||
config INFINIBAND_USER_ACCESS_CORE
|
||||
bool
|
||||
default y if INFINIBAND_USER_ACCESS != n
|
||||
|
||||
config INFINIBAND_USER_MEM
|
||||
bool
|
||||
depends on INFINIBAND_USER_ACCESS != n
|
||||
|
||||
@@ -5,19 +5,22 @@ user_access-$(CONFIG_INFINIBAND_ADDR_TRANS) := rdma_ucm.o
|
||||
obj-$(CONFIG_INFINIBAND) += ib_core.o ib_cm.o iw_cm.o \
|
||||
$(infiniband-y)
|
||||
obj-$(CONFIG_INFINIBAND_USER_MAD) += ib_umad.o
|
||||
obj-$(CONFIG_INFINIBAND_USER_ACCESS) += ib_uverbs.o $(user_access-y)
|
||||
obj-$(CONFIG_INFINIBAND_USER_ACCESS) += ib_uverbs.o \
|
||||
$(user_access-y) \
|
||||
ib_uverbs_support.o
|
||||
|
||||
ib_core-y := packer.o ud_header.o verbs.o cq.o rw.o sysfs.o \
|
||||
device.o cache.o netlink.o \
|
||||
roce_gid_mgmt.o mr_pool.o addr.o sa_query.o \
|
||||
multicast.o mad.o smi.o agent.o mad_rmpp.o \
|
||||
nldev.o restrack.o counters.o ib_core_uverbs.o \
|
||||
nldev.o restrack.o counters.o \
|
||||
trace.o lag.o iter.o frmr_pools.o
|
||||
|
||||
ib_core-$(CONFIG_SECURITY_INFINIBAND) += security.o
|
||||
ib_core-$(CONFIG_CGROUP_RDMA) += cgroup.o
|
||||
ib_core-$(CONFIG_INFINIBAND_USER_MEM) += umem.o umem_dmabuf.o
|
||||
ib_core-$(CONFIG_INFINIBAND_ON_DEMAND_PAGING) += umem_odp.o
|
||||
ib_core-$(CONFIG_INFINIBAND_USER_ACCESS_CORE) += ib_core_uverbs.o
|
||||
|
||||
ib_cm-y := cm.o cm_trace.o
|
||||
|
||||
@@ -33,7 +36,7 @@ rdma_ucm-y := ucma.o
|
||||
ib_umad-y := user_mad.o
|
||||
|
||||
ib_uverbs-y := uverbs_main.o uverbs_cmd.o uverbs_marshall.o \
|
||||
rdma_core.o uverbs_std_types.o uverbs_ioctl.o \
|
||||
uverbs_std_types.o uverbs_ioctl.o \
|
||||
uverbs_std_types_cq.o \
|
||||
uverbs_std_types_dmabuf.o \
|
||||
uverbs_std_types_dmah.o \
|
||||
@@ -43,5 +46,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 \
|
||||
ucaps.o
|
||||
uverbs_std_types_qp.o
|
||||
|
||||
ib_uverbs_support-y := rdma_core.o \
|
||||
ucaps.o \
|
||||
uverbs_flow.o
|
||||
|
||||
@@ -438,7 +438,7 @@ static int addr6_resolve(struct sockaddr *src_sock,
|
||||
static bool is_dst_local(const struct dst_entry *dst)
|
||||
{
|
||||
if (dst->ops->family == AF_INET)
|
||||
return !!(dst_rtable(dst)->rt_type & RTN_LOCAL);
|
||||
return dst_rtable(dst)->rt_type == RTN_LOCAL;
|
||||
else if (dst->ops->family == AF_INET6)
|
||||
return !!(dst_rt6_info(dst)->rt6i_flags & RTF_LOCAL);
|
||||
else
|
||||
@@ -850,7 +850,7 @@ static struct notifier_block nb = {
|
||||
|
||||
int addr_init(void)
|
||||
{
|
||||
addr_wq = alloc_ordered_workqueue("ib_addr", 0);
|
||||
addr_wq = alloc_workqueue("ib_addr", WQ_UNBOUND, 0);
|
||||
if (!addr_wq)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
@@ -530,6 +530,7 @@ static int cm_init_av_by_path(struct sa_path_rec *path,
|
||||
struct rdma_ah_attr new_ah_attr;
|
||||
struct cm_device *cm_dev;
|
||||
struct cm_port *port;
|
||||
u16 pkey_index;
|
||||
int ret;
|
||||
|
||||
port = get_cm_port_from_path(path, sgid_attr);
|
||||
@@ -538,12 +539,10 @@ static int cm_init_av_by_path(struct sa_path_rec *path,
|
||||
cm_dev = port->cm_dev;
|
||||
|
||||
ret = ib_find_cached_pkey(cm_dev->ib_device, port->port_num,
|
||||
be16_to_cpu(path->pkey), &av->pkey_index);
|
||||
be16_to_cpu(path->pkey), &pkey_index);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cm_set_av_port(av, port);
|
||||
|
||||
/*
|
||||
* av->ah_attr might be initialized based on wc or during
|
||||
* request processing time which might have reference to sgid_attr.
|
||||
@@ -558,6 +557,8 @@ static int cm_init_av_by_path(struct sa_path_rec *path,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
av->pkey_index = pkey_index;
|
||||
cm_set_av_port(av, port);
|
||||
av->timeout = path->packet_life_time + 1;
|
||||
rdma_move_ah_attr(&av->ah_attr, &new_ah_attr);
|
||||
return 0;
|
||||
@@ -2184,8 +2185,10 @@ static int cm_req_handler(struct cm_work *work)
|
||||
cm_id_priv->av.ah_attr.roce.dmac);
|
||||
work->path[0].hop_limit = grh->hop_limit;
|
||||
|
||||
/* This destroy call is needed to pair with cm_init_av_for_response */
|
||||
cm_destroy_av(&cm_id_priv->av);
|
||||
/*
|
||||
* cm_init_av_by_path() will internally pair with the above
|
||||
* cm_init_av_for_response() if it succeeds.
|
||||
*/
|
||||
ret = cm_init_av_by_path(&work->path[0], gid_attr, &cm_id_priv->av);
|
||||
if (ret) {
|
||||
int err;
|
||||
|
||||
@@ -255,7 +255,7 @@ static void release_cma_ports_group(struct config_item *item)
|
||||
cma_dev_group->ports = NULL;
|
||||
};
|
||||
|
||||
static struct configfs_item_operations cma_ports_item_ops = {
|
||||
static const struct configfs_item_operations cma_ports_item_ops = {
|
||||
.release = release_cma_ports_group
|
||||
};
|
||||
|
||||
@@ -264,7 +264,7 @@ static const struct config_item_type cma_ports_group_type = {
|
||||
.ct_owner = THIS_MODULE
|
||||
};
|
||||
|
||||
static struct configfs_item_operations cma_device_item_ops = {
|
||||
static const struct configfs_item_operations cma_device_item_ops = {
|
||||
.release = release_cma_dev
|
||||
};
|
||||
|
||||
@@ -327,7 +327,7 @@ static void drop_cma_dev(struct config_group *cgroup, struct config_item *item)
|
||||
config_item_put(item);
|
||||
}
|
||||
|
||||
static struct configfs_group_operations cma_subsys_group_ops = {
|
||||
static const struct configfs_group_operations cma_subsys_group_ops = {
|
||||
.make_group = make_cma_dev,
|
||||
.drop_item = drop_cma_dev,
|
||||
};
|
||||
|
||||
@@ -321,7 +321,7 @@ void nldev_exit(void);
|
||||
|
||||
struct ib_qp *ib_create_qp_user(struct ib_device *dev, struct ib_pd *pd,
|
||||
struct ib_qp_init_attr *attr,
|
||||
struct ib_udata *udata,
|
||||
struct uverbs_attr_bundle *uattrs,
|
||||
struct ib_uqp_object *uobj, const char *caller);
|
||||
|
||||
void ib_qp_usecnt_inc(struct ib_qp *qp);
|
||||
|
||||
@@ -198,12 +198,20 @@ static struct rdma_counter *alloc_and_bind(struct ib_device *dev, u32 port,
|
||||
|
||||
ret = __rdma_counter_bind_qp(counter, qp, port);
|
||||
if (ret)
|
||||
goto err_mode;
|
||||
goto err_bind;
|
||||
|
||||
rdma_restrack_parent_name(&counter->res, &qp->res);
|
||||
rdma_restrack_add(&counter->res);
|
||||
return counter;
|
||||
|
||||
err_bind:
|
||||
mutex_lock(&port_counter->lock);
|
||||
port_counter->num_counters--;
|
||||
if (!port_counter->num_counters &&
|
||||
port_counter->mode.mode == RDMA_COUNTER_MODE_MANUAL)
|
||||
__counter_set_mode(port_counter, RDMA_COUNTER_MODE_NONE, 0,
|
||||
false);
|
||||
mutex_unlock(&port_counter->lock);
|
||||
err_mode:
|
||||
rdma_free_hw_stats_struct(counter->stats);
|
||||
err_stats:
|
||||
@@ -661,7 +669,7 @@ void rdma_counter_init(struct ib_device *dev)
|
||||
|
||||
fail:
|
||||
for (i = port; i >= rdma_start_port(dev); i--) {
|
||||
port_counter = &dev->port_data[port].port_counter;
|
||||
port_counter = &dev->port_data[i].port_counter;
|
||||
rdma_free_hw_stats_struct(port_counter->hstats);
|
||||
port_counter->hstats = NULL;
|
||||
mutex_destroy(&port_counter->lock);
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
#include <linux/security.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/cc_platform.h>
|
||||
#include <rdma/rdma_netlink.h>
|
||||
#include <rdma/ib_addr.h>
|
||||
#include <rdma/ib_cache.h>
|
||||
@@ -1245,7 +1246,6 @@ out:
|
||||
*/
|
||||
static int setup_device(struct ib_device *device)
|
||||
{
|
||||
struct ib_udata uhw = {.outlen = 0, .inlen = 0};
|
||||
int ret;
|
||||
|
||||
ib_device_check_mandatory(device);
|
||||
@@ -1257,7 +1257,7 @@ static int setup_device(struct ib_device *device)
|
||||
}
|
||||
|
||||
memset(&device->attrs, 0, sizeof(device->attrs));
|
||||
ret = device->ops.query_device(device, &device->attrs, &uhw);
|
||||
ret = device->ops.query_device(device, &device->attrs, NULL);
|
||||
if (ret) {
|
||||
dev_warn(&device->dev,
|
||||
"Couldn't query the device attributes\n");
|
||||
@@ -1419,6 +1419,14 @@ int ib_register_device(struct ib_device *device, const char *name,
|
||||
*/
|
||||
WARN_ON(dma_device && !dma_device->dma_parms);
|
||||
device->dma_device = dma_device;
|
||||
/*
|
||||
* In a CoCo guest every device is currently assumed to be untrusted
|
||||
* (T=0) and therefore subject to DMA bouncing. Once trusted (T=1)
|
||||
* device detection is wired up, narrow this check to exclude such
|
||||
* devices.
|
||||
*/
|
||||
if (dma_device && cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT))
|
||||
device->cc_dma_bounce = 1;
|
||||
|
||||
ret = setup_device(device);
|
||||
if (ret)
|
||||
|
||||
@@ -97,13 +97,44 @@ static void destroy_all_handles_in_queue(struct ib_device *device,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Bulk-move all handles from @src into @dst without allocating new pages.
|
||||
* If @dst has a partial tail page, fill it handle-by-handle from @src first
|
||||
* to preserve the invariant that only the tail page is partial, then splice
|
||||
* the remaining @src pages onto @dst. On return @src is empty.
|
||||
*
|
||||
* Caller must hold the lock protecting both queues.
|
||||
*/
|
||||
static void splice_frmr_queue_locked(struct frmr_queue *dst,
|
||||
struct frmr_queue *src)
|
||||
{
|
||||
u32 free_in_tail = dst->ci % NUM_HANDLES_PER_PAGE;
|
||||
u32 handle;
|
||||
|
||||
if (free_in_tail) {
|
||||
free_in_tail = NUM_HANDLES_PER_PAGE - free_in_tail;
|
||||
while (free_in_tail && src->ci) {
|
||||
handle = pop_handle_from_queue_locked(src);
|
||||
push_handle_to_queue_locked(dst, handle);
|
||||
free_in_tail--;
|
||||
}
|
||||
}
|
||||
|
||||
if (src->ci > 0) {
|
||||
list_splice_tail_init(&src->pages_list, &dst->pages_list);
|
||||
dst->num_pages += src->num_pages;
|
||||
dst->ci += src->ci;
|
||||
src->num_pages = 0;
|
||||
src->ci = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static bool age_pinned_pool(struct ib_device *device, struct ib_frmr_pool *pool)
|
||||
{
|
||||
struct ib_frmr_pools *pools = device->frmr_pools;
|
||||
u32 total, to_destroy, destroyed = 0;
|
||||
bool has_work = false;
|
||||
u32 *handles;
|
||||
u32 handle;
|
||||
|
||||
spin_lock(&pool->lock);
|
||||
total = pool->queue.ci + pool->inactive_queue.ci + pool->in_use;
|
||||
@@ -112,7 +143,7 @@ static bool age_pinned_pool(struct ib_device *device, struct ib_frmr_pool *pool)
|
||||
return false;
|
||||
}
|
||||
|
||||
to_destroy = total - pool->pinned_handles;
|
||||
to_destroy = min(total - pool->pinned_handles, pool->inactive_queue.ci);
|
||||
|
||||
handles = kcalloc(to_destroy, sizeof(*handles), GFP_ATOMIC);
|
||||
if (!handles) {
|
||||
@@ -121,15 +152,13 @@ static bool age_pinned_pool(struct ib_device *device, struct ib_frmr_pool *pool)
|
||||
}
|
||||
|
||||
/* Destroy all excess handles in the inactive queue */
|
||||
while (pool->inactive_queue.ci && destroyed < to_destroy) {
|
||||
handles[destroyed++] = pop_handle_from_queue_locked(
|
||||
for (; destroyed < to_destroy; destroyed++)
|
||||
handles[destroyed] = pop_handle_from_queue_locked(
|
||||
&pool->inactive_queue);
|
||||
}
|
||||
|
||||
/* Move all handles from regular queue to inactive queue */
|
||||
while (pool->queue.ci) {
|
||||
handle = pop_handle_from_queue_locked(&pool->queue);
|
||||
push_handle_to_queue_locked(&pool->inactive_queue, handle);
|
||||
if (pool->queue.ci > 0) {
|
||||
splice_frmr_queue_locked(&pool->inactive_queue, &pool->queue);
|
||||
has_work = true;
|
||||
}
|
||||
|
||||
@@ -158,13 +187,7 @@ static void pool_aging_work(struct work_struct *work)
|
||||
/* Move all pages from regular queue to inactive queue */
|
||||
spin_lock(&pool->lock);
|
||||
if (pool->queue.ci > 0) {
|
||||
list_splice_tail_init(&pool->queue.pages_list,
|
||||
&pool->inactive_queue.pages_list);
|
||||
pool->inactive_queue.num_pages = pool->queue.num_pages;
|
||||
pool->inactive_queue.ci = pool->queue.ci;
|
||||
|
||||
pool->queue.num_pages = 0;
|
||||
pool->queue.ci = 0;
|
||||
splice_frmr_queue_locked(&pool->inactive_queue, &pool->queue);
|
||||
has_work = true;
|
||||
}
|
||||
spin_unlock(&pool->lock);
|
||||
@@ -426,7 +449,7 @@ int ib_frmr_pools_set_pinned(struct ib_device *device, struct ib_frmr_key *key,
|
||||
if (!handles)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = pools->pool_ops->create_frmrs(device, key, handles,
|
||||
ret = pools->pool_ops->create_frmrs(device, &driver_key, handles,
|
||||
needed_handles);
|
||||
if (ret) {
|
||||
kfree(handles);
|
||||
@@ -438,11 +461,16 @@ int ib_frmr_pools_set_pinned(struct ib_device *device, struct ib_frmr_key *key,
|
||||
ret = push_handle_to_queue_locked(&pool->queue,
|
||||
handles[i]);
|
||||
if (ret)
|
||||
goto end;
|
||||
break;
|
||||
}
|
||||
spin_unlock(&pool->lock);
|
||||
|
||||
if (ret) {
|
||||
/* Destroy handles created but never pushed to the pool. */
|
||||
pools->pool_ops->destroy_frmrs(device, &handles[i],
|
||||
needed_handles - i);
|
||||
}
|
||||
|
||||
end:
|
||||
spin_unlock(&pool->lock);
|
||||
kfree(handles);
|
||||
|
||||
schedule_aging:
|
||||
@@ -501,7 +529,9 @@ int ib_frmr_pool_pop(struct ib_device *device, struct ib_mr *mr)
|
||||
struct ib_frmr_pools *pools = device->frmr_pools;
|
||||
struct ib_frmr_pool *pool;
|
||||
|
||||
WARN_ON_ONCE(!device->frmr_pools);
|
||||
if (WARN_ON_ONCE(!pools))
|
||||
return -EINVAL;
|
||||
|
||||
pool = ib_frmr_pool_find(pools, &mr->frmr.key);
|
||||
if (!pool) {
|
||||
pool = create_frmr_pool(device, &mr->frmr.key);
|
||||
@@ -519,9 +549,8 @@ EXPORT_SYMBOL(ib_frmr_pool_pop);
|
||||
* @device: The device to push the FRMR handle to.
|
||||
* @mr: The MR containing the FRMR handle to push back to the pool.
|
||||
*
|
||||
* Returns 0 on success, negative error code on failure.
|
||||
*/
|
||||
int ib_frmr_pool_push(struct ib_device *device, struct ib_mr *mr)
|
||||
void ib_frmr_pool_push(struct ib_device *device, struct ib_mr *mr)
|
||||
{
|
||||
struct ib_frmr_pool *pool = mr->frmr.pool;
|
||||
struct ib_frmr_pools *pools = device->frmr_pools;
|
||||
@@ -529,19 +558,38 @@ int ib_frmr_pool_push(struct ib_device *device, struct ib_mr *mr)
|
||||
int ret;
|
||||
|
||||
spin_lock(&pool->lock);
|
||||
/* Schedule aging every time an empty pool becomes non-empty */
|
||||
if (pool->queue.ci == 0)
|
||||
schedule_aging = true;
|
||||
pool->in_use--;
|
||||
ret = push_handle_to_queue_locked(&pool->queue, mr->frmr.handle);
|
||||
if (ret == 0)
|
||||
pool->in_use--;
|
||||
|
||||
/* Schedule aging every time an empty pool becomes non-empty */
|
||||
if (!ret && pool->queue.ci == 1)
|
||||
schedule_aging = true;
|
||||
|
||||
spin_unlock(&pool->lock);
|
||||
|
||||
if (ret == 0 && schedule_aging)
|
||||
if (ret) {
|
||||
pools->pool_ops->destroy_frmrs(device, &mr->frmr.handle, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (schedule_aging)
|
||||
queue_delayed_work(pools->aging_wq, &pool->aging_work,
|
||||
secs_to_jiffies(READ_ONCE(pools->aging_period_sec)));
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(ib_frmr_pool_push);
|
||||
|
||||
/*
|
||||
* Drop a handle previously popped from the pool without returning it for
|
||||
* reuse. The caller is responsible for destroying the underlying hardware
|
||||
* resource.
|
||||
*/
|
||||
void ib_frmr_pool_drop(struct ib_mr *mr)
|
||||
{
|
||||
struct ib_frmr_pool *pool = mr->frmr.pool;
|
||||
|
||||
spin_lock(&pool->lock);
|
||||
pool->in_use--;
|
||||
spin_unlock(&pool->lock);
|
||||
}
|
||||
EXPORT_SYMBOL(ib_frmr_pool_drop);
|
||||
|
||||
@@ -398,7 +398,7 @@ EXPORT_SYMBOL(rdma_user_mmap_entry_insert);
|
||||
* The struct ib_device that is handling the uverbs call. Must not be called if
|
||||
* udata is NULL. The result can be NULL.
|
||||
*/
|
||||
struct ib_device *rdma_udata_to_dev(struct ib_udata *udata)
|
||||
static struct ib_device *rdma_udata_to_dev(struct ib_udata *udata)
|
||||
{
|
||||
struct uverbs_attr_bundle *bundle =
|
||||
rdma_udata_to_uverbs_attr_bundle(udata);
|
||||
@@ -415,10 +415,9 @@ struct ib_device *rdma_udata_to_dev(struct ib_udata *udata)
|
||||
return srcu_dereference(bundle->ufile->device->ib_dev,
|
||||
&bundle->ufile->device->disassociate_srcu);
|
||||
}
|
||||
EXPORT_SYMBOL(rdma_udata_to_dev);
|
||||
|
||||
#if IS_ENABLED(CONFIG_INFINIBAND_USER_ACCESS)
|
||||
uverbs_api_ioctl_handler_fn uverbs_get_handler_fn(struct ib_udata *udata)
|
||||
typedef int (*uverbs_api_ioctl_handler_fn)(struct uverbs_attr_bundle *attrs);
|
||||
static uverbs_api_ioctl_handler_fn uverbs_get_handler_fn(struct ib_udata *udata)
|
||||
{
|
||||
struct uverbs_attr_bundle *bundle =
|
||||
rdma_udata_to_uverbs_attr_bundle(udata);
|
||||
@@ -502,4 +501,258 @@ err_fault:
|
||||
return -EFAULT;
|
||||
}
|
||||
EXPORT_SYMBOL(_ib_respond_udata);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Must be called with the ufile->device->disassociate_srcu held, and the lock
|
||||
* must be held until use of the ucontext is finished.
|
||||
*/
|
||||
struct ib_ucontext *ib_uverbs_get_ucontext_file(struct ib_uverbs_file *ufile)
|
||||
{
|
||||
/*
|
||||
* We do not hold the hw_destroy_rwsem lock for this flow, instead
|
||||
* srcu is used. It does not matter if someone races this with
|
||||
* get_context, we get NULL or valid ucontext.
|
||||
*/
|
||||
struct ib_ucontext *ucontext = smp_load_acquire(&ufile->ucontext);
|
||||
|
||||
if (!srcu_dereference(ufile->device->ib_dev,
|
||||
&ufile->device->disassociate_srcu))
|
||||
return ERR_PTR(-EIO);
|
||||
|
||||
if (!ucontext)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return ucontext;
|
||||
}
|
||||
EXPORT_SYMBOL(ib_uverbs_get_ucontext_file);
|
||||
|
||||
int uverbs_destroy_def_handler(struct uverbs_attr_bundle *attrs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(uverbs_destroy_def_handler);
|
||||
|
||||
/*
|
||||
* When calling a destroy function during an error unwind we need to pass in
|
||||
* the udata that is sanitized of all user arguments. Ie from the driver
|
||||
* perspective it looks like no udata was passed.
|
||||
*/
|
||||
struct ib_udata *uverbs_get_cleared_udata(struct uverbs_attr_bundle *attrs)
|
||||
{
|
||||
attrs->driver_udata = (struct ib_udata){};
|
||||
return &attrs->driver_udata;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(uverbs_get_cleared_udata, "rdma_core");
|
||||
|
||||
/**
|
||||
* _uverbs_alloc() - Quickly allocate memory for use with a bundle
|
||||
* @bundle: The bundle
|
||||
* @size: Number of bytes to allocate
|
||||
* @flags: Allocator flags
|
||||
*
|
||||
* The bundle allocator is intended for allocations that are connected with
|
||||
* processing the system call related to the bundle. The allocated memory is
|
||||
* always freed once the system call completes, and cannot be freed any other
|
||||
* way.
|
||||
*
|
||||
* This tries to use a small pool of pre-allocated memory for performance.
|
||||
*/
|
||||
__malloc void *_uverbs_alloc(struct uverbs_attr_bundle *bundle, size_t size,
|
||||
gfp_t flags)
|
||||
{
|
||||
struct bundle_priv *pbundle =
|
||||
container_of(&bundle->hdr, struct bundle_priv, bundle);
|
||||
size_t new_used;
|
||||
void *res;
|
||||
|
||||
if (check_add_overflow(size, pbundle->internal_used, &new_used))
|
||||
return ERR_PTR(-EOVERFLOW);
|
||||
|
||||
if (new_used > pbundle->internal_avail) {
|
||||
struct bundle_alloc_head *buf;
|
||||
|
||||
buf = kvmalloc_flex(*buf, data, size, flags);
|
||||
if (!buf)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
buf->next = pbundle->allocated_mem;
|
||||
pbundle->allocated_mem = buf;
|
||||
return buf->data;
|
||||
}
|
||||
|
||||
res = (void *)pbundle->internal_buffer + pbundle->internal_used;
|
||||
pbundle->internal_used =
|
||||
ALIGN(new_used, sizeof(*pbundle->internal_buffer));
|
||||
if (want_init_on_alloc(flags))
|
||||
memset(res, 0, size);
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(_uverbs_alloc);
|
||||
|
||||
int uverbs_copy_to(const struct uverbs_attr_bundle *bundle, size_t idx,
|
||||
const void *from, size_t size)
|
||||
{
|
||||
const struct uverbs_attr *attr = uverbs_attr_get(bundle, idx);
|
||||
size_t min_size;
|
||||
|
||||
if (IS_ERR(attr))
|
||||
return PTR_ERR(attr);
|
||||
|
||||
min_size = min_t(size_t, attr->ptr_attr.len, size);
|
||||
if (copy_to_user(u64_to_user_ptr(attr->ptr_attr.data), from, min_size))
|
||||
return -EFAULT;
|
||||
|
||||
return uverbs_set_output(bundle, attr);
|
||||
}
|
||||
EXPORT_SYMBOL(uverbs_copy_to);
|
||||
|
||||
int uverbs_copy_to_struct_or_zero(const struct uverbs_attr_bundle *bundle,
|
||||
size_t idx, const void *from, size_t size)
|
||||
{
|
||||
const struct uverbs_attr *attr = uverbs_attr_get(bundle, idx);
|
||||
|
||||
if (IS_ERR(attr))
|
||||
return PTR_ERR(attr);
|
||||
|
||||
if (size < attr->ptr_attr.len) {
|
||||
if (clear_user(u64_to_user_ptr(attr->ptr_attr.data) + size,
|
||||
attr->ptr_attr.len - size))
|
||||
return -EFAULT;
|
||||
}
|
||||
return uverbs_copy_to(bundle, idx, from, size);
|
||||
}
|
||||
EXPORT_SYMBOL(uverbs_copy_to_struct_or_zero);
|
||||
|
||||
int _uverbs_get_const_unsigned(u64 *to,
|
||||
const struct uverbs_attr_bundle *attrs_bundle,
|
||||
size_t idx, u64 upper_bound, u64 *def_val)
|
||||
{
|
||||
const struct uverbs_attr *attr;
|
||||
|
||||
attr = uverbs_attr_get(attrs_bundle, idx);
|
||||
if (IS_ERR(attr)) {
|
||||
if ((PTR_ERR(attr) != -ENOENT) || !def_val)
|
||||
return PTR_ERR(attr);
|
||||
|
||||
*to = *def_val;
|
||||
} else {
|
||||
*to = attr->ptr_attr.data;
|
||||
}
|
||||
|
||||
if (*to > upper_bound)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(_uverbs_get_const_unsigned);
|
||||
|
||||
int _uverbs_get_const_signed(s64 *to,
|
||||
const struct uverbs_attr_bundle *attrs_bundle,
|
||||
size_t idx, s64 lower_bound, u64 upper_bound,
|
||||
s64 *def_val)
|
||||
{
|
||||
const struct uverbs_attr *attr;
|
||||
|
||||
attr = uverbs_attr_get(attrs_bundle, idx);
|
||||
if (IS_ERR(attr)) {
|
||||
if ((PTR_ERR(attr) != -ENOENT) || !def_val)
|
||||
return PTR_ERR(attr);
|
||||
|
||||
*to = *def_val;
|
||||
} else {
|
||||
*to = attr->ptr_attr.data;
|
||||
}
|
||||
|
||||
if (*to < lower_bound || (*to > 0 && (u64)*to > upper_bound))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(_uverbs_get_const_signed);
|
||||
|
||||
int uverbs_get_flags64(u64 *to, const struct uverbs_attr_bundle *attrs_bundle,
|
||||
size_t idx, u64 allowed_bits)
|
||||
{
|
||||
const struct uverbs_attr *attr;
|
||||
u64 flags;
|
||||
|
||||
attr = uverbs_attr_get(attrs_bundle, idx);
|
||||
/* Missing attribute means 0 flags */
|
||||
if (IS_ERR(attr)) {
|
||||
*to = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* New userspace code should use 8 bytes to pass flags, but we
|
||||
* transparently support old userspaces that were using 4 bytes as
|
||||
* well.
|
||||
*/
|
||||
if (attr->ptr_attr.len == 8)
|
||||
flags = attr->ptr_attr.data;
|
||||
else if (attr->ptr_attr.len == 4)
|
||||
flags = *(u32 *)&attr->ptr_attr.data;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
if (flags & ~allowed_bits)
|
||||
return -EINVAL;
|
||||
|
||||
*to = flags;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(uverbs_get_flags64);
|
||||
|
||||
int uverbs_get_flags32(u32 *to, const struct uverbs_attr_bundle *attrs_bundle,
|
||||
size_t idx, u64 allowed_bits)
|
||||
{
|
||||
u64 flags;
|
||||
int ret;
|
||||
|
||||
ret = uverbs_get_flags64(&flags, attrs_bundle, idx, allowed_bits);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (flags > U32_MAX)
|
||||
return -EINVAL;
|
||||
*to = flags;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(uverbs_get_flags32);
|
||||
|
||||
/**
|
||||
* uverbs_get_buffer_desc - Read a buffer descriptor from a uverbs attr.
|
||||
* @attrs_bundle: uverbs attribute bundle.
|
||||
* @attr_id: id of an UVERBS_ATTR_UMEM-typed attribute.
|
||||
* @desc: descriptor to fill.
|
||||
*
|
||||
* Return: 0 on success, -ENOENT if @attr_id is not set, -EINVAL on a
|
||||
* malformed descriptor, or any other negative errno propagated from
|
||||
* uverbs_copy_from() (notably -EFAULT on copy_from_user() failure).
|
||||
*/
|
||||
int uverbs_get_buffer_desc(const struct uverbs_attr_bundle *attrs_bundle,
|
||||
u16 attr_id, struct ib_uverbs_buffer_desc *desc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = uverbs_copy_from(desc, attrs_bundle, attr_id);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (desc->flags & ~IB_UVERBS_BUFFER_DESC_FLAGS_KNOWN_MASK)
|
||||
return -EINVAL;
|
||||
desc->optional_flags &= IB_UVERBS_BUFFER_DESC_OPTIONAL_FLAGS_KNOWN_MASK;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(uverbs_get_buffer_desc);
|
||||
|
||||
/* Once called an abort will call through to the type's destroy_hw() */
|
||||
void uverbs_finalize_uobj_create(const struct uverbs_attr_bundle *bundle,
|
||||
u16 idx)
|
||||
{
|
||||
struct bundle_priv *pbundle =
|
||||
container_of(&bundle->hdr, struct bundle_priv, bundle);
|
||||
|
||||
__set_bit(uapi_bkey_attr(uapi_key_attr(idx)),
|
||||
pbundle->uobj_hw_obj_valid);
|
||||
}
|
||||
EXPORT_SYMBOL(uverbs_finalize_uobj_create);
|
||||
|
||||
@@ -518,8 +518,8 @@ static int iw_cm_map(struct iw_cm_id *cm_id, bool active)
|
||||
cm_id->m_local_addr = cm_id->local_addr;
|
||||
cm_id->m_remote_addr = cm_id->remote_addr;
|
||||
|
||||
strcpy(pm_reg_msg.dev_name, devname);
|
||||
strcpy(pm_reg_msg.if_name, ifname);
|
||||
strscpy(pm_reg_msg.dev_name, devname);
|
||||
strscpy(pm_reg_msg.if_name, ifname);
|
||||
|
||||
if (iwpm_register_pid(&pm_reg_msg, RDMA_NL_IWCM) ||
|
||||
!iwpm_valid_pid())
|
||||
|
||||
@@ -695,7 +695,7 @@ static int fill_res_mr_entry(struct sk_buff *msg, bool has_cap_net_admin,
|
||||
struct rdma_restrack_entry *res, uint32_t port)
|
||||
{
|
||||
struct ib_mr *mr = container_of(res, struct ib_mr, res);
|
||||
struct ib_device *dev = mr->pd->device;
|
||||
struct ib_device *dev = mr->device;
|
||||
|
||||
if (has_cap_net_admin) {
|
||||
if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_RKEY, mr->rkey))
|
||||
@@ -711,9 +711,12 @@ static int fill_res_mr_entry(struct sk_buff *msg, bool has_cap_net_admin,
|
||||
if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_MRN, res->id))
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (!rdma_is_kernel_res(res) &&
|
||||
nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_PDN, mr->pd->res.id))
|
||||
return -EMSGSIZE;
|
||||
if (!rdma_is_kernel_res(res)) {
|
||||
struct ib_pd *pd = READ_ONCE(mr->pd);
|
||||
|
||||
if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_PDN, pd->res.id))
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
if (fill_res_name_pid(msg, res))
|
||||
return -EMSGSIZE;
|
||||
@@ -727,7 +730,7 @@ static int fill_res_mr_raw_entry(struct sk_buff *msg, bool has_cap_net_admin,
|
||||
struct rdma_restrack_entry *res, uint32_t port)
|
||||
{
|
||||
struct ib_mr *mr = container_of(res, struct ib_mr, res);
|
||||
struct ib_device *dev = mr->pd->device;
|
||||
struct ib_device *dev = mr->device;
|
||||
|
||||
if (!dev->ops.fill_res_mr_entry_raw)
|
||||
return -EINVAL;
|
||||
@@ -1017,7 +1020,7 @@ static int fill_stat_mr_entry(struct sk_buff *msg, bool has_cap_net_admin,
|
||||
struct rdma_restrack_entry *res, uint32_t port)
|
||||
{
|
||||
struct ib_mr *mr = container_of(res, struct ib_mr, res);
|
||||
struct ib_device *dev = mr->pd->device;
|
||||
struct ib_device *dev = mr->device;
|
||||
|
||||
if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_MRN, res->id))
|
||||
goto err;
|
||||
|
||||
@@ -42,6 +42,40 @@
|
||||
#include "core_priv.h"
|
||||
#include "rdma_core.h"
|
||||
|
||||
static void release_ufile_idr_uobject(struct ib_uverbs_file *ufile);
|
||||
|
||||
void ib_uverbs_release_file(struct kref *ref)
|
||||
{
|
||||
struct ib_uverbs_file *file =
|
||||
container_of(ref, struct ib_uverbs_file, ref);
|
||||
struct ib_device *ib_dev;
|
||||
int srcu_key;
|
||||
|
||||
release_ufile_idr_uobject(file);
|
||||
|
||||
srcu_key = srcu_read_lock(&file->device->disassociate_srcu);
|
||||
ib_dev = srcu_dereference(file->device->ib_dev,
|
||||
&file->device->disassociate_srcu);
|
||||
if (ib_dev && !ib_dev->ops.disassociate_ucontext)
|
||||
module_put(ib_dev->ops.owner);
|
||||
srcu_read_unlock(&file->device->disassociate_srcu, srcu_key);
|
||||
|
||||
if (refcount_dec_and_test(&file->device->refcount))
|
||||
ib_uverbs_comp_dev(file->device);
|
||||
|
||||
if (file->default_async_file)
|
||||
uverbs_uobject_put(&file->default_async_file->uobj);
|
||||
put_device(&file->device->dev);
|
||||
|
||||
if (file->disassociate_page)
|
||||
__free_pages(file->disassociate_page, 0);
|
||||
mutex_destroy(&file->disassociation_lock);
|
||||
mutex_destroy(&file->umap_lock);
|
||||
mutex_destroy(&file->ucontext_lock);
|
||||
kfree(file);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(ib_uverbs_release_file, "rdma_core");
|
||||
|
||||
static void uverbs_uobject_free(struct kref *ref)
|
||||
{
|
||||
kfree_rcu(container_of(ref, struct ib_uobject, ref), rcu);
|
||||
@@ -214,6 +248,7 @@ out_unlock:
|
||||
up_read(&ufile->hw_destroy_rwsem);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(uobj_destroy, "rdma_core");
|
||||
|
||||
/*
|
||||
* uobj_get_destroy destroys the HW object and returns a handle to the uobj
|
||||
@@ -239,6 +274,7 @@ struct ib_uobject *__uobj_get_destroy(const struct uverbs_api_object *obj,
|
||||
|
||||
return uobj;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(__uobj_get_destroy, "rdma_core");
|
||||
|
||||
/*
|
||||
* Does both uobj_get_destroy() and uobj_put_destroy(). Returns 0 on success
|
||||
@@ -255,6 +291,7 @@ int __uobj_perform_destroy(const struct uverbs_api_object *obj, u32 id,
|
||||
uobj_put_destroy(uobj);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(__uobj_perform_destroy, "rdma_core");
|
||||
|
||||
/* alloc_uobj must be undone by uverbs_destroy_uobject() */
|
||||
static struct ib_uobject *alloc_uobj(struct uverbs_attr_bundle *attrs,
|
||||
@@ -420,6 +457,7 @@ free:
|
||||
uverbs_uobject_put(uobj);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(rdma_lookup_get_uobject, "rdma_core");
|
||||
|
||||
static struct ib_uobject *
|
||||
alloc_begin_idr_uobject(const struct uverbs_api_object *obj,
|
||||
@@ -465,8 +503,8 @@ alloc_begin_fd_uobject(const struct uverbs_api_object *obj,
|
||||
|
||||
fd_type =
|
||||
container_of(obj->type_attrs, struct uverbs_obj_fd_type, type);
|
||||
if (WARN_ON(fd_type->fops && fd_type->fops->release != &uverbs_uobject_fd_release &&
|
||||
fd_type->fops->release != &uverbs_async_event_release)) {
|
||||
if (WARN_ON(fd_type->fops &&
|
||||
fd_type->fops->release != &uverbs_uobject_fd_release)) {
|
||||
ret = ERR_PTR(-EINVAL);
|
||||
goto err_fd;
|
||||
}
|
||||
@@ -522,6 +560,7 @@ struct ib_uobject *rdma_alloc_begin_uobject(const struct uverbs_api_object *obj,
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(rdma_alloc_begin_uobject, "rdma_core");
|
||||
|
||||
static void alloc_abort_idr_uobject(struct ib_uobject *uobj)
|
||||
{
|
||||
@@ -668,6 +707,7 @@ void rdma_alloc_commit_uobject(struct ib_uobject *uobj,
|
||||
/* Matches the down_read in rdma_alloc_begin_uobject */
|
||||
up_read(&ufile->hw_destroy_rwsem);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(rdma_alloc_commit_uobject, "rdma_core");
|
||||
|
||||
/*
|
||||
* new_uobj will be assigned to the handle currently used by to_uobj, and
|
||||
@@ -697,6 +737,7 @@ void rdma_assign_uobject(struct ib_uobject *to_uobj, struct ib_uobject *new_uobj
|
||||
*/
|
||||
uverbs_destroy_uobject(to_uobj, RDMA_REMOVE_DESTROY, attrs);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(rdma_assign_uobject, "rdma_core");
|
||||
|
||||
/*
|
||||
* This consumes the kref for uobj. It is up to the caller to unwind the HW
|
||||
@@ -727,6 +768,7 @@ void rdma_alloc_abort_uobject(struct ib_uobject *uobj,
|
||||
/* Matches the down_read in rdma_alloc_begin_uobject */
|
||||
up_read(&ufile->hw_destroy_rwsem);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(rdma_alloc_abort_uobject, "rdma_core");
|
||||
|
||||
static void lookup_put_idr_uobject(struct ib_uobject *uobj,
|
||||
enum rdma_lookup_mode mode)
|
||||
@@ -770,13 +812,15 @@ void rdma_lookup_put_uobject(struct ib_uobject *uobj,
|
||||
/* Pairs with the kref obtained by type->lookup_get */
|
||||
uverbs_uobject_put(uobj);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(rdma_lookup_put_uobject, "rdma_core");
|
||||
|
||||
void setup_ufile_idr_uobject(struct ib_uverbs_file *ufile)
|
||||
{
|
||||
xa_init_flags(&ufile->idr, XA_FLAGS_ALLOC);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(setup_ufile_idr_uobject, "rdma_core");
|
||||
|
||||
void release_ufile_idr_uobject(struct ib_uverbs_file *ufile)
|
||||
static void release_ufile_idr_uobject(struct ib_uverbs_file *ufile)
|
||||
{
|
||||
struct ib_uobject *entry;
|
||||
unsigned long id;
|
||||
@@ -839,6 +883,7 @@ int uverbs_uobject_release(struct ib_uobject *uobj)
|
||||
uverbs_uobject_put(uobj);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(uverbs_uobject_release, "rdma_core");
|
||||
|
||||
/*
|
||||
* Users of UVERBS_TYPE_ALLOC_FD should set this function as the struct
|
||||
@@ -846,51 +891,42 @@ int uverbs_uobject_release(struct ib_uobject *uobj)
|
||||
*/
|
||||
int uverbs_uobject_fd_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
void (*release_cleanup)(struct ib_uobject *uobj) = NULL;
|
||||
struct ib_uobject *uobj = filp->private_data;
|
||||
const struct uverbs_obj_type *type_attrs;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* This can only happen if the fput came from alloc_abort_fd_uobject()
|
||||
*/
|
||||
if (!filp->private_data)
|
||||
if (!uobj)
|
||||
return 0;
|
||||
|
||||
return uverbs_uobject_release(filp->private_data);
|
||||
/*
|
||||
* uverbs_disassociate_api() can NULL type_attrs after disassociate, but
|
||||
* it won't if release_cleanup is used.
|
||||
*/
|
||||
type_attrs = READ_ONCE(uobj->uapi_object->type_attrs);
|
||||
if (type_attrs)
|
||||
release_cleanup = container_of(type_attrs,
|
||||
struct uverbs_obj_fd_type, type)
|
||||
->release_cleanup;
|
||||
if (release_cleanup)
|
||||
uverbs_uobject_get(uobj);
|
||||
|
||||
ret = uverbs_uobject_release(uobj);
|
||||
|
||||
if (release_cleanup) {
|
||||
release_cleanup(uobj);
|
||||
uverbs_uobject_put(uobj);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(uverbs_uobject_fd_release);
|
||||
|
||||
/*
|
||||
* Drop the ucontext off the ufile and completely disconnect it from the
|
||||
* ib_device
|
||||
*/
|
||||
static void ufile_destroy_ucontext(struct ib_uverbs_file *ufile,
|
||||
enum rdma_remove_reason reason)
|
||||
{
|
||||
struct ib_ucontext *ucontext = ufile->ucontext;
|
||||
struct ib_device *ib_dev = ucontext->device;
|
||||
|
||||
/*
|
||||
* If we are closing the FD then the user mmap VMAs must have
|
||||
* already been destroyed as they hold on to the filep, otherwise
|
||||
* they need to be zap'd.
|
||||
*/
|
||||
if (reason == RDMA_REMOVE_DRIVER_REMOVE) {
|
||||
uverbs_user_mmap_disassociate(ufile);
|
||||
if (ib_dev->ops.disassociate_ucontext)
|
||||
ib_dev->ops.disassociate_ucontext(ucontext);
|
||||
}
|
||||
|
||||
ib_rdmacg_uncharge(&ucontext->cg_obj, ib_dev,
|
||||
RDMACG_RESOURCE_HCA_HANDLE);
|
||||
|
||||
rdma_restrack_del(&ucontext->res);
|
||||
|
||||
ib_dev->ops.dealloc_ucontext(ucontext);
|
||||
WARN_ON(!xa_empty(&ucontext->mmap_xa));
|
||||
kfree(ucontext);
|
||||
|
||||
ufile->ucontext = NULL;
|
||||
}
|
||||
|
||||
static int __uverbs_cleanup_ufile(struct ib_uverbs_file *ufile,
|
||||
enum rdma_remove_reason reason)
|
||||
int __uverbs_cleanup_ufile(struct ib_uverbs_file *ufile,
|
||||
enum rdma_remove_reason reason)
|
||||
{
|
||||
struct uverbs_attr_bundle attrs = { .ufile = ufile };
|
||||
struct ib_ucontext *ucontext = ufile->ucontext;
|
||||
@@ -931,36 +967,7 @@ static int __uverbs_cleanup_ufile(struct ib_uverbs_file *ufile,
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy the ucontext and every uobject associated with it.
|
||||
*
|
||||
* This is internally locked and can be called in parallel from multiple
|
||||
* contexts.
|
||||
*/
|
||||
void uverbs_destroy_ufile_hw(struct ib_uverbs_file *ufile,
|
||||
enum rdma_remove_reason reason)
|
||||
{
|
||||
down_write(&ufile->hw_destroy_rwsem);
|
||||
|
||||
/*
|
||||
* If a ucontext was never created then we can't have any uobjects to
|
||||
* cleanup, nothing to do.
|
||||
*/
|
||||
if (!ufile->ucontext)
|
||||
goto done;
|
||||
|
||||
while (!list_empty(&ufile->uobjects) &&
|
||||
!__uverbs_cleanup_ufile(ufile, reason)) {
|
||||
}
|
||||
|
||||
if (WARN_ON(!list_empty(&ufile->uobjects)))
|
||||
__uverbs_cleanup_ufile(ufile, RDMA_REMOVE_DRIVER_FAILURE);
|
||||
ufile_destroy_ucontext(ufile, reason);
|
||||
|
||||
done:
|
||||
up_write(&ufile->hw_destroy_rwsem);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(__uverbs_cleanup_ufile, "rdma_core");
|
||||
|
||||
const struct uverbs_obj_type_class uverbs_fd_class = {
|
||||
.alloc_begin = alloc_begin_fd_uobject,
|
||||
@@ -998,6 +1005,7 @@ uverbs_get_uobject_from_file(u16 object_id, enum uverbs_obj_access access,
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(uverbs_get_uobject_from_file, "rdma_core");
|
||||
|
||||
void uverbs_finalize_object(struct ib_uobject *uobj,
|
||||
enum uverbs_obj_access access, bool hw_obj_valid,
|
||||
@@ -1030,6 +1038,7 @@ void uverbs_finalize_object(struct ib_uobject *uobj,
|
||||
WARN_ON(true);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(uverbs_finalize_object, "rdma_core");
|
||||
|
||||
/**
|
||||
* rdma_uattrs_has_raw_cap() - Returns whether a rdma device linked to the
|
||||
@@ -1059,3 +1068,6 @@ out:
|
||||
return has_cap;
|
||||
}
|
||||
EXPORT_SYMBOL(rdma_uattrs_has_raw_cap);
|
||||
|
||||
MODULE_DESCRIPTION("InfiniBand uverbs objects");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
|
||||
@@ -70,9 +70,15 @@ void uverbs_finalize_object(struct ib_uobject *uobj,
|
||||
int uverbs_output_written(const struct uverbs_attr_bundle *bundle, size_t idx);
|
||||
|
||||
void setup_ufile_idr_uobject(struct ib_uverbs_file *ufile);
|
||||
void release_ufile_idr_uobject(struct ib_uverbs_file *ufile);
|
||||
|
||||
#if IS_ENABLED(CONFIG_INFINIBAND_USER_ACCESS)
|
||||
struct ib_udata *uverbs_get_cleared_udata(struct uverbs_attr_bundle *attrs);
|
||||
#else
|
||||
static inline struct ib_udata *uverbs_get_cleared_udata(struct uverbs_attr_bundle *attrs)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is the runtime description of the uverbs API, used by the syscall
|
||||
@@ -151,9 +157,6 @@ void uapi_compute_bundle_size(struct uverbs_api_ioctl_method *method_elm,
|
||||
unsigned int num_attrs);
|
||||
void uverbs_user_mmap_disassociate(struct ib_uverbs_file *ufile);
|
||||
|
||||
typedef int (*uverbs_api_ioctl_handler_fn)(struct uverbs_attr_bundle *attrs);
|
||||
uverbs_api_ioctl_handler_fn uverbs_get_handler_fn(struct ib_udata *udata);
|
||||
|
||||
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_cq[];
|
||||
|
||||
@@ -71,6 +71,8 @@ int rdma_restrack_count(struct ib_device *dev, enum rdma_restrack_type type,
|
||||
|
||||
xa_lock(&rt->xa);
|
||||
xas_for_each(&xas, e, U32_MAX) {
|
||||
if (xa_is_zero(e))
|
||||
continue;
|
||||
if (xa_get_mark(&rt->xa, e->id, RESTRACK_DD) && !show_details)
|
||||
continue;
|
||||
cnt++;
|
||||
@@ -276,6 +278,53 @@ int rdma_restrack_put(struct rdma_restrack_entry *res)
|
||||
}
|
||||
EXPORT_SYMBOL(rdma_restrack_put);
|
||||
|
||||
/**
|
||||
* rdma_restrack_sync() - Fence concurrent netlink dumps on an entry
|
||||
* @res: resource entry
|
||||
*
|
||||
* After this returns any concurrent netlink dump threads will see the current
|
||||
* value of the object. This is useful if the object has to be changed and there
|
||||
* is not locking to protect the nl side. Eg for mr->pd. This effectively
|
||||
* destroys the object from a kref/xarray perspective and then immediately
|
||||
* restores it. The kref is acting like a lock to barrier concurrent nl threads.
|
||||
* Callers must ensure rdma_restrack_del() is not concurrently called.
|
||||
*/
|
||||
void rdma_restrack_sync(struct rdma_restrack_entry *res)
|
||||
{
|
||||
struct rdma_restrack_entry *old;
|
||||
struct rdma_restrack_root *rt;
|
||||
struct task_struct *task;
|
||||
struct ib_device *dev;
|
||||
|
||||
if (!res->valid || res->no_track)
|
||||
return;
|
||||
|
||||
dev = res_to_dev(res);
|
||||
if (WARN_ON(!dev))
|
||||
return;
|
||||
|
||||
rt = &dev->res[res->type];
|
||||
if (WARN_ON(xa_get_mark(&rt->xa, res->id, RESTRACK_DD)))
|
||||
return;
|
||||
|
||||
old = xa_cmpxchg(&rt->xa, res->id, res, XA_ZERO_ENTRY, GFP_KERNEL);
|
||||
if (WARN_ON(old != res))
|
||||
return;
|
||||
|
||||
task = res->task;
|
||||
if (task)
|
||||
get_task_struct(task);
|
||||
rdma_restrack_put(res);
|
||||
wait_for_completion(&res->comp);
|
||||
reinit_completion(&res->comp);
|
||||
if (task)
|
||||
res->task = task;
|
||||
kref_init(&res->kref);
|
||||
|
||||
xa_cmpxchg(&rt->xa, res->id, XA_ZERO_ENTRY, res, GFP_KERNEL);
|
||||
}
|
||||
EXPORT_SYMBOL(rdma_restrack_sync);
|
||||
|
||||
/**
|
||||
* rdma_restrack_del() - delete object from the resource tracking database
|
||||
* @res: resource entry
|
||||
|
||||
@@ -27,6 +27,7 @@ int rdma_restrack_init(struct ib_device *dev);
|
||||
void rdma_restrack_clean(struct ib_device *dev);
|
||||
void rdma_restrack_add(struct rdma_restrack_entry *res);
|
||||
void rdma_restrack_del(struct rdma_restrack_entry *res);
|
||||
void rdma_restrack_sync(struct rdma_restrack_entry *res);
|
||||
void rdma_restrack_new(struct rdma_restrack_entry *res,
|
||||
enum rdma_restrack_type type);
|
||||
void rdma_restrack_set_name(struct rdma_restrack_entry *res,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user