Question

I am trying to understand the linux kernel power management at a low level and have finally got stuck since my C is not very strong. I am able to follow the code down to where it calls suspend_ops->enter(state); in the suspend_enter(suspend_state_t state, bool *wakeup) method (kernel/power/suspend.c) however I have not been able to follow the code any further.

It is my understanding that suspend_ops->enter(state) is invoking the "enter" method in some drivers that registered themselves with suspend_ops but I am not sure how to find them or the method that is being called. Can someone please tell me how to follow the suspend_ops->enter(state) call?

Était-ce utile?

La solution

Near the top of kernel/power/suspend.c note the line:

static struct platform_suspend_ops *suspend_ops;

This means that suspend_ops is a global variable that is private to this file. This means that in order to be of any use, there will have to be an assignment somewhere in suspend.c. So let's look for the assignment. Searching through suspend.c for suspend_ops we see only one assignment, in the subroutine suspend_set_ops. Searching for suspend_set_ops in suspend.c we see that there are no calls to it. So, the calls must be somewhere else!

We search the entire kernel for suspend_set_ops:

yba@tavas:~/linux-2.6/linux-2.6.31$ find . -type f | xargs grep suspend_set_ops
./kernel/power/suspend.c: * suspend_set_ops - Set the global suspend method table.
./kernel/power/suspend.c:void suspend_set_ops(struct platform_suspend_ops *ops)
./arch/mips/alchemy/devboards/pm.c: suspend_set_ops(&db1x_pm_ops);
./arch/arm/mach-sa1100/pm.c:    suspend_set_ops(&sa11x0_pm_ops);
./arch/arm/plat-s3c/pm.c:   suspend_set_ops(&s3c_pm_ops);
./arch/arm/mach-omap2/pm24xx.c: suspend_set_ops(&omap_pm_ops);
./arch/arm/mach-omap2/pm34xx.c: suspend_set_ops(&omap_pm_ops);
./arch/arm/mach-pxa/pm.c:   suspend_set_ops(&pxa_pm_ops);
./arch/arm/mach-pxa/sharpsl_pm.c:   suspend_set_ops(&sharpsl_pm_ops);
./arch/arm/mach-pxa/sharpsl_pm.c:   suspend_set_ops(NULL);
./arch/arm/mach-at91/pm.c:  suspend_set_ops(&at91_pm_ops);
./arch/arm/mach-omap1/pm.c: suspend_set_ops(&omap_pm_ops);
./arch/arm/mach-pnx4008/pm.c:   suspend_set_ops(&pnx4008_pm_ops);
./arch/sh/kernel/cpu/shmobile/pm.c: suspend_set_ops(&sh_pm_ops);
./arch/sh/boards/mach-hp6xx/pm.c:   suspend_set_ops(&hp6x0_pm_ops);
./arch/powerpc/platforms/83xx/suspend.c:    suspend_set_ops(&mpc83xx_suspend_ops);
./arch/powerpc/platforms/52xx/mpc52xx_pm.c: suspend_set_ops(&mpc52xx_pm_ops);
./arch/powerpc/platforms/52xx/lite5200_pm.c:    suspend_set_ops(&lite5200_pm_ops);
./arch/avr32/mach-at32ap/pm.c:  suspend_set_ops(&avr32_pm_ops);
./arch/blackfin/mach-common/pm.c:   suspend_set_ops(&bfin_pm_ops);
./include/linux/suspend.h: * suspend_set_ops - set platform dependent suspend operations
./include/linux/suspend.h:extern void suspend_set_ops(struct platform_suspend_ops *ops);
./include/linux/suspend.h:static inline void suspend_set_ops(struct platform_suspend_ops *ops) {}
./drivers/macintosh/via-pmu.c:  suspend_set_ops(&pmu_pm_ops);
./drivers/acpi/sleep.c: suspend_set_ops(old_suspend_ordering ?

What we see is that almost all of the calls to suspend_set_ops are in the architecture-specific directories, except for the two drivers at the end of the output: macintosh/via-pmu.c and acpi/sleep.c. So the next step is to look at the architecture-specific code for some platform. We'll use ./arch/powerpc/platforms/83xx/suspend.c as an example. You should do this yourself for whatever platform you are using. If it is x86, then you probably need to look in ./drivers/acpi/sleep.c.

Searching for suspend_set_ops in that file we see that it is called once, in the subroutine pmc_probe which is static. So we look for calls to pmc_probe in the same file until we find one that is not static. We find a static declaration of a structure of type `of_platform_driver called `pmc_driverthat assignspmc_probeto elementprobe``.

Since the pmc_driver structure is static, we look for references to it in the same file, and fine one, in the static function pmc_init. We look for calls to pmc_init and find one, as a parameter to the module_init call.

We search for module_init from the top of the kernel tree, limiting our search slightly:

find . -type f | xargs grep -w "module_init.*)[^;]" |more

Of the results we get, the most promissing is:

./include/linux/init.h:#define module_init(initfn)                  \

Looking in ./include/linux/init.h we see that module_init is a call to __initcall if MODULE is not defined or else

#define module_init(initfn)                                     \
    static inline initcall_t __inittest(void)               \
    { return initfn; }                                      \
    int init_module(void) __attribute__((alias(#initfn)));

At this point we are really on another subject - how drivers and modules are registered. HTH.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top