1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Mediated device Core Driver 4 * 5 * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. 6 * Author: Neo Jia <[email protected]> 7 * Kirti Wankhede <[email protected]> 8 */ 9 10 #include <linux/module.h> 11 #include <linux/slab.h> 12 #include <linux/sysfs.h> 13 #include <linux/mdev.h> 14 15 #include "mdev_private.h" 16 17 #define DRIVER_VERSION "0.1" 18 #define DRIVER_AUTHOR "NVIDIA Corporation" 19 #define DRIVER_DESC "Mediated device Core Driver" 20 21 static LIST_HEAD(parent_list); 22 static DEFINE_MUTEX(parent_list_lock); 23 static struct class_compat *mdev_bus_compat_class; 24 25 static LIST_HEAD(mdev_list); 26 static DEFINE_MUTEX(mdev_list_lock); 27 28 struct device *mdev_parent_dev(struct mdev_device *mdev) 29 { 30 return mdev->type->parent->dev; 31 } 32 EXPORT_SYMBOL(mdev_parent_dev); 33 34 /* 35 * Return the index in supported_type_groups that this mdev_device was created 36 * from. 37 */ 38 unsigned int mdev_get_type_group_id(struct mdev_device *mdev) 39 { 40 return mdev->type->type_group_id; 41 } 42 EXPORT_SYMBOL(mdev_get_type_group_id); 43 44 /* 45 * Used in mdev_type_attribute sysfs functions to return the index in the 46 * supported_type_groups that the sysfs is called from. 47 */ 48 unsigned int mtype_get_type_group_id(struct mdev_type *mtype) 49 { 50 return mtype->type_group_id; 51 } 52 EXPORT_SYMBOL(mtype_get_type_group_id); 53 54 /* 55 * Used in mdev_type_attribute sysfs functions to return the parent struct 56 * device 57 */ 58 struct device *mtype_get_parent_dev(struct mdev_type *mtype) 59 { 60 return mtype->parent->dev; 61 } 62 EXPORT_SYMBOL(mtype_get_parent_dev); 63 64 /* Should be called holding parent_list_lock */ 65 static struct mdev_parent *__find_parent_device(struct device *dev) 66 { 67 struct mdev_parent *parent; 68 69 list_for_each_entry(parent, &parent_list, next) { 70 if (parent->dev == dev) 71 return parent; 72 } 73 return NULL; 74 } 75 76 void mdev_release_parent(struct kref *kref) 77 { 78 struct mdev_parent *parent = container_of(kref, struct mdev_parent, 79 ref); 80 struct device *dev = parent->dev; 81 82 kfree(parent); 83 put_device(dev); 84 } 85 86 /* Caller must hold parent unreg_sem read or write lock */ 87 static void mdev_device_remove_common(struct mdev_device *mdev) 88 { 89 struct mdev_parent *parent = mdev->type->parent; 90 91 mdev_remove_sysfs_files(mdev); 92 device_del(&mdev->dev); 93 lockdep_assert_held(&parent->unreg_sem); 94 /* Balances with device_initialize() */ 95 put_device(&mdev->dev); 96 } 97 98 static int mdev_device_remove_cb(struct device *dev, void *data) 99 { 100 struct mdev_device *mdev = mdev_from_dev(dev); 101 102 if (mdev) 103 mdev_device_remove_common(mdev); 104 return 0; 105 } 106 107 /* 108 * mdev_register_device : Register a device 109 * @dev: device structure representing parent device. 110 * @mdev_driver: Device driver to bind to the newly created mdev 111 * 112 * Add device to list of registered parent devices. 113 * Returns a negative value on error, otherwise 0. 114 */ 115 int mdev_register_device(struct device *dev, struct mdev_driver *mdev_driver) 116 { 117 int ret; 118 struct mdev_parent *parent; 119 char *env_string = "MDEV_STATE=registered"; 120 char *envp[] = { env_string, NULL }; 121 122 /* check for mandatory ops */ 123 if (!mdev_driver->supported_type_groups) 124 return -EINVAL; 125 126 dev = get_device(dev); 127 if (!dev) 128 return -EINVAL; 129 130 mutex_lock(&parent_list_lock); 131 132 /* Check for duplicate */ 133 parent = __find_parent_device(dev); 134 if (parent) { 135 parent = NULL; 136 ret = -EEXIST; 137 goto add_dev_err; 138 } 139 140 parent = kzalloc(sizeof(*parent), GFP_KERNEL); 141 if (!parent) { 142 ret = -ENOMEM; 143 goto add_dev_err; 144 } 145 146 kref_init(&parent->ref); 147 init_rwsem(&parent->unreg_sem); 148 149 parent->dev = dev; 150 parent->mdev_driver = mdev_driver; 151 152 if (!mdev_bus_compat_class) { 153 mdev_bus_compat_class = class_compat_register("mdev_bus"); 154 if (!mdev_bus_compat_class) { 155 ret = -ENOMEM; 156 goto add_dev_err; 157 } 158 } 159 160 ret = parent_create_sysfs_files(parent); 161 if (ret) 162 goto add_dev_err; 163 164 ret = class_compat_create_link(mdev_bus_compat_class, dev, NULL); 165 if (ret) 166 dev_warn(dev, "Failed to create compatibility class link\n"); 167 168 list_add(&parent->next, &parent_list); 169 mutex_unlock(&parent_list_lock); 170 171 dev_info(dev, "MDEV: Registered\n"); 172 kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); 173 174 return 0; 175 176 add_dev_err: 177 mutex_unlock(&parent_list_lock); 178 if (parent) 179 mdev_put_parent(parent); 180 else 181 put_device(dev); 182 return ret; 183 } 184 EXPORT_SYMBOL(mdev_register_device); 185 186 /* 187 * mdev_unregister_device : Unregister a parent device 188 * @dev: device structure representing parent device. 189 * 190 * Remove device from list of registered parent devices. Give a chance to free 191 * existing mediated devices for given device. 192 */ 193 194 void mdev_unregister_device(struct device *dev) 195 { 196 struct mdev_parent *parent; 197 char *env_string = "MDEV_STATE=unregistered"; 198 char *envp[] = { env_string, NULL }; 199 200 mutex_lock(&parent_list_lock); 201 parent = __find_parent_device(dev); 202 203 if (!parent) { 204 mutex_unlock(&parent_list_lock); 205 return; 206 } 207 dev_info(dev, "MDEV: Unregistering\n"); 208 209 list_del(&parent->next); 210 mutex_unlock(&parent_list_lock); 211 212 down_write(&parent->unreg_sem); 213 214 class_compat_remove_link(mdev_bus_compat_class, dev, NULL); 215 216 device_for_each_child(dev, NULL, mdev_device_remove_cb); 217 218 parent_remove_sysfs_files(parent); 219 up_write(&parent->unreg_sem); 220 221 mdev_put_parent(parent); 222 223 /* We still have the caller's reference to use for the uevent */ 224 kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); 225 } 226 EXPORT_SYMBOL(mdev_unregister_device); 227 228 static void mdev_device_release(struct device *dev) 229 { 230 struct mdev_device *mdev = to_mdev_device(dev); 231 232 /* Pairs with the get in mdev_device_create() */ 233 kobject_put(&mdev->type->kobj); 234 235 mutex_lock(&mdev_list_lock); 236 list_del(&mdev->next); 237 mutex_unlock(&mdev_list_lock); 238 239 dev_dbg(&mdev->dev, "MDEV: destroying\n"); 240 kfree(mdev); 241 } 242 243 int mdev_device_create(struct mdev_type *type, const guid_t *uuid) 244 { 245 int ret; 246 struct mdev_device *mdev, *tmp; 247 struct mdev_parent *parent = type->parent; 248 struct mdev_driver *drv = parent->mdev_driver; 249 250 mutex_lock(&mdev_list_lock); 251 252 /* Check for duplicate */ 253 list_for_each_entry(tmp, &mdev_list, next) { 254 if (guid_equal(&tmp->uuid, uuid)) { 255 mutex_unlock(&mdev_list_lock); 256 return -EEXIST; 257 } 258 } 259 260 mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); 261 if (!mdev) { 262 mutex_unlock(&mdev_list_lock); 263 return -ENOMEM; 264 } 265 266 device_initialize(&mdev->dev); 267 mdev->dev.parent = parent->dev; 268 mdev->dev.bus = &mdev_bus_type; 269 mdev->dev.release = mdev_device_release; 270 mdev->dev.groups = mdev_device_groups; 271 mdev->type = type; 272 /* Pairs with the put in mdev_device_release() */ 273 kobject_get(&type->kobj); 274 275 guid_copy(&mdev->uuid, uuid); 276 list_add(&mdev->next, &mdev_list); 277 mutex_unlock(&mdev_list_lock); 278 279 ret = dev_set_name(&mdev->dev, "%pUl", uuid); 280 if (ret) 281 goto out_put_device; 282 283 /* Check if parent unregistration has started */ 284 if (!down_read_trylock(&parent->unreg_sem)) { 285 ret = -ENODEV; 286 goto out_put_device; 287 } 288 289 ret = device_add(&mdev->dev); 290 if (ret) 291 goto out_unlock; 292 293 ret = device_driver_attach(&drv->driver, &mdev->dev); 294 if (ret) 295 goto out_del; 296 297 ret = mdev_create_sysfs_files(mdev); 298 if (ret) 299 goto out_del; 300 301 mdev->active = true; 302 dev_dbg(&mdev->dev, "MDEV: created\n"); 303 up_read(&parent->unreg_sem); 304 305 return 0; 306 307 out_del: 308 device_del(&mdev->dev); 309 out_unlock: 310 up_read(&parent->unreg_sem); 311 out_put_device: 312 put_device(&mdev->dev); 313 return ret; 314 } 315 316 int mdev_device_remove(struct mdev_device *mdev) 317 { 318 struct mdev_device *tmp; 319 struct mdev_parent *parent = mdev->type->parent; 320 321 mutex_lock(&mdev_list_lock); 322 list_for_each_entry(tmp, &mdev_list, next) { 323 if (tmp == mdev) 324 break; 325 } 326 327 if (tmp != mdev) { 328 mutex_unlock(&mdev_list_lock); 329 return -ENODEV; 330 } 331 332 if (!mdev->active) { 333 mutex_unlock(&mdev_list_lock); 334 return -EAGAIN; 335 } 336 337 mdev->active = false; 338 mutex_unlock(&mdev_list_lock); 339 340 /* Check if parent unregistration has started */ 341 if (!down_read_trylock(&parent->unreg_sem)) 342 return -ENODEV; 343 344 mdev_device_remove_common(mdev); 345 up_read(&parent->unreg_sem); 346 return 0; 347 } 348 349 static int __init mdev_init(void) 350 { 351 return bus_register(&mdev_bus_type); 352 } 353 354 static void __exit mdev_exit(void) 355 { 356 if (mdev_bus_compat_class) 357 class_compat_unregister(mdev_bus_compat_class); 358 bus_unregister(&mdev_bus_type); 359 } 360 361 subsys_initcall(mdev_init) 362 module_exit(mdev_exit) 363 364 MODULE_VERSION(DRIVER_VERSION); 365 MODULE_LICENSE("GPL v2"); 366 MODULE_AUTHOR(DRIVER_AUTHOR); 367 MODULE_DESCRIPTION(DRIVER_DESC); 368