162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright(c) 2014 Intel Mobile Communications GmbH 462306a36Sopenharmony_ci * Copyright(c) 2015 Intel Deutschland GmbH 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Author: Johannes Berg <johannes@sipsolutions.net> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/device.h> 1062306a36Sopenharmony_ci#include <linux/devcoredump.h> 1162306a36Sopenharmony_ci#include <linux/list.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/fs.h> 1462306a36Sopenharmony_ci#include <linux/workqueue.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic struct class devcd_class; 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* global disable flag, for security purposes */ 1962306a36Sopenharmony_cistatic bool devcd_disabled; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* if data isn't read by userspace after 5 minutes then delete it */ 2262306a36Sopenharmony_ci#define DEVCD_TIMEOUT (HZ * 60 * 5) 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistruct devcd_entry { 2562306a36Sopenharmony_ci struct device devcd_dev; 2662306a36Sopenharmony_ci void *data; 2762306a36Sopenharmony_ci size_t datalen; 2862306a36Sopenharmony_ci /* 2962306a36Sopenharmony_ci * Here, mutex is required to serialize the calls to del_wk work between 3062306a36Sopenharmony_ci * user/kernel space which happens when devcd is added with device_add() 3162306a36Sopenharmony_ci * and that sends uevent to user space. User space reads the uevents, 3262306a36Sopenharmony_ci * and calls to devcd_data_write() which try to modify the work which is 3362306a36Sopenharmony_ci * not even initialized/queued from devcoredump. 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * cpu0(X) cpu1(Y) 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * dev_coredump() uevent sent to user space 4062306a36Sopenharmony_ci * device_add() ======================> user space process Y reads the 4162306a36Sopenharmony_ci * uevents writes to devcd fd 4262306a36Sopenharmony_ci * which results into writes to 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * devcd_data_write() 4562306a36Sopenharmony_ci * mod_delayed_work() 4662306a36Sopenharmony_ci * try_to_grab_pending() 4762306a36Sopenharmony_ci * del_timer() 4862306a36Sopenharmony_ci * debug_assert_init() 4962306a36Sopenharmony_ci * INIT_DELAYED_WORK() 5062306a36Sopenharmony_ci * schedule_delayed_work() 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * Also, mutex alone would not be enough to avoid scheduling of 5462306a36Sopenharmony_ci * del_wk work after it get flush from a call to devcd_free() 5562306a36Sopenharmony_ci * mentioned as below. 5662306a36Sopenharmony_ci * 5762306a36Sopenharmony_ci * disabled_store() 5862306a36Sopenharmony_ci * devcd_free() 5962306a36Sopenharmony_ci * mutex_lock() devcd_data_write() 6062306a36Sopenharmony_ci * flush_delayed_work() 6162306a36Sopenharmony_ci * mutex_unlock() 6262306a36Sopenharmony_ci * mutex_lock() 6362306a36Sopenharmony_ci * mod_delayed_work() 6462306a36Sopenharmony_ci * mutex_unlock() 6562306a36Sopenharmony_ci * So, delete_work flag is required. 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_ci struct mutex mutex; 6862306a36Sopenharmony_ci bool delete_work; 6962306a36Sopenharmony_ci struct module *owner; 7062306a36Sopenharmony_ci ssize_t (*read)(char *buffer, loff_t offset, size_t count, 7162306a36Sopenharmony_ci void *data, size_t datalen); 7262306a36Sopenharmony_ci void (*free)(void *data); 7362306a36Sopenharmony_ci struct delayed_work del_wk; 7462306a36Sopenharmony_ci struct device *failing_dev; 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic struct devcd_entry *dev_to_devcd(struct device *dev) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci return container_of(dev, struct devcd_entry, devcd_dev); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic void devcd_dev_release(struct device *dev) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct devcd_entry *devcd = dev_to_devcd(dev); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci devcd->free(devcd->data); 8762306a36Sopenharmony_ci module_put(devcd->owner); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* 9062306a36Sopenharmony_ci * this seems racy, but I don't see a notifier or such on 9162306a36Sopenharmony_ci * a struct device to know when it goes away? 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_ci if (devcd->failing_dev->kobj.sd) 9462306a36Sopenharmony_ci sysfs_delete_link(&devcd->failing_dev->kobj, &dev->kobj, 9562306a36Sopenharmony_ci "devcoredump"); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci put_device(devcd->failing_dev); 9862306a36Sopenharmony_ci kfree(devcd); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic void devcd_del(struct work_struct *wk) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct devcd_entry *devcd; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci devcd = container_of(wk, struct devcd_entry, del_wk.work); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci device_del(&devcd->devcd_dev); 10862306a36Sopenharmony_ci put_device(&devcd->devcd_dev); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic ssize_t devcd_data_read(struct file *filp, struct kobject *kobj, 11262306a36Sopenharmony_ci struct bin_attribute *bin_attr, 11362306a36Sopenharmony_ci char *buffer, loff_t offset, size_t count) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 11662306a36Sopenharmony_ci struct devcd_entry *devcd = dev_to_devcd(dev); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return devcd->read(buffer, offset, count, devcd->data, devcd->datalen); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic ssize_t devcd_data_write(struct file *filp, struct kobject *kobj, 12262306a36Sopenharmony_ci struct bin_attribute *bin_attr, 12362306a36Sopenharmony_ci char *buffer, loff_t offset, size_t count) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 12662306a36Sopenharmony_ci struct devcd_entry *devcd = dev_to_devcd(dev); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci mutex_lock(&devcd->mutex); 12962306a36Sopenharmony_ci if (!devcd->delete_work) { 13062306a36Sopenharmony_ci devcd->delete_work = true; 13162306a36Sopenharmony_ci mod_delayed_work(system_wq, &devcd->del_wk, 0); 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci mutex_unlock(&devcd->mutex); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return count; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic struct bin_attribute devcd_attr_data = { 13962306a36Sopenharmony_ci .attr = { .name = "data", .mode = S_IRUSR | S_IWUSR, }, 14062306a36Sopenharmony_ci .size = 0, 14162306a36Sopenharmony_ci .read = devcd_data_read, 14262306a36Sopenharmony_ci .write = devcd_data_write, 14362306a36Sopenharmony_ci}; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic struct bin_attribute *devcd_dev_bin_attrs[] = { 14662306a36Sopenharmony_ci &devcd_attr_data, NULL, 14762306a36Sopenharmony_ci}; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic const struct attribute_group devcd_dev_group = { 15062306a36Sopenharmony_ci .bin_attrs = devcd_dev_bin_attrs, 15162306a36Sopenharmony_ci}; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic const struct attribute_group *devcd_dev_groups[] = { 15462306a36Sopenharmony_ci &devcd_dev_group, NULL, 15562306a36Sopenharmony_ci}; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int devcd_free(struct device *dev, void *data) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct devcd_entry *devcd = dev_to_devcd(dev); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci mutex_lock(&devcd->mutex); 16262306a36Sopenharmony_ci if (!devcd->delete_work) 16362306a36Sopenharmony_ci devcd->delete_work = true; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci flush_delayed_work(&devcd->del_wk); 16662306a36Sopenharmony_ci mutex_unlock(&devcd->mutex); 16762306a36Sopenharmony_ci return 0; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic ssize_t disabled_show(const struct class *class, const struct class_attribute *attr, 17162306a36Sopenharmony_ci char *buf) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", devcd_disabled); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci/* 17762306a36Sopenharmony_ci * 17862306a36Sopenharmony_ci * disabled_store() worker() 17962306a36Sopenharmony_ci * class_for_each_device(&devcd_class, 18062306a36Sopenharmony_ci * NULL, NULL, devcd_free) 18162306a36Sopenharmony_ci * ... 18262306a36Sopenharmony_ci * ... 18362306a36Sopenharmony_ci * while ((dev = class_dev_iter_next(&iter)) 18462306a36Sopenharmony_ci * devcd_del() 18562306a36Sopenharmony_ci * device_del() 18662306a36Sopenharmony_ci * put_device() <- last reference 18762306a36Sopenharmony_ci * error = fn(dev, data) devcd_dev_release() 18862306a36Sopenharmony_ci * devcd_free(dev, data) kfree(devcd) 18962306a36Sopenharmony_ci * mutex_lock(&devcd->mutex); 19062306a36Sopenharmony_ci * 19162306a36Sopenharmony_ci * 19262306a36Sopenharmony_ci * In the above diagram, It looks like disabled_store() would be racing with parallely 19362306a36Sopenharmony_ci * running devcd_del() and result in memory abort while acquiring devcd->mutex which 19462306a36Sopenharmony_ci * is called after kfree of devcd memory after dropping its last reference with 19562306a36Sopenharmony_ci * put_device(). However, this will not happens as fn(dev, data) runs 19662306a36Sopenharmony_ci * with its own reference to device via klist_node so it is not its last reference. 19762306a36Sopenharmony_ci * so, above situation would not occur. 19862306a36Sopenharmony_ci */ 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic ssize_t disabled_store(const struct class *class, const struct class_attribute *attr, 20162306a36Sopenharmony_ci const char *buf, size_t count) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci long tmp = simple_strtol(buf, NULL, 10); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* 20662306a36Sopenharmony_ci * This essentially makes the attribute write-once, since you can't 20762306a36Sopenharmony_ci * go back to not having it disabled. This is intentional, it serves 20862306a36Sopenharmony_ci * as a system lockdown feature. 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_ci if (tmp != 1) 21162306a36Sopenharmony_ci return -EINVAL; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci devcd_disabled = true; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci class_for_each_device(&devcd_class, NULL, NULL, devcd_free); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci return count; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_cistatic CLASS_ATTR_RW(disabled); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic struct attribute *devcd_class_attrs[] = { 22262306a36Sopenharmony_ci &class_attr_disabled.attr, 22362306a36Sopenharmony_ci NULL, 22462306a36Sopenharmony_ci}; 22562306a36Sopenharmony_ciATTRIBUTE_GROUPS(devcd_class); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic struct class devcd_class = { 22862306a36Sopenharmony_ci .name = "devcoredump", 22962306a36Sopenharmony_ci .dev_release = devcd_dev_release, 23062306a36Sopenharmony_ci .dev_groups = devcd_dev_groups, 23162306a36Sopenharmony_ci .class_groups = devcd_class_groups, 23262306a36Sopenharmony_ci}; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic ssize_t devcd_readv(char *buffer, loff_t offset, size_t count, 23562306a36Sopenharmony_ci void *data, size_t datalen) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci return memory_read_from_buffer(buffer, count, &offset, data, datalen); 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic void devcd_freev(void *data) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci vfree(data); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci/** 24662306a36Sopenharmony_ci * dev_coredumpv - create device coredump with vmalloc data 24762306a36Sopenharmony_ci * @dev: the struct device for the crashed device 24862306a36Sopenharmony_ci * @data: vmalloc data containing the device coredump 24962306a36Sopenharmony_ci * @datalen: length of the data 25062306a36Sopenharmony_ci * @gfp: allocation flags 25162306a36Sopenharmony_ci * 25262306a36Sopenharmony_ci * This function takes ownership of the vmalloc'ed data and will free 25362306a36Sopenharmony_ci * it when it is no longer used. See dev_coredumpm() for more information. 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_civoid dev_coredumpv(struct device *dev, void *data, size_t datalen, 25662306a36Sopenharmony_ci gfp_t gfp) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci dev_coredumpm(dev, NULL, data, datalen, gfp, devcd_readv, devcd_freev); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_coredumpv); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic int devcd_match_failing(struct device *dev, const void *failing) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci struct devcd_entry *devcd = dev_to_devcd(dev); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci return devcd->failing_dev == failing; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci/** 27062306a36Sopenharmony_ci * devcd_free_sgtable - free all the memory of the given scatterlist table 27162306a36Sopenharmony_ci * (i.e. both pages and scatterlist instances) 27262306a36Sopenharmony_ci * NOTE: if two tables allocated with devcd_alloc_sgtable and then chained 27362306a36Sopenharmony_ci * using the sg_chain function then that function should be called only once 27462306a36Sopenharmony_ci * on the chained table 27562306a36Sopenharmony_ci * @data: pointer to sg_table to free 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_cistatic void devcd_free_sgtable(void *data) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci _devcd_free_sgtable(data); 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci/** 28362306a36Sopenharmony_ci * devcd_read_from_sgtable - copy data from sg_table to a given buffer 28462306a36Sopenharmony_ci * and return the number of bytes read 28562306a36Sopenharmony_ci * @buffer: the buffer to copy the data to it 28662306a36Sopenharmony_ci * @buf_len: the length of the buffer 28762306a36Sopenharmony_ci * @data: the scatterlist table to copy from 28862306a36Sopenharmony_ci * @offset: start copy from @offset@ bytes from the head of the data 28962306a36Sopenharmony_ci * in the given scatterlist 29062306a36Sopenharmony_ci * @data_len: the length of the data in the sg_table 29162306a36Sopenharmony_ci */ 29262306a36Sopenharmony_cistatic ssize_t devcd_read_from_sgtable(char *buffer, loff_t offset, 29362306a36Sopenharmony_ci size_t buf_len, void *data, 29462306a36Sopenharmony_ci size_t data_len) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci struct scatterlist *table = data; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (offset > data_len) 29962306a36Sopenharmony_ci return -EINVAL; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (offset + buf_len > data_len) 30262306a36Sopenharmony_ci buf_len = data_len - offset; 30362306a36Sopenharmony_ci return sg_pcopy_to_buffer(table, sg_nents(table), buffer, buf_len, 30462306a36Sopenharmony_ci offset); 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci/** 30862306a36Sopenharmony_ci * dev_coredumpm - create device coredump with read/free methods 30962306a36Sopenharmony_ci * @dev: the struct device for the crashed device 31062306a36Sopenharmony_ci * @owner: the module that contains the read/free functions, use %THIS_MODULE 31162306a36Sopenharmony_ci * @data: data cookie for the @read/@free functions 31262306a36Sopenharmony_ci * @datalen: length of the data 31362306a36Sopenharmony_ci * @gfp: allocation flags 31462306a36Sopenharmony_ci * @read: function to read from the given buffer 31562306a36Sopenharmony_ci * @free: function to free the given buffer 31662306a36Sopenharmony_ci * 31762306a36Sopenharmony_ci * Creates a new device coredump for the given device. If a previous one hasn't 31862306a36Sopenharmony_ci * been read yet, the new coredump is discarded. The data lifetime is determined 31962306a36Sopenharmony_ci * by the device coredump framework and when it is no longer needed the @free 32062306a36Sopenharmony_ci * function will be called to free the data. 32162306a36Sopenharmony_ci */ 32262306a36Sopenharmony_civoid dev_coredumpm(struct device *dev, struct module *owner, 32362306a36Sopenharmony_ci void *data, size_t datalen, gfp_t gfp, 32462306a36Sopenharmony_ci ssize_t (*read)(char *buffer, loff_t offset, size_t count, 32562306a36Sopenharmony_ci void *data, size_t datalen), 32662306a36Sopenharmony_ci void (*free)(void *data)) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci static atomic_t devcd_count = ATOMIC_INIT(0); 32962306a36Sopenharmony_ci struct devcd_entry *devcd; 33062306a36Sopenharmony_ci struct device *existing; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (devcd_disabled) 33362306a36Sopenharmony_ci goto free; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci existing = class_find_device(&devcd_class, NULL, dev, 33662306a36Sopenharmony_ci devcd_match_failing); 33762306a36Sopenharmony_ci if (existing) { 33862306a36Sopenharmony_ci put_device(existing); 33962306a36Sopenharmony_ci goto free; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (!try_module_get(owner)) 34362306a36Sopenharmony_ci goto free; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci devcd = kzalloc(sizeof(*devcd), gfp); 34662306a36Sopenharmony_ci if (!devcd) 34762306a36Sopenharmony_ci goto put_module; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci devcd->owner = owner; 35062306a36Sopenharmony_ci devcd->data = data; 35162306a36Sopenharmony_ci devcd->datalen = datalen; 35262306a36Sopenharmony_ci devcd->read = read; 35362306a36Sopenharmony_ci devcd->free = free; 35462306a36Sopenharmony_ci devcd->failing_dev = get_device(dev); 35562306a36Sopenharmony_ci devcd->delete_work = false; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci mutex_init(&devcd->mutex); 35862306a36Sopenharmony_ci device_initialize(&devcd->devcd_dev); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci dev_set_name(&devcd->devcd_dev, "devcd%d", 36162306a36Sopenharmony_ci atomic_inc_return(&devcd_count)); 36262306a36Sopenharmony_ci devcd->devcd_dev.class = &devcd_class; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci mutex_lock(&devcd->mutex); 36562306a36Sopenharmony_ci dev_set_uevent_suppress(&devcd->devcd_dev, true); 36662306a36Sopenharmony_ci if (device_add(&devcd->devcd_dev)) 36762306a36Sopenharmony_ci goto put_device; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* 37062306a36Sopenharmony_ci * These should normally not fail, but there is no problem 37162306a36Sopenharmony_ci * continuing without the links, so just warn instead of 37262306a36Sopenharmony_ci * failing. 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_ci if (sysfs_create_link(&devcd->devcd_dev.kobj, &dev->kobj, 37562306a36Sopenharmony_ci "failing_device") || 37662306a36Sopenharmony_ci sysfs_create_link(&dev->kobj, &devcd->devcd_dev.kobj, 37762306a36Sopenharmony_ci "devcoredump")) 37862306a36Sopenharmony_ci dev_warn(dev, "devcoredump create_link failed\n"); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci dev_set_uevent_suppress(&devcd->devcd_dev, false); 38162306a36Sopenharmony_ci kobject_uevent(&devcd->devcd_dev.kobj, KOBJ_ADD); 38262306a36Sopenharmony_ci INIT_DELAYED_WORK(&devcd->del_wk, devcd_del); 38362306a36Sopenharmony_ci schedule_delayed_work(&devcd->del_wk, DEVCD_TIMEOUT); 38462306a36Sopenharmony_ci mutex_unlock(&devcd->mutex); 38562306a36Sopenharmony_ci return; 38662306a36Sopenharmony_ci put_device: 38762306a36Sopenharmony_ci put_device(&devcd->devcd_dev); 38862306a36Sopenharmony_ci mutex_unlock(&devcd->mutex); 38962306a36Sopenharmony_ci put_module: 39062306a36Sopenharmony_ci module_put(owner); 39162306a36Sopenharmony_ci free: 39262306a36Sopenharmony_ci free(data); 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_coredumpm); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci/** 39762306a36Sopenharmony_ci * dev_coredumpsg - create device coredump that uses scatterlist as data 39862306a36Sopenharmony_ci * parameter 39962306a36Sopenharmony_ci * @dev: the struct device for the crashed device 40062306a36Sopenharmony_ci * @table: the dump data 40162306a36Sopenharmony_ci * @datalen: length of the data 40262306a36Sopenharmony_ci * @gfp: allocation flags 40362306a36Sopenharmony_ci * 40462306a36Sopenharmony_ci * Creates a new device coredump for the given device. If a previous one hasn't 40562306a36Sopenharmony_ci * been read yet, the new coredump is discarded. The data lifetime is determined 40662306a36Sopenharmony_ci * by the device coredump framework and when it is no longer needed 40762306a36Sopenharmony_ci * it will free the data. 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_civoid dev_coredumpsg(struct device *dev, struct scatterlist *table, 41062306a36Sopenharmony_ci size_t datalen, gfp_t gfp) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci dev_coredumpm(dev, NULL, table, datalen, gfp, devcd_read_from_sgtable, 41362306a36Sopenharmony_ci devcd_free_sgtable); 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_coredumpsg); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic int __init devcoredump_init(void) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci return class_register(&devcd_class); 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci__initcall(devcoredump_init); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic void __exit devcoredump_exit(void) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci class_for_each_device(&devcd_class, NULL, NULL, devcd_free); 42662306a36Sopenharmony_ci class_unregister(&devcd_class); 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci__exitcall(devcoredump_exit); 429