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