linux/drivers/base/devcoredump.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright(c) 2014 Intel Mobile Communications GmbH
   4 * Copyright(c) 2015 Intel Deutschland GmbH
   5 *
   6 * Contact Information:
   7 *  Intel Linux Wireless <ilw@linux.intel.com>
   8 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
   9 *
  10 * Author: Johannes Berg <johannes@sipsolutions.net>
  11 */
  12#include <linux/module.h>
  13#include <linux/device.h>
  14#include <linux/devcoredump.h>
  15#include <linux/list.h>
  16#include <linux/slab.h>
  17#include <linux/fs.h>
  18#include <linux/workqueue.h>
  19
  20static struct class devcd_class;
  21
  22/* global disable flag, for security purposes */
  23static bool devcd_disabled;
  24
  25/* if data isn't read by userspace after 5 minutes then delete it */
  26#define DEVCD_TIMEOUT   (HZ * 60 * 5)
  27
  28struct devcd_entry {
  29        struct device devcd_dev;
  30        void *data;
  31        size_t datalen;
  32        struct module *owner;
  33        ssize_t (*read)(char *buffer, loff_t offset, size_t count,
  34                        void *data, size_t datalen);
  35        void (*free)(void *data);
  36        struct delayed_work del_wk;
  37        struct device *failing_dev;
  38};
  39
  40static struct devcd_entry *dev_to_devcd(struct device *dev)
  41{
  42        return container_of(dev, struct devcd_entry, devcd_dev);
  43}
  44
  45static void devcd_dev_release(struct device *dev)
  46{
  47        struct devcd_entry *devcd = dev_to_devcd(dev);
  48
  49        devcd->free(devcd->data);
  50        module_put(devcd->owner);
  51
  52        /*
  53         * this seems racy, but I don't see a notifier or such on
  54         * a struct device to know when it goes away?
  55         */
  56        if (devcd->failing_dev->kobj.sd)
  57                sysfs_delete_link(&devcd->failing_dev->kobj, &dev->kobj,
  58                                  "devcoredump");
  59
  60        put_device(devcd->failing_dev);
  61        kfree(devcd);
  62}
  63
  64static void devcd_del(struct work_struct *wk)
  65{
  66        struct devcd_entry *devcd;
  67
  68        devcd = container_of(wk, struct devcd_entry, del_wk.work);
  69
  70        device_del(&devcd->devcd_dev);
  71        put_device(&devcd->devcd_dev);
  72}
  73
  74static ssize_t devcd_data_read(struct file *filp, struct kobject *kobj,
  75                               struct bin_attribute *bin_attr,
  76                               char *buffer, loff_t offset, size_t count)
  77{
  78        struct device *dev = kobj_to_dev(kobj);
  79        struct devcd_entry *devcd = dev_to_devcd(dev);
  80
  81        return devcd->read(buffer, offset, count, devcd->data, devcd->datalen);
  82}
  83
  84static ssize_t devcd_data_write(struct file *filp, struct kobject *kobj,
  85                                struct bin_attribute *bin_attr,
  86                                char *buffer, loff_t offset, size_t count)
  87{
  88        struct device *dev = kobj_to_dev(kobj);
  89        struct devcd_entry *devcd = dev_to_devcd(dev);
  90
  91        mod_delayed_work(system_wq, &devcd->del_wk, 0);
  92
  93        return count;
  94}
  95
  96static struct bin_attribute devcd_attr_data = {
  97        .attr = { .name = "data", .mode = S_IRUSR | S_IWUSR, },
  98        .size = 0,
  99        .read = devcd_data_read,
 100        .write = devcd_data_write,
 101};
 102
 103static struct bin_attribute *devcd_dev_bin_attrs[] = {
 104        &devcd_attr_data, NULL,
 105};
 106
 107static const struct attribute_group devcd_dev_group = {
 108        .bin_attrs = devcd_dev_bin_attrs,
 109};
 110
 111static const struct attribute_group *devcd_dev_groups[] = {
 112        &devcd_dev_group, NULL,
 113};
 114
 115static int devcd_free(struct device *dev, void *data)
 116{
 117        struct devcd_entry *devcd = dev_to_devcd(dev);
 118
 119        flush_delayed_work(&devcd->del_wk);
 120        return 0;
 121}
 122
 123static ssize_t disabled_show(struct class *class, struct class_attribute *attr,
 124                             char *buf)
 125{
 126        return sprintf(buf, "%d\n", devcd_disabled);
 127}
 128
 129static ssize_t disabled_store(struct class *class, struct class_attribute *attr,
 130                              const char *buf, size_t count)
 131{
 132        long tmp = simple_strtol(buf, NULL, 10);
 133
 134        /*
 135         * This essentially makes the attribute write-once, since you can't
 136         * go back to not having it disabled. This is intentional, it serves
 137         * as a system lockdown feature.
 138         */
 139        if (tmp != 1)
 140                return -EINVAL;
 141
 142        devcd_disabled = true;
 143
 144        class_for_each_device(&devcd_class, NULL, NULL, devcd_free);
 145
 146        return count;
 147}
 148static CLASS_ATTR_RW(disabled);
 149
 150static struct attribute *devcd_class_attrs[] = {
 151        &class_attr_disabled.attr,
 152        NULL,
 153};
 154ATTRIBUTE_GROUPS(devcd_class);
 155
 156static struct class devcd_class = {
 157        .name           = "devcoredump",
 158        .owner          = THIS_MODULE,
 159        .dev_release    = devcd_dev_release,
 160        .dev_groups     = devcd_dev_groups,
 161        .class_groups   = devcd_class_groups,
 162};
 163
 164static ssize_t devcd_readv(char *buffer, loff_t offset, size_t count,
 165                           void *data, size_t datalen)
 166{
 167        if (offset > datalen)
 168                return -EINVAL;
 169
 170        if (offset + count > datalen)
 171                count = datalen - offset;
 172
 173        if (count)
 174                memcpy(buffer, ((u8 *)data) + offset, count);
 175
 176        return count;
 177}
 178
 179static void devcd_freev(void *data)
 180{
 181        vfree(data);
 182}
 183
 184/**
 185 * dev_coredumpv - create device coredump with vmalloc data
 186 * @dev: the struct device for the crashed device
 187 * @data: vmalloc data containing the device coredump
 188 * @datalen: length of the data
 189 * @gfp: allocation flags
 190 *
 191 * This function takes ownership of the vmalloc'ed data and will free
 192 * it when it is no longer used. See dev_coredumpm() for more information.
 193 */
 194void dev_coredumpv(struct device *dev, void *data, size_t datalen,
 195                   gfp_t gfp)
 196{
 197        dev_coredumpm(dev, NULL, data, datalen, gfp, devcd_readv, devcd_freev);
 198}
 199EXPORT_SYMBOL_GPL(dev_coredumpv);
 200
 201static int devcd_match_failing(struct device *dev, const void *failing)
 202{
 203        struct devcd_entry *devcd = dev_to_devcd(dev);
 204
 205        return devcd->failing_dev == failing;
 206}
 207
 208/**
 209 * devcd_free_sgtable - free all the memory of the given scatterlist table
 210 * (i.e. both pages and scatterlist instances)
 211 * NOTE: if two tables allocated with devcd_alloc_sgtable and then chained
 212 * using the sg_chain function then that function should be called only once
 213 * on the chained table
 214 * @table: pointer to sg_table to free
 215 */
 216static void devcd_free_sgtable(void *data)
 217{
 218        _devcd_free_sgtable(data);
 219}
 220
 221/**
 222 * devcd_read_from_table - copy data from sg_table to a given buffer
 223 * and return the number of bytes read
 224 * @buffer: the buffer to copy the data to it
 225 * @buf_len: the length of the buffer
 226 * @data: the scatterlist table to copy from
 227 * @offset: start copy from @offset@ bytes from the head of the data
 228 *      in the given scatterlist
 229 * @data_len: the length of the data in the sg_table
 230 */
 231static ssize_t devcd_read_from_sgtable(char *buffer, loff_t offset,
 232                                       size_t buf_len, void *data,
 233                                       size_t data_len)
 234{
 235        struct scatterlist *table = data;
 236
 237        if (offset > data_len)
 238                return -EINVAL;
 239
 240        if (offset + buf_len > data_len)
 241                buf_len = data_len - offset;
 242        return sg_pcopy_to_buffer(table, sg_nents(table), buffer, buf_len,
 243                                  offset);
 244}
 245
 246/**
 247 * dev_coredumpm - create device coredump with read/free methods
 248 * @dev: the struct device for the crashed device
 249 * @owner: the module that contains the read/free functions, use %THIS_MODULE
 250 * @data: data cookie for the @read/@free functions
 251 * @datalen: length of the data
 252 * @gfp: allocation flags
 253 * @read: function to read from the given buffer
 254 * @free: function to free the given buffer
 255 *
 256 * Creates a new device coredump for the given device. If a previous one hasn't
 257 * been read yet, the new coredump is discarded. The data lifetime is determined
 258 * by the device coredump framework and when it is no longer needed the @free
 259 * function will be called to free the data.
 260 */
 261void dev_coredumpm(struct device *dev, struct module *owner,
 262                   void *data, size_t datalen, gfp_t gfp,
 263                   ssize_t (*read)(char *buffer, loff_t offset, size_t count,
 264                                   void *data, size_t datalen),
 265                   void (*free)(void *data))
 266{
 267        static atomic_t devcd_count = ATOMIC_INIT(0);
 268        struct devcd_entry *devcd;
 269        struct device *existing;
 270
 271        if (devcd_disabled)
 272                goto free;
 273
 274        existing = class_find_device(&devcd_class, NULL, dev,
 275                                     devcd_match_failing);
 276        if (existing) {
 277                put_device(existing);
 278                goto free;
 279        }
 280
 281        if (!try_module_get(owner))
 282                goto free;
 283
 284        devcd = kzalloc(sizeof(*devcd), gfp);
 285        if (!devcd)
 286                goto put_module;
 287
 288        devcd->owner = owner;
 289        devcd->data = data;
 290        devcd->datalen = datalen;
 291        devcd->read = read;
 292        devcd->free = free;
 293        devcd->failing_dev = get_device(dev);
 294
 295        device_initialize(&devcd->devcd_dev);
 296
 297        dev_set_name(&devcd->devcd_dev, "devcd%d",
 298                     atomic_inc_return(&devcd_count));
 299        devcd->devcd_dev.class = &devcd_class;
 300
 301        if (device_add(&devcd->devcd_dev))
 302                goto put_device;
 303
 304        if (sysfs_create_link(&devcd->devcd_dev.kobj, &dev->kobj,
 305                              "failing_device"))
 306                /* nothing - symlink will be missing */;
 307
 308        if (sysfs_create_link(&dev->kobj, &devcd->devcd_dev.kobj,
 309                              "devcoredump"))
 310                /* nothing - symlink will be missing */;
 311
 312        INIT_DELAYED_WORK(&devcd->del_wk, devcd_del);
 313        schedule_delayed_work(&devcd->del_wk, DEVCD_TIMEOUT);
 314
 315        return;
 316 put_device:
 317        put_device(&devcd->devcd_dev);
 318 put_module:
 319        module_put(owner);
 320 free:
 321        free(data);
 322}
 323EXPORT_SYMBOL_GPL(dev_coredumpm);
 324
 325/**
 326 * dev_coredumpmsg - create device coredump that uses scatterlist as data
 327 * parameter
 328 * @dev: the struct device for the crashed device
 329 * @table: the dump data
 330 * @datalen: length of the data
 331 * @gfp: allocation flags
 332 *
 333 * Creates a new device coredump for the given device. If a previous one hasn't
 334 * been read yet, the new coredump is discarded. The data lifetime is determined
 335 * by the device coredump framework and when it is no longer needed
 336 * it will free the data.
 337 */
 338void dev_coredumpsg(struct device *dev, struct scatterlist *table,
 339                    size_t datalen, gfp_t gfp)
 340{
 341        dev_coredumpm(dev, NULL, table, datalen, gfp, devcd_read_from_sgtable,
 342                      devcd_free_sgtable);
 343}
 344EXPORT_SYMBOL_GPL(dev_coredumpsg);
 345
 346static int __init devcoredump_init(void)
 347{
 348        return class_register(&devcd_class);
 349}
 350__initcall(devcoredump_init);
 351
 352static void __exit devcoredump_exit(void)
 353{
 354        class_for_each_device(&devcd_class, NULL, NULL, devcd_free);
 355        class_unregister(&devcd_class);
 356}
 357__exitcall(devcoredump_exit);
 358