| 1 | // SPDX-License-Identifier: GPL-2.0-only | 
|---|
| 2 | /* | 
|---|
| 3 | * IOMMU sysfs class support | 
|---|
| 4 | * | 
|---|
| 5 | * Copyright (C) 2014 Red Hat, Inc.  All rights reserved. | 
|---|
| 6 | *     Author: Alex Williamson <alex.williamson@redhat.com> | 
|---|
| 7 | */ | 
|---|
| 8 |  | 
|---|
| 9 | #include <linux/device.h> | 
|---|
| 10 | #include <linux/iommu.h> | 
|---|
| 11 | #include <linux/init.h> | 
|---|
| 12 | #include <linux/slab.h> | 
|---|
| 13 |  | 
|---|
| 14 | /* | 
|---|
| 15 | * We provide a common class "devices" group which initially has no attributes. | 
|---|
| 16 | * As devices are added to the IOMMU, we'll add links to the group. | 
|---|
| 17 | */ | 
|---|
| 18 | static struct attribute *devices_attr[] = { | 
|---|
| 19 | NULL, | 
|---|
| 20 | }; | 
|---|
| 21 |  | 
|---|
| 22 | static const struct attribute_group devices_attr_group = { | 
|---|
| 23 | .name = "devices", | 
|---|
| 24 | .attrs = devices_attr, | 
|---|
| 25 | }; | 
|---|
| 26 |  | 
|---|
| 27 | static const struct attribute_group *dev_groups[] = { | 
|---|
| 28 | &devices_attr_group, | 
|---|
| 29 | NULL, | 
|---|
| 30 | }; | 
|---|
| 31 |  | 
|---|
| 32 | static void release_device(struct device *dev) | 
|---|
| 33 | { | 
|---|
| 34 | kfree(objp: dev); | 
|---|
| 35 | } | 
|---|
| 36 |  | 
|---|
| 37 | static const struct class iommu_class = { | 
|---|
| 38 | .name = "iommu", | 
|---|
| 39 | .dev_release = release_device, | 
|---|
| 40 | .dev_groups = dev_groups, | 
|---|
| 41 | }; | 
|---|
| 42 |  | 
|---|
| 43 | static int __init iommu_dev_init(void) | 
|---|
| 44 | { | 
|---|
| 45 | return class_register(class: &iommu_class); | 
|---|
| 46 | } | 
|---|
| 47 | postcore_initcall(iommu_dev_init); | 
|---|
| 48 |  | 
|---|
| 49 | /* | 
|---|
| 50 | * Init the struct device for the IOMMU. IOMMU specific attributes can | 
|---|
| 51 | * be provided as an attribute group, allowing a unique namespace per | 
|---|
| 52 | * IOMMU type. | 
|---|
| 53 | */ | 
|---|
| 54 | int iommu_device_sysfs_add(struct iommu_device *iommu, | 
|---|
| 55 | struct device *parent, | 
|---|
| 56 | const struct attribute_group **groups, | 
|---|
| 57 | const char *fmt, ...) | 
|---|
| 58 | { | 
|---|
| 59 | va_list vargs; | 
|---|
| 60 | int ret; | 
|---|
| 61 |  | 
|---|
| 62 | iommu->dev = kzalloc(sizeof(*iommu->dev), GFP_KERNEL); | 
|---|
| 63 | if (!iommu->dev) | 
|---|
| 64 | return -ENOMEM; | 
|---|
| 65 |  | 
|---|
| 66 | device_initialize(dev: iommu->dev); | 
|---|
| 67 |  | 
|---|
| 68 | iommu->dev->class = &iommu_class; | 
|---|
| 69 | iommu->dev->parent = parent; | 
|---|
| 70 | iommu->dev->groups = groups; | 
|---|
| 71 |  | 
|---|
| 72 | va_start(vargs, fmt); | 
|---|
| 73 | ret = kobject_set_name_vargs(kobj: &iommu->dev->kobj, fmt, vargs); | 
|---|
| 74 | va_end(vargs); | 
|---|
| 75 | if (ret) | 
|---|
| 76 | goto error; | 
|---|
| 77 |  | 
|---|
| 78 | ret = device_add(dev: iommu->dev); | 
|---|
| 79 | if (ret) | 
|---|
| 80 | goto error; | 
|---|
| 81 |  | 
|---|
| 82 | dev_set_drvdata(dev: iommu->dev, data: iommu); | 
|---|
| 83 |  | 
|---|
| 84 | return 0; | 
|---|
| 85 |  | 
|---|
| 86 | error: | 
|---|
| 87 | put_device(dev: iommu->dev); | 
|---|
| 88 | return ret; | 
|---|
| 89 | } | 
|---|
| 90 | EXPORT_SYMBOL_GPL(iommu_device_sysfs_add); | 
|---|
| 91 |  | 
|---|
| 92 | void iommu_device_sysfs_remove(struct iommu_device *iommu) | 
|---|
| 93 | { | 
|---|
| 94 | dev_set_drvdata(dev: iommu->dev, NULL); | 
|---|
| 95 | device_unregister(dev: iommu->dev); | 
|---|
| 96 | iommu->dev = NULL; | 
|---|
| 97 | } | 
|---|
| 98 | EXPORT_SYMBOL_GPL(iommu_device_sysfs_remove); | 
|---|
| 99 |  | 
|---|
| 100 | /* | 
|---|
| 101 | * IOMMU drivers can indicate a device is managed by a given IOMMU using | 
|---|
| 102 | * this interface.  A link to the device will be created in the "devices" | 
|---|
| 103 | * directory of the IOMMU device in sysfs and an "iommu" link will be | 
|---|
| 104 | * created under the linked device, pointing back at the IOMMU device. | 
|---|
| 105 | */ | 
|---|
| 106 | int iommu_device_link(struct iommu_device *iommu, struct device *link) | 
|---|
| 107 | { | 
|---|
| 108 | int ret; | 
|---|
| 109 |  | 
|---|
| 110 | ret = sysfs_add_link_to_group(kobj: &iommu->dev->kobj, group_name: "devices", | 
|---|
| 111 | target: &link->kobj, link_name: dev_name(dev: link)); | 
|---|
| 112 | if (ret) | 
|---|
| 113 | return ret; | 
|---|
| 114 |  | 
|---|
| 115 | ret = sysfs_create_link_nowarn(kobj: &link->kobj, target: &iommu->dev->kobj, name: "iommu"); | 
|---|
| 116 | if (ret) | 
|---|
| 117 | sysfs_remove_link_from_group(kobj: &iommu->dev->kobj, group_name: "devices", | 
|---|
| 118 | link_name: dev_name(dev: link)); | 
|---|
| 119 |  | 
|---|
| 120 | return ret; | 
|---|
| 121 | } | 
|---|
| 122 |  | 
|---|
| 123 | void iommu_device_unlink(struct iommu_device *iommu, struct device *link) | 
|---|
| 124 | { | 
|---|
| 125 | sysfs_remove_link(kobj: &link->kobj, name: "iommu"); | 
|---|
| 126 | sysfs_remove_link_from_group(kobj: &iommu->dev->kobj, group_name: "devices", link_name: dev_name(dev: link)); | 
|---|
| 127 | } | 
|---|
| 128 |  | 
|---|