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