lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1376685563-1053-3-git-send-email-treding@nvidia.com>
Date:	Fri, 16 Aug 2013 22:39:21 +0200
From:	Thierry Reding <thierry.reding@...il.com>
To:	Grant Likely <grant.likely@...aro.org>
Cc:	Rob Herring <rob.herring@...xeda.com>,
	Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
	Stephen Warren <swarren@...dotorg.org>,
	Hiroshi Doyu <hdoyu@...dia.com>,
	Lorenzo Pieralisi <lorenzo.pieralisi@....com>,
	Sebastian Hesselbarth <sebastian.hesselbarth@...il.com>,
	devicetree@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: [RFC 2/4] driver core: Allow early registration of devices

Systems that boot from the devicetree currently have to rely on hacks or
workarounds to setup devices such as interrupt controllers early because
no (platform) device is available yet. This introduces inconsistencies
in the way that these drivers have to be written and precludes them from
using APIs (such as regmap or device logging functions) that rely on a
struct device being available.

This patch is an attempt at rectifying this by allowing devices to be
registered early. Early in this case means sometime after kmalloc() can
be used but before sysfs has been setup. The latter is important because
a large part of the device registration is hooking the device up with
sysfs. Since sysfs becomes available rather late in the boot process,
devices added early will be temporarily added to a list and not hooked
up to sysfs.

Once sysfs is up (or more precisely within driver_init()), the list of
early devices is processed to fully register the devices

Signed-off-by: Thierry Reding <treding@...dia.com>
---
 drivers/base/base.h |   3 ++
 drivers/base/core.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/base/init.c |   2 +
 3 files changed, 112 insertions(+)

diff --git a/drivers/base/base.h b/drivers/base/base.h
index 77e0722..bf33d2e 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -76,6 +76,7 @@ struct device_private {
 	struct klist_node knode_driver;
 	struct klist_node knode_bus;
 	struct list_head deferred_probe;
+	struct list_head early;
 	void *driver_data;
 	struct device *device;
 };
@@ -98,6 +99,8 @@ extern int hypervisor_init(void);
 #else
 static inline int hypervisor_init(void) { return 0; }
 #endif
+extern int device_early_init(void);
+extern int device_early_done(void);
 extern int platform_bus_init(void);
 #ifdef CONFIG_SOC_BUS
 extern int soc_bus_init(void);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 09a99d6..cb240a7 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -20,6 +20,7 @@
 #include <linux/notifier.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
+#include <linux/of_irq.h>
 #include <linux/genhd.h>
 #include <linux/kallsyms.h>
 #include <linux/mutex.h>
@@ -1020,6 +1021,102 @@ int device_private_init(struct device *dev)
 	klist_init(&dev->p->klist_children, klist_children_get,
 		   klist_children_put);
 	INIT_LIST_HEAD(&dev->p->deferred_probe);
+	INIT_LIST_HEAD(&dev->p->early);
+	return 0;
+}
+
+/*
+ * Early device registration support
+ *
+ * Some devices such as interrupt controllers need to be available very
+ * early in the boot process. One of the bigger issues is that a lot of
+ * the device registration code relies on sysfs having been initialized
+ * in order to construct proper entries. However that has not completed
+ * yet this early, so we initialize only the very minimal fields of the
+ * device early and defer full initialization (including creating sysfs
+ * directories and links) until later.
+ */
+static DEFINE_MUTEX(device_early_mutex);
+static LIST_HEAD(device_early_list);
+static bool device_is_early = true;
+
+/*
+ * Keep a list of early registered devices so that they can be fully
+ * registered at a later point in time.
+ */
+static void device_early_add(struct device *dev)
+{
+	mutex_lock(&device_early_mutex);
+	list_add_tail(&dev->p->early, &device_early_list);
+	mutex_unlock(&device_early_mutex);
+}
+
+/*
+ * Mark the early device registration phase as completed.
+ */
+int __init device_early_init(void)
+{
+	device_is_early = false;
+
+	return 0;
+}
+
+/*
+ * Fixup platform devices instantiated from device tree. The problem is
+ * that since early registration happens before interrupt controllers
+ * have been setup, the OF core code won't know how to map interrupts.
+ */
+int __init platform_device_early_fixup(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	unsigned int num_reg, num_irq, num;
+	struct resource *res;
+	int err;
+
+	num_reg = pdev->num_resources;
+	num_irq = of_irq_count(np);
+	num = num_reg + num_irq;
+
+	res = krealloc(pdev->resource, sizeof(*res) * num, GFP_KERNEL);
+	if (!res)
+		return -ENOMEM;
+
+	err = of_irq_to_resource_table(np, res + num_reg, num_irq);
+	if (err != num_irq)
+		return -EINVAL;
+
+	pdev->num_resources = num_reg + num_irq;
+	pdev->resource = res;
+
+	return 0;
+}
+
+/*
+ * Fully register early devices.
+ */
+int __init device_early_done(void)
+{
+	struct device_private *private;
+
+	list_for_each_entry(private, &device_early_list, early) {
+		struct device *dev = private->device;
+		int err;
+
+		if (dev->bus == &platform_bus_type) {
+			struct platform_device *pdev = to_platform_device(dev);
+
+			err = platform_device_early_fixup(pdev);
+			if (err < 0)
+				dev_err(&pdev->dev,
+					"failed to fixup device %s: %d\n",
+					dev_name(&pdev->dev), err);
+		}
+
+		err = device_add(dev);
+		if (err < 0)
+			pr_err("failed to add device %s\n", dev_name(dev));
+	}
+
 	return 0;
 }
 
@@ -1063,6 +1160,16 @@ int device_add(struct device *dev)
 	}
 
 	/*
+	 * If the device is registered early, defer everything below to a
+	 * later point in time where sysfs has been properly initialized.
+	 */
+	if (device_is_early) {
+		device_early_add(dev);
+		put_device(dev);
+		return 0;
+	}
+
+	/*
 	 * for statically allocated devices, which should all be converted
 	 * some day, we need to initialize the name. We prevent reading back
 	 * the name, and force the use of dev_name()
diff --git a/drivers/base/init.c b/drivers/base/init.c
index a4c4090..caa1c43 100644
--- a/drivers/base/init.c
+++ b/drivers/base/init.c
@@ -30,8 +30,10 @@ void __init driver_init(void)
 	/* These are also core pieces, but must come after the
 	 * core core pieces.
 	 */
+	device_early_init();
 	platform_bus_init();
 	soc_bus_init();
+	device_early_done();
 	cpu_dev_init();
 	memory_dev_init();
 }
-- 
1.8.3.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ