1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2/*
3 * Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved.
4 */
5
6#include <rdma/rdma_cm.h>
7#include <rdma/ib_verbs.h>
8#include <rdma/restrack.h>
9#include <rdma/rdma_counter.h>
10#include <linux/mutex.h>
11#include <linux/sched/task.h>
12#include <linux/pid_namespace.h>
13
14#include "cma_priv.h"
15#include "restrack.h"
16
17/**
18 * rdma_restrack_init() - initialize and allocate resource tracking
19 * @dev: IB device
20 *
21 * Return: 0 on success
22 */
23int rdma_restrack_init(struct ib_device *dev)
24{
25 struct rdma_restrack_root *rt;
26 int i;
27
28 dev->res = kcalloc(RDMA_RESTRACK_MAX, sizeof(*rt), GFP_KERNEL);
29 if (!dev->res)
30 return -ENOMEM;
31
32 rt = dev->res;
33
34 for (i = 0; i < RDMA_RESTRACK_MAX; i++)
35 xa_init_flags(xa: &rt[i].xa, XA_FLAGS_ALLOC);
36
37 return 0;
38}
39
40/**
41 * rdma_restrack_clean() - clean resource tracking
42 * @dev: IB device
43 */
44void rdma_restrack_clean(struct ib_device *dev)
45{
46 struct rdma_restrack_root *rt = dev->res;
47 int i;
48
49 for (i = 0 ; i < RDMA_RESTRACK_MAX; i++) {
50 struct xarray *xa = &dev->res[i].xa;
51
52 WARN_ON(!xa_empty(xa));
53 xa_destroy(xa);
54 }
55 kfree(objp: rt);
56}
57
58/**
59 * rdma_restrack_count() - the current usage of specific object
60 * @dev: IB device
61 * @type: actual type of object to operate
62 * @show_details: count driver specific objects
63 */
64int rdma_restrack_count(struct ib_device *dev, enum rdma_restrack_type type,
65 bool show_details)
66{
67 struct rdma_restrack_root *rt = &dev->res[type];
68 struct rdma_restrack_entry *e;
69 XA_STATE(xas, &rt->xa, 0);
70 u32 cnt = 0;
71
72 xa_lock(&rt->xa);
73 xas_for_each(&xas, e, U32_MAX) {
74 if (xa_get_mark(&rt->xa, index: e->id, RESTRACK_DD) && !show_details)
75 continue;
76 cnt++;
77 }
78 xa_unlock(&rt->xa);
79 return cnt;
80}
81EXPORT_SYMBOL(rdma_restrack_count);
82
83static struct ib_device *res_to_dev(struct rdma_restrack_entry *res)
84{
85 switch (res->type) {
86 case RDMA_RESTRACK_PD:
87 return container_of(res, struct ib_pd, res)->device;
88 case RDMA_RESTRACK_CQ:
89 return container_of(res, struct ib_cq, res)->device;
90 case RDMA_RESTRACK_QP:
91 return container_of(res, struct ib_qp, res)->device;
92 case RDMA_RESTRACK_CM_ID:
93 return container_of(res, struct rdma_id_private,
94 res)->id.device;
95 case RDMA_RESTRACK_MR:
96 return container_of(res, struct ib_mr, res)->device;
97 case RDMA_RESTRACK_CTX:
98 return container_of(res, struct ib_ucontext, res)->device;
99 case RDMA_RESTRACK_COUNTER:
100 return container_of(res, struct rdma_counter, res)->device;
101 case RDMA_RESTRACK_SRQ:
102 return container_of(res, struct ib_srq, res)->device;
103 case RDMA_RESTRACK_DMAH:
104 return container_of(res, struct ib_dmah, res)->device;
105 default:
106 WARN_ONCE(true, "Wrong resource tracking type %u\n", res->type);
107 return NULL;
108 }
109}
110
111/**
112 * rdma_restrack_attach_task() - attach the task onto this resource,
113 * valid for user space restrack entries.
114 * @res: resource entry
115 * @task: the task to attach
116 */
117static void rdma_restrack_attach_task(struct rdma_restrack_entry *res,
118 struct task_struct *task)
119{
120 if (WARN_ON_ONCE(!task))
121 return;
122
123 if (res->task)
124 put_task_struct(t: res->task);
125 get_task_struct(t: task);
126 res->task = task;
127 res->user = true;
128}
129
130/**
131 * rdma_restrack_set_name() - set the task for this resource
132 * @res: resource entry
133 * @caller: kernel name, the current task will be used if the caller is NULL.
134 */
135void rdma_restrack_set_name(struct rdma_restrack_entry *res, const char *caller)
136{
137 if (caller) {
138 res->kern_name = caller;
139 return;
140 }
141
142 rdma_restrack_attach_task(res, current);
143}
144EXPORT_SYMBOL(rdma_restrack_set_name);
145
146/**
147 * rdma_restrack_parent_name() - set the restrack name properties based
148 * on parent restrack
149 * @dst: destination resource entry
150 * @parent: parent resource entry
151 */
152void rdma_restrack_parent_name(struct rdma_restrack_entry *dst,
153 const struct rdma_restrack_entry *parent)
154{
155 if (rdma_is_kernel_res(res: parent))
156 dst->kern_name = parent->kern_name;
157 else
158 rdma_restrack_attach_task(res: dst, task: parent->task);
159}
160EXPORT_SYMBOL(rdma_restrack_parent_name);
161
162/**
163 * rdma_restrack_new() - Initializes new restrack entry to allow _put() interface
164 * to release memory in fully automatic way.
165 * @res: Entry to initialize
166 * @type: REstrack type
167 */
168void rdma_restrack_new(struct rdma_restrack_entry *res,
169 enum rdma_restrack_type type)
170{
171 kref_init(kref: &res->kref);
172 init_completion(x: &res->comp);
173 res->type = type;
174}
175EXPORT_SYMBOL(rdma_restrack_new);
176
177/**
178 * rdma_restrack_add() - add object to the resource tracking database
179 * @res: resource entry
180 */
181void rdma_restrack_add(struct rdma_restrack_entry *res)
182{
183 struct ib_device *dev = res_to_dev(res);
184 struct rdma_restrack_root *rt;
185 int ret = 0;
186
187 if (!dev)
188 return;
189
190 if (res->no_track)
191 goto out;
192
193 rt = &dev->res[res->type];
194
195 if (res->type == RDMA_RESTRACK_QP) {
196 /* Special case to ensure that LQPN points to right QP */
197 struct ib_qp *qp = container_of(res, struct ib_qp, res);
198
199 WARN_ONCE(qp->qp_num >> 24 || qp->port >> 8,
200 "QP number 0x%0X and port 0x%0X", qp->qp_num,
201 qp->port);
202 res->id = qp->qp_num;
203 if (qp->qp_type == IB_QPT_SMI || qp->qp_type == IB_QPT_GSI)
204 res->id |= qp->port << 24;
205 ret = xa_insert(xa: &rt->xa, index: res->id, entry: res, GFP_KERNEL);
206 if (ret)
207 res->id = 0;
208
209 if (qp->qp_type >= IB_QPT_DRIVER)
210 xa_set_mark(&rt->xa, index: res->id, RESTRACK_DD);
211 } else if (res->type == RDMA_RESTRACK_COUNTER) {
212 /* Special case to ensure that cntn points to right counter */
213 struct rdma_counter *counter;
214
215 counter = container_of(res, struct rdma_counter, res);
216 ret = xa_insert(xa: &rt->xa, index: counter->id, entry: res, GFP_KERNEL);
217 res->id = ret ? 0 : counter->id;
218 } else {
219 ret = xa_alloc_cyclic(xa: &rt->xa, id: &res->id, entry: res, xa_limit_32b,
220 next: &rt->next_id, GFP_KERNEL);
221 ret = (ret < 0) ? ret : 0;
222 }
223
224out:
225 if (!ret)
226 res->valid = true;
227}
228EXPORT_SYMBOL(rdma_restrack_add);
229
230int __must_check rdma_restrack_get(struct rdma_restrack_entry *res)
231{
232 return kref_get_unless_zero(kref: &res->kref);
233}
234EXPORT_SYMBOL(rdma_restrack_get);
235
236/**
237 * rdma_restrack_get_byid() - translate from ID to restrack object
238 * @dev: IB device
239 * @type: resource track type
240 * @id: ID to take a look
241 *
242 * Return: Pointer to restrack entry or -ENOENT in case of error.
243 */
244struct rdma_restrack_entry *
245rdma_restrack_get_byid(struct ib_device *dev,
246 enum rdma_restrack_type type, u32 id)
247{
248 struct rdma_restrack_root *rt = &dev->res[type];
249 struct rdma_restrack_entry *res;
250
251 xa_lock(&rt->xa);
252 res = xa_load(&rt->xa, index: id);
253 if (!res || !rdma_restrack_get(res))
254 res = ERR_PTR(error: -ENOENT);
255 xa_unlock(&rt->xa);
256
257 return res;
258}
259EXPORT_SYMBOL(rdma_restrack_get_byid);
260
261static void restrack_release(struct kref *kref)
262{
263 struct rdma_restrack_entry *res;
264
265 res = container_of(kref, struct rdma_restrack_entry, kref);
266 if (res->task) {
267 put_task_struct(t: res->task);
268 res->task = NULL;
269 }
270 complete(&res->comp);
271}
272
273int rdma_restrack_put(struct rdma_restrack_entry *res)
274{
275 return kref_put(kref: &res->kref, release: restrack_release);
276}
277EXPORT_SYMBOL(rdma_restrack_put);
278
279/**
280 * rdma_restrack_del() - delete object from the resource tracking database
281 * @res: resource entry
282 */
283void rdma_restrack_del(struct rdma_restrack_entry *res)
284{
285 struct rdma_restrack_entry *old;
286 struct rdma_restrack_root *rt;
287 struct ib_device *dev;
288
289 if (!res->valid) {
290 if (res->task) {
291 put_task_struct(t: res->task);
292 res->task = NULL;
293 }
294 return;
295 }
296
297 if (res->no_track)
298 goto out;
299
300 dev = res_to_dev(res);
301 if (WARN_ON(!dev))
302 return;
303
304 rt = &dev->res[res->type];
305
306 old = xa_erase(&rt->xa, index: res->id);
307 WARN_ON(old != res);
308
309out:
310 res->valid = false;
311 rdma_restrack_put(res);
312 wait_for_completion(&res->comp);
313}
314EXPORT_SYMBOL(rdma_restrack_del);
315

source code of linux/drivers/infiniband/core/restrack.c