|  | // SPDX-License-Identifier: GPL-2.0-only | 
|  | /* | 
|  | * IOMMU sysfs class support | 
|  | * | 
|  | * Copyright (C) 2014 Red Hat, Inc.  All rights reserved. | 
|  | *     Author: Alex Williamson <alex.williamson@redhat.com> | 
|  | */ | 
|  |  | 
|  | #include <linux/device.h> | 
|  | #include <linux/iommu.h> | 
|  | #include <linux/init.h> | 
|  | #include <linux/slab.h> | 
|  |  | 
|  | /* | 
|  | * We provide a common class "devices" group which initially has no attributes. | 
|  | * As devices are added to the IOMMU, we'll add links to the group. | 
|  | */ | 
|  | static struct attribute *devices_attr[] = { | 
|  | NULL, | 
|  | }; | 
|  |  | 
|  | static const struct attribute_group devices_attr_group = { | 
|  | .name = "devices", | 
|  | .attrs = devices_attr, | 
|  | }; | 
|  |  | 
|  | static const struct attribute_group *dev_groups[] = { | 
|  | &devices_attr_group, | 
|  | NULL, | 
|  | }; | 
|  |  | 
|  | static void release_device(struct device *dev) | 
|  | { | 
|  | kfree(dev); | 
|  | } | 
|  |  | 
|  | static struct class iommu_class = { | 
|  | .name = "iommu", | 
|  | .dev_release = release_device, | 
|  | .dev_groups = dev_groups, | 
|  | }; | 
|  |  | 
|  | static int __init iommu_dev_init(void) | 
|  | { | 
|  | return class_register(&iommu_class); | 
|  | } | 
|  | postcore_initcall(iommu_dev_init); | 
|  |  | 
|  | /* | 
|  | * Init the struct device for the IOMMU. IOMMU specific attributes can | 
|  | * be provided as an attribute group, allowing a unique namespace per | 
|  | * IOMMU type. | 
|  | */ | 
|  | int iommu_device_sysfs_add(struct iommu_device *iommu, | 
|  | struct device *parent, | 
|  | const struct attribute_group **groups, | 
|  | const char *fmt, ...) | 
|  | { | 
|  | va_list vargs; | 
|  | int ret; | 
|  |  | 
|  | iommu->dev = kzalloc(sizeof(*iommu->dev), GFP_KERNEL); | 
|  | if (!iommu->dev) | 
|  | return -ENOMEM; | 
|  |  | 
|  | device_initialize(iommu->dev); | 
|  |  | 
|  | iommu->dev->class = &iommu_class; | 
|  | iommu->dev->parent = parent; | 
|  | iommu->dev->groups = groups; | 
|  |  | 
|  | va_start(vargs, fmt); | 
|  | ret = kobject_set_name_vargs(&iommu->dev->kobj, fmt, vargs); | 
|  | va_end(vargs); | 
|  | if (ret) | 
|  | goto error; | 
|  |  | 
|  | ret = device_add(iommu->dev); | 
|  | if (ret) | 
|  | goto error; | 
|  |  | 
|  | dev_set_drvdata(iommu->dev, iommu); | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | error: | 
|  | put_device(iommu->dev); | 
|  | return ret; | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(iommu_device_sysfs_add); | 
|  |  | 
|  | void iommu_device_sysfs_remove(struct iommu_device *iommu) | 
|  | { | 
|  | dev_set_drvdata(iommu->dev, NULL); | 
|  | device_unregister(iommu->dev); | 
|  | iommu->dev = NULL; | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(iommu_device_sysfs_remove); | 
|  |  | 
|  | /* | 
|  | * IOMMU drivers can indicate a device is managed by a given IOMMU using | 
|  | * this interface.  A link to the device will be created in the "devices" | 
|  | * directory of the IOMMU device in sysfs and an "iommu" link will be | 
|  | * created under the linked device, pointing back at the IOMMU device. | 
|  | */ | 
|  | int iommu_device_link(struct iommu_device *iommu, struct device *link) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | ret = sysfs_add_link_to_group(&iommu->dev->kobj, "devices", | 
|  | &link->kobj, dev_name(link)); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | ret = sysfs_create_link_nowarn(&link->kobj, &iommu->dev->kobj, "iommu"); | 
|  | if (ret) | 
|  | sysfs_remove_link_from_group(&iommu->dev->kobj, "devices", | 
|  | dev_name(link)); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | void iommu_device_unlink(struct iommu_device *iommu, struct device *link) | 
|  | { | 
|  | sysfs_remove_link(&link->kobj, "iommu"); | 
|  | sysfs_remove_link_from_group(&iommu->dev->kobj, "devices", dev_name(link)); | 
|  | } |