File Index
Files and directories
Sign in
FILE

ucaps.c

File preview with syntax highlighting.

linux-6.19.14/drivers/infiniband/core/ucaps.c C
  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}
~/ Read-only file preview.