linux/drivers/staging/gasket/gasket_sysfs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/* Copyright (C) 2018 Google, Inc. */
   3#include "gasket_sysfs.h"
   4
   5#include "gasket_core.h"
   6
   7#include <linux/device.h>
   8#include <linux/printk.h>
   9
  10/*
  11 * Pair of kernel device and user-specified pointer. Used in lookups in sysfs
  12 * "show" functions to return user data.
  13 */
  14
  15struct gasket_sysfs_mapping {
  16        /*
  17         * The device bound to this mapping. If this is NULL, then this mapping
  18         * is free.
  19         */
  20        struct device *device;
  21
  22        /* The Gasket descriptor for this device. */
  23        struct gasket_dev *gasket_dev;
  24
  25        /* This device's set of sysfs attributes/nodes. */
  26        struct gasket_sysfs_attribute *attributes;
  27
  28        /* The number of live elements in "attributes". */
  29        int attribute_count;
  30
  31        /* Protects structure from simultaneous access. */
  32        struct mutex mutex;
  33
  34        /* Tracks active users of this mapping. */
  35        struct kref refcount;
  36};
  37
  38/*
  39 * Data needed to manage users of this sysfs utility.
  40 * Currently has a fixed size; if space is a concern, this can be dynamically
  41 * allocated.
  42 */
  43/*
  44 * 'Global' (file-scoped) list of mappings between devices and gasket_data
  45 * pointers. This removes the requirement to have a gasket_sysfs_data
  46 * handle in all files.
  47 */
  48static struct gasket_sysfs_mapping dev_mappings[GASKET_SYSFS_NUM_MAPPINGS];
  49
  50/* Callback when a mapping's refcount goes to zero. */
  51static void release_entry(struct kref *ref)
  52{
  53        /* All work is done after the return from kref_put. */
  54}
  55
  56/* Look up mapping information for the given device. */
  57static struct gasket_sysfs_mapping *get_mapping(struct device *device)
  58{
  59        int i;
  60
  61        for (i = 0; i < GASKET_SYSFS_NUM_MAPPINGS; i++) {
  62                mutex_lock(&dev_mappings[i].mutex);
  63                if (dev_mappings[i].device == device) {
  64                        kref_get(&dev_mappings[i].refcount);
  65                        mutex_unlock(&dev_mappings[i].mutex);
  66                        return &dev_mappings[i];
  67                }
  68                mutex_unlock(&dev_mappings[i].mutex);
  69        }
  70
  71        dev_dbg(device, "%s: Mapping to device %s not found\n",
  72                __func__, device->kobj.name);
  73        return NULL;
  74}
  75
  76/* Put a reference to a mapping. */
  77static void put_mapping(struct gasket_sysfs_mapping *mapping)
  78{
  79        int i;
  80        int num_files_to_remove = 0;
  81        struct device_attribute *files_to_remove;
  82        struct device *device;
  83
  84        if (!mapping) {
  85                pr_debug("%s: Mapping should not be NULL\n", __func__);
  86                return;
  87        }
  88
  89        mutex_lock(&mapping->mutex);
  90        if (kref_put(&mapping->refcount, release_entry)) {
  91                dev_dbg(mapping->device, "Removing Gasket sysfs mapping\n");
  92                /*
  93                 * We can't remove the sysfs nodes in the kref callback, since
  94                 * device_remove_file() blocks until the node is free.
  95                 * Readers/writers of sysfs nodes, though, will be blocked on
  96                 * the mapping mutex, resulting in deadlock. To fix this, the
  97                 * sysfs nodes are removed outside the lock.
  98                 */
  99                device = mapping->device;
 100                num_files_to_remove = mapping->attribute_count;
 101                files_to_remove = kcalloc(num_files_to_remove,
 102                                          sizeof(*files_to_remove),
 103                                          GFP_KERNEL);
 104                if (files_to_remove)
 105                        for (i = 0; i < num_files_to_remove; i++)
 106                                files_to_remove[i] =
 107                                    mapping->attributes[i].attr;
 108                else
 109                        num_files_to_remove = 0;
 110
 111                kfree(mapping->attributes);
 112                mapping->attributes = NULL;
 113                mapping->attribute_count = 0;
 114                put_device(mapping->device);
 115                mapping->device = NULL;
 116                mapping->gasket_dev = NULL;
 117        }
 118        mutex_unlock(&mapping->mutex);
 119
 120        if (num_files_to_remove != 0) {
 121                for (i = 0; i < num_files_to_remove; ++i)
 122                        device_remove_file(device, &files_to_remove[i]);
 123                kfree(files_to_remove);
 124        }
 125}
 126
 127/*
 128 * Put a reference to a mapping N times.
 129 *
 130 * In higher-level resource acquire/release function pairs, the release function
 131 * will need to release a mapping 2x - once for the refcount taken in the
 132 * release function itself, and once for the count taken in the acquire call.
 133 */
 134static void put_mapping_n(struct gasket_sysfs_mapping *mapping, int times)
 135{
 136        int i;
 137
 138        for (i = 0; i < times; i++)
 139                put_mapping(mapping);
 140}
 141
 142void gasket_sysfs_init(void)
 143{
 144        int i;
 145
 146        for (i = 0; i < GASKET_SYSFS_NUM_MAPPINGS; i++) {
 147                dev_mappings[i].device = NULL;
 148                mutex_init(&dev_mappings[i].mutex);
 149        }
 150}
 151
 152int gasket_sysfs_create_mapping(struct device *device,
 153                                struct gasket_dev *gasket_dev)
 154{
 155        struct gasket_sysfs_mapping *mapping;
 156        int map_idx = -1;
 157
 158        /*
 159         * We need a function-level mutex to protect against the same device
 160         * being added [multiple times] simultaneously.
 161         */
 162        static DEFINE_MUTEX(function_mutex);
 163
 164        mutex_lock(&function_mutex);
 165        dev_dbg(device, "Creating sysfs entries for device\n");
 166
 167        /* Check that the device we're adding hasn't already been added. */
 168        mapping = get_mapping(device);
 169        if (mapping) {
 170                dev_err(device,
 171                        "Attempting to re-initialize sysfs mapping for device\n");
 172                put_mapping(mapping);
 173                mutex_unlock(&function_mutex);
 174                return -EBUSY;
 175        }
 176
 177        /* Find the first empty entry in the array. */
 178        for (map_idx = 0; map_idx < GASKET_SYSFS_NUM_MAPPINGS; ++map_idx) {
 179                mutex_lock(&dev_mappings[map_idx].mutex);
 180                if (!dev_mappings[map_idx].device)
 181                        /* Break with the mutex held! */
 182                        break;
 183                mutex_unlock(&dev_mappings[map_idx].mutex);
 184        }
 185
 186        if (map_idx == GASKET_SYSFS_NUM_MAPPINGS) {
 187                dev_err(device, "All mappings have been exhausted\n");
 188                mutex_unlock(&function_mutex);
 189                return -ENOMEM;
 190        }
 191
 192        dev_dbg(device, "Creating sysfs mapping for device %s\n",
 193                device->kobj.name);
 194
 195        mapping = &dev_mappings[map_idx];
 196        mapping->attributes = kcalloc(GASKET_SYSFS_MAX_NODES,
 197                                      sizeof(*mapping->attributes),
 198                                      GFP_KERNEL);
 199        if (!mapping->attributes) {
 200                dev_dbg(device, "Unable to allocate sysfs attribute array\n");
 201                mutex_unlock(&mapping->mutex);
 202                mutex_unlock(&function_mutex);
 203                return -ENOMEM;
 204        }
 205
 206        kref_init(&mapping->refcount);
 207        mapping->device = get_device(device);
 208        mapping->gasket_dev = gasket_dev;
 209        mapping->attribute_count = 0;
 210        mutex_unlock(&mapping->mutex);
 211        mutex_unlock(&function_mutex);
 212
 213        /* Don't decrement the refcount here! One open count keeps it alive! */
 214        return 0;
 215}
 216
 217int gasket_sysfs_create_entries(struct device *device,
 218                                const struct gasket_sysfs_attribute *attrs)
 219{
 220        int i;
 221        int ret;
 222        struct gasket_sysfs_mapping *mapping = get_mapping(device);
 223
 224        if (!mapping) {
 225                dev_dbg(device,
 226                        "Creating entries for device without first initializing mapping\n");
 227                return -EINVAL;
 228        }
 229
 230        mutex_lock(&mapping->mutex);
 231        for (i = 0; strcmp(attrs[i].attr.attr.name, GASKET_ARRAY_END_MARKER);
 232                i++) {
 233                if (mapping->attribute_count == GASKET_SYSFS_MAX_NODES) {
 234                        dev_err(device,
 235                                "Maximum number of sysfs nodes reached for device\n");
 236                        mutex_unlock(&mapping->mutex);
 237                        put_mapping(mapping);
 238                        return -ENOMEM;
 239                }
 240
 241                ret = device_create_file(device, &attrs[i].attr);
 242                if (ret) {
 243                        dev_dbg(device, "Unable to create device entries\n");
 244                        mutex_unlock(&mapping->mutex);
 245                        put_mapping(mapping);
 246                        return ret;
 247                }
 248
 249                mapping->attributes[mapping->attribute_count] = attrs[i];
 250                ++mapping->attribute_count;
 251        }
 252
 253        mutex_unlock(&mapping->mutex);
 254        put_mapping(mapping);
 255        return 0;
 256}
 257EXPORT_SYMBOL(gasket_sysfs_create_entries);
 258
 259void gasket_sysfs_remove_mapping(struct device *device)
 260{
 261        struct gasket_sysfs_mapping *mapping = get_mapping(device);
 262
 263        if (!mapping) {
 264                dev_err(device,
 265                        "Attempted to remove non-existent sysfs mapping to device\n");
 266                return;
 267        }
 268
 269        put_mapping_n(mapping, 2);
 270}
 271
 272struct gasket_dev *gasket_sysfs_get_device_data(struct device *device)
 273{
 274        struct gasket_sysfs_mapping *mapping = get_mapping(device);
 275
 276        if (!mapping) {
 277                dev_err(device, "device not registered\n");
 278                return NULL;
 279        }
 280
 281        return mapping->gasket_dev;
 282}
 283EXPORT_SYMBOL(gasket_sysfs_get_device_data);
 284
 285void gasket_sysfs_put_device_data(struct device *device, struct gasket_dev *dev)
 286{
 287        struct gasket_sysfs_mapping *mapping = get_mapping(device);
 288
 289        if (!mapping)
 290                return;
 291
 292        /* See comment of put_mapping_n() for why the '2' is necessary. */
 293        put_mapping_n(mapping, 2);
 294}
 295EXPORT_SYMBOL(gasket_sysfs_put_device_data);
 296
 297struct gasket_sysfs_attribute *
 298gasket_sysfs_get_attr(struct device *device, struct device_attribute *attr)
 299{
 300        int i;
 301        int num_attrs;
 302        struct gasket_sysfs_mapping *mapping = get_mapping(device);
 303        struct gasket_sysfs_attribute *attrs = NULL;
 304
 305        if (!mapping)
 306                return NULL;
 307
 308        attrs = mapping->attributes;
 309        num_attrs = mapping->attribute_count;
 310        for (i = 0; i < num_attrs; ++i) {
 311                if (!strcmp(attrs[i].attr.attr.name, attr->attr.name))
 312                        return &attrs[i];
 313        }
 314
 315        dev_err(device, "Unable to find match for device_attribute %s\n",
 316                attr->attr.name);
 317        return NULL;
 318}
 319EXPORT_SYMBOL(gasket_sysfs_get_attr);
 320
 321void gasket_sysfs_put_attr(struct device *device,
 322                           struct gasket_sysfs_attribute *attr)
 323{
 324        int i;
 325        int num_attrs;
 326        struct gasket_sysfs_mapping *mapping = get_mapping(device);
 327        struct gasket_sysfs_attribute *attrs = NULL;
 328
 329        if (!mapping)
 330                return;
 331
 332        attrs = mapping->attributes;
 333        num_attrs = mapping->attribute_count;
 334        for (i = 0; i < num_attrs; ++i) {
 335                if (&attrs[i] == attr) {
 336                        put_mapping_n(mapping, 2);
 337                        return;
 338                }
 339        }
 340
 341        dev_err(device, "Unable to put unknown attribute: %s\n",
 342                attr->attr.attr.name);
 343}
 344EXPORT_SYMBOL(gasket_sysfs_put_attr);
 345
 346ssize_t gasket_sysfs_register_store(struct device *device,
 347                                    struct device_attribute *attr,
 348                                    const char *buf, size_t count)
 349{
 350        ulong parsed_value = 0;
 351        struct gasket_sysfs_mapping *mapping;
 352        struct gasket_dev *gasket_dev;
 353        struct gasket_sysfs_attribute *gasket_attr;
 354
 355        if (count < 3 || buf[0] != '0' || buf[1] != 'x') {
 356                dev_err(device,
 357                        "sysfs register write format: \"0x<hex value>\"\n");
 358                return -EINVAL;
 359        }
 360
 361        if (kstrtoul(buf, 16, &parsed_value) != 0) {
 362                dev_err(device,
 363                        "Unable to parse input as 64-bit hex value: %s\n", buf);
 364                return -EINVAL;
 365        }
 366
 367        mapping = get_mapping(device);
 368        if (!mapping) {
 369                dev_err(device, "Device driver may have been removed\n");
 370                return 0;
 371        }
 372
 373        gasket_dev = mapping->gasket_dev;
 374        if (!gasket_dev) {
 375                dev_err(device, "Device driver may have been removed\n");
 376                return 0;
 377        }
 378
 379        gasket_attr = gasket_sysfs_get_attr(device, attr);
 380        if (!gasket_attr) {
 381                put_mapping(mapping);
 382                return count;
 383        }
 384
 385        gasket_dev_write_64(gasket_dev, parsed_value,
 386                            gasket_attr->data.bar_address.bar,
 387                            gasket_attr->data.bar_address.offset);
 388
 389        if (gasket_attr->write_callback)
 390                gasket_attr->write_callback(gasket_dev, gasket_attr,
 391                                            parsed_value);
 392
 393        gasket_sysfs_put_attr(device, gasket_attr);
 394        put_mapping(mapping);
 395        return count;
 396}
 397EXPORT_SYMBOL(gasket_sysfs_register_store);
 398