xref: /linux/drivers/infiniband/core/ucaps.c (revision 834a4a689699090a406d1662b03affa8b155d025)
161e51682SChiara Meiohas // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
261e51682SChiara Meiohas /*
361e51682SChiara Meiohas  * Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved
461e51682SChiara Meiohas  */
561e51682SChiara Meiohas 
661e51682SChiara Meiohas #include <linux/kref.h>
761e51682SChiara Meiohas #include <linux/cdev.h>
861e51682SChiara Meiohas #include <linux/mutex.h>
961e51682SChiara Meiohas #include <linux/file.h>
1061e51682SChiara Meiohas #include <linux/fs.h>
1161e51682SChiara Meiohas #include <rdma/ib_ucaps.h>
1261e51682SChiara Meiohas 
1361e51682SChiara Meiohas #define RDMA_UCAP_FIRST RDMA_UCAP_MLX5_CTRL_LOCAL
1461e51682SChiara Meiohas 
1561e51682SChiara Meiohas static DEFINE_MUTEX(ucaps_mutex);
1661e51682SChiara Meiohas static struct ib_ucap *ucaps_list[RDMA_UCAP_MAX];
1761e51682SChiara Meiohas static bool ucaps_class_is_registered;
1861e51682SChiara Meiohas static dev_t ucaps_base_dev;
1961e51682SChiara Meiohas 
2061e51682SChiara Meiohas struct ib_ucap {
2161e51682SChiara Meiohas 	struct cdev cdev;
2261e51682SChiara Meiohas 	struct device dev;
2361e51682SChiara Meiohas 	struct kref ref;
2461e51682SChiara Meiohas };
2561e51682SChiara Meiohas 
2661e51682SChiara Meiohas static const char *ucap_names[RDMA_UCAP_MAX] = {
2761e51682SChiara Meiohas 	[RDMA_UCAP_MLX5_CTRL_LOCAL] = "mlx5_perm_ctrl_local",
2861e51682SChiara Meiohas 	[RDMA_UCAP_MLX5_CTRL_OTHER_VHCA] = "mlx5_perm_ctrl_other_vhca"
2961e51682SChiara Meiohas };
3061e51682SChiara Meiohas 
ucaps_devnode(const struct device * dev,umode_t * mode)3161e51682SChiara Meiohas static char *ucaps_devnode(const struct device *dev, umode_t *mode)
3261e51682SChiara Meiohas {
3361e51682SChiara Meiohas 	if (mode)
3461e51682SChiara Meiohas 		*mode = 0600;
3561e51682SChiara Meiohas 
3661e51682SChiara Meiohas 	return kasprintf(GFP_KERNEL, "infiniband/%s", dev_name(dev));
3761e51682SChiara Meiohas }
3861e51682SChiara Meiohas 
3961e51682SChiara Meiohas static const struct class ucaps_class = {
4061e51682SChiara Meiohas 	.name = "infiniband_ucaps",
4161e51682SChiara Meiohas 	.devnode = ucaps_devnode,
4261e51682SChiara Meiohas };
4361e51682SChiara Meiohas 
4461e51682SChiara Meiohas static const struct file_operations ucaps_cdev_fops = {
4561e51682SChiara Meiohas 	.owner = THIS_MODULE,
4661e51682SChiara Meiohas 	.open = simple_open,
4761e51682SChiara Meiohas };
4861e51682SChiara Meiohas 
4961e51682SChiara Meiohas /**
5061e51682SChiara Meiohas  * ib_cleanup_ucaps - cleanup all API resources and class.
5161e51682SChiara Meiohas  *
5261e51682SChiara Meiohas  * This is called once, when removing the ib_uverbs module.
5361e51682SChiara Meiohas  */
ib_cleanup_ucaps(void)5461e51682SChiara Meiohas void ib_cleanup_ucaps(void)
5561e51682SChiara Meiohas {
5661e51682SChiara Meiohas 	mutex_lock(&ucaps_mutex);
5761e51682SChiara Meiohas 	if (!ucaps_class_is_registered) {
5861e51682SChiara Meiohas 		mutex_unlock(&ucaps_mutex);
5961e51682SChiara Meiohas 		return;
6061e51682SChiara Meiohas 	}
6161e51682SChiara Meiohas 
6261e51682SChiara Meiohas 	for (int i = RDMA_UCAP_FIRST; i < RDMA_UCAP_MAX; i++)
6361e51682SChiara Meiohas 		WARN_ON(ucaps_list[i]);
6461e51682SChiara Meiohas 
6561e51682SChiara Meiohas 	class_unregister(&ucaps_class);
6661e51682SChiara Meiohas 	ucaps_class_is_registered = false;
6761e51682SChiara Meiohas 	unregister_chrdev_region(ucaps_base_dev, RDMA_UCAP_MAX);
6861e51682SChiara Meiohas 	mutex_unlock(&ucaps_mutex);
6961e51682SChiara Meiohas }
7061e51682SChiara Meiohas 
get_ucap_from_devt(dev_t devt,u64 * idx_mask)7161e51682SChiara Meiohas static int get_ucap_from_devt(dev_t devt, u64 *idx_mask)
7261e51682SChiara Meiohas {
7361e51682SChiara Meiohas 	for (int type = RDMA_UCAP_FIRST; type < RDMA_UCAP_MAX; type++) {
7461e51682SChiara Meiohas 		if (ucaps_list[type] && ucaps_list[type]->dev.devt == devt) {
7561e51682SChiara Meiohas 			*idx_mask |= 1 << type;
7661e51682SChiara Meiohas 			return 0;
7761e51682SChiara Meiohas 		}
7861e51682SChiara Meiohas 	}
7961e51682SChiara Meiohas 
8061e51682SChiara Meiohas 	return -EINVAL;
8161e51682SChiara Meiohas }
8261e51682SChiara Meiohas 
get_devt_from_fd(unsigned int fd,dev_t * ret_dev)8361e51682SChiara Meiohas static int get_devt_from_fd(unsigned int fd, dev_t *ret_dev)
8461e51682SChiara Meiohas {
8561e51682SChiara Meiohas 	struct file *file;
8661e51682SChiara Meiohas 
8761e51682SChiara Meiohas 	file = fget(fd);
8861e51682SChiara Meiohas 	if (!file)
8961e51682SChiara Meiohas 		return -EBADF;
9061e51682SChiara Meiohas 
9161e51682SChiara Meiohas 	*ret_dev = file_inode(file)->i_rdev;
9261e51682SChiara Meiohas 	fput(file);
9361e51682SChiara Meiohas 	return 0;
9461e51682SChiara Meiohas }
9561e51682SChiara Meiohas 
9661e51682SChiara Meiohas /**
9761e51682SChiara Meiohas  * ib_ucaps_init - Initialization required before ucap creation.
9861e51682SChiara Meiohas  *
9961e51682SChiara Meiohas  * Return: 0 on success, or a negative errno value on failure
10061e51682SChiara Meiohas  */
ib_ucaps_init(void)10161e51682SChiara Meiohas static int ib_ucaps_init(void)
10261e51682SChiara Meiohas {
10361e51682SChiara Meiohas 	int ret = 0;
10461e51682SChiara Meiohas 
10561e51682SChiara Meiohas 	if (ucaps_class_is_registered)
10661e51682SChiara Meiohas 		return ret;
10761e51682SChiara Meiohas 
10861e51682SChiara Meiohas 	ret = class_register(&ucaps_class);
10961e51682SChiara Meiohas 	if (ret)
11061e51682SChiara Meiohas 		return ret;
11161e51682SChiara Meiohas 
11261e51682SChiara Meiohas 	ret = alloc_chrdev_region(&ucaps_base_dev, 0, RDMA_UCAP_MAX,
11361e51682SChiara Meiohas 				  ucaps_class.name);
11461e51682SChiara Meiohas 	if (ret < 0) {
11561e51682SChiara Meiohas 		class_unregister(&ucaps_class);
11661e51682SChiara Meiohas 		return ret;
11761e51682SChiara Meiohas 	}
11861e51682SChiara Meiohas 
11961e51682SChiara Meiohas 	ucaps_class_is_registered = true;
12061e51682SChiara Meiohas 
12161e51682SChiara Meiohas 	return 0;
12261e51682SChiara Meiohas }
12361e51682SChiara Meiohas 
ucap_dev_release(struct device * device)12461e51682SChiara Meiohas static void ucap_dev_release(struct device *device)
12561e51682SChiara Meiohas {
12661e51682SChiara Meiohas 	struct ib_ucap *ucap = container_of(device, struct ib_ucap, dev);
12761e51682SChiara Meiohas 
12861e51682SChiara Meiohas 	kfree(ucap);
12961e51682SChiara Meiohas }
13061e51682SChiara Meiohas 
13161e51682SChiara Meiohas /**
13261e51682SChiara Meiohas  * ib_create_ucap - Add a ucap character device
13361e51682SChiara Meiohas  * @type: UCAP type
13461e51682SChiara Meiohas  *
13561e51682SChiara Meiohas  * Creates a ucap character device in the /dev/infiniband directory. By default,
13661e51682SChiara Meiohas  * the device has root-only read-write access.
13761e51682SChiara Meiohas  *
13861e51682SChiara Meiohas  * A driver may call this multiple times with the same UCAP type. A reference
13961e51682SChiara Meiohas  * count tracks creations and deletions.
14061e51682SChiara Meiohas  *
14161e51682SChiara Meiohas  * Return: 0 on success, or a negative errno value on failure
14261e51682SChiara Meiohas  */
ib_create_ucap(enum rdma_user_cap type)14361e51682SChiara Meiohas int ib_create_ucap(enum rdma_user_cap type)
14461e51682SChiara Meiohas {
14561e51682SChiara Meiohas 	struct ib_ucap *ucap;
14661e51682SChiara Meiohas 	int ret;
14761e51682SChiara Meiohas 
14861e51682SChiara Meiohas 	if (type >= RDMA_UCAP_MAX)
14961e51682SChiara Meiohas 		return -EINVAL;
15061e51682SChiara Meiohas 
15161e51682SChiara Meiohas 	mutex_lock(&ucaps_mutex);
15261e51682SChiara Meiohas 	ret = ib_ucaps_init();
15361e51682SChiara Meiohas 	if (ret)
15461e51682SChiara Meiohas 		goto unlock;
15561e51682SChiara Meiohas 
15661e51682SChiara Meiohas 	ucap = ucaps_list[type];
15761e51682SChiara Meiohas 	if (ucap) {
15861e51682SChiara Meiohas 		kref_get(&ucap->ref);
15961e51682SChiara Meiohas 		mutex_unlock(&ucaps_mutex);
16061e51682SChiara Meiohas 		return 0;
16161e51682SChiara Meiohas 	}
16261e51682SChiara Meiohas 
16361e51682SChiara Meiohas 	ucap = kzalloc(sizeof(*ucap), GFP_KERNEL);
16461e51682SChiara Meiohas 	if (!ucap) {
16561e51682SChiara Meiohas 		ret = -ENOMEM;
16661e51682SChiara Meiohas 		goto unlock;
16761e51682SChiara Meiohas 	}
16861e51682SChiara Meiohas 
16961e51682SChiara Meiohas 	device_initialize(&ucap->dev);
17061e51682SChiara Meiohas 	ucap->dev.class = &ucaps_class;
17161e51682SChiara Meiohas 	ucap->dev.devt = MKDEV(MAJOR(ucaps_base_dev), type);
17261e51682SChiara Meiohas 	ucap->dev.release = ucap_dev_release;
173*62dd71e6SArnd Bergmann 	ret = dev_set_name(&ucap->dev, "%s", ucap_names[type]);
17461e51682SChiara Meiohas 	if (ret)
17561e51682SChiara Meiohas 		goto err_device;
17661e51682SChiara Meiohas 
17761e51682SChiara Meiohas 	cdev_init(&ucap->cdev, &ucaps_cdev_fops);
17861e51682SChiara Meiohas 	ucap->cdev.owner = THIS_MODULE;
17961e51682SChiara Meiohas 
18061e51682SChiara Meiohas 	ret = cdev_device_add(&ucap->cdev, &ucap->dev);
18161e51682SChiara Meiohas 	if (ret)
18261e51682SChiara Meiohas 		goto err_device;
18361e51682SChiara Meiohas 
18461e51682SChiara Meiohas 	kref_init(&ucap->ref);
18561e51682SChiara Meiohas 	ucaps_list[type] = ucap;
18661e51682SChiara Meiohas 	mutex_unlock(&ucaps_mutex);
18761e51682SChiara Meiohas 
18861e51682SChiara Meiohas 	return 0;
18961e51682SChiara Meiohas 
19061e51682SChiara Meiohas err_device:
19161e51682SChiara Meiohas 	put_device(&ucap->dev);
19261e51682SChiara Meiohas unlock:
19361e51682SChiara Meiohas 	mutex_unlock(&ucaps_mutex);
19461e51682SChiara Meiohas 	return ret;
19561e51682SChiara Meiohas }
19661e51682SChiara Meiohas EXPORT_SYMBOL(ib_create_ucap);
19761e51682SChiara Meiohas 
ib_release_ucap(struct kref * ref)19861e51682SChiara Meiohas static void ib_release_ucap(struct kref *ref)
19961e51682SChiara Meiohas {
20061e51682SChiara Meiohas 	struct ib_ucap *ucap = container_of(ref, struct ib_ucap, ref);
20161e51682SChiara Meiohas 	enum rdma_user_cap type;
20261e51682SChiara Meiohas 
20361e51682SChiara Meiohas 	for (type = RDMA_UCAP_FIRST; type < RDMA_UCAP_MAX; type++) {
20461e51682SChiara Meiohas 		if (ucaps_list[type] == ucap)
20561e51682SChiara Meiohas 			break;
20661e51682SChiara Meiohas 	}
20761e51682SChiara Meiohas 	WARN_ON(type == RDMA_UCAP_MAX);
20861e51682SChiara Meiohas 
20961e51682SChiara Meiohas 	ucaps_list[type] = NULL;
21061e51682SChiara Meiohas 	cdev_device_del(&ucap->cdev, &ucap->dev);
21161e51682SChiara Meiohas 	put_device(&ucap->dev);
21261e51682SChiara Meiohas }
21361e51682SChiara Meiohas 
21461e51682SChiara Meiohas /**
21561e51682SChiara Meiohas  * ib_remove_ucap - Remove a ucap character device
21661e51682SChiara Meiohas  * @type: User cap type
21761e51682SChiara Meiohas  *
21861e51682SChiara Meiohas  * Removes the ucap character device according to type. The device is completely
21961e51682SChiara Meiohas  * removed from the filesystem when its reference count reaches 0.
22061e51682SChiara Meiohas  */
ib_remove_ucap(enum rdma_user_cap type)22161e51682SChiara Meiohas void ib_remove_ucap(enum rdma_user_cap type)
22261e51682SChiara Meiohas {
22361e51682SChiara Meiohas 	struct ib_ucap *ucap;
22461e51682SChiara Meiohas 
22561e51682SChiara Meiohas 	mutex_lock(&ucaps_mutex);
22661e51682SChiara Meiohas 	ucap = ucaps_list[type];
22761e51682SChiara Meiohas 	if (WARN_ON(!ucap))
22861e51682SChiara Meiohas 		goto end;
22961e51682SChiara Meiohas 
23061e51682SChiara Meiohas 	kref_put(&ucap->ref, ib_release_ucap);
23161e51682SChiara Meiohas end:
23261e51682SChiara Meiohas 	mutex_unlock(&ucaps_mutex);
23361e51682SChiara Meiohas }
23461e51682SChiara Meiohas EXPORT_SYMBOL(ib_remove_ucap);
23561e51682SChiara Meiohas 
23661e51682SChiara Meiohas /**
23761e51682SChiara Meiohas  * ib_get_ucaps - Get bitmask of ucap types from file descriptors
23861e51682SChiara Meiohas  * @fds: Array of file descriptors
23961e51682SChiara Meiohas  * @fd_count: Number of file descriptors in the array
24061e51682SChiara Meiohas  * @idx_mask: Bitmask to be updated based on the ucaps in the fd list
24161e51682SChiara Meiohas  *
24261e51682SChiara Meiohas  * Given an array of file descriptors, this function returns a bitmask of
24361e51682SChiara Meiohas  * the ucaps where a bit is set if an FD for that ucap type was in the array.
24461e51682SChiara Meiohas  *
24561e51682SChiara Meiohas  * Return: 0 on success, or a negative errno value on failure
24661e51682SChiara Meiohas  */
ib_get_ucaps(int * fds,int fd_count,uint64_t * idx_mask)24761e51682SChiara Meiohas int ib_get_ucaps(int *fds, int fd_count, uint64_t *idx_mask)
24861e51682SChiara Meiohas {
24961e51682SChiara Meiohas 	int ret = 0;
25061e51682SChiara Meiohas 	dev_t dev;
25161e51682SChiara Meiohas 
25261e51682SChiara Meiohas 	*idx_mask = 0;
25361e51682SChiara Meiohas 	mutex_lock(&ucaps_mutex);
25461e51682SChiara Meiohas 	for (int i = 0; i < fd_count; i++) {
25561e51682SChiara Meiohas 		ret = get_devt_from_fd(fds[i], &dev);
25661e51682SChiara Meiohas 		if (ret)
25761e51682SChiara Meiohas 			goto end;
25861e51682SChiara Meiohas 
25961e51682SChiara Meiohas 		ret = get_ucap_from_devt(dev, idx_mask);
26061e51682SChiara Meiohas 		if (ret)
26161e51682SChiara Meiohas 			goto end;
26261e51682SChiara Meiohas 	}
26361e51682SChiara Meiohas 
26461e51682SChiara Meiohas end:
26561e51682SChiara Meiohas 	mutex_unlock(&ucaps_mutex);
26661e51682SChiara Meiohas 	return ret;
26761e51682SChiara Meiohas }
268