@@ -291,7 +291,10 @@ struct ib_device *ib_alloc_device(size_t size)
if (!device)
return NULL;
- rdma_restrack_init(device);
+ if (rdma_restrack_init(device)) {
+ kfree(device);
+ return NULL;
+ }
device->dev.class = &ib_class;
device_initialize(&device->dev);
@@ -1071,6 +1071,7 @@ static int res_get_common_dumpit(struct sk_buff *skb,
unsigned long id = 0;
u32 index, port = 0;
bool filled = false;
+ struct xarray *xa;
err = nlmsg_parse(cb->nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
nldev_policy, NULL);
@@ -1118,8 +1119,9 @@ static int res_get_common_dumpit(struct sk_buff *skb,
has_cap_net_admin = netlink_capable(cb->skb, CAP_NET_ADMIN);
- down_read(&device->res.rwsem);
- xa_for_each(&device->res.xa[res_type], res, id, ULONG_MAX, XA_PRESENT) {
+ xa = rdma_dev_to_xa(device, res_type);
+ rdma_rt_read_lock(device, res_type);
+ xa_for_each(xa, res, id, ULONG_MAX, XA_PRESENT) {
if (idx < start)
goto next;
@@ -1136,9 +1138,9 @@ static int res_get_common_dumpit(struct sk_buff *skb,
filled = true;
- up_read(&device->res.rwsem);
+ rdma_rt_read_unlock(device, res_type);
ret = fe->fill_res_func(skb, has_cap_net_admin, res, port);
- down_read(&device->res.rwsem);
+ rdma_rt_read_lock(device, res_type);
/*
* Return resource back, but it won't be released till
* the &device->res.rwsem will be released for write.
@@ -1156,7 +1158,7 @@ static int res_get_common_dumpit(struct sk_buff *skb,
goto res_err;
next: idx++;
}
- up_read(&device->res.rwsem);
+ rdma_rt_read_unlock(device, res_type);
nla_nest_end(skb, table_attr);
nlmsg_end(skb, nlh);
@@ -1174,7 +1176,7 @@ next: idx++;
res_err:
nla_nest_cancel(skb, table_attr);
- up_read(&device->res.rwsem);
+ rdma_rt_read_unlock(device, res_type);
err:
nlmsg_cancel(skb, nlh);
@@ -9,28 +9,52 @@
#include <linux/mutex.h>
#include <linux/sched/task.h>
#include <linux/pid_namespace.h>
+#include <linux/rwsem.h>
#include "cma_priv.h"
-static int fill_res_noop(struct sk_buff *msg,
- struct rdma_restrack_entry *entry)
-{
- return 0;
-}
+/**
+ * struct rdma_restrack_root - main resource tracking management
+ * entity, per-device
+ */
+struct rdma_restrack_root {
+ /*
+ * @rwsem: Read/write lock to protect erase of entry.
+ * Lists and insertions are protected by XArray internal lock.
+ */
+ struct rw_semaphore rwsem;
+ /**
+ * @xa: Array of XArray structures to hold restrack entries.
+ * We want to use array of XArrays because insertion is type
+ * dependent. For types with xisiting unique ID (like QPN),
+ * we will insert to that unique index. For other types,
+ * we insert based on pointers and auto-allocate unique index.
+ */
+ struct xarray xa[RDMA_RESTRACK_MAX];
+};
/**
- * rdma_restrack_init() - initialize resource tracking
+ * rdma_restrack_init() - initialize and allocate resource tracking
* @dev: IB device
+ *
+ * Return: 0 on success
*/
-void rdma_restrack_init(struct ib_device *dev)
+int rdma_restrack_init(struct ib_device *dev)
{
- struct rdma_restrack_root *res = &dev->res;
+ struct rdma_restrack_root *rt;
int i;
+ dev->res = kzalloc(sizeof(*rt), GFP_KERNEL);
+ if (!dev->res)
+ return -ENOMEM;
+
+ rt = dev->res;
+
for (i = 0 ; i < RDMA_RESTRACK_MAX; i++)
- xa_init_flags(&res->xa[i], XA_FLAGS_ALLOC);
+ xa_init_flags(&rt->xa[i], XA_FLAGS_ALLOC);
+ init_rwsem(&rt->rwsem);
- init_rwsem(&res->rwsem);
+ return 0;
}
static const char *type2str(enum rdma_restrack_type type)
@@ -47,13 +71,52 @@ static const char *type2str(enum rdma_restrack_type type)
return names[type];
};
+/**
+ * rdma_dev_to_xa() - translate from device to XArray DB
+ * @dev: IB device to work
+ * @type: resource track type
+ *
+ * Return: XArray DB to use for xa_for_each() iterations
+ */
+struct xarray *rdma_dev_to_xa(struct ib_device *dev,
+ enum rdma_restrack_type type)
+{
+ return &dev->res->xa[type];
+
+}
+EXPORT_SYMBOL(rdma_dev_to_xa);
+
+/**
+ * rdma_rt_read_lock() - Lock XArray for read, needed while iterating
+ * with xa_for_each()
+ * @dev: IB device to work
+ * @type: resource track type
+ */
+void rdma_rt_read_lock(struct ib_device *dev, enum rdma_restrack_type type)
+{
+ down_read(&dev->res->rwsem);
+}
+EXPORT_SYMBOL(rdma_rt_read_lock);
+
+/**
+ * rdma_rt_read_unlock() - Unlock XArray for read, needed while iterating
+ * with xa_for_each()
+ * @dev: IB device to work
+ * @type: resource track type
+ */
+void rdma_rt_read_unlock(struct ib_device *dev, enum rdma_restrack_type type)
+{
+ up_read(&dev->res->rwsem);
+}
+EXPORT_SYMBOL(rdma_rt_read_unlock);
+
/**
* rdma_restrack_clean() - clean resource tracking
* @dev: IB device
*/
void rdma_restrack_clean(struct ib_device *dev)
{
- struct rdma_restrack_root *res = &dev->res;
+ struct rdma_restrack_root *rt = dev->res;
struct rdma_restrack_entry *e;
char buf[TASK_COMM_LEN];
bool found = false;
@@ -61,14 +124,16 @@ void rdma_restrack_clean(struct ib_device *dev)
int i;
for (i = 0 ; i < RDMA_RESTRACK_MAX; i++) {
- if (!xa_empty(&res->xa[i])) {
+ struct xarray *xa = rdma_dev_to_xa(dev, i);
+
+ if (!xa_empty(xa)) {
unsigned long index = 0;
if (!found) {
pr_err("restrack: %s", CUT_HERE);
dev_err(&dev->dev, "BUG: RESTRACK detected leak of resources\n");
}
- xa_for_each(&res->xa[i], e, index, ULONG_MAX, XA_PRESENT) {
+ xa_for_each(xa, e, index, ULONG_MAX, XA_PRESENT) {
if (rdma_is_kernel_res(e)) {
owner = e->kern_name;
} else {
@@ -88,10 +153,12 @@ void rdma_restrack_clean(struct ib_device *dev)
}
found = true;
}
- xa_destroy(&res->xa[i]);
+ xa_destroy(xa);
}
if (found)
pr_err("restrack: %s", CUT_HERE);
+
+ kfree(rt);
}
/**
@@ -103,19 +170,19 @@ void rdma_restrack_clean(struct ib_device *dev)
int rdma_restrack_count(struct ib_device *dev, enum rdma_restrack_type type,
struct pid_namespace *ns)
{
- struct rdma_restrack_root *res = &dev->res;
+ struct xarray *xa = rdma_dev_to_xa(dev, type);
struct rdma_restrack_entry *e;
unsigned long index = 0;
u32 cnt = 0;
- down_read(&res->rwsem);
- xa_for_each(&res->xa[type], e, index, ULONG_MAX, XA_PRESENT) {
+ rdma_rt_read_lock(dev, type);
+ xa_for_each(xa, e, index, ULONG_MAX, XA_PRESENT) {
if (ns == &init_pid_ns ||
(!rdma_is_kernel_res(e) &&
ns == task_active_pid_ns(e->task)))
cnt++;
}
- up_read(&res->rwsem);
+ rdma_rt_read_unlock(dev, type);
return cnt;
}
EXPORT_SYMBOL(rdma_restrack_count);
@@ -202,6 +269,7 @@ static unsigned long res_to_id(struct rdma_restrack_entry *res)
static void rdma_restrack_add(struct rdma_restrack_entry *res)
{
struct ib_device *dev = res_to_dev(res);
+ struct xarray *xa = rdma_dev_to_xa(dev, res->type);
unsigned long id;
int ret;
@@ -224,7 +292,7 @@ static void rdma_restrack_add(struct rdma_restrack_entry *res)
res->valid = true;
id = res_to_id(res);
- ret = xa_insert(&dev->res.xa[res->type], id, res, GFP_KERNEL);
+ ret = xa_insert(xa, id, res, GFP_KERNEL);
WARN_ONCE(ret == -EEXIST, "Tried to add non-unique type %d entry\n",
res->type);
if (ret)
@@ -271,10 +339,10 @@ struct rdma_restrack_entry *
rdma_restrack_get_byid(struct ib_device *dev,
enum rdma_restrack_type type, u32 id)
{
- struct rdma_restrack_root *rt = &dev->res;
+ struct xarray *xa = rdma_dev_to_xa(dev, type);
struct rdma_restrack_entry *res;
- res = xa_load(&rt->xa[type], id);
+ res = xa_load(xa, id);
if (!res || xa_is_err(res) || !rdma_restrack_get(res))
return ERR_PTR(-ENOENT);
return res;
@@ -297,25 +365,42 @@ EXPORT_SYMBOL(rdma_restrack_put);
void rdma_restrack_del(struct rdma_restrack_entry *res)
{
- struct ib_device *dev;
+ struct ib_device *dev = res_to_dev(res);
+ struct xarray *xa;
unsigned long id;
if (!res->valid)
goto out;
- dev = res_to_dev(res);
+ /*
+ * All objects except CM_ID set valid device immediately
+ * after new object is created, it means that for not valid
+ * objects will still have "dev".
+ *
+ * It is not the case for CM_ID, newly created object has
+ * this field set to NULL and it is set in _cma_attach_to_dev()
+ * only.
+ *
+ * Because we don't want to add any conditions on call
+ * to rdma_restrack_del(), the check below protects from
+ * NULL-dereference.
+ */
if (!dev)
return;
+ xa = rdma_dev_to_xa(dev, res->type);
+ id = res_to_id(res);
+ if (!xa_load(xa, id))
+ goto out;
+
rdma_restrack_put(res);
wait_for_completion(&res->comp);
- id = res_to_id(res);
- down_write(&dev->res.rwsem);
- xa_erase(&dev->res.xa[res->type], id);
+ down_write(&dev->res->rwsem);
+ xa_erase(xa, id);
res->valid = false;
- up_write(&dev->res.rwsem);
+ up_write(&dev->res->rwsem);
out:
if (res->task) {
@@ -2517,6 +2517,8 @@ struct ib_device_ops {
struct rdma_restrack_entry *entry);
};
+struct rdma_restrack_root;
+
struct ib_device {
/* Do not access @dma_device directly from ULP nor from HW drivers. */
struct device *dma_device;
@@ -2581,10 +2583,7 @@ struct ib_device {
#endif
u32 index;
- /*
- * Implementation details of the RDMA core, don't use in drivers
- */
- struct rdma_restrack_root res;
+ struct rdma_restrack_root *res;
const struct uapi_definition *driver_def;
enum rdma_driver_id driver_id;
@@ -7,7 +7,6 @@
#define _RDMA_RESTRACK_H_
#include <linux/typecheck.h>
-#include <linux/rwsem.h>
#include <linux/sched.h>
#include <linux/kref.h>
#include <linux/completion.h>
@@ -50,27 +49,6 @@ enum rdma_restrack_type {
};
struct ib_device;
-struct rdma_restrack_entry;
-
-/**
- * struct rdma_restrack_root - main resource tracking management
- * entity, per-device
- */
-struct rdma_restrack_root {
- /*
- * @rwsem: Read/write lock to protect erase of entry.
- * Lists and insertions are protected by XArray internal lock.
- */
- struct rw_semaphore rwsem;
- /**
- * @xa: Array of XArray structures to hold restrack entries.
- * We want to use array of XArrays because insertion is type
- * dependent. For types with xisiting unique ID (like QPN),
- * we will insert to that unique index. For other types,
- * we insert based on pointers and auto-allocate unique index.
- */
- struct xarray xa[RDMA_RESTRACK_MAX];
-};
/**
* struct rdma_restrack_entry - metadata per-entry
@@ -117,7 +95,7 @@ struct rdma_restrack_entry {
bool user;
};
-void rdma_restrack_init(struct ib_device *dev);
+int rdma_restrack_init(struct ib_device *dev);
void rdma_restrack_clean(struct ib_device *dev);
int rdma_restrack_count(struct ib_device *dev,
enum rdma_restrack_type type,
@@ -175,4 +153,8 @@ int rdma_nl_put_driver_u64_hex(struct sk_buff *msg, const char *name,
struct rdma_restrack_entry *rdma_restrack_get_byid(struct ib_device *dev,
enum rdma_restrack_type type,
u32 id);
+struct xarray *rdma_dev_to_xa(struct ib_device *dev,
+ enum rdma_restrack_type type);
+void rdma_rt_read_lock(struct ib_device *dev, enum rdma_restrack_type type);
+void rdma_rt_read_unlock(struct ib_device *dev, enum rdma_restrack_type type);
#endif /* _RDMA_RESTRACK_H_ */