linux/drivers/vfio/mdev/mdev_sysfs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * File attributes for Mediated devices
   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/sysfs.h>
  11#include <linux/ctype.h>
  12#include <linux/device.h>
  13#include <linux/slab.h>
  14#include <linux/uuid.h>
  15#include <linux/mdev.h>
  16
  17#include "mdev_private.h"
  18
  19/* Static functions */
  20
  21static ssize_t mdev_type_attr_show(struct kobject *kobj,
  22                                     struct attribute *__attr, char *buf)
  23{
  24        struct mdev_type_attribute *attr = to_mdev_type_attr(__attr);
  25        struct mdev_type *type = to_mdev_type(kobj);
  26        ssize_t ret = -EIO;
  27
  28        if (attr->show)
  29                ret = attr->show(kobj, type->parent->dev, buf);
  30        return ret;
  31}
  32
  33static ssize_t mdev_type_attr_store(struct kobject *kobj,
  34                                      struct attribute *__attr,
  35                                      const char *buf, size_t count)
  36{
  37        struct mdev_type_attribute *attr = to_mdev_type_attr(__attr);
  38        struct mdev_type *type = to_mdev_type(kobj);
  39        ssize_t ret = -EIO;
  40
  41        if (attr->store)
  42                ret = attr->store(&type->kobj, type->parent->dev, buf, count);
  43        return ret;
  44}
  45
  46static const struct sysfs_ops mdev_type_sysfs_ops = {
  47        .show = mdev_type_attr_show,
  48        .store = mdev_type_attr_store,
  49};
  50
  51static ssize_t create_store(struct kobject *kobj, struct device *dev,
  52                            const char *buf, size_t count)
  53{
  54        char *str;
  55        guid_t uuid;
  56        int ret;
  57
  58        if ((count < UUID_STRING_LEN) || (count > UUID_STRING_LEN + 1))
  59                return -EINVAL;
  60
  61        str = kstrndup(buf, count, GFP_KERNEL);
  62        if (!str)
  63                return -ENOMEM;
  64
  65        ret = guid_parse(str, &uuid);
  66        kfree(str);
  67        if (ret)
  68                return ret;
  69
  70        ret = mdev_device_create(kobj, dev, &uuid);
  71        if (ret)
  72                return ret;
  73
  74        return count;
  75}
  76
  77MDEV_TYPE_ATTR_WO(create);
  78
  79static void mdev_type_release(struct kobject *kobj)
  80{
  81        struct mdev_type *type = to_mdev_type(kobj);
  82
  83        pr_debug("Releasing group %s\n", kobj->name);
  84        kfree(type);
  85}
  86
  87static struct kobj_type mdev_type_ktype = {
  88        .sysfs_ops = &mdev_type_sysfs_ops,
  89        .release = mdev_type_release,
  90};
  91
  92static struct mdev_type *add_mdev_supported_type(struct mdev_parent *parent,
  93                                                 struct attribute_group *group)
  94{
  95        struct mdev_type *type;
  96        int ret;
  97
  98        if (!group->name) {
  99                pr_err("%s: Type name empty!\n", __func__);
 100                return ERR_PTR(-EINVAL);
 101        }
 102
 103        type = kzalloc(sizeof(*type), GFP_KERNEL);
 104        if (!type)
 105                return ERR_PTR(-ENOMEM);
 106
 107        type->kobj.kset = parent->mdev_types_kset;
 108
 109        ret = kobject_init_and_add(&type->kobj, &mdev_type_ktype, NULL,
 110                                   "%s-%s", dev_driver_string(parent->dev),
 111                                   group->name);
 112        if (ret) {
 113                kfree(type);
 114                return ERR_PTR(ret);
 115        }
 116
 117        ret = sysfs_create_file(&type->kobj, &mdev_type_attr_create.attr);
 118        if (ret)
 119                goto attr_create_failed;
 120
 121        type->devices_kobj = kobject_create_and_add("devices", &type->kobj);
 122        if (!type->devices_kobj) {
 123                ret = -ENOMEM;
 124                goto attr_devices_failed;
 125        }
 126
 127        ret = sysfs_create_files(&type->kobj,
 128                                 (const struct attribute **)group->attrs);
 129        if (ret) {
 130                ret = -ENOMEM;
 131                goto attrs_failed;
 132        }
 133
 134        type->group = group;
 135        type->parent = parent;
 136        return type;
 137
 138attrs_failed:
 139        kobject_put(type->devices_kobj);
 140attr_devices_failed:
 141        sysfs_remove_file(&type->kobj, &mdev_type_attr_create.attr);
 142attr_create_failed:
 143        kobject_del(&type->kobj);
 144        kobject_put(&type->kobj);
 145        return ERR_PTR(ret);
 146}
 147
 148static void remove_mdev_supported_type(struct mdev_type *type)
 149{
 150        sysfs_remove_files(&type->kobj,
 151                           (const struct attribute **)type->group->attrs);
 152        kobject_put(type->devices_kobj);
 153        sysfs_remove_file(&type->kobj, &mdev_type_attr_create.attr);
 154        kobject_del(&type->kobj);
 155        kobject_put(&type->kobj);
 156}
 157
 158static int add_mdev_supported_type_groups(struct mdev_parent *parent)
 159{
 160        int i;
 161
 162        for (i = 0; parent->ops->supported_type_groups[i]; i++) {
 163                struct mdev_type *type;
 164
 165                type = add_mdev_supported_type(parent,
 166                                        parent->ops->supported_type_groups[i]);
 167                if (IS_ERR(type)) {
 168                        struct mdev_type *ltype, *tmp;
 169
 170                        list_for_each_entry_safe(ltype, tmp, &parent->type_list,
 171                                                  next) {
 172                                list_del(&ltype->next);
 173                                remove_mdev_supported_type(ltype);
 174                        }
 175                        return PTR_ERR(type);
 176                }
 177                list_add(&type->next, &parent->type_list);
 178        }
 179        return 0;
 180}
 181
 182/* mdev sysfs functions */
 183void parent_remove_sysfs_files(struct mdev_parent *parent)
 184{
 185        struct mdev_type *type, *tmp;
 186
 187        list_for_each_entry_safe(type, tmp, &parent->type_list, next) {
 188                list_del(&type->next);
 189                remove_mdev_supported_type(type);
 190        }
 191
 192        sysfs_remove_groups(&parent->dev->kobj, parent->ops->dev_attr_groups);
 193        kset_unregister(parent->mdev_types_kset);
 194}
 195
 196int parent_create_sysfs_files(struct mdev_parent *parent)
 197{
 198        int ret;
 199
 200        parent->mdev_types_kset = kset_create_and_add("mdev_supported_types",
 201                                               NULL, &parent->dev->kobj);
 202
 203        if (!parent->mdev_types_kset)
 204                return -ENOMEM;
 205
 206        INIT_LIST_HEAD(&parent->type_list);
 207
 208        ret = sysfs_create_groups(&parent->dev->kobj,
 209                                  parent->ops->dev_attr_groups);
 210        if (ret)
 211                goto create_err;
 212
 213        ret = add_mdev_supported_type_groups(parent);
 214        if (ret)
 215                sysfs_remove_groups(&parent->dev->kobj,
 216                                    parent->ops->dev_attr_groups);
 217        else
 218                return ret;
 219
 220create_err:
 221        kset_unregister(parent->mdev_types_kset);
 222        return ret;
 223}
 224
 225static ssize_t remove_store(struct device *dev, struct device_attribute *attr,
 226                            const char *buf, size_t count)
 227{
 228        unsigned long val;
 229
 230        if (kstrtoul(buf, 0, &val) < 0)
 231                return -EINVAL;
 232
 233        if (val && device_remove_file_self(dev, attr)) {
 234                int ret;
 235
 236                ret = mdev_device_remove(dev);
 237                if (ret)
 238                        return ret;
 239        }
 240
 241        return count;
 242}
 243
 244static DEVICE_ATTR_WO(remove);
 245
 246static const struct attribute *mdev_device_attrs[] = {
 247        &dev_attr_remove.attr,
 248        NULL,
 249};
 250
 251int  mdev_create_sysfs_files(struct device *dev, struct mdev_type *type)
 252{
 253        int ret;
 254
 255        ret = sysfs_create_link(type->devices_kobj, &dev->kobj, dev_name(dev));
 256        if (ret)
 257                return ret;
 258
 259        ret = sysfs_create_link(&dev->kobj, &type->kobj, "mdev_type");
 260        if (ret)
 261                goto type_link_failed;
 262
 263        ret = sysfs_create_files(&dev->kobj, mdev_device_attrs);
 264        if (ret)
 265                goto create_files_failed;
 266
 267        return ret;
 268
 269create_files_failed:
 270        sysfs_remove_link(&dev->kobj, "mdev_type");
 271type_link_failed:
 272        sysfs_remove_link(type->devices_kobj, dev_name(dev));
 273        return ret;
 274}
 275
 276void mdev_remove_sysfs_files(struct device *dev, struct mdev_type *type)
 277{
 278        sysfs_remove_files(&dev->kobj, mdev_device_attrs);
 279        sysfs_remove_link(&dev->kobj, "mdev_type");
 280        sysfs_remove_link(type->devices_kobj, dev_name(dev));
 281}
 282