Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2/*
3 * Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved
4 */
5
6#include <linux/kref.h>
7#include <linux/cdev.h>
8#include <linux/mutex.h>
9#include <linux/file.h>
10#include <linux/fs.h>
11#include <rdma/ib_ucaps.h>
12
13#define RDMA_UCAP_FIRST RDMA_UCAP_MLX5_CTRL_LOCAL
14
15static DEFINE_MUTEX(ucaps_mutex);
16static struct ib_ucap *ucaps_list[RDMA_UCAP_MAX];
17static bool ucaps_class_is_registered;
18static dev_t ucaps_base_dev;
19
20struct ib_ucap {
21 struct cdev cdev;
22 struct device dev;
23 struct kref ref;
24};
25
26static const char *ucap_names[RDMA_UCAP_MAX] = {
27 [RDMA_UCAP_MLX5_CTRL_LOCAL] = "mlx5_perm_ctrl_local",
28 [RDMA_UCAP_MLX5_CTRL_OTHER_VHCA] = "mlx5_perm_ctrl_other_vhca"
29};
30
31static char *ucaps_devnode(const struct device *dev, umode_t *mode)
32{
33 if (mode)
34 *mode = 0600;
35
36 return kasprintf(GFP_KERNEL, "infiniband/%s", dev_name(dev));
37}
38
39static const struct class ucaps_class = {
40 .name = "infiniband_ucaps",
41 .devnode = ucaps_devnode,
42};
43
44static const struct file_operations ucaps_cdev_fops = {
45 .owner = THIS_MODULE,
46 .open = simple_open,
47};
48
49/**
50 * ib_cleanup_ucaps - cleanup all API resources and class.
51 *
52 * This is called once, when removing the ib_uverbs module.
53 */
54void ib_cleanup_ucaps(void)
55{
56 mutex_lock(&ucaps_mutex);
57 if (!ucaps_class_is_registered) {
58 mutex_unlock(&ucaps_mutex);
59 return;
60 }
61
62 for (int i = RDMA_UCAP_FIRST; i < RDMA_UCAP_MAX; i++)
63 WARN_ON(ucaps_list[i]);
64
65 class_unregister(&ucaps_class);
66 ucaps_class_is_registered = false;
67 unregister_chrdev_region(ucaps_base_dev, RDMA_UCAP_MAX);
68 mutex_unlock(&ucaps_mutex);
69}
70
71static int get_ucap_from_devt(dev_t devt, u64 *idx_mask)
72{
73 for (int type = RDMA_UCAP_FIRST; type < RDMA_UCAP_MAX; type++) {
74 if (ucaps_list[type] && ucaps_list[type]->dev.devt == devt) {
75 *idx_mask |= 1 << type;
76 return 0;
77 }
78 }
79
80 return -EINVAL;
81}
82
83static int get_devt_from_fd(unsigned int fd, dev_t *ret_dev)
84{
85 struct file *file;
86
87 file = fget(fd);
88 if (!file)
89 return -EBADF;
90
91 *ret_dev = file_inode(file)->i_rdev;
92 fput(file);
93 return 0;
94}
95
96/**
97 * ib_ucaps_init - Initialization required before ucap creation.
98 *
99 * Return: 0 on success, or a negative errno value on failure
100 */
101static int ib_ucaps_init(void)
102{
103 int ret = 0;
104
105 if (ucaps_class_is_registered)
106 return ret;
107
108 ret = class_register(&ucaps_class);
109 if (ret)
110 return ret;
111
112 ret = alloc_chrdev_region(&ucaps_base_dev, 0, RDMA_UCAP_MAX,
113 ucaps_class.name);
114 if (ret < 0) {
115 class_unregister(&ucaps_class);
116 return ret;
117 }
118
119 ucaps_class_is_registered = true;
120
121 return 0;
122}
123
124static void ucap_dev_release(struct device *device)
125{
126 struct ib_ucap *ucap = container_of(device, struct ib_ucap, dev);
127
128 kfree(ucap);
129}
130
131/**
132 * ib_create_ucap - Add a ucap character device
133 * @type: UCAP type
134 *
135 * Creates a ucap character device in the /dev/infiniband directory. By default,
136 * the device has root-only read-write access.
137 *
138 * A driver may call this multiple times with the same UCAP type. A reference
139 * count tracks creations and deletions.
140 *
141 * Return: 0 on success, or a negative errno value on failure
142 */
143int ib_create_ucap(enum rdma_user_cap type)
144{
145 struct ib_ucap *ucap;
146 int ret;
147
148 if (type >= RDMA_UCAP_MAX)
149 return -EINVAL;
150
151 mutex_lock(&ucaps_mutex);
152 ret = ib_ucaps_init();
153 if (ret)
154 goto unlock;
155
156 ucap = ucaps_list[type];
157 if (ucap) {
158 kref_get(&ucap->ref);
159 mutex_unlock(&ucaps_mutex);
160 return 0;
161 }
162
163 ucap = kzalloc(sizeof(*ucap), GFP_KERNEL);
164 if (!ucap) {
165 ret = -ENOMEM;
166 goto unlock;
167 }
168
169 device_initialize(&ucap->dev);
170 ucap->dev.class = &ucaps_class;
171 ucap->dev.devt = MKDEV(MAJOR(ucaps_base_dev), type);
172 ucap->dev.release = ucap_dev_release;
173 ret = dev_set_name(&ucap->dev, "%s", ucap_names[type]);
174 if (ret)
175 goto err_device;
176
177 cdev_init(&ucap->cdev, &ucaps_cdev_fops);
178 ucap->cdev.owner = THIS_MODULE;
179
180 ret = cdev_device_add(&ucap->cdev, &ucap->dev);
181 if (ret)
182 goto err_device;
183
184 kref_init(&ucap->ref);
185 ucaps_list[type] = ucap;
186 mutex_unlock(&ucaps_mutex);
187
188 return 0;
189
190err_device:
191 put_device(&ucap->dev);
192unlock:
193 mutex_unlock(&ucaps_mutex);
194 return ret;
195}
196EXPORT_SYMBOL(ib_create_ucap);
197
198static void ib_release_ucap(struct kref *ref)
199{
200 struct ib_ucap *ucap = container_of(ref, struct ib_ucap, ref);
201 enum rdma_user_cap type;
202
203 for (type = RDMA_UCAP_FIRST; type < RDMA_UCAP_MAX; type++) {
204 if (ucaps_list[type] == ucap)
205 break;
206 }
207 WARN_ON(type == RDMA_UCAP_MAX);
208
209 ucaps_list[type] = NULL;
210 cdev_device_del(&ucap->cdev, &ucap->dev);
211 put_device(&ucap->dev);
212}
213
214/**
215 * ib_remove_ucap - Remove a ucap character device
216 * @type: User cap type
217 *
218 * Removes the ucap character device according to type. The device is completely
219 * removed from the filesystem when its reference count reaches 0.
220 */
221void ib_remove_ucap(enum rdma_user_cap type)
222{
223 struct ib_ucap *ucap;
224
225 mutex_lock(&ucaps_mutex);
226 ucap = ucaps_list[type];
227 if (WARN_ON(!ucap))
228 goto end;
229
230 kref_put(&ucap->ref, ib_release_ucap);
231end:
232 mutex_unlock(&ucaps_mutex);
233}
234EXPORT_SYMBOL(ib_remove_ucap);
235
236/**
237 * ib_get_ucaps - Get bitmask of ucap types from file descriptors
238 * @fds: Array of file descriptors
239 * @fd_count: Number of file descriptors in the array
240 * @idx_mask: Bitmask to be updated based on the ucaps in the fd list
241 *
242 * Given an array of file descriptors, this function returns a bitmask of
243 * the ucaps where a bit is set if an FD for that ucap type was in the array.
244 *
245 * Return: 0 on success, or a negative errno value on failure
246 */
247int ib_get_ucaps(int *fds, int fd_count, uint64_t *idx_mask)
248{
249 int ret = 0;
250 dev_t dev;
251
252 *idx_mask = 0;
253 mutex_lock(&ucaps_mutex);
254 for (int i = 0; i < fd_count; i++) {
255 ret = get_devt_from_fd(fds[i], &dev);
256 if (ret)
257 goto end;
258
259 ret = get_ucap_from_devt(dev, idx_mask);
260 if (ret)
261 goto end;
262 }
263
264end:
265 mutex_unlock(&ucaps_mutex);
266 return ret;
267}