The dev_coredumpv(), dev_coredumpm() and dev_coredumpsg() could not be used in atomic context, because they call kvasprintf_const() and kstrdup() with GFP_KERNEL parameter. The process is shown below: dev_coredumpv(.., gfp_t gfp) dev_coredumpm(.., gfp_t gfp) dev_set_name kobject_set_name_vargs kvasprintf_const(GFP_KERNEL, ...); //may sleep kstrdup(s, GFP_KERNEL); //may sleep This patch adds new APIs that remove gfp_t parameter of dev_coredumpv(), dev_coredumpm() and dev_coredumpsg() in order to show they could not be used in atomic context. These new APIs will ultimately replace the dev_coredumpv(), dev_coredumpm() and dev_coredumpsg() when there are no users of the old APIs. Fixes: 833c95456a70 ("device coredump: add new device coredump class") Signed-off-by: Duoming Zhou <duoming@xxxxxxxxxx> --- Changes in v8: - add new APIs to prepare migration of users from old device coredump related APIs. drivers/base/devcoredump.c | 116 ++++++++++++++++++++++++++++++++++++ include/linux/devcoredump.h | 34 +++++++++++ 2 files changed, 150 insertions(+) diff --git a/drivers/base/devcoredump.c b/drivers/base/devcoredump.c index f4d794d6bb8..728457b12ce 100644 --- a/drivers/base/devcoredump.c +++ b/drivers/base/devcoredump.c @@ -185,6 +185,21 @@ void dev_coredumpv(struct device *dev, void *data, size_t datalen, } EXPORT_SYMBOL_GPL(dev_coredumpv); +/** + * dev_core_dumpv - create device coredump with vmalloc data + * @dev: the struct device for the crashed device + * @data: vmalloc data containing the device coredump + * @datalen: length of the data + * + * This function takes ownership of the vmalloc'ed data and will free + * it when it is no longer used. See dev_core_dumpm() for more information. + */ +void dev_core_dumpv(struct device *dev, void *data, size_t datalen) +{ + dev_core_dumpm(dev, NULL, data, datalen, devcd_readv, devcd_freev); +} +EXPORT_SYMBOL_GPL(dev_core_dumpv); + static int devcd_match_failing(struct device *dev, const void *failing) { struct devcd_entry *devcd = dev_to_devcd(dev); @@ -312,6 +327,87 @@ void dev_coredumpm(struct device *dev, struct module *owner, } EXPORT_SYMBOL_GPL(dev_coredumpm); +/** + * dev_core_dumpm - create device coredump with read/free methods + * @dev: the struct device for the crashed device + * @owner: the module that contains the read/free functions, use %THIS_MODULE + * @data: data cookie for the @read/@free functions + * @datalen: length of the data + * @read: function to read from the given buffer + * @free: function to free the given buffer + * + * Creates a new device coredump for the given device. If a previous one hasn't + * been read yet, the new coredump is discarded. The data lifetime is determined + * by the device coredump framework and when it is no longer needed the @free + * function will be called to free the data. + */ +void dev_core_dumpm(struct device *dev, struct module *owner, + void *data, size_t datalen, + ssize_t (*read)(char *buffer, loff_t offset, size_t count, + void *data, size_t datalen), + void (*free)(void *data)) +{ + static atomic_t devcd_count = ATOMIC_INIT(0); + struct devcd_entry *devcd; + struct device *existing; + + if (devcd_disabled) + goto free; + + existing = class_find_device(&devcd_class, NULL, dev, + devcd_match_failing); + if (existing) { + put_device(existing); + goto free; + } + + if (!try_module_get(owner)) + goto free; + + devcd = kzalloc(sizeof(*devcd), GFP_KERNEL); + if (!devcd) + goto put_module; + + devcd->owner = owner; + devcd->data = data; + devcd->datalen = datalen; + devcd->read = read; + devcd->free = free; + devcd->failing_dev = get_device(dev); + + device_initialize(&devcd->devcd_dev); + + dev_set_name(&devcd->devcd_dev, "devcd%d", + atomic_inc_return(&devcd_count)); + devcd->devcd_dev.class = &devcd_class; + + if (device_add(&devcd->devcd_dev)) + goto put_device; + + /* + * These should normally not fail, but there is no problem + * continuing without the links, so just warn instead of + * failing. + */ + if (sysfs_create_link(&devcd->devcd_dev.kobj, &dev->kobj, + "failing_device") || + sysfs_create_link(&dev->kobj, &devcd->devcd_dev.kobj, + "devcoredump")) + dev_warn(dev, "devcoredump create_link failed\n"); + + INIT_DELAYED_WORK(&devcd->del_wk, devcd_del); + schedule_delayed_work(&devcd->del_wk, DEVCD_TIMEOUT); + + return; + put_device: + put_device(&devcd->devcd_dev); + put_module: + module_put(owner); + free: + free(data); +} +EXPORT_SYMBOL_GPL(dev_core_dumpm); + /** * dev_coredumpsg - create device coredump that uses scatterlist as data * parameter @@ -333,6 +429,26 @@ void dev_coredumpsg(struct device *dev, struct scatterlist *table, } EXPORT_SYMBOL_GPL(dev_coredumpsg); +/** + * dev_core_dumpsg - create device coredump that uses scatterlist as data + * parameter + * @dev: the struct device for the crashed device + * @table: the dump data + * @datalen: length of the data + * + * Creates a new device coredump for the given device. If a previous one hasn't + * been read yet, the new coredump is discarded. The data lifetime is determined + * by the device coredump framework and when it is no longer needed + * it will free the data. + */ +void dev_core_dumpsg(struct device *dev, struct scatterlist *table, + size_t datalen) +{ + dev_core_dumpm(dev, NULL, table, datalen, devcd_read_from_sgtable, + devcd_free_sgtable); +} +EXPORT_SYMBOL_GPL(dev_core_dumpsg); + static int __init devcoredump_init(void) { return class_register(&devcd_class); diff --git a/include/linux/devcoredump.h b/include/linux/devcoredump.h index c008169ed2c..113fe63800a 100644 --- a/include/linux/devcoredump.h +++ b/include/linux/devcoredump.h @@ -55,14 +55,26 @@ static inline void _devcd_free_sgtable(struct scatterlist *table) void dev_coredumpv(struct device *dev, void *data, size_t datalen, gfp_t gfp); +void dev_core_dumpv(struct device *dev, void *data, size_t datalen); + void dev_coredumpm(struct device *dev, struct module *owner, void *data, size_t datalen, gfp_t gfp, ssize_t (*read)(char *buffer, loff_t offset, size_t count, void *data, size_t datalen), void (*free)(void *data)); +void dev_core_dumpm(struct device *dev, struct module *owner, + void *data, size_t datalen, + ssize_t (*read)(char *buffer, loff_t offset, size_t count, + void *data, size_t datalen), + void (*free)(void *data)); + void dev_coredumpsg(struct device *dev, struct scatterlist *table, size_t datalen, gfp_t gfp); + +void dev_core_dumpsg(struct device *dev, struct scatterlist *table, + size_t datalen); + #else static inline void dev_coredumpv(struct device *dev, void *data, size_t datalen, gfp_t gfp) @@ -70,6 +82,12 @@ static inline void dev_coredumpv(struct device *dev, void *data, vfree(data); } +static inline void dev_core_dumpv(struct device *dev, void *data, + size_t datalen) +{ + vfree(data); +} + static inline void dev_coredumpm(struct device *dev, struct module *owner, void *data, size_t datalen, gfp_t gfp, @@ -80,11 +98,27 @@ dev_coredumpm(struct device *dev, struct module *owner, free(data); } +static inline void +dev_core_dumpm(struct device *dev, struct module *owner, + void *data, size_t datalen, + ssize_t (*read)(char *buffer, loff_t offset, size_t count, + void *data, size_t datalen), + void (*free)(void *data)) +{ + free(data); +} + static inline void dev_coredumpsg(struct device *dev, struct scatterlist *table, size_t datalen, gfp_t gfp) { _devcd_free_sgtable(table); } + +static inline void dev_core_dumpsg(struct device *dev, struct scatterlist *table, + size_t datalen) +{ + _devcd_free_sgtable(table); +} #endif /* CONFIG_DEV_COREDUMP */ #endif /* __DEVCOREDUMP_H */ -- 2.17.1