Merge tag 'xarray-5.1-rc1' of git://git.infradead.org/users/willy/linux-dax
[muen/linux.git] / drivers / infiniband / core / restrack.c
index 46a5c553c6244a60cf2bc8409e211cfe86618f23..3b5ff2f7b5f8759416f56b69758c161c18afbfc2 100644 (file)
 #include <linux/pid_namespace.h>
 
 #include "cma_priv.h"
+#include "restrack.h"
 
-static int fill_res_noop(struct sk_buff *msg,
-                        struct rdma_restrack_entry *entry)
+/**
+ * rdma_restrack_init() - initialize and allocate resource tracking
+ * @dev:  IB device
+ *
+ * Return: 0 on success
+ */
+int rdma_restrack_init(struct ib_device *dev)
 {
-       return 0;
-}
+       struct rdma_restrack_root *rt;
+       int i;
 
-void rdma_restrack_init(struct rdma_restrack_root *res)
-{
-       init_rwsem(&res->rwsem);
-       res->fill_res_entry = fill_res_noop;
+       dev->res = kcalloc(RDMA_RESTRACK_MAX, sizeof(*rt), GFP_KERNEL);
+       if (!dev->res)
+               return -ENOMEM;
+
+       rt = dev->res;
+
+       for (i = 0; i < RDMA_RESTRACK_MAX; i++)
+               xa_init_flags(&rt[i].xa, XA_FLAGS_ALLOC);
+
+       return 0;
 }
 
 static const char *type2str(enum rdma_restrack_type type)
@@ -38,55 +50,79 @@ static const char *type2str(enum rdma_restrack_type type)
        return names[type];
 };
 
-void rdma_restrack_clean(struct rdma_restrack_root *res)
+/**
+ * rdma_restrack_clean() - clean resource tracking
+ * @dev:  IB device
+ */
+void rdma_restrack_clean(struct ib_device *dev)
 {
+       struct rdma_restrack_root *rt = dev->res;
        struct rdma_restrack_entry *e;
        char buf[TASK_COMM_LEN];
-       struct ib_device *dev;
+       bool found = false;
        const char *owner;
-       int bkt;
-
-       if (hash_empty(res->hash))
-               return;
-
-       dev = container_of(res, struct ib_device, res);
-       pr_err("restrack: %s", CUT_HERE);
-       dev_err(&dev->dev, "BUG: RESTRACK detected leak of resources\n");
-       hash_for_each(res->hash, bkt, e, node) {
-               if (rdma_is_kernel_res(e)) {
-                       owner = e->kern_name;
-               } else {
-                       /*
-                        * There is no need to call get_task_struct here,
-                        * because we can be here only if there are more
-                        * get_task_struct() call than put_task_struct().
-                        */
-                       get_task_comm(buf, e->task);
-                       owner = buf;
+       int i;
+
+       for (i = 0 ; i < RDMA_RESTRACK_MAX; i++) {
+               struct xarray *xa = &dev->res[i].xa;
+
+               if (!xa_empty(xa)) {
+                       unsigned long index;
+
+                       if (!found) {
+                               pr_err("restrack: %s", CUT_HERE);
+                               dev_err(&dev->dev, "BUG: RESTRACK detected leak of resources\n");
+                       }
+                       xa_for_each(xa, index, e) {
+                               if (rdma_is_kernel_res(e)) {
+                                       owner = e->kern_name;
+                               } else {
+                                       /*
+                                        * There is no need to call get_task_struct here,
+                                        * because we can be here only if there are more
+                                        * get_task_struct() call than put_task_struct().
+                                        */
+                                       get_task_comm(buf, e->task);
+                                       owner = buf;
+                               }
+
+                               pr_err("restrack: %s %s object allocated by %s is not freed\n",
+                                      rdma_is_kernel_res(e) ? "Kernel" :
+                                                              "User",
+                                      type2str(e->type), owner);
+                       }
+                       found = true;
                }
-
-               pr_err("restrack: %s %s object allocated by %s is not freed\n",
-                      rdma_is_kernel_res(e) ? "Kernel" : "User",
-                      type2str(e->type), owner);
+               xa_destroy(xa);
        }
-       pr_err("restrack: %s", CUT_HERE);
+       if (found)
+               pr_err("restrack: %s", CUT_HERE);
+
+       kfree(rt);
 }
 
-int rdma_restrack_count(struct rdma_restrack_root *res,
-                       enum rdma_restrack_type type,
+/**
+ * rdma_restrack_count() - the current usage of specific object
+ * @dev:  IB device
+ * @type: actual type of object to operate
+ * @ns:   PID namespace
+ */
+int rdma_restrack_count(struct ib_device *dev, enum rdma_restrack_type type,
                        struct pid_namespace *ns)
 {
+       struct rdma_restrack_root *rt = &dev->res[type];
        struct rdma_restrack_entry *e;
+       XA_STATE(xas, &rt->xa, 0);
        u32 cnt = 0;
 
-       down_read(&res->rwsem);
-       hash_for_each_possible(res->hash, e, node, type) {
+       xa_lock(&rt->xa);
+       xas_for_each(&xas, e, U32_MAX) {
                if (ns == &init_pid_ns ||
                    (!rdma_is_kernel_res(e) &&
                     ns == task_active_pid_ns(e->task)))
                        cnt++;
        }
-       up_read(&res->rwsem);
+       xa_unlock(&rt->xa);
        return cnt;
 }
 EXPORT_SYMBOL(rdma_restrack_count);
@@ -157,28 +193,29 @@ EXPORT_SYMBOL(rdma_restrack_set_task);
 static void rdma_restrack_add(struct rdma_restrack_entry *res)
 {
        struct ib_device *dev = res_to_dev(res);
+       struct rdma_restrack_root *rt;
+       int ret;
 
        if (!dev)
                return;
 
-       if (res->type != RDMA_RESTRACK_CM_ID || rdma_is_kernel_res(res))
-               res->task = NULL;
-
-       if (!rdma_is_kernel_res(res)) {
-               if (!res->task)
-                       rdma_restrack_set_task(res, NULL);
-               res->kern_name = NULL;
-       } else {
-               set_kern_name(res);
-       }
+       rt = &dev->res[res->type];
 
        kref_init(&res->kref);
        init_completion(&res->comp);
-       res->valid = true;
+       if (res->type != RDMA_RESTRACK_QP)
+               ret = xa_alloc_cyclic(&rt->xa, &res->id, res, xa_limit_32b,
+                               &rt->next_id, GFP_KERNEL);
+       else {
+               /* Special case to ensure that LQPN points to right QP */
+               struct ib_qp *qp = container_of(res, struct ib_qp, res);
+
+               ret = xa_insert(&rt->xa, qp->qp_num, res, GFP_KERNEL);
+               res->id = ret ? 0 : qp->qp_num;
+       }
 
-       down_write(&dev->res.rwsem);
-       hash_add(dev->res.hash, &res->node, res->type);
-       up_write(&dev->res.rwsem);
+       if (!ret)
+               res->valid = true;
 }
 
 /**
@@ -187,6 +224,8 @@ static void rdma_restrack_add(struct rdma_restrack_entry *res)
  */
 void rdma_restrack_kadd(struct rdma_restrack_entry *res)
 {
+       res->task = NULL;
+       set_kern_name(res);
        res->user = false;
        rdma_restrack_add(res);
 }
@@ -198,6 +237,13 @@ EXPORT_SYMBOL(rdma_restrack_kadd);
  */
 void rdma_restrack_uadd(struct rdma_restrack_entry *res)
 {
+       if (res->type != RDMA_RESTRACK_CM_ID)
+               res->task = NULL;
+
+       if (!res->task)
+               rdma_restrack_set_task(res, NULL);
+       res->kern_name = NULL;
+
        res->user = true;
        rdma_restrack_add(res);
 }
@@ -209,6 +255,31 @@ int __must_check rdma_restrack_get(struct rdma_restrack_entry *res)
 }
 EXPORT_SYMBOL(rdma_restrack_get);
 
+/**
+ * rdma_restrack_get_byid() - translate from ID to restrack object
+ * @dev: IB device
+ * @type: resource track type
+ * @id: ID to take a look
+ *
+ * Return: Pointer to restrack entry or -ENOENT in case of error.
+ */
+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[type];
+       struct rdma_restrack_entry *res;
+
+       xa_lock(&rt->xa);
+       res = xa_load(&rt->xa, id);
+       if (!res || !rdma_restrack_get(res))
+               res = ERR_PTR(-ENOENT);
+       xa_unlock(&rt->xa);
+
+       return res;
+}
+EXPORT_SYMBOL(rdma_restrack_get_byid);
+
 static void restrack_release(struct kref *kref)
 {
        struct rdma_restrack_entry *res;
@@ -225,23 +296,25 @@ EXPORT_SYMBOL(rdma_restrack_put);
 
 void rdma_restrack_del(struct rdma_restrack_entry *res)
 {
+       struct rdma_restrack_entry *old;
+       struct rdma_restrack_root *rt;
        struct ib_device *dev;
 
        if (!res->valid)
                goto out;
 
        dev = res_to_dev(res);
-       if (!dev)
+       if (WARN_ON(!dev))
                return;
 
-       rdma_restrack_put(res);
-
-       wait_for_completion(&res->comp);
+       rt = &dev->res[res->type];
 
-       down_write(&dev->res.rwsem);
-       hash_del(&res->node);
+       old = xa_erase(&rt->xa, res->id);
+       WARN_ON(old != res);
        res->valid = false;
-       up_write(&dev->res.rwsem);
+
+       rdma_restrack_put(res);
+       wait_for_completion(&res->comp);
 
 out:
        if (res->task) {