lkml.org 
[lkml]   [2009]   [Jun]   [19]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH] platform_driver_register: warn if probe is in .init.text
    Date
    Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
    ---
    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 81cb01b..851ba84 100644
    --- a/drivers/base/platform.c
    +++ b/drivers/base/platform.c
    @@ -470,11 +470,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)
    @@ -489,6 +485,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);

    /**
    @@ -525,7 +555,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
    * the list of drivers in order to probe new devices. Check to see
    diff --git a/include/linux/kernel.h b/include/linux/kernel.h
    index c5a71c3..5db1817 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 505f20d..ff07937 100644
    --- a/include/linux/module.h
    +++ b/include/linux/module.h
    @@ -379,9 +379,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)
    {
    @@ -550,6 +552,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;
    @@ -560,6 +567,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 215aaab..a637213 100644
    --- a/kernel/module.c
    +++ b/kernel/module.c
    @@ -2874,6 +2874,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.
    *
    @@ -2893,6 +2909,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.3.1
    --
    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/

    \
     
     \ /
      Last update: 2009-06-19 15:45    [W:4.224 / U:0.380 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site