diff mbox series

[v8,1/2] devcoredump: add new APIs to support migration of users from old device coredump related APIs

Message ID 9e5b72d4fee30704fbd067342ee69b769931318b.1661252818.git.duoming@zju.edu.cn (mailing list archive)
State Awaiting Upstream
Headers show
Series Add new APIs of devcoredump and fix bugs | expand

Checks

Context Check Description
netdev/tree_selection success Not a local patch

Commit Message

Duoming Zhou Aug. 23, 2022, 11:21 a.m. UTC
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@zju.edu.cn>
---
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 mbox series

Patch

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 */