drivers/base/devcoredump.c

Source file repositories/reference/linux-study-clean/drivers/base/devcoredump.c

File Facts

System
Linux kernel
Corpus path
drivers/base/devcoredump.c
Extension
.c
Size
13935 bytes
Lines
474
Domain
Driver Families
Bucket
drivers/base
Inferred role
Driver Families: exported/initcall integration point
Status
integration implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

* To solve this, hold the mutex during device_add(), and set
	 * init_completed on success before releasing the mutex.
	 *
	 * That way the timer will never fire until device_add() is called,
	 * it will do nothing if init_completed is not set. The timer is also
	 * cancelled in that case.
	 *
	 * The second race involves multiple parallel invocations of devcd_free(),
	 * add a deleted flag so only 1 can call the destructor.
	 */
	struct mutex mutex;
	bool init_completed, deleted;
	struct module *owner;
	ssize_t (*read)(char *buffer, loff_t offset, size_t count,
			void *data, size_t datalen);
	void (*free)(void *data);
	/*
	 * If nothing interferes and device_add() was returns success,
	 * del_wk will destroy the device after the timer fires.
	 *
	 * Multiple userspace processes can interfere in the working of the timer:
	 * - Writing to the coredump will reschedule the timer to run immediately,
	 *   if still armed.
	 *
	 *   This is handled by using "if (cancel_delayed_work()) {
	 *   schedule_delayed_work() }", to prevent re-arming after having
	 *   been previously fired.
	 * - Writing to /sys/class/devcoredump/disabled will destroy the
	 *   coredump synchronously.
	 *   This is handled by using disable_delayed_work_sync(), and then
	 *   checking if deleted flag is set with &devcd->mutex held.
	 */
	struct delayed_work del_wk;
	struct device *failing_dev;
};

static struct devcd_entry *dev_to_devcd(struct device *dev)
{
	return container_of(dev, struct devcd_entry, devcd_dev);
}

static void devcd_dev_release(struct device *dev)
{
	struct devcd_entry *devcd = dev_to_devcd(dev);

	devcd->free(devcd->data);
	module_put(devcd->owner);

	/*
	 * this seems racy, but I don't see a notifier or such on
	 * a struct device to know when it goes away?
	 */
	if (devcd->failing_dev->kobj.sd)
		sysfs_delete_link(&devcd->failing_dev->kobj, &dev->kobj,
				  "devcoredump");

	put_device(devcd->failing_dev);
	kfree(devcd);
}

static void __devcd_del(struct devcd_entry *devcd)
{
	devcd->deleted = true;
	device_del(&devcd->devcd_dev);
	put_device(&devcd->devcd_dev);
}

static void devcd_del(struct work_struct *wk)
{
	struct devcd_entry *devcd;
	bool init_completed;

	devcd = container_of(wk, struct devcd_entry, del_wk.work);

	/* devcd->mutex serializes against dev_coredumpm_timeout */
	mutex_lock(&devcd->mutex);
	init_completed = devcd->init_completed;
	mutex_unlock(&devcd->mutex);

	if (init_completed)
		__devcd_del(devcd);
}

static ssize_t devcd_data_read(struct file *filp, struct kobject *kobj,
			       const struct bin_attribute *bin_attr,
			       char *buffer, loff_t offset, size_t count)
{
	struct device *dev = kobj_to_dev(kobj);
	struct devcd_entry *devcd = dev_to_devcd(dev);

Annotation

Implementation Notes