Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753361Ab0AVTt3 (ORCPT ); Fri, 22 Jan 2010 14:49:29 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1753184Ab0AVTt3 (ORCPT ); Fri, 22 Jan 2010 14:49:29 -0500 Received: from metis.ext.pengutronix.de ([92.198.50.35]:41354 "EHLO metis.ext.pengutronix.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752989Ab0AVTt2 (ORCPT ); Fri, 22 Jan 2010 14:49:28 -0500 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= To: Greg KH Cc: linux-kernel@vger.kernel.org Subject: [PATCH] platform_driver_register: warn if probe is in .init.text Date: Fri, 22 Jan 2010 20:49:18 +0100 Message-Id: <1264189758-7197-1-git-send-email-u.kleine-koenig@pengutronix.de> X-Mailer: git-send-email 1.6.6 In-Reply-To: <20100122173846.GA11057@suse.de> References: <20100122173846.GA11057@suse.de> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-SA-Exim-Connect-IP: 2001:6f8:1178:2:215:17ff:fe12:23b0 X-SA-Exim-Mail-From: ukl@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-kernel@vger.kernel.org Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 7020 Lines: 223 Signed-off-by: Uwe Kleine-König --- drivers/base/platform.c | 42 ++++++++++++++++++++++++++++++++++++------ include/linux/kernel.h | 2 ++ include/linux/module.h | 12 ++++++++++++ kernel/extable.c | 12 ++++++++++++ kernel/module.c | 36 ++++++++++++++++++++++++++++++++++++ 5 files changed, 98 insertions(+), 6 deletions(-) diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 58efaf2..92d2a75 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -472,11 +472,7 @@ static void platform_drv_shutdown(struct device *_dev) drv->shutdown(dev); } -/** - * platform_driver_register - * @drv: platform driver structure - */ -int platform_driver_register(struct platform_driver *drv) +static int __platform_driver_register(struct platform_driver *drv) { drv->driver.bus = &platform_bus_type; if (drv->probe) @@ -488,6 +484,40 @@ int platform_driver_register(struct platform_driver *drv) return driver_register(&drv->driver); } + +/** + * platform_driver_register + * @drv: platform driver structure + */ +int platform_driver_register(struct platform_driver *drv) +{ + int ret = __platform_driver_register(drv); + +#if defined(CONFIG_HOTPLUG) + /* + * drivers that are registered by platform_driver_register + * should not have their probe function in .init.text. The + * reason is that a probe can happen after .init.text is + * discarded which then results in an oops. The alternatives + * are using .devinit.text for the probe function or "register" + * with platform_driver_probe. + */ + if (drv->probe && kernel_init_text_address((unsigned long)drv->probe)) + pr_warning("oops-warning: probe function of platform driver %s" + " lives in .init.text\n", drv->driver.name); +#else + /* + * without HOTPLUG probe functions can be discarded after the driver is + * loaded. + * There is a little chance for false positives, namely if the driver is + * registered after the .init sections are discarded. + */ + if (drv->probe && !kernel_init_text_address((unsigned long)drv->probe)) + pr_info("probably the probe function of platform driver %s can" + " be moved to .init.text\n", drv->driver.name); +#endif + return ret; +} EXPORT_SYMBOL_GPL(platform_driver_register); /** @@ -527,7 +557,7 @@ int __init_or_module platform_driver_probe(struct platform_driver *drv, /* temporary section violation during probe() */ drv->probe = probe; - retval = code = platform_driver_register(drv); + retval = code = __platform_driver_register(drv); /* * Fixup that section violation, being paranoid about code scanning diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 328bca6..dc8b191 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -203,8 +203,10 @@ extern char *get_options(const char *str, int nints, int *ints); extern unsigned long long memparse(const char *ptr, char **retptr); extern int core_kernel_text(unsigned long addr); +extern int core_kernel_init_text(unsigned long addr) extern int __kernel_text_address(unsigned long addr); extern int kernel_text_address(unsigned long addr); +extern int kernel_init_text_address(unsigned long addr); extern int func_ptr_is_kernel_text(void *ptr); struct pid; diff --git a/include/linux/module.h b/include/linux/module.h index 6cb1a3c..00a8bc3 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -391,9 +391,11 @@ static inline int module_is_live(struct module *mod) } struct module *__module_text_address(unsigned long addr); +struct module *__module_init_text_address(unsigned long addr); struct module *__module_address(unsigned long addr); bool is_module_address(unsigned long addr); bool is_module_text_address(unsigned long addr); +bool is_module_init_text_address(unsigned long addr); static inline int within_module_core(unsigned long addr, struct module *mod) { @@ -566,6 +568,11 @@ static inline struct module *__module_text_address(unsigned long addr) return NULL; } +static inline struct module *__module_init_text_address(unsigned long addr) +{ + return NULL; +} + static inline bool is_module_address(unsigned long addr) { return false; @@ -576,6 +583,11 @@ static inline bool is_module_text_address(unsigned long addr) return false; } +static inline bool is_module_init_text_address(unsigned long addr) +{ + return false; +} + /* Get/put a kernel symbol (calls should be symmetric) */ #define symbol_get(x) ({ extern typeof(x) x __attribute__((weak)); &(x); }) #define symbol_put(x) do { } while(0) diff --git a/kernel/extable.c b/kernel/extable.c index 7f8f263..bfd7bda 100644 --- a/kernel/extable.c +++ b/kernel/extable.c @@ -66,6 +66,11 @@ int core_kernel_text(unsigned long addr) addr <= (unsigned long)_etext) return 1; + return core_kernel_init_text; +} + +int core_kernel_init_text(unsigned long addr) +{ if (system_state == SYSTEM_BOOTING && init_kernel_text(addr)) return 1; @@ -98,6 +103,13 @@ int kernel_text_address(unsigned long addr) return is_module_text_address(addr); } +int kernel_init_text_address(unsigned long addr) +{ + if (core_kernel_init_text(addr)) + return 1; + return is_module_init_text_address(addr); +} + /* * On some architectures (PPC64, IA64) function pointers * are actually only tokens to some data that then holds the diff --git a/kernel/module.c b/kernel/module.c index f82386b..219827d 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -2922,6 +2922,22 @@ bool is_module_text_address(unsigned long addr) } /* + * is_module_init_text_address - is this address inside a module's .init.text + * section? + * @addr: the address to check. + */ +bool is_module_init_text_address(unsigned long addr) +{ + bool ret; + + preempt_disable(); + ret = __module_init_text_address(addr) != NULL; + preempt_enable(); + + return ret; +} + +/* * __module_text_address - get the module whose code contains an address. * @addr: the address. * @@ -2941,6 +2957,26 @@ struct module *__module_text_address(unsigned long addr) } EXPORT_SYMBOL_GPL(__module_text_address); +/* + * __module_init_text_address - get the module whose .init.text contains an + * address. + * @addr: the address. + * + * Must be called with preempt disabled or module mutex held so that + * module doesn't get freed during this. + */ +struct module *__module_init_text_address(unsigned long addr) +{ + struct module *mod = __module_address(addr); + if (mod) { + /* Make sure it's within the .init.text section. */ + if (!within(addr, mod->module_init, mod->init_text_size)) + mod = NULL; + } + return mod; +} +EXPORT_SYMBOL_GPL(__module_init_text_address); + /* Don't grab lock, we're oopsing. */ void print_modules(void) { -- 1.6.6 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/