* [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time @ 2012-10-11 14:42 ` Lee Jones 0 siblings, 0 replies; 33+ messages in thread From: Lee Jones @ 2012-10-11 14:42 UTC (permalink / raw) To: linux-arm-kernel From: Jonas Aaberg <jonas.aberg@stericsson.com> The overhead is very low and the results will be found under sysfs/bootime, as well as detailed results in debugfs under boottime/. The bootgraph* files are compatible with scripts/bootgraph.pl. The reason for this patch is to provide data (sysfs/boottime) suitable for automatic testcases as well as help for developers to reduce the boot time (debugfs). Cc: Russell King <linux@arm.linux.org.uk> Cc: Will Deacon <will.deacon@arm.com> Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com> Signed-off-by: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> Signed-off-by: Lee Jones <lee.jones@linaro.org> Reviewed-by: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com> Tested-by: Mian Yousaf KAUKAB <mian.yousaf.kaukab@stericsson.com> --- arch/arm/common/Makefile | 1 + arch/arm/common/boottime.c | 46 +++++ arch/arm/include/asm/setup.h | 21 ++ include/linux/boottime.h | 89 ++++++++ init/Kconfig | 9 + init/Makefile | 1 + init/boottime.c | 467 ++++++++++++++++++++++++++++++++++++++++++ init/main.c | 6 + 8 files changed, 640 insertions(+) create mode 100644 arch/arm/common/boottime.c create mode 100644 include/linux/boottime.h create mode 100644 init/boottime.c diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile index e8a4e58..8522356 100644 --- a/arch/arm/common/Makefile +++ b/arch/arm/common/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o obj-$(CONFIG_SHARP_SCOOP) += scoop.o obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o +obj-$(CONFIG_BOOTTIME) += boottime.o diff --git a/arch/arm/common/boottime.c b/arch/arm/common/boottime.c new file mode 100644 index 0000000..73e9e04 --- /dev/null +++ b/arch/arm/common/boottime.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) ST-Ericsson SA 2009-2010 + * + * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson + * + * License terms: GNU General Public License (GPL) version 2 + * + * Store boot times measured during for example u-boot startup. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/boottime.h> +#include <linux/string.h> +#include <asm/setup.h> + +static u32 bootloader_idle; +static u32 bootloader_total; + +static int __init boottime_parse_tag(const struct tag *tag) +{ + int i; + char buff[BOOTTIME_MAX_NAME_LEN]; + + bootloader_idle = tag->u.boottime.idle; + bootloader_total = tag->u.boottime.total; + + for (i = 0; i < tag->u.boottime.num; i++) { + snprintf(buff, BOOTTIME_MAX_NAME_LEN, "%s+0x0/0x0", + tag->u.boottime.entry[i].name); + buff[BOOTTIME_MAX_NAME_LEN - 1] = '\0'; + boottime_mark_wtime(buff, tag->u.boottime.entry[i].time); + } + + return 0; +} + +__tagtable(ATAG_BOOTTIME, boottime_parse_tag); + +int boottime_bootloader_idle(void) +{ + if (bootloader_total == 0) + return 0; + + return (int) ((bootloader_idle) / (bootloader_total / 100)); +} diff --git a/arch/arm/include/asm/setup.h b/arch/arm/include/asm/setup.h index 24d284a..e8da062 100644 --- a/arch/arm/include/asm/setup.h +++ b/arch/arm/include/asm/setup.h @@ -143,6 +143,23 @@ struct tag_memclk { __u32 fmemclk; }; +/* for automatic boot timing testcases */ +#define ATAG_BOOTTIME 0x41000403 +#define BOOTTIME_MAX_NAME_LEN 64 +#define BOOTTIME_MAX 10 + +struct boottime_entry { + u32 time; /* in us */ + u8 name[BOOTTIME_MAX_NAME_LEN]; +}; + +struct tag_boottime { + struct boottime_entry entry[BOOTTIME_MAX]; + u32 idle; /* in us */ + u32 total; /* in us */ + u8 num; +}; + struct tag { struct tag_header hdr; union { @@ -165,6 +182,10 @@ struct tag { * DC21285 specific */ struct tag_memclk memclk; + /* + * Boot time + */ + struct tag_boottime boottime; } u; }; diff --git a/include/linux/boottime.h b/include/linux/boottime.h new file mode 100644 index 0000000..9836c5b --- /dev/null +++ b/include/linux/boottime.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) ST-Ericsson SA 2009-2010 + * + * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson + * + * License terms: GNU General Public License (GPL) version 2 + * + * boottime is a tool for collecting start-up timing + * information and can together with boot loader support + * display a total system start-up time. + * + */ + +#ifndef LINUX_BOOTTIME_H +#define LINUX_BOOTTIME_H + +#ifdef CONFIG_BOOTTIME +#include <linux/kernel.h> + +/** + * struct boottime_timer - Callbacks for generic timer. + * @init: Function to call at boottime initialization + * @get_time: Returns the number of us since start-up + * Preferable this is based upon a free running timer. + * This is the only required entry. + * @finalize: Called before init is executed and boottime is done. + */ +struct boottime_timer { + int (*init)(void); + unsigned long (*get_time)(void); + void (*finalize)(void); +}; + +/** + * boottime_mark_wtime() + * Add a sample point with a given time. Useful for adding data collected + * by for example a boot loader. + * @name: The name of the sample point + * @time: The time in us when this point was reached + */ +void __init boottime_mark_wtime(char *name, unsigned long time); + +/** + * boottime_mark() + * Add a sample point with the current time. + * @name: The name of this sample point + */ +void __init boottime_mark(char *name); + +/** + * boottime_mark_symbolic() + * Add a sample point where the name is a symbolic function + * and %pF is needed to get the correct function name. + * @name: function name. + */ +void __init boottime_mark_symbolic(void *name); + +/** + * boottime_activate() + * Activates boottime and register callbacks. + * @bt: struct with callbacks. + */ +void __ref boottime_activate(struct boottime_timer *bt); + +/** + * boottime_deactivate() + * This function is called when the kernel boot is done. + * (before "free init memory" is called) + */ +void __init boottime_deactivate(void); + +/** + * boottime_system_up() + * A function is called when the basics of the kernel + * is up and running. + */ +void __init boottime_system_up(void); + +#else + +#define boottime_mark_wtime(name, time) +#define boottime_mark(name) +#define boottime_mark_symbolic(name) +#define boottime_activate(bt) +#define boottime_deactivate() +#define boottime_system_up() +#endif + +#endif /* LINUX_BOOTTIME_H */ diff --git a/init/Kconfig b/init/Kconfig index af6c7f8..a85601f 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1487,6 +1487,15 @@ config PROFILING Say Y here to enable the extended profiling support mechanisms used by profilers such as OProfile. +config BOOTTIME + bool "Boot time measurments" + default n + help + Adds sysfs entries (boottime/) with start-up timing information. + If CONFIG_DEBUG_FS is enabled, detailed information about the + boot time, including system load during boot can be extraced. + This information can be visualised with help of the bootgraph script. + # # Place an empty function call at each tracepoint site. Can be # dynamically changed for a probe function. diff --git a/init/Makefile b/init/Makefile index 7bc47ee..356d529 100644 --- a/init/Makefile +++ b/init/Makefile @@ -9,6 +9,7 @@ else obj-$(CONFIG_BLK_DEV_INITRD) += initramfs.o endif obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o +obj-$(CONFIG_BOOTTIME) += boottime.o ifneq ($(CONFIG_ARCH_INIT_TASK),y) obj-y += init_task.o diff --git a/init/boottime.c b/init/boottime.c new file mode 100644 index 0000000..be73e0e --- /dev/null +++ b/init/boottime.c @@ -0,0 +1,467 @@ +/* + * Copyright (C) ST-Ericsson SA 2009-2010 + * + * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson + * + * License terms: GNU General Public License (GPL) version 2 + * + * boottime is a tool for collecting start-up timing + * information and can together with boot loader support + * display a total system start-up time. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/list.h> +#include <linux/seq_file.h> +#include <linux/debugfs.h> +#include <linux/spinlock.h> +#include <linux/boottime.h> +#include <linux/kernel_stat.h> +#include <linux/kobject.h> +#include <linux/device.h> +#include <linux/sysfs.h> +#include <linux/slab.h> + +/* + * BOOTTIME_MAX_NAME_LEN is defined in arch/arm/include/asm/setup.h to 64. + * No crisis if they don't match. + */ +#ifndef BOOTTIME_MAX_NAME_LEN +#define BOOTTIME_MAX_NAME_LEN 64 +#endif + +/* + * We have a few static entries, since it is good to have measure points + * before the system is up and running properly + */ +#define NUM_STATIC_BOOTTIME_ENTRIES 16 + +struct boottime_list { + struct list_head list; + char name[BOOTTIME_MAX_NAME_LEN]; + /* Time in us since power on, possible including boot loader. */ + unsigned long time; + bool cpu_load; + struct cpu_usage_stat cpu_usage[NR_CPUS]; +}; + +enum boottime_filter_type { + BOOTTIME_FILTER_OUT_ZERO, + BOOTTIME_FILTER_OUT_LESS_100, + BOOTTIME_FILTER_NOTHING, +}; + +enum boottime_symbolic_print { + BOOTTIME_SYMBOLIC_PRINT, + BOOTTIME_NORMAL_PRINT, +}; + +enum boottime_cpu_load { + BOOTTIME_CPU_LOAD, + BOOTTIME_NO_CPU_LOAD, +}; + +static LIST_HEAD(boottime_list); +static __initdata DEFINE_SPINLOCK(boottime_list_lock); +static __initdata struct boottime_timer boottime_timer; +static __initdata int num_const_boottime_list; +static struct boottime_list const_boottime_list[NUM_STATIC_BOOTTIME_ENTRIES]; +static unsigned long time_kernel_done; +static unsigned long time_bootloader_done; +static __initdata bool system_up; +static bool boottime_done; + +int __attribute__((weak)) boottime_arch_startup(void) +{ + return 0; +} + +int __attribute__((weak)) boottime_bootloader_idle(void) +{ + return 0; +} + +static void __init boottime_mark_core(char *name, + unsigned long time, + enum boottime_symbolic_print symbolic, + enum boottime_cpu_load cpu_load) +{ + struct boottime_list *b; + unsigned long flags = 0; + int i; + + if (system_up) { + b = kmalloc(sizeof(struct boottime_list), GFP_KERNEL); + if (!b) { + printk(KERN_ERR + "boottime: failed to allocate memory!\n"); + return; + } + + } else { + if (num_const_boottime_list < NUM_STATIC_BOOTTIME_ENTRIES) { + b = &const_boottime_list[num_const_boottime_list]; + num_const_boottime_list++; + } else { + printk(KERN_ERR + "boottime: too many early measure points!\n"); + return; + } + } + + INIT_LIST_HEAD(&b->list); + + if (symbolic == BOOTTIME_SYMBOLIC_PRINT) + snprintf(b->name, BOOTTIME_MAX_NAME_LEN, "%pF", name); + else + strncpy(b->name, name, BOOTTIME_MAX_NAME_LEN); + + b->name[BOOTTIME_MAX_NAME_LEN - 1] = '\0'; + b->time = time; + b->cpu_load = cpu_load; + + if (cpu_load == BOOTTIME_CPU_LOAD && system_up) + for_each_possible_cpu(i) { + b->cpu_usage[i].system = kstat_cpu(i).cpustat.system; + b->cpu_usage[i].idle = kstat_cpu(i).cpustat.idle; + b->cpu_usage[i].iowait = kstat_cpu(i).cpustat.iowait; + b->cpu_usage[i].irq = kstat_cpu(i).cpustat.irq; + /* + * TODO: Make sure that user, nice, softirq, steal + * and guest are not used during boot + */ + } + else + b->cpu_load = BOOTTIME_NO_CPU_LOAD; + + if (system_up) { + spin_lock_irqsave(&boottime_list_lock, flags); + list_add(&b->list, &boottime_list); + spin_unlock_irqrestore(&boottime_list_lock, flags); + } else { + list_add(&b->list, &boottime_list); + } +} + +void __init boottime_mark_wtime(char *name, unsigned long time) +{ + boottime_mark_core(name, time, + BOOTTIME_NORMAL_PRINT, + BOOTTIME_NO_CPU_LOAD); +} + +void __ref boottime_mark_symbolic(void *name) +{ + + if (boottime_done) + return; + + if (boottime_timer.get_time) + boottime_mark_core((char *) name, + boottime_timer.get_time(), + BOOTTIME_SYMBOLIC_PRINT, + BOOTTIME_CPU_LOAD); +} + +void __init boottime_mark(char *name) +{ + if (boottime_timer.get_time) + boottime_mark_core(name, + boottime_timer.get_time(), + BOOTTIME_NORMAL_PRINT, + BOOTTIME_CPU_LOAD); +} + +void __init boottime_activate(struct boottime_timer *bt) +{ + struct boottime_list *b; + int res = 0; + unsigned long flags; + + if (bt == NULL) { + printk(KERN_ERR + "boottime: error: bad configured\n"); + return; + } + + if (bt->get_time == NULL) { + printk(KERN_ERR + "boottime: error: you must provide a get_time() function\n"); + return; + } + memcpy(&boottime_timer, bt, sizeof(struct boottime_timer)); + + if (boottime_timer.init) + res = boottime_timer.init(); + + if (res) { + printk(KERN_ERR "boottime: initialization failed\n"); + return; + } + + if (boottime_arch_startup()) + printk(KERN_ERR + "boottime: arch specfic initialization failed\n"); + + spin_lock_irqsave(&boottime_list_lock, flags); + + if (!list_empty(&boottime_list)) { + + b = list_first_entry(&boottime_list, struct boottime_list, + list); + if (b) + time_bootloader_done = b->time; + } + + spin_unlock_irqrestore(&boottime_list_lock, flags); +} + +void __init boottime_system_up(void) +{ + system_up = true; +} + +void __init boottime_deactivate(void) +{ + struct boottime_list *b; + unsigned long flags; + + boottime_mark("execute_init+0x0/0x0"); + + boottime_done = true; + + spin_lock_irqsave(&boottime_list_lock, flags); + b = list_first_entry(&boottime_list, struct boottime_list, list); + spin_unlock_irqrestore(&boottime_list_lock, flags); + + time_kernel_done = b->time; + + if (boottime_timer.finalize) + boottime_timer.finalize(); +} + +#ifdef CONFIG_DEBUG_FS +static void boottime_debugfs_load(struct seq_file *s, + struct boottime_list *b, + struct boottime_list *p) +{ + int i; + unsigned long total_p, total_b; + unsigned long system_total, idle_total, irq_total, iowait_total; + unsigned long system_load, idle_load, irq_load, iowait_load; + + for_each_possible_cpu(i) { + total_b = (b->cpu_usage[i].system + + b->cpu_usage[i].idle + + b->cpu_usage[i].iowait + + b->cpu_usage[i].irq); + + total_p = (p->cpu_usage[i].system + + p->cpu_usage[i].idle + + p->cpu_usage[i].iowait + + p->cpu_usage[i].irq); + + if (total_b == total_p) + continue; + + system_total = b->cpu_usage[i].system - p->cpu_usage[i].system; + idle_total = b->cpu_usage[i].idle - p->cpu_usage[i].idle; + irq_total = b->cpu_usage[i].irq - p->cpu_usage[i].irq; + iowait_total = b->cpu_usage[i].iowait - p->cpu_usage[i].iowait; + + system_load = (100 * system_total / (total_b - total_p)); + idle_load = (100 * idle_total / (total_b - total_p)); + irq_load = (100 * irq_total / (total_b - total_p)); + iowait_load = (100 * iowait_total / (total_b - total_p)); + + seq_printf(s, + " cpu%d system: %lu%% idle: %lu%% iowait: %lu%% irq: %lu%%", + i, + system_load, + idle_load, + iowait_load, + irq_load); + } + seq_printf(s, "\n"); +} + +static void boottime_debugfs_print(struct seq_file *s, + struct boottime_list *b, + struct boottime_list *p) +{ + seq_printf(s, "[%5lu.%06lu] calling %s\n", + p->time / 1000000, + (p->time % 1000000), + p->name); + seq_printf(s, "[%5lu.%06lu] initcall %s returned 0 after %ld msecs.", + b->time / 1000000, + (b->time % 1000000), + p->name, (b->time - p->time) / 1000); + + if (p->cpu_load == BOOTTIME_NO_CPU_LOAD || + b->cpu_load == BOOTTIME_NO_CPU_LOAD) { + seq_printf(s, "\n"); + return; + } + + boottime_debugfs_load(s, b, p); +} + +static int boottime_debugfs_bootgraph_show(struct seq_file *s, void *iter) +{ + struct boottime_list *b, *p = NULL, *old_p = NULL; + enum boottime_filter_type filter = (int)s->private; + + list_for_each_entry_reverse(b, &boottime_list, list) { + if (p) { + if (!(filter == BOOTTIME_FILTER_OUT_ZERO && + (b->time - p->time) / 1000 == 0) + && !(filter == BOOTTIME_FILTER_OUT_LESS_100 && + (b->time - p->time) < 100 * 1000)) + boottime_debugfs_print(s, b, p); + old_p = p; + } + p = b; + } + + if (filter == BOOTTIME_FILTER_NOTHING && p) + boottime_debugfs_print(s, p, p); + + if (p) + seq_printf(s, "[%5lu.%06lu] Freeing init memory: 0K\n", + p->time / 1000000, p->time % 1000000); + return 0; +} + +static int boottime_debugfs_summary_show(struct seq_file *s, void *data) +{ + struct boottime_list *b, b_zero; + + if (time_bootloader_done) + seq_printf(s, "bootloader: %ld msecs\n", + time_bootloader_done / 1000); + + seq_printf(s, "kernel: %ld msecs\ntotal: %ld msecs\n", + (time_kernel_done - time_bootloader_done) / 1000, + time_kernel_done / 1000); + seq_printf(s, "kernel:"); + b = list_first_entry(&boottime_list, + struct boottime_list, list); + memset(&b_zero, 0, sizeof(struct boottime_list)); + boottime_debugfs_load(s, b, &b_zero); + + if (time_bootloader_done) + seq_printf(s, + "bootloader: cpu0 system: %d%% idle: %d%% iowait: 0%% irq: 0%%\n", + 100 - boottime_bootloader_idle(), + boottime_bootloader_idle()); + return 0; +} + +static int boottime_debugfs_bootgraph_open(struct inode *inode, + struct file *file) +{ + return single_open(file, + boottime_debugfs_bootgraph_show, + inode->i_private); +} + +static int boottime_debugfs_summary_open(struct inode *inode, + struct file *file) +{ + return single_open(file, + boottime_debugfs_summary_show, + inode->i_private); +} + +static const struct file_operations boottime_debugfs_bootgraph_operations = { + .open = boottime_debugfs_bootgraph_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct file_operations boottime_debugfs_summary_operations = { + .open = boottime_debugfs_summary_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +void boottime_debugfs_init(void) +{ + struct dentry *dir; + + dir = debugfs_create_dir("boottime", NULL); + + (void) debugfs_create_file("bootgraph", S_IFREG | S_IRUGO, + dir, (void *)BOOTTIME_FILTER_NOTHING, + &boottime_debugfs_bootgraph_operations); + (void) debugfs_create_file("bootgraph_all_except0", S_IFREG | S_IRUGO, + dir, (void *)BOOTTIME_FILTER_OUT_ZERO, + &boottime_debugfs_bootgraph_operations); + (void) debugfs_create_file("bootgraph_larger100", + S_IFREG | S_IRUGO, + dir, (void *)BOOTTIME_FILTER_OUT_LESS_100, + &boottime_debugfs_bootgraph_operations); + (void) debugfs_create_file("summary", S_IFREG | S_IRUGO, + dir, NULL, + &boottime_debugfs_summary_operations); +} +#else +#define boottime_debugfs_init(x) +#endif + +static ssize_t show_bootloader(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%ld\n", time_bootloader_done / 1000); +} + +static ssize_t show_kernel(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%ld\n", + (time_kernel_done - time_bootloader_done) / 1000); +} + +DEVICE_ATTR(kernel, 0444, show_kernel, NULL); +DEVICE_ATTR(bootloader, 0444, show_bootloader, NULL); + +static struct attribute *boottime_sysfs_entries[] = { + &dev_attr_kernel.attr, + &dev_attr_bootloader.attr, + NULL +}; + +static struct attribute_group boottime_attr_grp = { + .name = NULL, + .attrs = boottime_sysfs_entries, +}; + +static int __init boottime_init(void) +{ + struct kobject *boottime_kobj; + + boottime_kobj = kobject_create_and_add("boottime", NULL); + if (!boottime_kobj) { + printk(KERN_ERR "boottime: out of memory!\n"); + return -ENOMEM; + } + + if (sysfs_create_group(boottime_kobj, &boottime_attr_grp) < 0) { + kobject_put(boottime_kobj); + printk(KERN_ERR "boottime: Failed creating sysfs group\n"); + return -ENOMEM; + } + + boottime_debugfs_init(); + + return 0; +} + +late_initcall(boottime_init); diff --git a/init/main.c b/init/main.c index b286730..c7f94b6 100644 --- a/init/main.c +++ b/init/main.c @@ -69,6 +69,7 @@ #include <linux/slab.h> #include <linux/perf_event.h> #include <linux/file.h> +#include <linux/boottime.h> #include <asm/io.h> #include <asm/bugs.h> @@ -676,6 +677,8 @@ int __init_or_module do_one_initcall(initcall_t fn) int count = preempt_count(); int ret; + boottime_mark_symbolic(fn); + if (initcall_debug) ret = do_one_initcall_debug(fn); else @@ -801,6 +804,7 @@ static noinline int init_post(void) { /* need to finish all async __init code before freeing the memory */ async_synchronize_full(); + boottime_deactivate(); free_initmem(); mark_rodata_ro(); system_state = SYSTEM_RUNNING; @@ -860,6 +864,7 @@ static int __init kernel_init(void * unused) do_pre_smp_initcalls(); lockup_detector_init(); + boottime_system_up(); smp_init(); sched_init_smp(); @@ -882,6 +887,7 @@ static int __init kernel_init(void * unused) if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { ramdisk_execute_command = NULL; + boottime_mark("mount+0x0/0x0"); prepare_namespace(); } -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time @ 2012-10-11 14:42 ` Lee Jones 0 siblings, 0 replies; 33+ messages in thread From: Lee Jones @ 2012-10-11 14:42 UTC (permalink / raw) To: linux-arm-kernel, linux-kernel Cc: arnd, linus.walleij, Jonas Aaberg, Russell King, Will Deacon, Mian Yousaf Kaukab, Lee Jones From: Jonas Aaberg <jonas.aberg@stericsson.com> The overhead is very low and the results will be found under sysfs/bootime, as well as detailed results in debugfs under boottime/. The bootgraph* files are compatible with scripts/bootgraph.pl. The reason for this patch is to provide data (sysfs/boottime) suitable for automatic testcases as well as help for developers to reduce the boot time (debugfs). Cc: Russell King <linux@arm.linux.org.uk> Cc: Will Deacon <will.deacon@arm.com> Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com> Signed-off-by: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> Signed-off-by: Lee Jones <lee.jones@linaro.org> Reviewed-by: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com> Tested-by: Mian Yousaf KAUKAB <mian.yousaf.kaukab@stericsson.com> --- arch/arm/common/Makefile | 1 + arch/arm/common/boottime.c | 46 +++++ arch/arm/include/asm/setup.h | 21 ++ include/linux/boottime.h | 89 ++++++++ init/Kconfig | 9 + init/Makefile | 1 + init/boottime.c | 467 ++++++++++++++++++++++++++++++++++++++++++ init/main.c | 6 + 8 files changed, 640 insertions(+) create mode 100644 arch/arm/common/boottime.c create mode 100644 include/linux/boottime.h create mode 100644 init/boottime.c diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile index e8a4e58..8522356 100644 --- a/arch/arm/common/Makefile +++ b/arch/arm/common/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o obj-$(CONFIG_SHARP_SCOOP) += scoop.o obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o +obj-$(CONFIG_BOOTTIME) += boottime.o diff --git a/arch/arm/common/boottime.c b/arch/arm/common/boottime.c new file mode 100644 index 0000000..73e9e04 --- /dev/null +++ b/arch/arm/common/boottime.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) ST-Ericsson SA 2009-2010 + * + * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson + * + * License terms: GNU General Public License (GPL) version 2 + * + * Store boot times measured during for example u-boot startup. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/boottime.h> +#include <linux/string.h> +#include <asm/setup.h> + +static u32 bootloader_idle; +static u32 bootloader_total; + +static int __init boottime_parse_tag(const struct tag *tag) +{ + int i; + char buff[BOOTTIME_MAX_NAME_LEN]; + + bootloader_idle = tag->u.boottime.idle; + bootloader_total = tag->u.boottime.total; + + for (i = 0; i < tag->u.boottime.num; i++) { + snprintf(buff, BOOTTIME_MAX_NAME_LEN, "%s+0x0/0x0", + tag->u.boottime.entry[i].name); + buff[BOOTTIME_MAX_NAME_LEN - 1] = '\0'; + boottime_mark_wtime(buff, tag->u.boottime.entry[i].time); + } + + return 0; +} + +__tagtable(ATAG_BOOTTIME, boottime_parse_tag); + +int boottime_bootloader_idle(void) +{ + if (bootloader_total == 0) + return 0; + + return (int) ((bootloader_idle) / (bootloader_total / 100)); +} diff --git a/arch/arm/include/asm/setup.h b/arch/arm/include/asm/setup.h index 24d284a..e8da062 100644 --- a/arch/arm/include/asm/setup.h +++ b/arch/arm/include/asm/setup.h @@ -143,6 +143,23 @@ struct tag_memclk { __u32 fmemclk; }; +/* for automatic boot timing testcases */ +#define ATAG_BOOTTIME 0x41000403 +#define BOOTTIME_MAX_NAME_LEN 64 +#define BOOTTIME_MAX 10 + +struct boottime_entry { + u32 time; /* in us */ + u8 name[BOOTTIME_MAX_NAME_LEN]; +}; + +struct tag_boottime { + struct boottime_entry entry[BOOTTIME_MAX]; + u32 idle; /* in us */ + u32 total; /* in us */ + u8 num; +}; + struct tag { struct tag_header hdr; union { @@ -165,6 +182,10 @@ struct tag { * DC21285 specific */ struct tag_memclk memclk; + /* + * Boot time + */ + struct tag_boottime boottime; } u; }; diff --git a/include/linux/boottime.h b/include/linux/boottime.h new file mode 100644 index 0000000..9836c5b --- /dev/null +++ b/include/linux/boottime.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) ST-Ericsson SA 2009-2010 + * + * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson + * + * License terms: GNU General Public License (GPL) version 2 + * + * boottime is a tool for collecting start-up timing + * information and can together with boot loader support + * display a total system start-up time. + * + */ + +#ifndef LINUX_BOOTTIME_H +#define LINUX_BOOTTIME_H + +#ifdef CONFIG_BOOTTIME +#include <linux/kernel.h> + +/** + * struct boottime_timer - Callbacks for generic timer. + * @init: Function to call at boottime initialization + * @get_time: Returns the number of us since start-up + * Preferable this is based upon a free running timer. + * This is the only required entry. + * @finalize: Called before init is executed and boottime is done. + */ +struct boottime_timer { + int (*init)(void); + unsigned long (*get_time)(void); + void (*finalize)(void); +}; + +/** + * boottime_mark_wtime() + * Add a sample point with a given time. Useful for adding data collected + * by for example a boot loader. + * @name: The name of the sample point + * @time: The time in us when this point was reached + */ +void __init boottime_mark_wtime(char *name, unsigned long time); + +/** + * boottime_mark() + * Add a sample point with the current time. + * @name: The name of this sample point + */ +void __init boottime_mark(char *name); + +/** + * boottime_mark_symbolic() + * Add a sample point where the name is a symbolic function + * and %pF is needed to get the correct function name. + * @name: function name. + */ +void __init boottime_mark_symbolic(void *name); + +/** + * boottime_activate() + * Activates boottime and register callbacks. + * @bt: struct with callbacks. + */ +void __ref boottime_activate(struct boottime_timer *bt); + +/** + * boottime_deactivate() + * This function is called when the kernel boot is done. + * (before "free init memory" is called) + */ +void __init boottime_deactivate(void); + +/** + * boottime_system_up() + * A function is called when the basics of the kernel + * is up and running. + */ +void __init boottime_system_up(void); + +#else + +#define boottime_mark_wtime(name, time) +#define boottime_mark(name) +#define boottime_mark_symbolic(name) +#define boottime_activate(bt) +#define boottime_deactivate() +#define boottime_system_up() +#endif + +#endif /* LINUX_BOOTTIME_H */ diff --git a/init/Kconfig b/init/Kconfig index af6c7f8..a85601f 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1487,6 +1487,15 @@ config PROFILING Say Y here to enable the extended profiling support mechanisms used by profilers such as OProfile. +config BOOTTIME + bool "Boot time measurments" + default n + help + Adds sysfs entries (boottime/) with start-up timing information. + If CONFIG_DEBUG_FS is enabled, detailed information about the + boot time, including system load during boot can be extraced. + This information can be visualised with help of the bootgraph script. + # # Place an empty function call at each tracepoint site. Can be # dynamically changed for a probe function. diff --git a/init/Makefile b/init/Makefile index 7bc47ee..356d529 100644 --- a/init/Makefile +++ b/init/Makefile @@ -9,6 +9,7 @@ else obj-$(CONFIG_BLK_DEV_INITRD) += initramfs.o endif obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o +obj-$(CONFIG_BOOTTIME) += boottime.o ifneq ($(CONFIG_ARCH_INIT_TASK),y) obj-y += init_task.o diff --git a/init/boottime.c b/init/boottime.c new file mode 100644 index 0000000..be73e0e --- /dev/null +++ b/init/boottime.c @@ -0,0 +1,467 @@ +/* + * Copyright (C) ST-Ericsson SA 2009-2010 + * + * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson + * + * License terms: GNU General Public License (GPL) version 2 + * + * boottime is a tool for collecting start-up timing + * information and can together with boot loader support + * display a total system start-up time. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/list.h> +#include <linux/seq_file.h> +#include <linux/debugfs.h> +#include <linux/spinlock.h> +#include <linux/boottime.h> +#include <linux/kernel_stat.h> +#include <linux/kobject.h> +#include <linux/device.h> +#include <linux/sysfs.h> +#include <linux/slab.h> + +/* + * BOOTTIME_MAX_NAME_LEN is defined in arch/arm/include/asm/setup.h to 64. + * No crisis if they don't match. + */ +#ifndef BOOTTIME_MAX_NAME_LEN +#define BOOTTIME_MAX_NAME_LEN 64 +#endif + +/* + * We have a few static entries, since it is good to have measure points + * before the system is up and running properly + */ +#define NUM_STATIC_BOOTTIME_ENTRIES 16 + +struct boottime_list { + struct list_head list; + char name[BOOTTIME_MAX_NAME_LEN]; + /* Time in us since power on, possible including boot loader. */ + unsigned long time; + bool cpu_load; + struct cpu_usage_stat cpu_usage[NR_CPUS]; +}; + +enum boottime_filter_type { + BOOTTIME_FILTER_OUT_ZERO, + BOOTTIME_FILTER_OUT_LESS_100, + BOOTTIME_FILTER_NOTHING, +}; + +enum boottime_symbolic_print { + BOOTTIME_SYMBOLIC_PRINT, + BOOTTIME_NORMAL_PRINT, +}; + +enum boottime_cpu_load { + BOOTTIME_CPU_LOAD, + BOOTTIME_NO_CPU_LOAD, +}; + +static LIST_HEAD(boottime_list); +static __initdata DEFINE_SPINLOCK(boottime_list_lock); +static __initdata struct boottime_timer boottime_timer; +static __initdata int num_const_boottime_list; +static struct boottime_list const_boottime_list[NUM_STATIC_BOOTTIME_ENTRIES]; +static unsigned long time_kernel_done; +static unsigned long time_bootloader_done; +static __initdata bool system_up; +static bool boottime_done; + +int __attribute__((weak)) boottime_arch_startup(void) +{ + return 0; +} + +int __attribute__((weak)) boottime_bootloader_idle(void) +{ + return 0; +} + +static void __init boottime_mark_core(char *name, + unsigned long time, + enum boottime_symbolic_print symbolic, + enum boottime_cpu_load cpu_load) +{ + struct boottime_list *b; + unsigned long flags = 0; + int i; + + if (system_up) { + b = kmalloc(sizeof(struct boottime_list), GFP_KERNEL); + if (!b) { + printk(KERN_ERR + "boottime: failed to allocate memory!\n"); + return; + } + + } else { + if (num_const_boottime_list < NUM_STATIC_BOOTTIME_ENTRIES) { + b = &const_boottime_list[num_const_boottime_list]; + num_const_boottime_list++; + } else { + printk(KERN_ERR + "boottime: too many early measure points!\n"); + return; + } + } + + INIT_LIST_HEAD(&b->list); + + if (symbolic == BOOTTIME_SYMBOLIC_PRINT) + snprintf(b->name, BOOTTIME_MAX_NAME_LEN, "%pF", name); + else + strncpy(b->name, name, BOOTTIME_MAX_NAME_LEN); + + b->name[BOOTTIME_MAX_NAME_LEN - 1] = '\0'; + b->time = time; + b->cpu_load = cpu_load; + + if (cpu_load == BOOTTIME_CPU_LOAD && system_up) + for_each_possible_cpu(i) { + b->cpu_usage[i].system = kstat_cpu(i).cpustat.system; + b->cpu_usage[i].idle = kstat_cpu(i).cpustat.idle; + b->cpu_usage[i].iowait = kstat_cpu(i).cpustat.iowait; + b->cpu_usage[i].irq = kstat_cpu(i).cpustat.irq; + /* + * TODO: Make sure that user, nice, softirq, steal + * and guest are not used during boot + */ + } + else + b->cpu_load = BOOTTIME_NO_CPU_LOAD; + + if (system_up) { + spin_lock_irqsave(&boottime_list_lock, flags); + list_add(&b->list, &boottime_list); + spin_unlock_irqrestore(&boottime_list_lock, flags); + } else { + list_add(&b->list, &boottime_list); + } +} + +void __init boottime_mark_wtime(char *name, unsigned long time) +{ + boottime_mark_core(name, time, + BOOTTIME_NORMAL_PRINT, + BOOTTIME_NO_CPU_LOAD); +} + +void __ref boottime_mark_symbolic(void *name) +{ + + if (boottime_done) + return; + + if (boottime_timer.get_time) + boottime_mark_core((char *) name, + boottime_timer.get_time(), + BOOTTIME_SYMBOLIC_PRINT, + BOOTTIME_CPU_LOAD); +} + +void __init boottime_mark(char *name) +{ + if (boottime_timer.get_time) + boottime_mark_core(name, + boottime_timer.get_time(), + BOOTTIME_NORMAL_PRINT, + BOOTTIME_CPU_LOAD); +} + +void __init boottime_activate(struct boottime_timer *bt) +{ + struct boottime_list *b; + int res = 0; + unsigned long flags; + + if (bt == NULL) { + printk(KERN_ERR + "boottime: error: bad configured\n"); + return; + } + + if (bt->get_time == NULL) { + printk(KERN_ERR + "boottime: error: you must provide a get_time() function\n"); + return; + } + memcpy(&boottime_timer, bt, sizeof(struct boottime_timer)); + + if (boottime_timer.init) + res = boottime_timer.init(); + + if (res) { + printk(KERN_ERR "boottime: initialization failed\n"); + return; + } + + if (boottime_arch_startup()) + printk(KERN_ERR + "boottime: arch specfic initialization failed\n"); + + spin_lock_irqsave(&boottime_list_lock, flags); + + if (!list_empty(&boottime_list)) { + + b = list_first_entry(&boottime_list, struct boottime_list, + list); + if (b) + time_bootloader_done = b->time; + } + + spin_unlock_irqrestore(&boottime_list_lock, flags); +} + +void __init boottime_system_up(void) +{ + system_up = true; +} + +void __init boottime_deactivate(void) +{ + struct boottime_list *b; + unsigned long flags; + + boottime_mark("execute_init+0x0/0x0"); + + boottime_done = true; + + spin_lock_irqsave(&boottime_list_lock, flags); + b = list_first_entry(&boottime_list, struct boottime_list, list); + spin_unlock_irqrestore(&boottime_list_lock, flags); + + time_kernel_done = b->time; + + if (boottime_timer.finalize) + boottime_timer.finalize(); +} + +#ifdef CONFIG_DEBUG_FS +static void boottime_debugfs_load(struct seq_file *s, + struct boottime_list *b, + struct boottime_list *p) +{ + int i; + unsigned long total_p, total_b; + unsigned long system_total, idle_total, irq_total, iowait_total; + unsigned long system_load, idle_load, irq_load, iowait_load; + + for_each_possible_cpu(i) { + total_b = (b->cpu_usage[i].system + + b->cpu_usage[i].idle + + b->cpu_usage[i].iowait + + b->cpu_usage[i].irq); + + total_p = (p->cpu_usage[i].system + + p->cpu_usage[i].idle + + p->cpu_usage[i].iowait + + p->cpu_usage[i].irq); + + if (total_b == total_p) + continue; + + system_total = b->cpu_usage[i].system - p->cpu_usage[i].system; + idle_total = b->cpu_usage[i].idle - p->cpu_usage[i].idle; + irq_total = b->cpu_usage[i].irq - p->cpu_usage[i].irq; + iowait_total = b->cpu_usage[i].iowait - p->cpu_usage[i].iowait; + + system_load = (100 * system_total / (total_b - total_p)); + idle_load = (100 * idle_total / (total_b - total_p)); + irq_load = (100 * irq_total / (total_b - total_p)); + iowait_load = (100 * iowait_total / (total_b - total_p)); + + seq_printf(s, + " cpu%d system: %lu%% idle: %lu%% iowait: %lu%% irq: %lu%%", + i, + system_load, + idle_load, + iowait_load, + irq_load); + } + seq_printf(s, "\n"); +} + +static void boottime_debugfs_print(struct seq_file *s, + struct boottime_list *b, + struct boottime_list *p) +{ + seq_printf(s, "[%5lu.%06lu] calling %s\n", + p->time / 1000000, + (p->time % 1000000), + p->name); + seq_printf(s, "[%5lu.%06lu] initcall %s returned 0 after %ld msecs.", + b->time / 1000000, + (b->time % 1000000), + p->name, (b->time - p->time) / 1000); + + if (p->cpu_load == BOOTTIME_NO_CPU_LOAD || + b->cpu_load == BOOTTIME_NO_CPU_LOAD) { + seq_printf(s, "\n"); + return; + } + + boottime_debugfs_load(s, b, p); +} + +static int boottime_debugfs_bootgraph_show(struct seq_file *s, void *iter) +{ + struct boottime_list *b, *p = NULL, *old_p = NULL; + enum boottime_filter_type filter = (int)s->private; + + list_for_each_entry_reverse(b, &boottime_list, list) { + if (p) { + if (!(filter == BOOTTIME_FILTER_OUT_ZERO && + (b->time - p->time) / 1000 == 0) + && !(filter == BOOTTIME_FILTER_OUT_LESS_100 && + (b->time - p->time) < 100 * 1000)) + boottime_debugfs_print(s, b, p); + old_p = p; + } + p = b; + } + + if (filter == BOOTTIME_FILTER_NOTHING && p) + boottime_debugfs_print(s, p, p); + + if (p) + seq_printf(s, "[%5lu.%06lu] Freeing init memory: 0K\n", + p->time / 1000000, p->time % 1000000); + return 0; +} + +static int boottime_debugfs_summary_show(struct seq_file *s, void *data) +{ + struct boottime_list *b, b_zero; + + if (time_bootloader_done) + seq_printf(s, "bootloader: %ld msecs\n", + time_bootloader_done / 1000); + + seq_printf(s, "kernel: %ld msecs\ntotal: %ld msecs\n", + (time_kernel_done - time_bootloader_done) / 1000, + time_kernel_done / 1000); + seq_printf(s, "kernel:"); + b = list_first_entry(&boottime_list, + struct boottime_list, list); + memset(&b_zero, 0, sizeof(struct boottime_list)); + boottime_debugfs_load(s, b, &b_zero); + + if (time_bootloader_done) + seq_printf(s, + "bootloader: cpu0 system: %d%% idle: %d%% iowait: 0%% irq: 0%%\n", + 100 - boottime_bootloader_idle(), + boottime_bootloader_idle()); + return 0; +} + +static int boottime_debugfs_bootgraph_open(struct inode *inode, + struct file *file) +{ + return single_open(file, + boottime_debugfs_bootgraph_show, + inode->i_private); +} + +static int boottime_debugfs_summary_open(struct inode *inode, + struct file *file) +{ + return single_open(file, + boottime_debugfs_summary_show, + inode->i_private); +} + +static const struct file_operations boottime_debugfs_bootgraph_operations = { + .open = boottime_debugfs_bootgraph_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct file_operations boottime_debugfs_summary_operations = { + .open = boottime_debugfs_summary_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +void boottime_debugfs_init(void) +{ + struct dentry *dir; + + dir = debugfs_create_dir("boottime", NULL); + + (void) debugfs_create_file("bootgraph", S_IFREG | S_IRUGO, + dir, (void *)BOOTTIME_FILTER_NOTHING, + &boottime_debugfs_bootgraph_operations); + (void) debugfs_create_file("bootgraph_all_except0", S_IFREG | S_IRUGO, + dir, (void *)BOOTTIME_FILTER_OUT_ZERO, + &boottime_debugfs_bootgraph_operations); + (void) debugfs_create_file("bootgraph_larger100", + S_IFREG | S_IRUGO, + dir, (void *)BOOTTIME_FILTER_OUT_LESS_100, + &boottime_debugfs_bootgraph_operations); + (void) debugfs_create_file("summary", S_IFREG | S_IRUGO, + dir, NULL, + &boottime_debugfs_summary_operations); +} +#else +#define boottime_debugfs_init(x) +#endif + +static ssize_t show_bootloader(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%ld\n", time_bootloader_done / 1000); +} + +static ssize_t show_kernel(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%ld\n", + (time_kernel_done - time_bootloader_done) / 1000); +} + +DEVICE_ATTR(kernel, 0444, show_kernel, NULL); +DEVICE_ATTR(bootloader, 0444, show_bootloader, NULL); + +static struct attribute *boottime_sysfs_entries[] = { + &dev_attr_kernel.attr, + &dev_attr_bootloader.attr, + NULL +}; + +static struct attribute_group boottime_attr_grp = { + .name = NULL, + .attrs = boottime_sysfs_entries, +}; + +static int __init boottime_init(void) +{ + struct kobject *boottime_kobj; + + boottime_kobj = kobject_create_and_add("boottime", NULL); + if (!boottime_kobj) { + printk(KERN_ERR "boottime: out of memory!\n"); + return -ENOMEM; + } + + if (sysfs_create_group(boottime_kobj, &boottime_attr_grp) < 0) { + kobject_put(boottime_kobj); + printk(KERN_ERR "boottime: Failed creating sysfs group\n"); + return -ENOMEM; + } + + boottime_debugfs_init(); + + return 0; +} + +late_initcall(boottime_init); diff --git a/init/main.c b/init/main.c index b286730..c7f94b6 100644 --- a/init/main.c +++ b/init/main.c @@ -69,6 +69,7 @@ #include <linux/slab.h> #include <linux/perf_event.h> #include <linux/file.h> +#include <linux/boottime.h> #include <asm/io.h> #include <asm/bugs.h> @@ -676,6 +677,8 @@ int __init_or_module do_one_initcall(initcall_t fn) int count = preempt_count(); int ret; + boottime_mark_symbolic(fn); + if (initcall_debug) ret = do_one_initcall_debug(fn); else @@ -801,6 +804,7 @@ static noinline int init_post(void) { /* need to finish all async __init code before freeing the memory */ async_synchronize_full(); + boottime_deactivate(); free_initmem(); mark_rodata_ro(); system_state = SYSTEM_RUNNING; @@ -860,6 +864,7 @@ static int __init kernel_init(void * unused) do_pre_smp_initcalls(); lockup_detector_init(); + boottime_system_up(); smp_init(); sched_init_smp(); @@ -882,6 +887,7 @@ static int __init kernel_init(void * unused) if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { ramdisk_execute_command = NULL; + boottime_mark("mount+0x0/0x0"); prepare_namespace(); } -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time 2012-10-11 14:42 ` Lee Jones @ 2012-10-11 15:24 ` Christian Gmeiner -1 siblings, 0 replies; 33+ messages in thread From: Christian Gmeiner @ 2012-10-11 15:24 UTC (permalink / raw) To: linux-arm-kernel 2012/10/11 Lee Jones <lee.jones@linaro.org>: > From: Jonas Aaberg <jonas.aberg@stericsson.com> > > The overhead is very low and the results will be found under > sysfs/bootime, as well as detailed results in debugfs under > boottime/. The bootgraph* files are compatible with > scripts/bootgraph.pl. The reason for this patch is to provide > data (sysfs/boottime) suitable for automatic testcases as > well as help for developers to reduce the boot time (debugfs). > Nice idea... what about x86? --- Christian Gmeiner, MSc ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time @ 2012-10-11 15:24 ` Christian Gmeiner 0 siblings, 0 replies; 33+ messages in thread From: Christian Gmeiner @ 2012-10-11 15:24 UTC (permalink / raw) To: Lee Jones Cc: linux-arm-kernel, linux-kernel, arnd, linus.walleij, Jonas Aaberg, Russell King, Will Deacon, Mian Yousaf Kaukab 2012/10/11 Lee Jones <lee.jones@linaro.org>: > From: Jonas Aaberg <jonas.aberg@stericsson.com> > > The overhead is very low and the results will be found under > sysfs/bootime, as well as detailed results in debugfs under > boottime/. The bootgraph* files are compatible with > scripts/bootgraph.pl. The reason for this patch is to provide > data (sysfs/boottime) suitable for automatic testcases as > well as help for developers to reduce the boot time (debugfs). > Nice idea... what about x86? --- Christian Gmeiner, MSc ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time 2012-10-11 15:24 ` Christian Gmeiner @ 2012-10-11 15:36 ` Lee Jones -1 siblings, 0 replies; 33+ messages in thread From: Lee Jones @ 2012-10-11 15:36 UTC (permalink / raw) To: linux-arm-kernel On Thu, 11 Oct 2012, Christian Gmeiner wrote: > 2012/10/11 Lee Jones <lee.jones@linaro.org>: > > From: Jonas Aaberg <jonas.aberg@stericsson.com> > > > > The overhead is very low and the results will be found under > > sysfs/bootime, as well as detailed results in debugfs under > > boottime/. The bootgraph* files are compatible with > > scripts/bootgraph.pl. The reason for this patch is to provide > > data (sysfs/boottime) suitable for automatic testcases as > > well as help for developers to reduce the boot time (debugfs). > > > > Nice idea... what about x86? What about it? You want to extend the functionality to include it? :) -- Lee Jones Linaro ST-Ericsson Landing Team Lead Linaro.org ? Open source software for ARM SoCs Follow Linaro: Facebook | Twitter | Blog ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time @ 2012-10-11 15:36 ` Lee Jones 0 siblings, 0 replies; 33+ messages in thread From: Lee Jones @ 2012-10-11 15:36 UTC (permalink / raw) To: Christian Gmeiner Cc: linux-arm-kernel, linux-kernel, arnd, linus.walleij, Jonas Aaberg, Russell King, Will Deacon, Mian Yousaf Kaukab On Thu, 11 Oct 2012, Christian Gmeiner wrote: > 2012/10/11 Lee Jones <lee.jones@linaro.org>: > > From: Jonas Aaberg <jonas.aberg@stericsson.com> > > > > The overhead is very low and the results will be found under > > sysfs/bootime, as well as detailed results in debugfs under > > boottime/. The bootgraph* files are compatible with > > scripts/bootgraph.pl. The reason for this patch is to provide > > data (sysfs/boottime) suitable for automatic testcases as > > well as help for developers to reduce the boot time (debugfs). > > > > Nice idea... what about x86? What about it? You want to extend the functionality to include it? :) -- Lee Jones Linaro ST-Ericsson Landing Team Lead Linaro.org │ Open source software for ARM SoCs Follow Linaro: Facebook | Twitter | Blog ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time 2012-10-11 14:42 ` Lee Jones @ 2012-10-11 20:17 ` Nishanth Menon -1 siblings, 0 replies; 33+ messages in thread From: Nishanth Menon @ 2012-10-11 20:17 UTC (permalink / raw) To: linux-arm-kernel On 15:42-20121011, Lee Jones wrote: > From: Jonas Aaberg <jonas.aberg@stericsson.com> > > The overhead is very low and the results will be found under > sysfs/bootime, as well as detailed results in debugfs under > boottime/. The bootgraph* files are compatible with > scripts/bootgraph.pl. The reason for this patch is to provide > data (sysfs/boottime) suitable for automatic testcases as > well as help for developers to reduce the boot time (debugfs). Tried master: 250d8 Merge branch 'i2c-embedded/for-next' of git://git.pengutronix.de/git/wsa/linux and v3.6-rc7 with omap2plus_defconfig + CONFIG_BOOTTIME enabled: init/boottime.c:47:9: error: ?cpu_usage_stat? defined as wrong kind of tag init/boottime.c:47:24: error: array type has incomplete element type init/boottime.c: In function ?boottime_mark_core?: init/boottime.c:127:313: error: ?struct kernel_stat? has no member named ?cpustat? init/boottime.c:128:311: error: ?struct kernel_stat? has no member named ?cpustat? init/boottime.c:129:313: error: ?struct kernel_stat? has no member named ?cpustat? init/boottime.c:130:310: error: ?struct kernel_stat? has no member named ?cpustat? if it depended on some other patch or a specific maintainer branch, it was'nt clear. -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time @ 2012-10-11 20:17 ` Nishanth Menon 0 siblings, 0 replies; 33+ messages in thread From: Nishanth Menon @ 2012-10-11 20:17 UTC (permalink / raw) To: Lee Jones Cc: linux-arm-kernel, linux-kernel, Russell King, linus.walleij, Jonas Aaberg, Will Deacon, arnd, Mian Yousaf Kaukab, dmurphy On 15:42-20121011, Lee Jones wrote: > From: Jonas Aaberg <jonas.aberg@stericsson.com> > > The overhead is very low and the results will be found under > sysfs/bootime, as well as detailed results in debugfs under > boottime/. The bootgraph* files are compatible with > scripts/bootgraph.pl. The reason for this patch is to provide > data (sysfs/boottime) suitable for automatic testcases as > well as help for developers to reduce the boot time (debugfs). Tried master: 250d8 Merge branch 'i2c-embedded/for-next' of git://git.pengutronix.de/git/wsa/linux and v3.6-rc7 with omap2plus_defconfig + CONFIG_BOOTTIME enabled: init/boottime.c:47:9: error: ‘cpu_usage_stat’ defined as wrong kind of tag init/boottime.c:47:24: error: array type has incomplete element type init/boottime.c: In function ‘boottime_mark_core’: init/boottime.c:127:313: error: ‘struct kernel_stat’ has no member named ‘cpustat’ init/boottime.c:128:311: error: ‘struct kernel_stat’ has no member named ‘cpustat’ init/boottime.c:129:313: error: ‘struct kernel_stat’ has no member named ‘cpustat’ init/boottime.c:130:310: error: ‘struct kernel_stat’ has no member named ‘cpustat’ if it depended on some other patch or a specific maintainer branch, it was'nt clear. -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time 2012-10-11 20:17 ` Nishanth Menon @ 2012-10-12 9:37 ` Lee Jones -1 siblings, 0 replies; 33+ messages in thread From: Lee Jones @ 2012-10-12 9:37 UTC (permalink / raw) To: linux-arm-kernel On Thu, 11 Oct 2012, Nishanth Menon wrote: > On 15:42-20121011, Lee Jones wrote: > > From: Jonas Aaberg <jonas.aberg@stericsson.com> > > > > The overhead is very low and the results will be found under > > sysfs/bootime, as well as detailed results in debugfs under > > boottime/. The bootgraph* files are compatible with > > scripts/bootgraph.pl. The reason for this patch is to provide > > data (sysfs/boottime) suitable for automatic testcases as > > well as help for developers to reduce the boot time (debugfs). > Tried master: > 250d8 Merge branch 'i2c-embedded/for-next' of git://git.pengutronix.de/git/wsa/linux > and v3.6-rc7 > with omap2plus_defconfig + CONFIG_BOOTTIME enabled: > > init/boottime.c:47:9: error: ?cpu_usage_stat? defined as wrong kind of tag > init/boottime.c:47:24: error: array type has incomplete element type > init/boottime.c: In function ?boottime_mark_core?: > init/boottime.c:127:313: error: ?struct kernel_stat? has no member named ?cpustat? > init/boottime.c:128:311: error: ?struct kernel_stat? has no member named ?cpustat? > init/boottime.c:129:313: error: ?struct kernel_stat? has no member named ?cpustat? > init/boottime.c:130:310: error: ?struct kernel_stat? has no member named ?cpustat? > > if it depended on some other patch or a specific maintainer branch, > it was'nt clear. No, this is completely my fault. There are added dependencies and fixups that I missed. I'll fixup and resubmit. Unfortunately, there are now other authors involved, so I need to seek permission before squashing them in. Please bear with me. -- Lee Jones Linaro ST-Ericsson Landing Team Lead Linaro.org ? Open source software for ARM SoCs Follow Linaro: Facebook | Twitter | Blog ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time @ 2012-10-12 9:37 ` Lee Jones 0 siblings, 0 replies; 33+ messages in thread From: Lee Jones @ 2012-10-12 9:37 UTC (permalink / raw) To: Nishanth Menon Cc: linux-arm-kernel, linux-kernel, Russell King, linus.walleij, Jonas Aaberg, Will Deacon, arnd, Mian Yousaf Kaukab, dmurphy On Thu, 11 Oct 2012, Nishanth Menon wrote: > On 15:42-20121011, Lee Jones wrote: > > From: Jonas Aaberg <jonas.aberg@stericsson.com> > > > > The overhead is very low and the results will be found under > > sysfs/bootime, as well as detailed results in debugfs under > > boottime/. The bootgraph* files are compatible with > > scripts/bootgraph.pl. The reason for this patch is to provide > > data (sysfs/boottime) suitable for automatic testcases as > > well as help for developers to reduce the boot time (debugfs). > Tried master: > 250d8 Merge branch 'i2c-embedded/for-next' of git://git.pengutronix.de/git/wsa/linux > and v3.6-rc7 > with omap2plus_defconfig + CONFIG_BOOTTIME enabled: > > init/boottime.c:47:9: error: ‘cpu_usage_stat’ defined as wrong kind of tag > init/boottime.c:47:24: error: array type has incomplete element type > init/boottime.c: In function ‘boottime_mark_core’: > init/boottime.c:127:313: error: ‘struct kernel_stat’ has no member named ‘cpustat’ > init/boottime.c:128:311: error: ‘struct kernel_stat’ has no member named ‘cpustat’ > init/boottime.c:129:313: error: ‘struct kernel_stat’ has no member named ‘cpustat’ > init/boottime.c:130:310: error: ‘struct kernel_stat’ has no member named ‘cpustat’ > > if it depended on some other patch or a specific maintainer branch, > it was'nt clear. No, this is completely my fault. There are added dependencies and fixups that I missed. I'll fixup and resubmit. Unfortunately, there are now other authors involved, so I need to seek permission before squashing them in. Please bear with me. -- Lee Jones Linaro ST-Ericsson Landing Team Lead Linaro.org │ Open source software for ARM SoCs Follow Linaro: Facebook | Twitter | Blog ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time 2012-10-11 20:17 ` Nishanth Menon @ 2012-10-12 9:51 ` Lee Jones -1 siblings, 0 replies; 33+ messages in thread From: Lee Jones @ 2012-10-12 9:51 UTC (permalink / raw) To: linux-arm-kernel Okay, please disgard the last patch. Lots of fixups since. Author: Lee Jones <lee.jones@linaro.org> Date: Wed Jun 30 14:00:40 2010 +0200 Boottime: A tool for automatic measurement of kernel/bootloader boot time The overhead is very low and the results will be found under sysfs/bootime, as well as detailed results in debugfs under boottime/. The bootgraph* files are compatible with scripts/bootgraph.pl. The reason for this patch is to provide data (sysfs/boottime) suitable for automatic test-cases as well as help for developers to reduce the boot time (debugfs). Based heavily on the original driver by Jonas Aaberg. Cc: Russell King <linux@arm.linux.org.uk> Cc: Will Deacon <will.deacon@arm.com> Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com> Signed-off-by: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> Signed-off-by: Lee Jones <lee.jones@linaro.org> Reviewed-by: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com> Tested-by: Mian Yousaf KAUKAB <mian.yousaf.kaukab@stericsson.com> diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile index e8a4e58..8522356 100644 --- a/arch/arm/common/Makefile +++ b/arch/arm/common/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o obj-$(CONFIG_SHARP_SCOOP) += scoop.o obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o +obj-$(CONFIG_BOOTTIME) += boottime.o diff --git a/arch/arm/common/boottime.c b/arch/arm/common/boottime.c new file mode 100644 index 0000000..73e9e04 --- /dev/null +++ b/arch/arm/common/boottime.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) ST-Ericsson SA 2009-2010 + * + * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson + * + * License terms: GNU General Public License (GPL) version 2 + * + * Store boot times measured during for example u-boot startup. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/boottime.h> +#include <linux/string.h> +#include <asm/setup.h> + +static u32 bootloader_idle; +static u32 bootloader_total; + +static int __init boottime_parse_tag(const struct tag *tag) +{ + int i; + char buff[BOOTTIME_MAX_NAME_LEN]; + + bootloader_idle = tag->u.boottime.idle; + bootloader_total = tag->u.boottime.total; + + for (i = 0; i < tag->u.boottime.num; i++) { + snprintf(buff, BOOTTIME_MAX_NAME_LEN, "%s+0x0/0x0", + tag->u.boottime.entry[i].name); + buff[BOOTTIME_MAX_NAME_LEN - 1] = '\0'; + boottime_mark_wtime(buff, tag->u.boottime.entry[i].time); + } + + return 0; +} + +__tagtable(ATAG_BOOTTIME, boottime_parse_tag); + +int boottime_bootloader_idle(void) +{ + if (bootloader_total == 0) + return 0; + + return (int) ((bootloader_idle) / (bootloader_total / 100)); +} diff --git a/arch/arm/include/asm/setup.h b/arch/arm/include/asm/setup.h index 24d284a..e8da062 100644 --- a/arch/arm/include/asm/setup.h +++ b/arch/arm/include/asm/setup.h @@ -143,6 +143,23 @@ struct tag_memclk { __u32 fmemclk; }; +/* for automatic boot timing testcases */ +#define ATAG_BOOTTIME 0x41000403 +#define BOOTTIME_MAX_NAME_LEN 64 +#define BOOTTIME_MAX 10 + +struct boottime_entry { + u32 time; /* in us */ + u8 name[BOOTTIME_MAX_NAME_LEN]; +}; + +struct tag_boottime { + struct boottime_entry entry[BOOTTIME_MAX]; + u32 idle; /* in us */ + u32 total; /* in us */ + u8 num; +}; + struct tag { struct tag_header hdr; union { @@ -165,6 +182,10 @@ struct tag { * DC21285 specific */ struct tag_memclk memclk; + /* + * Boot time + */ + struct tag_boottime boottime; } u; }; diff --git a/include/linux/boottime.h b/include/linux/boottime.h new file mode 100644 index 0000000..d330ecd --- /dev/null +++ b/include/linux/boottime.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) ST-Ericsson SA 2009-2010 + * + * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson + * + * License terms: GNU General Public License (GPL) version 2 + * + * boottime is a tool for collecting start-up timing + * information and can together with boot loader support + * display a total system start-up time. + * + */ + +#ifndef LINUX_BOOTTIME_H +#define LINUX_BOOTTIME_H + +#ifdef CONFIG_BOOTTIME +#include <linux/kernel.h> + +/** + * struct boottime_timer - Callbacks for generic timer. + * @init: Function to call at boottime initialization + * @get_time: Returns the number of us since start-up + * Preferable this is based upon a free running timer. + * This is the only required entry. + * @finalize: Called before init is executed and boottime is done. + */ +struct boottime_timer { + int (*init)(void); + unsigned long (*get_time)(void); + void (*finalize)(void); +}; + +/** + * boottime_mark_wtime() + * Add a sample point with a given time. Useful for adding data collected + * by for example a boot loader. + * @name: The name of the sample point + * @time: The time in us when this point was reached + */ +void __init boottime_mark_wtime(char *name, unsigned long time); + +/** + * boottime_mark() + * Add a sample point with the current time. + * @name: The name of this sample point + */ +void boottime_mark(char *name); + +/** + * boottime_mark_symbolic() + * Add a sample point where the name is a symbolic function + * and %pF is needed to get the correct function name. + * @name: function name. + */ +void __init boottime_mark_symbolic(void *name); + +/** + * boottime_activate() + * Activates boottime and register callbacks. + * @bt: struct with callbacks. + */ +void __ref boottime_activate(struct boottime_timer *bt); + +/** + * boottime_deactivate() + * This function is called when the kernel boot is done. + * (before "free init memory" is called) + */ +void boottime_deactivate(void); + +/** + * boottime_system_up() + * A function is called when the basics of the kernel + * is up and running. + */ +void __init boottime_system_up(void); + +#else + +#define boottime_mark_wtime(name, time) +#define boottime_mark(name) +#define boottime_mark_symbolic(name) +#define boottime_activate(bt) +#define boottime_deactivate() +#define boottime_system_up() +#endif + +#endif /* LINUX_BOOTTIME_H */ diff --git a/init/Kconfig b/init/Kconfig index 4c93533..d0df8ff 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1464,6 +1464,15 @@ config PROFILING Say Y here to enable the extended profiling support mechanisms used by profilers such as OProfile. +config BOOTTIME + bool "Boot time measurements" + default n + help + Adds sysfs entries (boottime/) with start-up timing information. + If CONFIG_DEBUG_FS is enabled, detailed information about the + boot time, including system load during boot can be extracted. + This information can be visualised with help of the bootgraph script. + # # Place an empty function call at each tracepoint site. Can be # dynamically changed for a probe function. diff --git a/init/Makefile b/init/Makefile index 7bc47ee..356d529 100644 --- a/init/Makefile +++ b/init/Makefile @@ -9,6 +9,7 @@ else obj-$(CONFIG_BLK_DEV_INITRD) += initramfs.o endif obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o +obj-$(CONFIG_BOOTTIME) += boottime.o ifneq ($(CONFIG_ARCH_INIT_TASK),y) obj-y += init_task.o diff --git a/init/boottime.c b/init/boottime.c new file mode 100644 index 0000000..793c184 --- /dev/null +++ b/init/boottime.c @@ -0,0 +1,475 @@ +/* + * Copyright (C) ST-Ericsson SA 2009-2010 + * + * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson + * + * License terms: GNU General Public License (GPL) version 2 + * + * boottime is a tool for collecting start-up timing + * information and can together with boot loader support + * display a total system start-up time. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/list.h> +#include <linux/seq_file.h> +#include <linux/debugfs.h> +#include <linux/spinlock.h> +#include <linux/boottime.h> +#include <linux/kernel_stat.h> +#include <linux/kobject.h> +#include <linux/device.h> +#include <linux/sysfs.h> +#include <linux/slab.h> + +/* + * BOOTTIME_MAX_NAME_LEN is defined in arch/arm/include/asm/setup.h to 64. + * No crisis if they don't match. + */ +#ifndef BOOTTIME_MAX_NAME_LEN +#define BOOTTIME_MAX_NAME_LEN 64 +#endif + +/* + * We have a few static entries, since it is good to have measure points + * before the system is up and running properly + */ +#define NUM_STATIC_BOOTTIME_ENTRIES 32 + +struct boottime_list { + struct list_head list; + char name[BOOTTIME_MAX_NAME_LEN]; + /* Time in us since power on, possible including boot loader. */ + unsigned long time; + bool cpu_load; + struct kernel_cpustat cpu_usage[NR_CPUS]; +}; + +enum boottime_filter_type { + BOOTTIME_FILTER_OUT_ZERO, + BOOTTIME_FILTER_OUT_LESS_100, + BOOTTIME_FILTER_NOTHING, +}; + +enum boottime_symbolic_print { + BOOTTIME_SYMBOLIC_PRINT, + BOOTTIME_NORMAL_PRINT, +}; + +enum boottime_cpu_load { + BOOTTIME_CPU_LOAD, + BOOTTIME_NO_CPU_LOAD, +}; + +static LIST_HEAD(boottime_list); +static DEFINE_SPINLOCK(boottime_list_lock); +static struct boottime_timer boottime_timer; +static int num_const_boottime_list; +static struct boottime_list const_boottime_list[NUM_STATIC_BOOTTIME_ENTRIES]; +static unsigned long time_kernel_done; +static unsigned long time_bootloader_done; +static bool system_up; +static bool boottime_done; + +int __attribute__((weak)) boottime_arch_startup(void) +{ + return 0; +} + +int __attribute__((weak)) boottime_bootloader_idle(void) +{ + return 0; +} + +static void boottime_mark_core(char *name, + unsigned long time, + enum boottime_symbolic_print symbolic, + enum boottime_cpu_load cpu_load) +{ + struct boottime_list *b; + unsigned long flags = 0; + int i; + + if (system_up) { + b = kmalloc(sizeof(struct boottime_list), GFP_KERNEL); + if (!b) { + printk(KERN_ERR + "boottime: failed to allocate memory!\n"); + return; + } + + } else { + if (num_const_boottime_list < NUM_STATIC_BOOTTIME_ENTRIES) { + b = &const_boottime_list[num_const_boottime_list]; + num_const_boottime_list++; + } else { + printk(KERN_ERR + "boottime: too many early measure points!\n"); + return; + } + } + + INIT_LIST_HEAD(&b->list); + + if (symbolic == BOOTTIME_SYMBOLIC_PRINT) + snprintf(b->name, BOOTTIME_MAX_NAME_LEN, "%pF", name); + else + strncpy(b->name, name, BOOTTIME_MAX_NAME_LEN); + + b->name[BOOTTIME_MAX_NAME_LEN - 1] = '\0'; + b->time = time; + b->cpu_load = cpu_load; + + if (cpu_load == BOOTTIME_CPU_LOAD && system_up) + for_each_possible_cpu(i) { + b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] = + kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]; + b->cpu_usage[i].cpustat[CPUTIME_IDLE] = + kcpustat_cpu(i).cpustat[CPUTIME_IDLE]; + b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] = + kcpustat_cpu(i).cpustat[CPUTIME_IOWAIT]; + b->cpu_usage[i].cpustat[CPUTIME_IRQ] = + kcpustat_cpu(i).cpustat[CPUTIME_IRQ]; + /* + * TODO: Make sure that user, nice, softirq, steal + * and guest are not used during boot + */ + } + else + b->cpu_load = BOOTTIME_NO_CPU_LOAD; + + if (system_up) { + spin_lock_irqsave(&boottime_list_lock, flags); + list_add(&b->list, &boottime_list); + spin_unlock_irqrestore(&boottime_list_lock, flags); + } else { + list_add(&b->list, &boottime_list); + } +} + +void __init boottime_mark_wtime(char *name, unsigned long time) +{ + boottime_mark_core(name, time, + BOOTTIME_NORMAL_PRINT, + BOOTTIME_NO_CPU_LOAD); +} + +void __ref boottime_mark_symbolic(void *name) +{ + + if (boottime_done) + return; + + if (boottime_timer.get_time) + boottime_mark_core((char *) name, + boottime_timer.get_time(), + BOOTTIME_SYMBOLIC_PRINT, + BOOTTIME_CPU_LOAD); +} + +void boottime_mark(char *name) +{ + if (boottime_timer.get_time) + boottime_mark_core(name, + boottime_timer.get_time(), + BOOTTIME_NORMAL_PRINT, + BOOTTIME_CPU_LOAD); +} + +void __init boottime_activate(struct boottime_timer *bt) +{ + struct boottime_list *b; + int res = 0; + unsigned long flags; + + if (bt == NULL) { + printk(KERN_ERR + "boottime: error: bad configured\n"); + return; + } + + if (bt->get_time == NULL) { + printk(KERN_ERR + "boottime: error: you must provide a get_time() function\n"); + return; + } + memcpy(&boottime_timer, bt, sizeof(struct boottime_timer)); + + if (boottime_timer.init) + res = boottime_timer.init(); + + if (res) { + printk(KERN_ERR "boottime: initialization failed\n"); + return; + } + + if (boottime_arch_startup()) + printk(KERN_ERR + "boottime: arch specfic initialization failed\n"); + + spin_lock_irqsave(&boottime_list_lock, flags); + + if (!list_empty(&boottime_list)) { + + b = list_first_entry(&boottime_list, struct boottime_list, + list); + if (b) + time_bootloader_done = b->time; + } + + spin_unlock_irqrestore(&boottime_list_lock, flags); +} + +void __init boottime_system_up(void) +{ + system_up = true; +} + +void boottime_deactivate(void) +{ + struct boottime_list *b; + unsigned long flags; + + boottime_mark("execute_init+0x0/0x0"); + + boottime_done = true; + + spin_lock_irqsave(&boottime_list_lock, flags); + b = list_first_entry(&boottime_list, struct boottime_list, list); + spin_unlock_irqrestore(&boottime_list_lock, flags); + + time_kernel_done = b->time; + + if (boottime_timer.finalize) + boottime_timer.finalize(); +} + +#ifdef CONFIG_DEBUG_FS +static void boottime_debugfs_load(struct seq_file *s, + struct boottime_list *b, + struct boottime_list *p) +{ + int i; + unsigned long total_p, total_b; + unsigned long system_total, idle_total, irq_total, iowait_total; + unsigned long system_load, idle_load, irq_load, iowait_load; + + for_each_possible_cpu(i) { + total_b = (b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] + + b->cpu_usage[i].cpustat[CPUTIME_IDLE] + + b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] + + b->cpu_usage[i].cpustat[CPUTIME_IRQ]); + + total_p = (p->cpu_usage[i].cpustat[CPUTIME_SYSTEM] + + p->cpu_usage[i].cpustat[CPUTIME_IDLE] + + p->cpu_usage[i].cpustat[CPUTIME_IOWAIT] + + p->cpu_usage[i].cpustat[CPUTIME_IRQ]); + + if (total_b == total_p) + continue; + + system_total = b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] + - p->cpu_usage[i].cpustat[CPUTIME_SYSTEM]; + idle_total = b->cpu_usage[i].cpustat[CPUTIME_IDLE] + - p->cpu_usage[i].cpustat[CPUTIME_IDLE]; + irq_total = b->cpu_usage[i].cpustat[CPUTIME_IRQ] + - p->cpu_usage[i].cpustat[CPUTIME_IRQ]; + iowait_total = b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] + - p->cpu_usage[i].cpustat[CPUTIME_IOWAIT]; + + system_load = (100 * system_total / (total_b - total_p)); + idle_load = (100 * idle_total / (total_b - total_p)); + irq_load = (100 * irq_total / (total_b - total_p)); + iowait_load = (100 * iowait_total / (total_b - total_p)); + + seq_printf(s, + " cpu%d system: %lu%% idle: %lu%% iowait: %lu%% irq: %lu%%", + i, + system_load, + idle_load, + iowait_load, + irq_load); + } + seq_printf(s, "\n"); +} + +static void boottime_debugfs_print(struct seq_file *s, + struct boottime_list *b, + struct boottime_list *p) +{ + seq_printf(s, "[%5lu.%06lu] calling %s\n", + p->time / 1000000, + (p->time % 1000000), + p->name); + seq_printf(s, "[%5lu.%06lu] initcall %s returned 0 after %ld msecs.", + b->time / 1000000, + (b->time % 1000000), + p->name, (b->time - p->time) / 1000); + + if (p->cpu_load == BOOTTIME_NO_CPU_LOAD || + b->cpu_load == BOOTTIME_NO_CPU_LOAD) { + seq_printf(s, "\n"); + return; + } + + boottime_debugfs_load(s, b, p); +} + +static int boottime_debugfs_bootgraph_show(struct seq_file *s, void *iter) +{ + struct boottime_list *b, *p = NULL, *old_p = NULL; + enum boottime_filter_type filter = (int)s->private; + + list_for_each_entry_reverse(b, &boottime_list, list) { + if (p) { + if (!(filter == BOOTTIME_FILTER_OUT_ZERO && + (b->time - p->time) / 1000 == 0) + && !(filter == BOOTTIME_FILTER_OUT_LESS_100 && + (b->time - p->time) < 100 * 1000)) + boottime_debugfs_print(s, b, p); + old_p = p; + } + p = b; + } + + if (filter == BOOTTIME_FILTER_NOTHING && p) + boottime_debugfs_print(s, p, p); + + if (p) + seq_printf(s, "[%5lu.%06lu] Freeing init memory: 0K\n", + p->time / 1000000, p->time % 1000000); + return 0; +} + +static int boottime_debugfs_summary_show(struct seq_file *s, void *data) +{ + struct boottime_list *b, b_zero; + + if (time_bootloader_done) + seq_printf(s, "bootloader: %ld msecs\n", + time_bootloader_done / 1000); + + seq_printf(s, "kernel: %ld msecs\ntotal: %ld msecs\n", + (time_kernel_done - time_bootloader_done) / 1000, + time_kernel_done / 1000); + seq_printf(s, "kernel:"); + b = list_first_entry(&boottime_list, + struct boottime_list, list); + memset(&b_zero, 0, sizeof(struct boottime_list)); + boottime_debugfs_load(s, b, &b_zero); + + if (time_bootloader_done) + seq_printf(s, + "bootloader: cpu0 system: %d%% idle: %d%% iowait: 0%% irq: 0%%\n", + 100 - boottime_bootloader_idle(), + boottime_bootloader_idle()); + return 0; +} + +static int boottime_debugfs_bootgraph_open(struct inode *inode, + struct file *file) +{ + return single_open(file, + boottime_debugfs_bootgraph_show, + inode->i_private); +} + +static int boottime_debugfs_summary_open(struct inode *inode, + struct file *file) +{ + return single_open(file, + boottime_debugfs_summary_show, + inode->i_private); +} + +static const struct file_operations boottime_debugfs_bootgraph_operations = { + .open = boottime_debugfs_bootgraph_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct file_operations boottime_debugfs_summary_operations = { + .open = boottime_debugfs_summary_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +void boottime_debugfs_init(void) +{ + struct dentry *dir; + + dir = debugfs_create_dir("boottime", NULL); + + (void) debugfs_create_file("bootgraph", S_IFREG | S_IRUGO, + dir, (void *)BOOTTIME_FILTER_NOTHING, + &boottime_debugfs_bootgraph_operations); + (void) debugfs_create_file("bootgraph_all_except0", S_IFREG | S_IRUGO, + dir, (void *)BOOTTIME_FILTER_OUT_ZERO, + &boottime_debugfs_bootgraph_operations); + (void) debugfs_create_file("bootgraph_larger100", + S_IFREG | S_IRUGO, + dir, (void *)BOOTTIME_FILTER_OUT_LESS_100, + &boottime_debugfs_bootgraph_operations); + (void) debugfs_create_file("summary", S_IFREG | S_IRUGO, + dir, NULL, + &boottime_debugfs_summary_operations); +} +#else +#define boottime_debugfs_init(x) +#endif + +static ssize_t show_bootloader(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%ld\n", time_bootloader_done / 1000); +} + +static ssize_t show_kernel(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%ld\n", + (time_kernel_done - time_bootloader_done) / 1000); +} + +DEVICE_ATTR(kernel, 0444, show_kernel, NULL); +DEVICE_ATTR(bootloader, 0444, show_bootloader, NULL); + +static struct attribute *boottime_sysfs_entries[] = { + &dev_attr_kernel.attr, + &dev_attr_bootloader.attr, + NULL +}; + +static struct attribute_group boottime_attr_grp = { + .name = NULL, + .attrs = boottime_sysfs_entries, +}; + +static int __init boottime_init(void) +{ + struct kobject *boottime_kobj; + + boottime_kobj = kobject_create_and_add("boottime", NULL); + if (!boottime_kobj) { + printk(KERN_ERR "boottime: out of memory!\n"); + return -ENOMEM; + } + + if (sysfs_create_group(boottime_kobj, &boottime_attr_grp) < 0) { + kobject_put(boottime_kobj); + printk(KERN_ERR "boottime: Failed creating sysfs group\n"); + return -ENOMEM; + } + + boottime_debugfs_init(); + + return 0; +} + +late_initcall(boottime_init); diff --git a/init/main.c b/init/main.c index 313360f..c06afd0 100644 --- a/init/main.c +++ b/init/main.c @@ -69,6 +69,7 @@ #include <linux/slab.h> #include <linux/perf_event.h> #include <linux/file.h> +#include <linux/boottime.h> #include <asm/io.h> #include <asm/bugs.h> @@ -679,6 +680,8 @@ int __init_or_module do_one_initcall(initcall_t fn) int count = preempt_count(); int ret; + boottime_mark_symbolic(fn); + if (initcall_debug) ret = do_one_initcall_debug(fn); else @@ -804,6 +807,7 @@ static noinline int init_post(void) { /* need to finish all async __init code before freeing the memory */ async_synchronize_full(); + boottime_deactivate(); free_initmem(); mark_rodata_ro(); system_state = SYSTEM_RUNNING; @@ -863,6 +867,7 @@ static int __init kernel_init(void * unused) do_pre_smp_initcalls(); lockup_detector_init(); + boottime_system_up(); smp_init(); sched_init_smp(); @@ -885,6 +890,7 @@ static int __init kernel_init(void * unused) if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { ramdisk_execute_command = NULL; + boottime_mark("mount+0x0/0x0"); prepare_namespace(); } ^ permalink raw reply related [flat|nested] 33+ messages in thread
* Re: [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time @ 2012-10-12 9:51 ` Lee Jones 0 siblings, 0 replies; 33+ messages in thread From: Lee Jones @ 2012-10-12 9:51 UTC (permalink / raw) To: Nishanth Menon Cc: linux-arm-kernel, linux-kernel, Russell King, linus.walleij, Jonas Aaberg, Will Deacon, arnd, Mian Yousaf Kaukab, dmurphy Okay, please disgard the last patch. Lots of fixups since. Author: Lee Jones <lee.jones@linaro.org> Date: Wed Jun 30 14:00:40 2010 +0200 Boottime: A tool for automatic measurement of kernel/bootloader boot time The overhead is very low and the results will be found under sysfs/bootime, as well as detailed results in debugfs under boottime/. The bootgraph* files are compatible with scripts/bootgraph.pl. The reason for this patch is to provide data (sysfs/boottime) suitable for automatic test-cases as well as help for developers to reduce the boot time (debugfs). Based heavily on the original driver by Jonas Aaberg. Cc: Russell King <linux@arm.linux.org.uk> Cc: Will Deacon <will.deacon@arm.com> Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com> Signed-off-by: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> Signed-off-by: Lee Jones <lee.jones@linaro.org> Reviewed-by: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com> Tested-by: Mian Yousaf KAUKAB <mian.yousaf.kaukab@stericsson.com> diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile index e8a4e58..8522356 100644 --- a/arch/arm/common/Makefile +++ b/arch/arm/common/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o obj-$(CONFIG_SHARP_SCOOP) += scoop.o obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o +obj-$(CONFIG_BOOTTIME) += boottime.o diff --git a/arch/arm/common/boottime.c b/arch/arm/common/boottime.c new file mode 100644 index 0000000..73e9e04 --- /dev/null +++ b/arch/arm/common/boottime.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) ST-Ericsson SA 2009-2010 + * + * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson + * + * License terms: GNU General Public License (GPL) version 2 + * + * Store boot times measured during for example u-boot startup. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/boottime.h> +#include <linux/string.h> +#include <asm/setup.h> + +static u32 bootloader_idle; +static u32 bootloader_total; + +static int __init boottime_parse_tag(const struct tag *tag) +{ + int i; + char buff[BOOTTIME_MAX_NAME_LEN]; + + bootloader_idle = tag->u.boottime.idle; + bootloader_total = tag->u.boottime.total; + + for (i = 0; i < tag->u.boottime.num; i++) { + snprintf(buff, BOOTTIME_MAX_NAME_LEN, "%s+0x0/0x0", + tag->u.boottime.entry[i].name); + buff[BOOTTIME_MAX_NAME_LEN - 1] = '\0'; + boottime_mark_wtime(buff, tag->u.boottime.entry[i].time); + } + + return 0; +} + +__tagtable(ATAG_BOOTTIME, boottime_parse_tag); + +int boottime_bootloader_idle(void) +{ + if (bootloader_total == 0) + return 0; + + return (int) ((bootloader_idle) / (bootloader_total / 100)); +} diff --git a/arch/arm/include/asm/setup.h b/arch/arm/include/asm/setup.h index 24d284a..e8da062 100644 --- a/arch/arm/include/asm/setup.h +++ b/arch/arm/include/asm/setup.h @@ -143,6 +143,23 @@ struct tag_memclk { __u32 fmemclk; }; +/* for automatic boot timing testcases */ +#define ATAG_BOOTTIME 0x41000403 +#define BOOTTIME_MAX_NAME_LEN 64 +#define BOOTTIME_MAX 10 + +struct boottime_entry { + u32 time; /* in us */ + u8 name[BOOTTIME_MAX_NAME_LEN]; +}; + +struct tag_boottime { + struct boottime_entry entry[BOOTTIME_MAX]; + u32 idle; /* in us */ + u32 total; /* in us */ + u8 num; +}; + struct tag { struct tag_header hdr; union { @@ -165,6 +182,10 @@ struct tag { * DC21285 specific */ struct tag_memclk memclk; + /* + * Boot time + */ + struct tag_boottime boottime; } u; }; diff --git a/include/linux/boottime.h b/include/linux/boottime.h new file mode 100644 index 0000000..d330ecd --- /dev/null +++ b/include/linux/boottime.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) ST-Ericsson SA 2009-2010 + * + * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson + * + * License terms: GNU General Public License (GPL) version 2 + * + * boottime is a tool for collecting start-up timing + * information and can together with boot loader support + * display a total system start-up time. + * + */ + +#ifndef LINUX_BOOTTIME_H +#define LINUX_BOOTTIME_H + +#ifdef CONFIG_BOOTTIME +#include <linux/kernel.h> + +/** + * struct boottime_timer - Callbacks for generic timer. + * @init: Function to call at boottime initialization + * @get_time: Returns the number of us since start-up + * Preferable this is based upon a free running timer. + * This is the only required entry. + * @finalize: Called before init is executed and boottime is done. + */ +struct boottime_timer { + int (*init)(void); + unsigned long (*get_time)(void); + void (*finalize)(void); +}; + +/** + * boottime_mark_wtime() + * Add a sample point with a given time. Useful for adding data collected + * by for example a boot loader. + * @name: The name of the sample point + * @time: The time in us when this point was reached + */ +void __init boottime_mark_wtime(char *name, unsigned long time); + +/** + * boottime_mark() + * Add a sample point with the current time. + * @name: The name of this sample point + */ +void boottime_mark(char *name); + +/** + * boottime_mark_symbolic() + * Add a sample point where the name is a symbolic function + * and %pF is needed to get the correct function name. + * @name: function name. + */ +void __init boottime_mark_symbolic(void *name); + +/** + * boottime_activate() + * Activates boottime and register callbacks. + * @bt: struct with callbacks. + */ +void __ref boottime_activate(struct boottime_timer *bt); + +/** + * boottime_deactivate() + * This function is called when the kernel boot is done. + * (before "free init memory" is called) + */ +void boottime_deactivate(void); + +/** + * boottime_system_up() + * A function is called when the basics of the kernel + * is up and running. + */ +void __init boottime_system_up(void); + +#else + +#define boottime_mark_wtime(name, time) +#define boottime_mark(name) +#define boottime_mark_symbolic(name) +#define boottime_activate(bt) +#define boottime_deactivate() +#define boottime_system_up() +#endif + +#endif /* LINUX_BOOTTIME_H */ diff --git a/init/Kconfig b/init/Kconfig index 4c93533..d0df8ff 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1464,6 +1464,15 @@ config PROFILING Say Y here to enable the extended profiling support mechanisms used by profilers such as OProfile. +config BOOTTIME + bool "Boot time measurements" + default n + help + Adds sysfs entries (boottime/) with start-up timing information. + If CONFIG_DEBUG_FS is enabled, detailed information about the + boot time, including system load during boot can be extracted. + This information can be visualised with help of the bootgraph script. + # # Place an empty function call at each tracepoint site. Can be # dynamically changed for a probe function. diff --git a/init/Makefile b/init/Makefile index 7bc47ee..356d529 100644 --- a/init/Makefile +++ b/init/Makefile @@ -9,6 +9,7 @@ else obj-$(CONFIG_BLK_DEV_INITRD) += initramfs.o endif obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o +obj-$(CONFIG_BOOTTIME) += boottime.o ifneq ($(CONFIG_ARCH_INIT_TASK),y) obj-y += init_task.o diff --git a/init/boottime.c b/init/boottime.c new file mode 100644 index 0000000..793c184 --- /dev/null +++ b/init/boottime.c @@ -0,0 +1,475 @@ +/* + * Copyright (C) ST-Ericsson SA 2009-2010 + * + * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson + * + * License terms: GNU General Public License (GPL) version 2 + * + * boottime is a tool for collecting start-up timing + * information and can together with boot loader support + * display a total system start-up time. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/list.h> +#include <linux/seq_file.h> +#include <linux/debugfs.h> +#include <linux/spinlock.h> +#include <linux/boottime.h> +#include <linux/kernel_stat.h> +#include <linux/kobject.h> +#include <linux/device.h> +#include <linux/sysfs.h> +#include <linux/slab.h> + +/* + * BOOTTIME_MAX_NAME_LEN is defined in arch/arm/include/asm/setup.h to 64. + * No crisis if they don't match. + */ +#ifndef BOOTTIME_MAX_NAME_LEN +#define BOOTTIME_MAX_NAME_LEN 64 +#endif + +/* + * We have a few static entries, since it is good to have measure points + * before the system is up and running properly + */ +#define NUM_STATIC_BOOTTIME_ENTRIES 32 + +struct boottime_list { + struct list_head list; + char name[BOOTTIME_MAX_NAME_LEN]; + /* Time in us since power on, possible including boot loader. */ + unsigned long time; + bool cpu_load; + struct kernel_cpustat cpu_usage[NR_CPUS]; +}; + +enum boottime_filter_type { + BOOTTIME_FILTER_OUT_ZERO, + BOOTTIME_FILTER_OUT_LESS_100, + BOOTTIME_FILTER_NOTHING, +}; + +enum boottime_symbolic_print { + BOOTTIME_SYMBOLIC_PRINT, + BOOTTIME_NORMAL_PRINT, +}; + +enum boottime_cpu_load { + BOOTTIME_CPU_LOAD, + BOOTTIME_NO_CPU_LOAD, +}; + +static LIST_HEAD(boottime_list); +static DEFINE_SPINLOCK(boottime_list_lock); +static struct boottime_timer boottime_timer; +static int num_const_boottime_list; +static struct boottime_list const_boottime_list[NUM_STATIC_BOOTTIME_ENTRIES]; +static unsigned long time_kernel_done; +static unsigned long time_bootloader_done; +static bool system_up; +static bool boottime_done; + +int __attribute__((weak)) boottime_arch_startup(void) +{ + return 0; +} + +int __attribute__((weak)) boottime_bootloader_idle(void) +{ + return 0; +} + +static void boottime_mark_core(char *name, + unsigned long time, + enum boottime_symbolic_print symbolic, + enum boottime_cpu_load cpu_load) +{ + struct boottime_list *b; + unsigned long flags = 0; + int i; + + if (system_up) { + b = kmalloc(sizeof(struct boottime_list), GFP_KERNEL); + if (!b) { + printk(KERN_ERR + "boottime: failed to allocate memory!\n"); + return; + } + + } else { + if (num_const_boottime_list < NUM_STATIC_BOOTTIME_ENTRIES) { + b = &const_boottime_list[num_const_boottime_list]; + num_const_boottime_list++; + } else { + printk(KERN_ERR + "boottime: too many early measure points!\n"); + return; + } + } + + INIT_LIST_HEAD(&b->list); + + if (symbolic == BOOTTIME_SYMBOLIC_PRINT) + snprintf(b->name, BOOTTIME_MAX_NAME_LEN, "%pF", name); + else + strncpy(b->name, name, BOOTTIME_MAX_NAME_LEN); + + b->name[BOOTTIME_MAX_NAME_LEN - 1] = '\0'; + b->time = time; + b->cpu_load = cpu_load; + + if (cpu_load == BOOTTIME_CPU_LOAD && system_up) + for_each_possible_cpu(i) { + b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] = + kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]; + b->cpu_usage[i].cpustat[CPUTIME_IDLE] = + kcpustat_cpu(i).cpustat[CPUTIME_IDLE]; + b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] = + kcpustat_cpu(i).cpustat[CPUTIME_IOWAIT]; + b->cpu_usage[i].cpustat[CPUTIME_IRQ] = + kcpustat_cpu(i).cpustat[CPUTIME_IRQ]; + /* + * TODO: Make sure that user, nice, softirq, steal + * and guest are not used during boot + */ + } + else + b->cpu_load = BOOTTIME_NO_CPU_LOAD; + + if (system_up) { + spin_lock_irqsave(&boottime_list_lock, flags); + list_add(&b->list, &boottime_list); + spin_unlock_irqrestore(&boottime_list_lock, flags); + } else { + list_add(&b->list, &boottime_list); + } +} + +void __init boottime_mark_wtime(char *name, unsigned long time) +{ + boottime_mark_core(name, time, + BOOTTIME_NORMAL_PRINT, + BOOTTIME_NO_CPU_LOAD); +} + +void __ref boottime_mark_symbolic(void *name) +{ + + if (boottime_done) + return; + + if (boottime_timer.get_time) + boottime_mark_core((char *) name, + boottime_timer.get_time(), + BOOTTIME_SYMBOLIC_PRINT, + BOOTTIME_CPU_LOAD); +} + +void boottime_mark(char *name) +{ + if (boottime_timer.get_time) + boottime_mark_core(name, + boottime_timer.get_time(), + BOOTTIME_NORMAL_PRINT, + BOOTTIME_CPU_LOAD); +} + +void __init boottime_activate(struct boottime_timer *bt) +{ + struct boottime_list *b; + int res = 0; + unsigned long flags; + + if (bt == NULL) { + printk(KERN_ERR + "boottime: error: bad configured\n"); + return; + } + + if (bt->get_time == NULL) { + printk(KERN_ERR + "boottime: error: you must provide a get_time() function\n"); + return; + } + memcpy(&boottime_timer, bt, sizeof(struct boottime_timer)); + + if (boottime_timer.init) + res = boottime_timer.init(); + + if (res) { + printk(KERN_ERR "boottime: initialization failed\n"); + return; + } + + if (boottime_arch_startup()) + printk(KERN_ERR + "boottime: arch specfic initialization failed\n"); + + spin_lock_irqsave(&boottime_list_lock, flags); + + if (!list_empty(&boottime_list)) { + + b = list_first_entry(&boottime_list, struct boottime_list, + list); + if (b) + time_bootloader_done = b->time; + } + + spin_unlock_irqrestore(&boottime_list_lock, flags); +} + +void __init boottime_system_up(void) +{ + system_up = true; +} + +void boottime_deactivate(void) +{ + struct boottime_list *b; + unsigned long flags; + + boottime_mark("execute_init+0x0/0x0"); + + boottime_done = true; + + spin_lock_irqsave(&boottime_list_lock, flags); + b = list_first_entry(&boottime_list, struct boottime_list, list); + spin_unlock_irqrestore(&boottime_list_lock, flags); + + time_kernel_done = b->time; + + if (boottime_timer.finalize) + boottime_timer.finalize(); +} + +#ifdef CONFIG_DEBUG_FS +static void boottime_debugfs_load(struct seq_file *s, + struct boottime_list *b, + struct boottime_list *p) +{ + int i; + unsigned long total_p, total_b; + unsigned long system_total, idle_total, irq_total, iowait_total; + unsigned long system_load, idle_load, irq_load, iowait_load; + + for_each_possible_cpu(i) { + total_b = (b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] + + b->cpu_usage[i].cpustat[CPUTIME_IDLE] + + b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] + + b->cpu_usage[i].cpustat[CPUTIME_IRQ]); + + total_p = (p->cpu_usage[i].cpustat[CPUTIME_SYSTEM] + + p->cpu_usage[i].cpustat[CPUTIME_IDLE] + + p->cpu_usage[i].cpustat[CPUTIME_IOWAIT] + + p->cpu_usage[i].cpustat[CPUTIME_IRQ]); + + if (total_b == total_p) + continue; + + system_total = b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] + - p->cpu_usage[i].cpustat[CPUTIME_SYSTEM]; + idle_total = b->cpu_usage[i].cpustat[CPUTIME_IDLE] + - p->cpu_usage[i].cpustat[CPUTIME_IDLE]; + irq_total = b->cpu_usage[i].cpustat[CPUTIME_IRQ] + - p->cpu_usage[i].cpustat[CPUTIME_IRQ]; + iowait_total = b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] + - p->cpu_usage[i].cpustat[CPUTIME_IOWAIT]; + + system_load = (100 * system_total / (total_b - total_p)); + idle_load = (100 * idle_total / (total_b - total_p)); + irq_load = (100 * irq_total / (total_b - total_p)); + iowait_load = (100 * iowait_total / (total_b - total_p)); + + seq_printf(s, + " cpu%d system: %lu%% idle: %lu%% iowait: %lu%% irq: %lu%%", + i, + system_load, + idle_load, + iowait_load, + irq_load); + } + seq_printf(s, "\n"); +} + +static void boottime_debugfs_print(struct seq_file *s, + struct boottime_list *b, + struct boottime_list *p) +{ + seq_printf(s, "[%5lu.%06lu] calling %s\n", + p->time / 1000000, + (p->time % 1000000), + p->name); + seq_printf(s, "[%5lu.%06lu] initcall %s returned 0 after %ld msecs.", + b->time / 1000000, + (b->time % 1000000), + p->name, (b->time - p->time) / 1000); + + if (p->cpu_load == BOOTTIME_NO_CPU_LOAD || + b->cpu_load == BOOTTIME_NO_CPU_LOAD) { + seq_printf(s, "\n"); + return; + } + + boottime_debugfs_load(s, b, p); +} + +static int boottime_debugfs_bootgraph_show(struct seq_file *s, void *iter) +{ + struct boottime_list *b, *p = NULL, *old_p = NULL; + enum boottime_filter_type filter = (int)s->private; + + list_for_each_entry_reverse(b, &boottime_list, list) { + if (p) { + if (!(filter == BOOTTIME_FILTER_OUT_ZERO && + (b->time - p->time) / 1000 == 0) + && !(filter == BOOTTIME_FILTER_OUT_LESS_100 && + (b->time - p->time) < 100 * 1000)) + boottime_debugfs_print(s, b, p); + old_p = p; + } + p = b; + } + + if (filter == BOOTTIME_FILTER_NOTHING && p) + boottime_debugfs_print(s, p, p); + + if (p) + seq_printf(s, "[%5lu.%06lu] Freeing init memory: 0K\n", + p->time / 1000000, p->time % 1000000); + return 0; +} + +static int boottime_debugfs_summary_show(struct seq_file *s, void *data) +{ + struct boottime_list *b, b_zero; + + if (time_bootloader_done) + seq_printf(s, "bootloader: %ld msecs\n", + time_bootloader_done / 1000); + + seq_printf(s, "kernel: %ld msecs\ntotal: %ld msecs\n", + (time_kernel_done - time_bootloader_done) / 1000, + time_kernel_done / 1000); + seq_printf(s, "kernel:"); + b = list_first_entry(&boottime_list, + struct boottime_list, list); + memset(&b_zero, 0, sizeof(struct boottime_list)); + boottime_debugfs_load(s, b, &b_zero); + + if (time_bootloader_done) + seq_printf(s, + "bootloader: cpu0 system: %d%% idle: %d%% iowait: 0%% irq: 0%%\n", + 100 - boottime_bootloader_idle(), + boottime_bootloader_idle()); + return 0; +} + +static int boottime_debugfs_bootgraph_open(struct inode *inode, + struct file *file) +{ + return single_open(file, + boottime_debugfs_bootgraph_show, + inode->i_private); +} + +static int boottime_debugfs_summary_open(struct inode *inode, + struct file *file) +{ + return single_open(file, + boottime_debugfs_summary_show, + inode->i_private); +} + +static const struct file_operations boottime_debugfs_bootgraph_operations = { + .open = boottime_debugfs_bootgraph_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct file_operations boottime_debugfs_summary_operations = { + .open = boottime_debugfs_summary_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +void boottime_debugfs_init(void) +{ + struct dentry *dir; + + dir = debugfs_create_dir("boottime", NULL); + + (void) debugfs_create_file("bootgraph", S_IFREG | S_IRUGO, + dir, (void *)BOOTTIME_FILTER_NOTHING, + &boottime_debugfs_bootgraph_operations); + (void) debugfs_create_file("bootgraph_all_except0", S_IFREG | S_IRUGO, + dir, (void *)BOOTTIME_FILTER_OUT_ZERO, + &boottime_debugfs_bootgraph_operations); + (void) debugfs_create_file("bootgraph_larger100", + S_IFREG | S_IRUGO, + dir, (void *)BOOTTIME_FILTER_OUT_LESS_100, + &boottime_debugfs_bootgraph_operations); + (void) debugfs_create_file("summary", S_IFREG | S_IRUGO, + dir, NULL, + &boottime_debugfs_summary_operations); +} +#else +#define boottime_debugfs_init(x) +#endif + +static ssize_t show_bootloader(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%ld\n", time_bootloader_done / 1000); +} + +static ssize_t show_kernel(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%ld\n", + (time_kernel_done - time_bootloader_done) / 1000); +} + +DEVICE_ATTR(kernel, 0444, show_kernel, NULL); +DEVICE_ATTR(bootloader, 0444, show_bootloader, NULL); + +static struct attribute *boottime_sysfs_entries[] = { + &dev_attr_kernel.attr, + &dev_attr_bootloader.attr, + NULL +}; + +static struct attribute_group boottime_attr_grp = { + .name = NULL, + .attrs = boottime_sysfs_entries, +}; + +static int __init boottime_init(void) +{ + struct kobject *boottime_kobj; + + boottime_kobj = kobject_create_and_add("boottime", NULL); + if (!boottime_kobj) { + printk(KERN_ERR "boottime: out of memory!\n"); + return -ENOMEM; + } + + if (sysfs_create_group(boottime_kobj, &boottime_attr_grp) < 0) { + kobject_put(boottime_kobj); + printk(KERN_ERR "boottime: Failed creating sysfs group\n"); + return -ENOMEM; + } + + boottime_debugfs_init(); + + return 0; +} + +late_initcall(boottime_init); diff --git a/init/main.c b/init/main.c index 313360f..c06afd0 100644 --- a/init/main.c +++ b/init/main.c @@ -69,6 +69,7 @@ #include <linux/slab.h> #include <linux/perf_event.h> #include <linux/file.h> +#include <linux/boottime.h> #include <asm/io.h> #include <asm/bugs.h> @@ -679,6 +680,8 @@ int __init_or_module do_one_initcall(initcall_t fn) int count = preempt_count(); int ret; + boottime_mark_symbolic(fn); + if (initcall_debug) ret = do_one_initcall_debug(fn); else @@ -804,6 +807,7 @@ static noinline int init_post(void) { /* need to finish all async __init code before freeing the memory */ async_synchronize_full(); + boottime_deactivate(); free_initmem(); mark_rodata_ro(); system_state = SYSTEM_RUNNING; @@ -863,6 +867,7 @@ static int __init kernel_init(void * unused) do_pre_smp_initcalls(); lockup_detector_init(); + boottime_system_up(); smp_init(); sched_init_smp(); @@ -885,6 +890,7 @@ static int __init kernel_init(void * unused) if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { ramdisk_execute_command = NULL; + boottime_mark("mount+0x0/0x0"); prepare_namespace(); } ^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time 2012-10-12 9:51 ` Lee Jones @ 2012-10-12 13:35 ` Dan Murphy -1 siblings, 0 replies; 33+ messages in thread From: Dan Murphy @ 2012-10-12 13:35 UTC (permalink / raw) To: linux-arm-kernel On a note on testing. OK this at least compiles on my system with 3.4. The weird thing is that I get the same exact kernel boot time for two different machines. When I cat the summary for the kernel. I am not profiling the bootloader time here as it is neglible so no ATAG argument. kernel: 3221288 msecs total: 3221288 msecs kernel: cpu0 system: 0% idle: 0% iowait: 0% irq: 0% cpu1 system: 0% idle: 0% iowait: 0% irq: 0% And I get no output from any of the bootgraph entries. Dan On 10/12/2012 04:51 AM, Lee Jones wrote: > Okay, please disgard the last patch. Lots of fixups since. > > Author: Lee Jones <lee.jones@linaro.org> > Date: Wed Jun 30 14:00:40 2010 +0200 > > Boottime: A tool for automatic measurement of kernel/bootloader boot time > > The overhead is very low and the results will be found under > sysfs/bootime, as well as detailed results in debugfs under > boottime/. The bootgraph* files are compatible with > scripts/bootgraph.pl. The reason for this patch is to provide > data (sysfs/boottime) suitable for automatic test-cases as > well as help for developers to reduce the boot time (debugfs). > > Based heavily on the original driver by Jonas Aaberg. > > Cc: Russell King <linux@arm.linux.org.uk> > Cc: Will Deacon <will.deacon@arm.com> > Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com> > Signed-off-by: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> > Signed-off-by: Lee Jones <lee.jones@linaro.org> > Reviewed-by: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com> > Tested-by: Mian Yousaf KAUKAB <mian.yousaf.kaukab@stericsson.com> > > diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile > index e8a4e58..8522356 100644 > --- a/arch/arm/common/Makefile > +++ b/arch/arm/common/Makefile > @@ -13,3 +13,4 @@ obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o > obj-$(CONFIG_SHARP_SCOOP) += scoop.o > obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o > obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o > +obj-$(CONFIG_BOOTTIME) += boottime.o > diff --git a/arch/arm/common/boottime.c b/arch/arm/common/boottime.c > new file mode 100644 > index 0000000..73e9e04 > --- /dev/null > +++ b/arch/arm/common/boottime.c > @@ -0,0 +1,46 @@ > +/* > + * Copyright (C) ST-Ericsson SA 2009-2010 > + * > + * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson > + * > + * License terms: GNU General Public License (GPL) version 2 > + * > + * Store boot times measured during for example u-boot startup. > + */ > + > +#include <linux/kernel.h> > +#include <linux/init.h> > +#include <linux/boottime.h> > +#include <linux/string.h> > +#include <asm/setup.h> > + > +static u32 bootloader_idle; > +static u32 bootloader_total; > + > +static int __init boottime_parse_tag(const struct tag *tag) > +{ > + int i; > + char buff[BOOTTIME_MAX_NAME_LEN]; > + > + bootloader_idle = tag->u.boottime.idle; > + bootloader_total = tag->u.boottime.total; > + > + for (i = 0; i < tag->u.boottime.num; i++) { > + snprintf(buff, BOOTTIME_MAX_NAME_LEN, "%s+0x0/0x0", > + tag->u.boottime.entry[i].name); > + buff[BOOTTIME_MAX_NAME_LEN - 1] = '\0'; > + boottime_mark_wtime(buff, tag->u.boottime.entry[i].time); > + } > + > + return 0; > +} > + > +__tagtable(ATAG_BOOTTIME, boottime_parse_tag); > + > +int boottime_bootloader_idle(void) > +{ > + if (bootloader_total == 0) > + return 0; > + > + return (int) ((bootloader_idle) / (bootloader_total / 100)); > +} > diff --git a/arch/arm/include/asm/setup.h b/arch/arm/include/asm/setup.h > index 24d284a..e8da062 100644 > --- a/arch/arm/include/asm/setup.h > +++ b/arch/arm/include/asm/setup.h > @@ -143,6 +143,23 @@ struct tag_memclk { > __u32 fmemclk; > }; > > +/* for automatic boot timing testcases */ > +#define ATAG_BOOTTIME 0x41000403 > +#define BOOTTIME_MAX_NAME_LEN 64 > +#define BOOTTIME_MAX 10 > + > +struct boottime_entry { > + u32 time; /* in us */ > + u8 name[BOOTTIME_MAX_NAME_LEN]; > +}; > + > +struct tag_boottime { > + struct boottime_entry entry[BOOTTIME_MAX]; > + u32 idle; /* in us */ > + u32 total; /* in us */ > + u8 num; > +}; > + > struct tag { > struct tag_header hdr; > union { > @@ -165,6 +182,10 @@ struct tag { > * DC21285 specific > */ > struct tag_memclk memclk; > + /* > + * Boot time > + */ > + struct tag_boottime boottime; > } u; > }; > > diff --git a/include/linux/boottime.h b/include/linux/boottime.h > new file mode 100644 > index 0000000..d330ecd > --- /dev/null > +++ b/include/linux/boottime.h > @@ -0,0 +1,89 @@ > +/* > + * Copyright (C) ST-Ericsson SA 2009-2010 > + * > + * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson > + * > + * License terms: GNU General Public License (GPL) version 2 > + * > + * boottime is a tool for collecting start-up timing > + * information and can together with boot loader support > + * display a total system start-up time. > + * > + */ > + > +#ifndef LINUX_BOOTTIME_H > +#define LINUX_BOOTTIME_H > + > +#ifdef CONFIG_BOOTTIME > +#include <linux/kernel.h> > + > +/** > + * struct boottime_timer - Callbacks for generic timer. > + * @init: Function to call at boottime initialization > + * @get_time: Returns the number of us since start-up > + * Preferable this is based upon a free running timer. > + * This is the only required entry. > + * @finalize: Called before init is executed and boottime is done. > + */ > +struct boottime_timer { > + int (*init)(void); > + unsigned long (*get_time)(void); > + void (*finalize)(void); > +}; > + > +/** > + * boottime_mark_wtime() > + * Add a sample point with a given time. Useful for adding data collected > + * by for example a boot loader. > + * @name: The name of the sample point > + * @time: The time in us when this point was reached > + */ > +void __init boottime_mark_wtime(char *name, unsigned long time); > + > +/** > + * boottime_mark() > + * Add a sample point with the current time. > + * @name: The name of this sample point > + */ > +void boottime_mark(char *name); > + > +/** > + * boottime_mark_symbolic() > + * Add a sample point where the name is a symbolic function > + * and %pF is needed to get the correct function name. > + * @name: function name. > + */ > +void __init boottime_mark_symbolic(void *name); > + > +/** > + * boottime_activate() > + * Activates boottime and register callbacks. > + * @bt: struct with callbacks. > + */ > +void __ref boottime_activate(struct boottime_timer *bt); > + > +/** > + * boottime_deactivate() > + * This function is called when the kernel boot is done. > + * (before "free init memory" is called) > + */ > +void boottime_deactivate(void); > + > +/** > + * boottime_system_up() > + * A function is called when the basics of the kernel > + * is up and running. > + */ > +void __init boottime_system_up(void); > + > +#else > + > +#define boottime_mark_wtime(name, time) > +#define boottime_mark(name) > +#define boottime_mark_symbolic(name) > +#define boottime_activate(bt) > +#define boottime_deactivate() > +#define boottime_system_up() > +#endif > + > +#endif /* LINUX_BOOTTIME_H */ > diff --git a/init/Kconfig b/init/Kconfig > index 4c93533..d0df8ff 100644 > --- a/init/Kconfig > +++ b/init/Kconfig > @@ -1464,6 +1464,15 @@ config PROFILING > Say Y here to enable the extended profiling support mechanisms used > by profilers such as OProfile. > > +config BOOTTIME > + bool "Boot time measurements" > + default n > + help > + Adds sysfs entries (boottime/) with start-up timing information. > + If CONFIG_DEBUG_FS is enabled, detailed information about the > + boot time, including system load during boot can be extracted. > + This information can be visualised with help of the bootgraph script. > + > # > # Place an empty function call at each tracepoint site. Can be > # dynamically changed for a probe function. > diff --git a/init/Makefile b/init/Makefile > index 7bc47ee..356d529 100644 > --- a/init/Makefile > +++ b/init/Makefile > @@ -9,6 +9,7 @@ else > obj-$(CONFIG_BLK_DEV_INITRD) += initramfs.o > endif > obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o > +obj-$(CONFIG_BOOTTIME) += boottime.o > > ifneq ($(CONFIG_ARCH_INIT_TASK),y) > obj-y += init_task.o > diff --git a/init/boottime.c b/init/boottime.c > new file mode 100644 > index 0000000..793c184 > --- /dev/null > +++ b/init/boottime.c > @@ -0,0 +1,475 @@ > +/* > + * Copyright (C) ST-Ericsson SA 2009-2010 > + * > + * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson > + * > + * License terms: GNU General Public License (GPL) version 2 > + * > + * boottime is a tool for collecting start-up timing > + * information and can together with boot loader support > + * display a total system start-up time. > + * > + */ > + > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/list.h> > +#include <linux/seq_file.h> > +#include <linux/debugfs.h> > +#include <linux/spinlock.h> > +#include <linux/boottime.h> > +#include <linux/kernel_stat.h> > +#include <linux/kobject.h> > +#include <linux/device.h> > +#include <linux/sysfs.h> > +#include <linux/slab.h> > + > +/* > + * BOOTTIME_MAX_NAME_LEN is defined in arch/arm/include/asm/setup.h to 64. > + * No crisis if they don't match. > + */ > +#ifndef BOOTTIME_MAX_NAME_LEN > +#define BOOTTIME_MAX_NAME_LEN 64 > +#endif > + > +/* > + * We have a few static entries, since it is good to have measure points > + * before the system is up and running properly > + */ > +#define NUM_STATIC_BOOTTIME_ENTRIES 32 > + > +struct boottime_list { > + struct list_head list; > + char name[BOOTTIME_MAX_NAME_LEN]; > + /* Time in us since power on, possible including boot loader. */ > + unsigned long time; > + bool cpu_load; > + struct kernel_cpustat cpu_usage[NR_CPUS]; > +}; > + > +enum boottime_filter_type { > + BOOTTIME_FILTER_OUT_ZERO, > + BOOTTIME_FILTER_OUT_LESS_100, > + BOOTTIME_FILTER_NOTHING, > +}; > + > +enum boottime_symbolic_print { > + BOOTTIME_SYMBOLIC_PRINT, > + BOOTTIME_NORMAL_PRINT, > +}; > + > +enum boottime_cpu_load { > + BOOTTIME_CPU_LOAD, > + BOOTTIME_NO_CPU_LOAD, > +}; > + > +static LIST_HEAD(boottime_list); > +static DEFINE_SPINLOCK(boottime_list_lock); > +static struct boottime_timer boottime_timer; > +static int num_const_boottime_list; > +static struct boottime_list const_boottime_list[NUM_STATIC_BOOTTIME_ENTRIES]; > +static unsigned long time_kernel_done; > +static unsigned long time_bootloader_done; > +static bool system_up; > +static bool boottime_done; > + > +int __attribute__((weak)) boottime_arch_startup(void) > +{ > + return 0; > +} > + > +int __attribute__((weak)) boottime_bootloader_idle(void) > +{ > + return 0; > +} > + > +static void boottime_mark_core(char *name, > + unsigned long time, > + enum boottime_symbolic_print symbolic, > + enum boottime_cpu_load cpu_load) > +{ > + struct boottime_list *b; > + unsigned long flags = 0; > + int i; > + > + if (system_up) { > + b = kmalloc(sizeof(struct boottime_list), GFP_KERNEL); > + if (!b) { > + printk(KERN_ERR > + "boottime: failed to allocate memory!\n"); > + return; > + } > + > + } else { > + if (num_const_boottime_list < NUM_STATIC_BOOTTIME_ENTRIES) { > + b = &const_boottime_list[num_const_boottime_list]; > + num_const_boottime_list++; > + } else { > + printk(KERN_ERR > + "boottime: too many early measure points!\n"); > + return; > + } > + } > + > + INIT_LIST_HEAD(&b->list); > + > + if (symbolic == BOOTTIME_SYMBOLIC_PRINT) > + snprintf(b->name, BOOTTIME_MAX_NAME_LEN, "%pF", name); > + else > + strncpy(b->name, name, BOOTTIME_MAX_NAME_LEN); > + > + b->name[BOOTTIME_MAX_NAME_LEN - 1] = '\0'; > + b->time = time; > + b->cpu_load = cpu_load; > + > + if (cpu_load == BOOTTIME_CPU_LOAD && system_up) > + for_each_possible_cpu(i) { > + b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] = > + kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]; > + b->cpu_usage[i].cpustat[CPUTIME_IDLE] = > + kcpustat_cpu(i).cpustat[CPUTIME_IDLE]; > + b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] = > + kcpustat_cpu(i).cpustat[CPUTIME_IOWAIT]; > + b->cpu_usage[i].cpustat[CPUTIME_IRQ] = > + kcpustat_cpu(i).cpustat[CPUTIME_IRQ]; > + /* > + * TODO: Make sure that user, nice, softirq, steal > + * and guest are not used during boot > + */ > + } > + else > + b->cpu_load = BOOTTIME_NO_CPU_LOAD; > + > + if (system_up) { > + spin_lock_irqsave(&boottime_list_lock, flags); > + list_add(&b->list, &boottime_list); > + spin_unlock_irqrestore(&boottime_list_lock, flags); > + } else { > + list_add(&b->list, &boottime_list); > + } > +} > + > +void __init boottime_mark_wtime(char *name, unsigned long time) > +{ > + boottime_mark_core(name, time, > + BOOTTIME_NORMAL_PRINT, > + BOOTTIME_NO_CPU_LOAD); > +} > + > +void __ref boottime_mark_symbolic(void *name) > +{ > + > + if (boottime_done) > + return; > + > + if (boottime_timer.get_time) > + boottime_mark_core((char *) name, > + boottime_timer.get_time(), > + BOOTTIME_SYMBOLIC_PRINT, > + BOOTTIME_CPU_LOAD); > +} > + > +void boottime_mark(char *name) > +{ > + if (boottime_timer.get_time) > + boottime_mark_core(name, > + boottime_timer.get_time(), > + BOOTTIME_NORMAL_PRINT, > + BOOTTIME_CPU_LOAD); > +} > + > +void __init boottime_activate(struct boottime_timer *bt) > +{ > + struct boottime_list *b; > + int res = 0; > + unsigned long flags; > + > + if (bt == NULL) { > + printk(KERN_ERR > + "boottime: error: bad configured\n"); > + return; > + } > + > + if (bt->get_time == NULL) { > + printk(KERN_ERR > + "boottime: error: you must provide a get_time() function\n"); > + return; > + } > + memcpy(&boottime_timer, bt, sizeof(struct boottime_timer)); > + > + if (boottime_timer.init) > + res = boottime_timer.init(); > + > + if (res) { > + printk(KERN_ERR "boottime: initialization failed\n"); > + return; > + } > + > + if (boottime_arch_startup()) > + printk(KERN_ERR > + "boottime: arch specfic initialization failed\n"); > + > + spin_lock_irqsave(&boottime_list_lock, flags); > + > + if (!list_empty(&boottime_list)) { > + > + b = list_first_entry(&boottime_list, struct boottime_list, > + list); > + if (b) > + time_bootloader_done = b->time; > + } > + > + spin_unlock_irqrestore(&boottime_list_lock, flags); > +} > + > +void __init boottime_system_up(void) > +{ > + system_up = true; > +} > + > +void boottime_deactivate(void) > +{ > + struct boottime_list *b; > + unsigned long flags; > + > + boottime_mark("execute_init+0x0/0x0"); > + > + boottime_done = true; > + > + spin_lock_irqsave(&boottime_list_lock, flags); > + b = list_first_entry(&boottime_list, struct boottime_list, list); > + spin_unlock_irqrestore(&boottime_list_lock, flags); > + > + time_kernel_done = b->time; > + > + if (boottime_timer.finalize) > + boottime_timer.finalize(); > +} > + > +#ifdef CONFIG_DEBUG_FS > +static void boottime_debugfs_load(struct seq_file *s, > + struct boottime_list *b, > + struct boottime_list *p) > +{ > + int i; > + unsigned long total_p, total_b; > + unsigned long system_total, idle_total, irq_total, iowait_total; > + unsigned long system_load, idle_load, irq_load, iowait_load; > + > + for_each_possible_cpu(i) { > + total_b = (b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] + > + b->cpu_usage[i].cpustat[CPUTIME_IDLE] + > + b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] + > + b->cpu_usage[i].cpustat[CPUTIME_IRQ]); > + > + total_p = (p->cpu_usage[i].cpustat[CPUTIME_SYSTEM] + > + p->cpu_usage[i].cpustat[CPUTIME_IDLE] + > + p->cpu_usage[i].cpustat[CPUTIME_IOWAIT] + > + p->cpu_usage[i].cpustat[CPUTIME_IRQ]); > + > + if (total_b == total_p) > + continue; > + > + system_total = b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] > + - p->cpu_usage[i].cpustat[CPUTIME_SYSTEM]; > + idle_total = b->cpu_usage[i].cpustat[CPUTIME_IDLE] > + - p->cpu_usage[i].cpustat[CPUTIME_IDLE]; > + irq_total = b->cpu_usage[i].cpustat[CPUTIME_IRQ] > + - p->cpu_usage[i].cpustat[CPUTIME_IRQ]; > + iowait_total = b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] > + - p->cpu_usage[i].cpustat[CPUTIME_IOWAIT]; > + > + system_load = (100 * system_total / (total_b - total_p)); > + idle_load = (100 * idle_total / (total_b - total_p)); > + irq_load = (100 * irq_total / (total_b - total_p)); > + iowait_load = (100 * iowait_total / (total_b - total_p)); > + > + seq_printf(s, > + " cpu%d system: %lu%% idle: %lu%% iowait: %lu%% irq: %lu%%", > + i, > + system_load, > + idle_load, > + iowait_load, > + irq_load); > + } > + seq_printf(s, "\n"); > +} > + > +static void boottime_debugfs_print(struct seq_file *s, > + struct boottime_list *b, > + struct boottime_list *p) > +{ > + seq_printf(s, "[%5lu.%06lu] calling %s\n", > + p->time / 1000000, > + (p->time % 1000000), > + p->name); > + seq_printf(s, "[%5lu.%06lu] initcall %s returned 0 after %ld msecs.", > + b->time / 1000000, > + (b->time % 1000000), > + p->name, (b->time - p->time) / 1000); > + > + if (p->cpu_load == BOOTTIME_NO_CPU_LOAD || > + b->cpu_load == BOOTTIME_NO_CPU_LOAD) { > + seq_printf(s, "\n"); > + return; > + } > + > + boottime_debugfs_load(s, b, p); > +} > + > +static int boottime_debugfs_bootgraph_show(struct seq_file *s, void *iter) > +{ > + struct boottime_list *b, *p = NULL, *old_p = NULL; > + enum boottime_filter_type filter = (int)s->private; > + > + list_for_each_entry_reverse(b, &boottime_list, list) { > + if (p) { > + if (!(filter == BOOTTIME_FILTER_OUT_ZERO && > + (b->time - p->time) / 1000 == 0) > + && !(filter == BOOTTIME_FILTER_OUT_LESS_100 && > + (b->time - p->time) < 100 * 1000)) > + boottime_debugfs_print(s, b, p); > + old_p = p; > + } > + p = b; > + } > + > + if (filter == BOOTTIME_FILTER_NOTHING && p) > + boottime_debugfs_print(s, p, p); > + > + if (p) > + seq_printf(s, "[%5lu.%06lu] Freeing init memory: 0K\n", > + p->time / 1000000, p->time % 1000000); > + return 0; > +} > + > +static int boottime_debugfs_summary_show(struct seq_file *s, void *data) > +{ > + struct boottime_list *b, b_zero; > + > + if (time_bootloader_done) > + seq_printf(s, "bootloader: %ld msecs\n", > + time_bootloader_done / 1000); > + > + seq_printf(s, "kernel: %ld msecs\ntotal: %ld msecs\n", > + (time_kernel_done - time_bootloader_done) / 1000, > + time_kernel_done / 1000); > + seq_printf(s, "kernel:"); > + b = list_first_entry(&boottime_list, > + struct boottime_list, list); > + memset(&b_zero, 0, sizeof(struct boottime_list)); > + boottime_debugfs_load(s, b, &b_zero); > + > + if (time_bootloader_done) > + seq_printf(s, > + "bootloader: cpu0 system: %d%% idle: %d%% iowait: 0%% irq: 0%%\n", > + 100 - boottime_bootloader_idle(), > + boottime_bootloader_idle()); > + return 0; > +} > + > +static int boottime_debugfs_bootgraph_open(struct inode *inode, > + struct file *file) > +{ > + return single_open(file, > + boottime_debugfs_bootgraph_show, > + inode->i_private); > +} > + > +static int boottime_debugfs_summary_open(struct inode *inode, > + struct file *file) > +{ > + return single_open(file, > + boottime_debugfs_summary_show, > + inode->i_private); > +} > + > +static const struct file_operations boottime_debugfs_bootgraph_operations = { > + .open = boottime_debugfs_bootgraph_open, > + .read = seq_read, > + .llseek = seq_lseek, > + .release = single_release, > +}; > + > +static const struct file_operations boottime_debugfs_summary_operations = { > + .open = boottime_debugfs_summary_open, > + .read = seq_read, > + .llseek = seq_lseek, > + .release = single_release, > +}; > + > +void boottime_debugfs_init(void) > +{ > + struct dentry *dir; > + > + dir = debugfs_create_dir("boottime", NULL); > + > + (void) debugfs_create_file("bootgraph", S_IFREG | S_IRUGO, > + dir, (void *)BOOTTIME_FILTER_NOTHING, > + &boottime_debugfs_bootgraph_operations); > + (void) debugfs_create_file("bootgraph_all_except0", S_IFREG | S_IRUGO, > + dir, (void *)BOOTTIME_FILTER_OUT_ZERO, > + &boottime_debugfs_bootgraph_operations); > + (void) debugfs_create_file("bootgraph_larger100", > + S_IFREG | S_IRUGO, > + dir, (void *)BOOTTIME_FILTER_OUT_LESS_100, > + &boottime_debugfs_bootgraph_operations); > + (void) debugfs_create_file("summary", S_IFREG | S_IRUGO, > + dir, NULL, > + &boottime_debugfs_summary_operations); > +} > +#else > +#define boottime_debugfs_init(x) > +#endif > + > +static ssize_t show_bootloader(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + return sprintf(buf, "%ld\n", time_bootloader_done / 1000); > +} > + > +static ssize_t show_kernel(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + return sprintf(buf, "%ld\n", > + (time_kernel_done - time_bootloader_done) / 1000); > +} > + > +DEVICE_ATTR(kernel, 0444, show_kernel, NULL); > +DEVICE_ATTR(bootloader, 0444, show_bootloader, NULL); > + > +static struct attribute *boottime_sysfs_entries[] = { > + &dev_attr_kernel.attr, > + &dev_attr_bootloader.attr, > + NULL > +}; > + > +static struct attribute_group boottime_attr_grp = { > + .name = NULL, > + .attrs = boottime_sysfs_entries, > +}; > + > +static int __init boottime_init(void) > +{ > + struct kobject *boottime_kobj; > + > + boottime_kobj = kobject_create_and_add("boottime", NULL); > + if (!boottime_kobj) { > + printk(KERN_ERR "boottime: out of memory!\n"); > + return -ENOMEM; > + } > + > + if (sysfs_create_group(boottime_kobj, &boottime_attr_grp) < 0) { > + kobject_put(boottime_kobj); > + printk(KERN_ERR "boottime: Failed creating sysfs group\n"); > + return -ENOMEM; > + } > + > + boottime_debugfs_init(); > + > + return 0; > +} > + > +late_initcall(boottime_init); > diff --git a/init/main.c b/init/main.c > index 313360f..c06afd0 100644 > --- a/init/main.c > +++ b/init/main.c > @@ -69,6 +69,7 @@ > #include <linux/slab.h> > #include <linux/perf_event.h> > #include <linux/file.h> > +#include <linux/boottime.h> > > #include <asm/io.h> > #include <asm/bugs.h> > @@ -679,6 +680,8 @@ int __init_or_module do_one_initcall(initcall_t fn) > int count = preempt_count(); > int ret; > > + boottime_mark_symbolic(fn); > + > if (initcall_debug) > ret = do_one_initcall_debug(fn); > else > @@ -804,6 +807,7 @@ static noinline int init_post(void) > { > /* need to finish all async __init code before freeing the memory */ > async_synchronize_full(); > + boottime_deactivate(); > free_initmem(); > mark_rodata_ro(); > system_state = SYSTEM_RUNNING; > @@ -863,6 +867,7 @@ static int __init kernel_init(void * unused) > > do_pre_smp_initcalls(); > lockup_detector_init(); > + boottime_system_up(); > > smp_init(); > sched_init_smp(); > @@ -885,6 +890,7 @@ static int __init kernel_init(void * unused) > > if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { > ramdisk_execute_command = NULL; > + boottime_mark("mount+0x0/0x0"); > prepare_namespace(); > } ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time @ 2012-10-12 13:35 ` Dan Murphy 0 siblings, 0 replies; 33+ messages in thread From: Dan Murphy @ 2012-10-12 13:35 UTC (permalink / raw) To: Lee Jones Cc: Nishanth Menon, linux-arm-kernel, linux-kernel, Russell King, linus.walleij, Jonas Aaberg, Will Deacon, arnd, Mian Yousaf Kaukab On a note on testing. OK this at least compiles on my system with 3.4. The weird thing is that I get the same exact kernel boot time for two different machines. When I cat the summary for the kernel. I am not profiling the bootloader time here as it is neglible so no ATAG argument. kernel: 3221288 msecs total: 3221288 msecs kernel: cpu0 system: 0% idle: 0% iowait: 0% irq: 0% cpu1 system: 0% idle: 0% iowait: 0% irq: 0% And I get no output from any of the bootgraph entries. Dan On 10/12/2012 04:51 AM, Lee Jones wrote: > Okay, please disgard the last patch. Lots of fixups since. > > Author: Lee Jones <lee.jones@linaro.org> > Date: Wed Jun 30 14:00:40 2010 +0200 > > Boottime: A tool for automatic measurement of kernel/bootloader boot time > > The overhead is very low and the results will be found under > sysfs/bootime, as well as detailed results in debugfs under > boottime/. The bootgraph* files are compatible with > scripts/bootgraph.pl. The reason for this patch is to provide > data (sysfs/boottime) suitable for automatic test-cases as > well as help for developers to reduce the boot time (debugfs). > > Based heavily on the original driver by Jonas Aaberg. > > Cc: Russell King <linux@arm.linux.org.uk> > Cc: Will Deacon <will.deacon@arm.com> > Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com> > Signed-off-by: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> > Signed-off-by: Lee Jones <lee.jones@linaro.org> > Reviewed-by: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com> > Tested-by: Mian Yousaf KAUKAB <mian.yousaf.kaukab@stericsson.com> > > diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile > index e8a4e58..8522356 100644 > --- a/arch/arm/common/Makefile > +++ b/arch/arm/common/Makefile > @@ -13,3 +13,4 @@ obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o > obj-$(CONFIG_SHARP_SCOOP) += scoop.o > obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o > obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o > +obj-$(CONFIG_BOOTTIME) += boottime.o > diff --git a/arch/arm/common/boottime.c b/arch/arm/common/boottime.c > new file mode 100644 > index 0000000..73e9e04 > --- /dev/null > +++ b/arch/arm/common/boottime.c > @@ -0,0 +1,46 @@ > +/* > + * Copyright (C) ST-Ericsson SA 2009-2010 > + * > + * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson > + * > + * License terms: GNU General Public License (GPL) version 2 > + * > + * Store boot times measured during for example u-boot startup. > + */ > + > +#include <linux/kernel.h> > +#include <linux/init.h> > +#include <linux/boottime.h> > +#include <linux/string.h> > +#include <asm/setup.h> > + > +static u32 bootloader_idle; > +static u32 bootloader_total; > + > +static int __init boottime_parse_tag(const struct tag *tag) > +{ > + int i; > + char buff[BOOTTIME_MAX_NAME_LEN]; > + > + bootloader_idle = tag->u.boottime.idle; > + bootloader_total = tag->u.boottime.total; > + > + for (i = 0; i < tag->u.boottime.num; i++) { > + snprintf(buff, BOOTTIME_MAX_NAME_LEN, "%s+0x0/0x0", > + tag->u.boottime.entry[i].name); > + buff[BOOTTIME_MAX_NAME_LEN - 1] = '\0'; > + boottime_mark_wtime(buff, tag->u.boottime.entry[i].time); > + } > + > + return 0; > +} > + > +__tagtable(ATAG_BOOTTIME, boottime_parse_tag); > + > +int boottime_bootloader_idle(void) > +{ > + if (bootloader_total == 0) > + return 0; > + > + return (int) ((bootloader_idle) / (bootloader_total / 100)); > +} > diff --git a/arch/arm/include/asm/setup.h b/arch/arm/include/asm/setup.h > index 24d284a..e8da062 100644 > --- a/arch/arm/include/asm/setup.h > +++ b/arch/arm/include/asm/setup.h > @@ -143,6 +143,23 @@ struct tag_memclk { > __u32 fmemclk; > }; > > +/* for automatic boot timing testcases */ > +#define ATAG_BOOTTIME 0x41000403 > +#define BOOTTIME_MAX_NAME_LEN 64 > +#define BOOTTIME_MAX 10 > + > +struct boottime_entry { > + u32 time; /* in us */ > + u8 name[BOOTTIME_MAX_NAME_LEN]; > +}; > + > +struct tag_boottime { > + struct boottime_entry entry[BOOTTIME_MAX]; > + u32 idle; /* in us */ > + u32 total; /* in us */ > + u8 num; > +}; > + > struct tag { > struct tag_header hdr; > union { > @@ -165,6 +182,10 @@ struct tag { > * DC21285 specific > */ > struct tag_memclk memclk; > + /* > + * Boot time > + */ > + struct tag_boottime boottime; > } u; > }; > > diff --git a/include/linux/boottime.h b/include/linux/boottime.h > new file mode 100644 > index 0000000..d330ecd > --- /dev/null > +++ b/include/linux/boottime.h > @@ -0,0 +1,89 @@ > +/* > + * Copyright (C) ST-Ericsson SA 2009-2010 > + * > + * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson > + * > + * License terms: GNU General Public License (GPL) version 2 > + * > + * boottime is a tool for collecting start-up timing > + * information and can together with boot loader support > + * display a total system start-up time. > + * > + */ > + > +#ifndef LINUX_BOOTTIME_H > +#define LINUX_BOOTTIME_H > + > +#ifdef CONFIG_BOOTTIME > +#include <linux/kernel.h> > + > +/** > + * struct boottime_timer - Callbacks for generic timer. > + * @init: Function to call at boottime initialization > + * @get_time: Returns the number of us since start-up > + * Preferable this is based upon a free running timer. > + * This is the only required entry. > + * @finalize: Called before init is executed and boottime is done. > + */ > +struct boottime_timer { > + int (*init)(void); > + unsigned long (*get_time)(void); > + void (*finalize)(void); > +}; > + > +/** > + * boottime_mark_wtime() > + * Add a sample point with a given time. Useful for adding data collected > + * by for example a boot loader. > + * @name: The name of the sample point > + * @time: The time in us when this point was reached > + */ > +void __init boottime_mark_wtime(char *name, unsigned long time); > + > +/** > + * boottime_mark() > + * Add a sample point with the current time. > + * @name: The name of this sample point > + */ > +void boottime_mark(char *name); > + > +/** > + * boottime_mark_symbolic() > + * Add a sample point where the name is a symbolic function > + * and %pF is needed to get the correct function name. > + * @name: function name. > + */ > +void __init boottime_mark_symbolic(void *name); > + > +/** > + * boottime_activate() > + * Activates boottime and register callbacks. > + * @bt: struct with callbacks. > + */ > +void __ref boottime_activate(struct boottime_timer *bt); > + > +/** > + * boottime_deactivate() > + * This function is called when the kernel boot is done. > + * (before "free init memory" is called) > + */ > +void boottime_deactivate(void); > + > +/** > + * boottime_system_up() > + * A function is called when the basics of the kernel > + * is up and running. > + */ > +void __init boottime_system_up(void); > + > +#else > + > +#define boottime_mark_wtime(name, time) > +#define boottime_mark(name) > +#define boottime_mark_symbolic(name) > +#define boottime_activate(bt) > +#define boottime_deactivate() > +#define boottime_system_up() > +#endif > + > +#endif /* LINUX_BOOTTIME_H */ > diff --git a/init/Kconfig b/init/Kconfig > index 4c93533..d0df8ff 100644 > --- a/init/Kconfig > +++ b/init/Kconfig > @@ -1464,6 +1464,15 @@ config PROFILING > Say Y here to enable the extended profiling support mechanisms used > by profilers such as OProfile. > > +config BOOTTIME > + bool "Boot time measurements" > + default n > + help > + Adds sysfs entries (boottime/) with start-up timing information. > + If CONFIG_DEBUG_FS is enabled, detailed information about the > + boot time, including system load during boot can be extracted. > + This information can be visualised with help of the bootgraph script. > + > # > # Place an empty function call at each tracepoint site. Can be > # dynamically changed for a probe function. > diff --git a/init/Makefile b/init/Makefile > index 7bc47ee..356d529 100644 > --- a/init/Makefile > +++ b/init/Makefile > @@ -9,6 +9,7 @@ else > obj-$(CONFIG_BLK_DEV_INITRD) += initramfs.o > endif > obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o > +obj-$(CONFIG_BOOTTIME) += boottime.o > > ifneq ($(CONFIG_ARCH_INIT_TASK),y) > obj-y += init_task.o > diff --git a/init/boottime.c b/init/boottime.c > new file mode 100644 > index 0000000..793c184 > --- /dev/null > +++ b/init/boottime.c > @@ -0,0 +1,475 @@ > +/* > + * Copyright (C) ST-Ericsson SA 2009-2010 > + * > + * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson > + * > + * License terms: GNU General Public License (GPL) version 2 > + * > + * boottime is a tool for collecting start-up timing > + * information and can together with boot loader support > + * display a total system start-up time. > + * > + */ > + > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/list.h> > +#include <linux/seq_file.h> > +#include <linux/debugfs.h> > +#include <linux/spinlock.h> > +#include <linux/boottime.h> > +#include <linux/kernel_stat.h> > +#include <linux/kobject.h> > +#include <linux/device.h> > +#include <linux/sysfs.h> > +#include <linux/slab.h> > + > +/* > + * BOOTTIME_MAX_NAME_LEN is defined in arch/arm/include/asm/setup.h to 64. > + * No crisis if they don't match. > + */ > +#ifndef BOOTTIME_MAX_NAME_LEN > +#define BOOTTIME_MAX_NAME_LEN 64 > +#endif > + > +/* > + * We have a few static entries, since it is good to have measure points > + * before the system is up and running properly > + */ > +#define NUM_STATIC_BOOTTIME_ENTRIES 32 > + > +struct boottime_list { > + struct list_head list; > + char name[BOOTTIME_MAX_NAME_LEN]; > + /* Time in us since power on, possible including boot loader. */ > + unsigned long time; > + bool cpu_load; > + struct kernel_cpustat cpu_usage[NR_CPUS]; > +}; > + > +enum boottime_filter_type { > + BOOTTIME_FILTER_OUT_ZERO, > + BOOTTIME_FILTER_OUT_LESS_100, > + BOOTTIME_FILTER_NOTHING, > +}; > + > +enum boottime_symbolic_print { > + BOOTTIME_SYMBOLIC_PRINT, > + BOOTTIME_NORMAL_PRINT, > +}; > + > +enum boottime_cpu_load { > + BOOTTIME_CPU_LOAD, > + BOOTTIME_NO_CPU_LOAD, > +}; > + > +static LIST_HEAD(boottime_list); > +static DEFINE_SPINLOCK(boottime_list_lock); > +static struct boottime_timer boottime_timer; > +static int num_const_boottime_list; > +static struct boottime_list const_boottime_list[NUM_STATIC_BOOTTIME_ENTRIES]; > +static unsigned long time_kernel_done; > +static unsigned long time_bootloader_done; > +static bool system_up; > +static bool boottime_done; > + > +int __attribute__((weak)) boottime_arch_startup(void) > +{ > + return 0; > +} > + > +int __attribute__((weak)) boottime_bootloader_idle(void) > +{ > + return 0; > +} > + > +static void boottime_mark_core(char *name, > + unsigned long time, > + enum boottime_symbolic_print symbolic, > + enum boottime_cpu_load cpu_load) > +{ > + struct boottime_list *b; > + unsigned long flags = 0; > + int i; > + > + if (system_up) { > + b = kmalloc(sizeof(struct boottime_list), GFP_KERNEL); > + if (!b) { > + printk(KERN_ERR > + "boottime: failed to allocate memory!\n"); > + return; > + } > + > + } else { > + if (num_const_boottime_list < NUM_STATIC_BOOTTIME_ENTRIES) { > + b = &const_boottime_list[num_const_boottime_list]; > + num_const_boottime_list++; > + } else { > + printk(KERN_ERR > + "boottime: too many early measure points!\n"); > + return; > + } > + } > + > + INIT_LIST_HEAD(&b->list); > + > + if (symbolic == BOOTTIME_SYMBOLIC_PRINT) > + snprintf(b->name, BOOTTIME_MAX_NAME_LEN, "%pF", name); > + else > + strncpy(b->name, name, BOOTTIME_MAX_NAME_LEN); > + > + b->name[BOOTTIME_MAX_NAME_LEN - 1] = '\0'; > + b->time = time; > + b->cpu_load = cpu_load; > + > + if (cpu_load == BOOTTIME_CPU_LOAD && system_up) > + for_each_possible_cpu(i) { > + b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] = > + kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]; > + b->cpu_usage[i].cpustat[CPUTIME_IDLE] = > + kcpustat_cpu(i).cpustat[CPUTIME_IDLE]; > + b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] = > + kcpustat_cpu(i).cpustat[CPUTIME_IOWAIT]; > + b->cpu_usage[i].cpustat[CPUTIME_IRQ] = > + kcpustat_cpu(i).cpustat[CPUTIME_IRQ]; > + /* > + * TODO: Make sure that user, nice, softirq, steal > + * and guest are not used during boot > + */ > + } > + else > + b->cpu_load = BOOTTIME_NO_CPU_LOAD; > + > + if (system_up) { > + spin_lock_irqsave(&boottime_list_lock, flags); > + list_add(&b->list, &boottime_list); > + spin_unlock_irqrestore(&boottime_list_lock, flags); > + } else { > + list_add(&b->list, &boottime_list); > + } > +} > + > +void __init boottime_mark_wtime(char *name, unsigned long time) > +{ > + boottime_mark_core(name, time, > + BOOTTIME_NORMAL_PRINT, > + BOOTTIME_NO_CPU_LOAD); > +} > + > +void __ref boottime_mark_symbolic(void *name) > +{ > + > + if (boottime_done) > + return; > + > + if (boottime_timer.get_time) > + boottime_mark_core((char *) name, > + boottime_timer.get_time(), > + BOOTTIME_SYMBOLIC_PRINT, > + BOOTTIME_CPU_LOAD); > +} > + > +void boottime_mark(char *name) > +{ > + if (boottime_timer.get_time) > + boottime_mark_core(name, > + boottime_timer.get_time(), > + BOOTTIME_NORMAL_PRINT, > + BOOTTIME_CPU_LOAD); > +} > + > +void __init boottime_activate(struct boottime_timer *bt) > +{ > + struct boottime_list *b; > + int res = 0; > + unsigned long flags; > + > + if (bt == NULL) { > + printk(KERN_ERR > + "boottime: error: bad configured\n"); > + return; > + } > + > + if (bt->get_time == NULL) { > + printk(KERN_ERR > + "boottime: error: you must provide a get_time() function\n"); > + return; > + } > + memcpy(&boottime_timer, bt, sizeof(struct boottime_timer)); > + > + if (boottime_timer.init) > + res = boottime_timer.init(); > + > + if (res) { > + printk(KERN_ERR "boottime: initialization failed\n"); > + return; > + } > + > + if (boottime_arch_startup()) > + printk(KERN_ERR > + "boottime: arch specfic initialization failed\n"); > + > + spin_lock_irqsave(&boottime_list_lock, flags); > + > + if (!list_empty(&boottime_list)) { > + > + b = list_first_entry(&boottime_list, struct boottime_list, > + list); > + if (b) > + time_bootloader_done = b->time; > + } > + > + spin_unlock_irqrestore(&boottime_list_lock, flags); > +} > + > +void __init boottime_system_up(void) > +{ > + system_up = true; > +} > + > +void boottime_deactivate(void) > +{ > + struct boottime_list *b; > + unsigned long flags; > + > + boottime_mark("execute_init+0x0/0x0"); > + > + boottime_done = true; > + > + spin_lock_irqsave(&boottime_list_lock, flags); > + b = list_first_entry(&boottime_list, struct boottime_list, list); > + spin_unlock_irqrestore(&boottime_list_lock, flags); > + > + time_kernel_done = b->time; > + > + if (boottime_timer.finalize) > + boottime_timer.finalize(); > +} > + > +#ifdef CONFIG_DEBUG_FS > +static void boottime_debugfs_load(struct seq_file *s, > + struct boottime_list *b, > + struct boottime_list *p) > +{ > + int i; > + unsigned long total_p, total_b; > + unsigned long system_total, idle_total, irq_total, iowait_total; > + unsigned long system_load, idle_load, irq_load, iowait_load; > + > + for_each_possible_cpu(i) { > + total_b = (b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] + > + b->cpu_usage[i].cpustat[CPUTIME_IDLE] + > + b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] + > + b->cpu_usage[i].cpustat[CPUTIME_IRQ]); > + > + total_p = (p->cpu_usage[i].cpustat[CPUTIME_SYSTEM] + > + p->cpu_usage[i].cpustat[CPUTIME_IDLE] + > + p->cpu_usage[i].cpustat[CPUTIME_IOWAIT] + > + p->cpu_usage[i].cpustat[CPUTIME_IRQ]); > + > + if (total_b == total_p) > + continue; > + > + system_total = b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] > + - p->cpu_usage[i].cpustat[CPUTIME_SYSTEM]; > + idle_total = b->cpu_usage[i].cpustat[CPUTIME_IDLE] > + - p->cpu_usage[i].cpustat[CPUTIME_IDLE]; > + irq_total = b->cpu_usage[i].cpustat[CPUTIME_IRQ] > + - p->cpu_usage[i].cpustat[CPUTIME_IRQ]; > + iowait_total = b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] > + - p->cpu_usage[i].cpustat[CPUTIME_IOWAIT]; > + > + system_load = (100 * system_total / (total_b - total_p)); > + idle_load = (100 * idle_total / (total_b - total_p)); > + irq_load = (100 * irq_total / (total_b - total_p)); > + iowait_load = (100 * iowait_total / (total_b - total_p)); > + > + seq_printf(s, > + " cpu%d system: %lu%% idle: %lu%% iowait: %lu%% irq: %lu%%", > + i, > + system_load, > + idle_load, > + iowait_load, > + irq_load); > + } > + seq_printf(s, "\n"); > +} > + > +static void boottime_debugfs_print(struct seq_file *s, > + struct boottime_list *b, > + struct boottime_list *p) > +{ > + seq_printf(s, "[%5lu.%06lu] calling %s\n", > + p->time / 1000000, > + (p->time % 1000000), > + p->name); > + seq_printf(s, "[%5lu.%06lu] initcall %s returned 0 after %ld msecs.", > + b->time / 1000000, > + (b->time % 1000000), > + p->name, (b->time - p->time) / 1000); > + > + if (p->cpu_load == BOOTTIME_NO_CPU_LOAD || > + b->cpu_load == BOOTTIME_NO_CPU_LOAD) { > + seq_printf(s, "\n"); > + return; > + } > + > + boottime_debugfs_load(s, b, p); > +} > + > +static int boottime_debugfs_bootgraph_show(struct seq_file *s, void *iter) > +{ > + struct boottime_list *b, *p = NULL, *old_p = NULL; > + enum boottime_filter_type filter = (int)s->private; > + > + list_for_each_entry_reverse(b, &boottime_list, list) { > + if (p) { > + if (!(filter == BOOTTIME_FILTER_OUT_ZERO && > + (b->time - p->time) / 1000 == 0) > + && !(filter == BOOTTIME_FILTER_OUT_LESS_100 && > + (b->time - p->time) < 100 * 1000)) > + boottime_debugfs_print(s, b, p); > + old_p = p; > + } > + p = b; > + } > + > + if (filter == BOOTTIME_FILTER_NOTHING && p) > + boottime_debugfs_print(s, p, p); > + > + if (p) > + seq_printf(s, "[%5lu.%06lu] Freeing init memory: 0K\n", > + p->time / 1000000, p->time % 1000000); > + return 0; > +} > + > +static int boottime_debugfs_summary_show(struct seq_file *s, void *data) > +{ > + struct boottime_list *b, b_zero; > + > + if (time_bootloader_done) > + seq_printf(s, "bootloader: %ld msecs\n", > + time_bootloader_done / 1000); > + > + seq_printf(s, "kernel: %ld msecs\ntotal: %ld msecs\n", > + (time_kernel_done - time_bootloader_done) / 1000, > + time_kernel_done / 1000); > + seq_printf(s, "kernel:"); > + b = list_first_entry(&boottime_list, > + struct boottime_list, list); > + memset(&b_zero, 0, sizeof(struct boottime_list)); > + boottime_debugfs_load(s, b, &b_zero); > + > + if (time_bootloader_done) > + seq_printf(s, > + "bootloader: cpu0 system: %d%% idle: %d%% iowait: 0%% irq: 0%%\n", > + 100 - boottime_bootloader_idle(), > + boottime_bootloader_idle()); > + return 0; > +} > + > +static int boottime_debugfs_bootgraph_open(struct inode *inode, > + struct file *file) > +{ > + return single_open(file, > + boottime_debugfs_bootgraph_show, > + inode->i_private); > +} > + > +static int boottime_debugfs_summary_open(struct inode *inode, > + struct file *file) > +{ > + return single_open(file, > + boottime_debugfs_summary_show, > + inode->i_private); > +} > + > +static const struct file_operations boottime_debugfs_bootgraph_operations = { > + .open = boottime_debugfs_bootgraph_open, > + .read = seq_read, > + .llseek = seq_lseek, > + .release = single_release, > +}; > + > +static const struct file_operations boottime_debugfs_summary_operations = { > + .open = boottime_debugfs_summary_open, > + .read = seq_read, > + .llseek = seq_lseek, > + .release = single_release, > +}; > + > +void boottime_debugfs_init(void) > +{ > + struct dentry *dir; > + > + dir = debugfs_create_dir("boottime", NULL); > + > + (void) debugfs_create_file("bootgraph", S_IFREG | S_IRUGO, > + dir, (void *)BOOTTIME_FILTER_NOTHING, > + &boottime_debugfs_bootgraph_operations); > + (void) debugfs_create_file("bootgraph_all_except0", S_IFREG | S_IRUGO, > + dir, (void *)BOOTTIME_FILTER_OUT_ZERO, > + &boottime_debugfs_bootgraph_operations); > + (void) debugfs_create_file("bootgraph_larger100", > + S_IFREG | S_IRUGO, > + dir, (void *)BOOTTIME_FILTER_OUT_LESS_100, > + &boottime_debugfs_bootgraph_operations); > + (void) debugfs_create_file("summary", S_IFREG | S_IRUGO, > + dir, NULL, > + &boottime_debugfs_summary_operations); > +} > +#else > +#define boottime_debugfs_init(x) > +#endif > + > +static ssize_t show_bootloader(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + return sprintf(buf, "%ld\n", time_bootloader_done / 1000); > +} > + > +static ssize_t show_kernel(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + return sprintf(buf, "%ld\n", > + (time_kernel_done - time_bootloader_done) / 1000); > +} > + > +DEVICE_ATTR(kernel, 0444, show_kernel, NULL); > +DEVICE_ATTR(bootloader, 0444, show_bootloader, NULL); > + > +static struct attribute *boottime_sysfs_entries[] = { > + &dev_attr_kernel.attr, > + &dev_attr_bootloader.attr, > + NULL > +}; > + > +static struct attribute_group boottime_attr_grp = { > + .name = NULL, > + .attrs = boottime_sysfs_entries, > +}; > + > +static int __init boottime_init(void) > +{ > + struct kobject *boottime_kobj; > + > + boottime_kobj = kobject_create_and_add("boottime", NULL); > + if (!boottime_kobj) { > + printk(KERN_ERR "boottime: out of memory!\n"); > + return -ENOMEM; > + } > + > + if (sysfs_create_group(boottime_kobj, &boottime_attr_grp) < 0) { > + kobject_put(boottime_kobj); > + printk(KERN_ERR "boottime: Failed creating sysfs group\n"); > + return -ENOMEM; > + } > + > + boottime_debugfs_init(); > + > + return 0; > +} > + > +late_initcall(boottime_init); > diff --git a/init/main.c b/init/main.c > index 313360f..c06afd0 100644 > --- a/init/main.c > +++ b/init/main.c > @@ -69,6 +69,7 @@ > #include <linux/slab.h> > #include <linux/perf_event.h> > #include <linux/file.h> > +#include <linux/boottime.h> > > #include <asm/io.h> > #include <asm/bugs.h> > @@ -679,6 +680,8 @@ int __init_or_module do_one_initcall(initcall_t fn) > int count = preempt_count(); > int ret; > > + boottime_mark_symbolic(fn); > + > if (initcall_debug) > ret = do_one_initcall_debug(fn); > else > @@ -804,6 +807,7 @@ static noinline int init_post(void) > { > /* need to finish all async __init code before freeing the memory */ > async_synchronize_full(); > + boottime_deactivate(); > free_initmem(); > mark_rodata_ro(); > system_state = SYSTEM_RUNNING; > @@ -863,6 +867,7 @@ static int __init kernel_init(void * unused) > > do_pre_smp_initcalls(); > lockup_detector_init(); > + boottime_system_up(); > > smp_init(); > sched_init_smp(); > @@ -885,6 +890,7 @@ static int __init kernel_init(void * unused) > > if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { > ramdisk_execute_command = NULL; > + boottime_mark("mount+0x0/0x0"); > prepare_namespace(); > } ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time 2012-10-12 13:35 ` Dan Murphy @ 2012-10-12 13:45 ` Lee Jones -1 siblings, 0 replies; 33+ messages in thread From: Lee Jones @ 2012-10-12 13:45 UTC (permalink / raw) To: linux-arm-kernel On Fri, 12 Oct 2012, Dan Murphy wrote: > On a note on testing. > > OK this at least compiles on my system with 3.4. > > The weird thing is that I get the same exact kernel boot time for > two different machines. > When I cat the summary for the kernel. > I am not profiling the bootloader time here as it is neglible so no > ATAG argument. > > kernel: 3221288 msecs > total: 3221288 msecs > kernel: cpu0 system: 0% idle: 0% iowait: 0% irq: 0% cpu1 system: 0% > idle: 0% iowait: 0% irq: 0% > > And I get no output from any of the bootgraph entries. That's odd. I get: root at ME:/ cat sys/boottime/bootloader 0 root at ME:/ cat sys/boottime/kernel 4276 root at ME:/ cat /sys/kernel/debug/boottime/summary kernel: 4276 msecs total: 4276 msecs root at ME:/ cat /sys/kernel/debug/boottime/bootgraph [ 0.185254] calling splash+0x0/0x0 [ 2.984335] initcall splash+0x0/0x0 returned 0 after 2799 msecs. [ 2.984335] calling autoboot_delay+0x0/0x0 [ 4.089513] initcall autoboot_delay+0x0/0x0 returned 0 after 1105 msecs. [ 4.089513] calling load_kernel+0x0/0x0 [ 4.239174] initcall load_kernel+0x0/0x0 returned 0 after 149 msecs. [ 4.239174] calling boot_kernel+0x0/0x0 [ 4.276260] initcall boot_kernel+0x0/0x0 returned 0 after 37 msecs. [ 4.276260] calling uncompress_ll_init+0x0/0x0 [ 4.276260] initcall uncompress_ll_init+0x0/0x0 returned 0 after 0 msecs. [ 4.276260] Freeing init memory: 0K > >Author: Lee Jones <lee.jones@linaro.org> > >Date: Wed Jun 30 14:00:40 2010 +0200 > > > > Boottime: A tool for automatic measurement of kernel/bootloader boot time > > The overhead is very low and the results will be found under > > sysfs/bootime, as well as detailed results in debugfs under > > boottime/. The bootgraph* files are compatible with > > scripts/bootgraph.pl. The reason for this patch is to provide > > data (sysfs/boottime) suitable for automatic test-cases as > > well as help for developers to reduce the boot time (debugfs). > > Based heavily on the original driver by Jonas Aaberg. > > Cc: Russell King <linux@arm.linux.org.uk> > > Cc: Will Deacon <will.deacon@arm.com> > > Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com> > > Signed-off-by: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> > > Signed-off-by: Lee Jones <lee.jones@linaro.org> > > Reviewed-by: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com> > > Tested-by: Mian Yousaf KAUKAB <mian.yousaf.kaukab@stericsson.com> > > > >diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile > >index e8a4e58..8522356 100644 > >--- a/arch/arm/common/Makefile > >+++ b/arch/arm/common/Makefile > >@@ -13,3 +13,4 @@ obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o > > obj-$(CONFIG_SHARP_SCOOP) += scoop.o > > obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o > > obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o > >+obj-$(CONFIG_BOOTTIME) += boottime.o > >diff --git a/arch/arm/common/boottime.c b/arch/arm/common/boottime.c > >new file mode 100644 > >index 0000000..73e9e04 > >--- /dev/null > >+++ b/arch/arm/common/boottime.c > >@@ -0,0 +1,46 @@ > >+/* > >+ * Copyright (C) ST-Ericsson SA 2009-2010 > >+ * > >+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson > >+ * > >+ * License terms: GNU General Public License (GPL) version 2 > >+ * > >+ * Store boot times measured during for example u-boot startup. > >+ */ > >+ > >+#include <linux/kernel.h> > >+#include <linux/init.h> > >+#include <linux/boottime.h> > >+#include <linux/string.h> > >+#include <asm/setup.h> > >+ > >+static u32 bootloader_idle; > >+static u32 bootloader_total; > >+ > >+static int __init boottime_parse_tag(const struct tag *tag) > >+{ > >+ int i; > >+ char buff[BOOTTIME_MAX_NAME_LEN]; > >+ > >+ bootloader_idle = tag->u.boottime.idle; > >+ bootloader_total = tag->u.boottime.total; > >+ > >+ for (i = 0; i < tag->u.boottime.num; i++) { > >+ snprintf(buff, BOOTTIME_MAX_NAME_LEN, "%s+0x0/0x0", > >+ tag->u.boottime.entry[i].name); > >+ buff[BOOTTIME_MAX_NAME_LEN - 1] = '\0'; > >+ boottime_mark_wtime(buff, tag->u.boottime.entry[i].time); > >+ } > >+ > >+ return 0; > >+} > >+ > >+__tagtable(ATAG_BOOTTIME, boottime_parse_tag); > >+ > >+int boottime_bootloader_idle(void) > >+{ > >+ if (bootloader_total == 0) > >+ return 0; > >+ > >+ return (int) ((bootloader_idle) / (bootloader_total / 100)); > >+} > >diff --git a/arch/arm/include/asm/setup.h b/arch/arm/include/asm/setup.h > >index 24d284a..e8da062 100644 > >--- a/arch/arm/include/asm/setup.h > >+++ b/arch/arm/include/asm/setup.h > >@@ -143,6 +143,23 @@ struct tag_memclk { > > __u32 fmemclk; > > }; > >+/* for automatic boot timing testcases */ > >+#define ATAG_BOOTTIME 0x41000403 > >+#define BOOTTIME_MAX_NAME_LEN 64 > >+#define BOOTTIME_MAX 10 > >+ > >+struct boottime_entry { > >+ u32 time; /* in us */ > >+ u8 name[BOOTTIME_MAX_NAME_LEN]; > >+}; > >+ > >+struct tag_boottime { > >+ struct boottime_entry entry[BOOTTIME_MAX]; > >+ u32 idle; /* in us */ > >+ u32 total; /* in us */ > >+ u8 num; > >+}; > >+ > > struct tag { > > struct tag_header hdr; > > union { > >@@ -165,6 +182,10 @@ struct tag { > > * DC21285 specific > > */ > > struct tag_memclk memclk; > >+ /* > >+ * Boot time > >+ */ > >+ struct tag_boottime boottime; > > } u; > > }; > >diff --git a/include/linux/boottime.h b/include/linux/boottime.h > >new file mode 100644 > >index 0000000..d330ecd > >--- /dev/null > >+++ b/include/linux/boottime.h > >@@ -0,0 +1,89 @@ > >+/* > >+ * Copyright (C) ST-Ericsson SA 2009-2010 > >+ * > >+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson > >+ * > >+ * License terms: GNU General Public License (GPL) version 2 > >+ * > >+ * boottime is a tool for collecting start-up timing > >+ * information and can together with boot loader support > >+ * display a total system start-up time. > >+ * > >+ */ > >+ > >+#ifndef LINUX_BOOTTIME_H > >+#define LINUX_BOOTTIME_H > >+ > >+#ifdef CONFIG_BOOTTIME > >+#include <linux/kernel.h> > >+ > >+/** > >+ * struct boottime_timer - Callbacks for generic timer. > >+ * @init: Function to call at boottime initialization > >+ * @get_time: Returns the number of us since start-up > >+ * Preferable this is based upon a free running timer. > >+ * This is the only required entry. > >+ * @finalize: Called before init is executed and boottime is done. > >+ */ > >+struct boottime_timer { > >+ int (*init)(void); > >+ unsigned long (*get_time)(void); > >+ void (*finalize)(void); > >+}; > >+ > >+/** > >+ * boottime_mark_wtime() > >+ * Add a sample point with a given time. Useful for adding data collected > >+ * by for example a boot loader. > >+ * @name: The name of the sample point > >+ * @time: The time in us when this point was reached > >+ */ > >+void __init boottime_mark_wtime(char *name, unsigned long time); > >+ > >+/** > >+ * boottime_mark() > >+ * Add a sample point with the current time. > >+ * @name: The name of this sample point > >+ */ > >+void boottime_mark(char *name); > >+ > >+/** > >+ * boottime_mark_symbolic() > >+ * Add a sample point where the name is a symbolic function > >+ * and %pF is needed to get the correct function name. > >+ * @name: function name. > >+ */ > >+void __init boottime_mark_symbolic(void *name); > >+ > >+/** > >+ * boottime_activate() > >+ * Activates boottime and register callbacks. > >+ * @bt: struct with callbacks. > >+ */ > >+void __ref boottime_activate(struct boottime_timer *bt); > >+ > >+/** > >+ * boottime_deactivate() > >+ * This function is called when the kernel boot is done. > >+ * (before "free init memory" is called) > >+ */ > >+void boottime_deactivate(void); > >+ > >+/** > >+ * boottime_system_up() > >+ * A function is called when the basics of the kernel > >+ * is up and running. > >+ */ > >+void __init boottime_system_up(void); > >+ > >+#else > >+ > >+#define boottime_mark_wtime(name, time) > >+#define boottime_mark(name) > >+#define boottime_mark_symbolic(name) > >+#define boottime_activate(bt) > >+#define boottime_deactivate() > >+#define boottime_system_up() > >+#endif > >+ > >+#endif /* LINUX_BOOTTIME_H */ > >diff --git a/init/Kconfig b/init/Kconfig > >index 4c93533..d0df8ff 100644 > >--- a/init/Kconfig > >+++ b/init/Kconfig > >@@ -1464,6 +1464,15 @@ config PROFILING > > Say Y here to enable the extended profiling support mechanisms used > > by profilers such as OProfile. > >+config BOOTTIME > >+ bool "Boot time measurements" > >+ default n > >+ help > >+ Adds sysfs entries (boottime/) with start-up timing information. > >+ If CONFIG_DEBUG_FS is enabled, detailed information about the > >+ boot time, including system load during boot can be extracted. > >+ This information can be visualised with help of the bootgraph script. > >+ > > # > > # Place an empty function call at each tracepoint site. Can be > > # dynamically changed for a probe function. > >diff --git a/init/Makefile b/init/Makefile > >index 7bc47ee..356d529 100644 > >--- a/init/Makefile > >+++ b/init/Makefile > >@@ -9,6 +9,7 @@ else > > obj-$(CONFIG_BLK_DEV_INITRD) += initramfs.o > > endif > > obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o > >+obj-$(CONFIG_BOOTTIME) += boottime.o > > ifneq ($(CONFIG_ARCH_INIT_TASK),y) > > obj-y += init_task.o > >diff --git a/init/boottime.c b/init/boottime.c > >new file mode 100644 > >index 0000000..793c184 > >--- /dev/null > >+++ b/init/boottime.c > >@@ -0,0 +1,475 @@ > >+/* > >+ * Copyright (C) ST-Ericsson SA 2009-2010 > >+ * > >+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson > >+ * > >+ * License terms: GNU General Public License (GPL) version 2 > >+ * > >+ * boottime is a tool for collecting start-up timing > >+ * information and can together with boot loader support > >+ * display a total system start-up time. > >+ * > >+ */ > >+ > >+#include <linux/kernel.h> > >+#include <linux/module.h> > >+#include <linux/list.h> > >+#include <linux/seq_file.h> > >+#include <linux/debugfs.h> > >+#include <linux/spinlock.h> > >+#include <linux/boottime.h> > >+#include <linux/kernel_stat.h> > >+#include <linux/kobject.h> > >+#include <linux/device.h> > >+#include <linux/sysfs.h> > >+#include <linux/slab.h> > >+ > >+/* > >+ * BOOTTIME_MAX_NAME_LEN is defined in arch/arm/include/asm/setup.h to 64. > >+ * No crisis if they don't match. > >+ */ > >+#ifndef BOOTTIME_MAX_NAME_LEN > >+#define BOOTTIME_MAX_NAME_LEN 64 > >+#endif > >+ > >+/* > >+ * We have a few static entries, since it is good to have measure points > >+ * before the system is up and running properly > >+ */ > >+#define NUM_STATIC_BOOTTIME_ENTRIES 32 > >+ > >+struct boottime_list { > >+ struct list_head list; > >+ char name[BOOTTIME_MAX_NAME_LEN]; > >+ /* Time in us since power on, possible including boot loader. */ > >+ unsigned long time; > >+ bool cpu_load; > >+ struct kernel_cpustat cpu_usage[NR_CPUS]; > >+}; > >+ > >+enum boottime_filter_type { > >+ BOOTTIME_FILTER_OUT_ZERO, > >+ BOOTTIME_FILTER_OUT_LESS_100, > >+ BOOTTIME_FILTER_NOTHING, > >+}; > >+ > >+enum boottime_symbolic_print { > >+ BOOTTIME_SYMBOLIC_PRINT, > >+ BOOTTIME_NORMAL_PRINT, > >+}; > >+ > >+enum boottime_cpu_load { > >+ BOOTTIME_CPU_LOAD, > >+ BOOTTIME_NO_CPU_LOAD, > >+}; > >+ > >+static LIST_HEAD(boottime_list); > >+static DEFINE_SPINLOCK(boottime_list_lock); > >+static struct boottime_timer boottime_timer; > >+static int num_const_boottime_list; > >+static struct boottime_list const_boottime_list[NUM_STATIC_BOOTTIME_ENTRIES]; > >+static unsigned long time_kernel_done; > >+static unsigned long time_bootloader_done; > >+static bool system_up; > >+static bool boottime_done; > >+ > >+int __attribute__((weak)) boottime_arch_startup(void) > >+{ > >+ return 0; > >+} > >+ > >+int __attribute__((weak)) boottime_bootloader_idle(void) > >+{ > >+ return 0; > >+} > >+ > >+static void boottime_mark_core(char *name, > >+ unsigned long time, > >+ enum boottime_symbolic_print symbolic, > >+ enum boottime_cpu_load cpu_load) > >+{ > >+ struct boottime_list *b; > >+ unsigned long flags = 0; > >+ int i; > >+ > >+ if (system_up) { > >+ b = kmalloc(sizeof(struct boottime_list), GFP_KERNEL); > >+ if (!b) { > >+ printk(KERN_ERR > >+ "boottime: failed to allocate memory!\n"); > >+ return; > >+ } > >+ > >+ } else { > >+ if (num_const_boottime_list < NUM_STATIC_BOOTTIME_ENTRIES) { > >+ b = &const_boottime_list[num_const_boottime_list]; > >+ num_const_boottime_list++; > >+ } else { > >+ printk(KERN_ERR > >+ "boottime: too many early measure points!\n"); > >+ return; > >+ } > >+ } > >+ > >+ INIT_LIST_HEAD(&b->list); > >+ > >+ if (symbolic == BOOTTIME_SYMBOLIC_PRINT) > >+ snprintf(b->name, BOOTTIME_MAX_NAME_LEN, "%pF", name); > >+ else > >+ strncpy(b->name, name, BOOTTIME_MAX_NAME_LEN); > >+ > >+ b->name[BOOTTIME_MAX_NAME_LEN - 1] = '\0'; > >+ b->time = time; > >+ b->cpu_load = cpu_load; > >+ > >+ if (cpu_load == BOOTTIME_CPU_LOAD && system_up) > >+ for_each_possible_cpu(i) { > >+ b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] = > >+ kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]; > >+ b->cpu_usage[i].cpustat[CPUTIME_IDLE] = > >+ kcpustat_cpu(i).cpustat[CPUTIME_IDLE]; > >+ b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] = > >+ kcpustat_cpu(i).cpustat[CPUTIME_IOWAIT]; > >+ b->cpu_usage[i].cpustat[CPUTIME_IRQ] = > >+ kcpustat_cpu(i).cpustat[CPUTIME_IRQ]; > >+ /* > >+ * TODO: Make sure that user, nice, softirq, steal > >+ * and guest are not used during boot > >+ */ > >+ } > >+ else > >+ b->cpu_load = BOOTTIME_NO_CPU_LOAD; > >+ > >+ if (system_up) { > >+ spin_lock_irqsave(&boottime_list_lock, flags); > >+ list_add(&b->list, &boottime_list); > >+ spin_unlock_irqrestore(&boottime_list_lock, flags); > >+ } else { > >+ list_add(&b->list, &boottime_list); > >+ } > >+} > >+ > >+void __init boottime_mark_wtime(char *name, unsigned long time) > >+{ > >+ boottime_mark_core(name, time, > >+ BOOTTIME_NORMAL_PRINT, > >+ BOOTTIME_NO_CPU_LOAD); > >+} > >+ > >+void __ref boottime_mark_symbolic(void *name) > >+{ > >+ > >+ if (boottime_done) > >+ return; > >+ > >+ if (boottime_timer.get_time) > >+ boottime_mark_core((char *) name, > >+ boottime_timer.get_time(), > >+ BOOTTIME_SYMBOLIC_PRINT, > >+ BOOTTIME_CPU_LOAD); > >+} > >+ > >+void boottime_mark(char *name) > >+{ > >+ if (boottime_timer.get_time) > >+ boottime_mark_core(name, > >+ boottime_timer.get_time(), > >+ BOOTTIME_NORMAL_PRINT, > >+ BOOTTIME_CPU_LOAD); > >+} > >+ > >+void __init boottime_activate(struct boottime_timer *bt) > >+{ > >+ struct boottime_list *b; > >+ int res = 0; > >+ unsigned long flags; > >+ > >+ if (bt == NULL) { > >+ printk(KERN_ERR > >+ "boottime: error: bad configured\n"); > >+ return; > >+ } > >+ > >+ if (bt->get_time == NULL) { > >+ printk(KERN_ERR > >+ "boottime: error: you must provide a get_time() function\n"); > >+ return; > >+ } > >+ memcpy(&boottime_timer, bt, sizeof(struct boottime_timer)); > >+ > >+ if (boottime_timer.init) > >+ res = boottime_timer.init(); > >+ > >+ if (res) { > >+ printk(KERN_ERR "boottime: initialization failed\n"); > >+ return; > >+ } > >+ > >+ if (boottime_arch_startup()) > >+ printk(KERN_ERR > >+ "boottime: arch specfic initialization failed\n"); > >+ > >+ spin_lock_irqsave(&boottime_list_lock, flags); > >+ > >+ if (!list_empty(&boottime_list)) { > >+ > >+ b = list_first_entry(&boottime_list, struct boottime_list, > >+ list); > >+ if (b) > >+ time_bootloader_done = b->time; > >+ } > >+ > >+ spin_unlock_irqrestore(&boottime_list_lock, flags); > >+} > >+ > >+void __init boottime_system_up(void) > >+{ > >+ system_up = true; > >+} > >+ > >+void boottime_deactivate(void) > >+{ > >+ struct boottime_list *b; > >+ unsigned long flags; > >+ > >+ boottime_mark("execute_init+0x0/0x0"); > >+ > >+ boottime_done = true; > >+ > >+ spin_lock_irqsave(&boottime_list_lock, flags); > >+ b = list_first_entry(&boottime_list, struct boottime_list, list); > >+ spin_unlock_irqrestore(&boottime_list_lock, flags); > >+ > >+ time_kernel_done = b->time; > >+ > >+ if (boottime_timer.finalize) > >+ boottime_timer.finalize(); > >+} > >+ > >+#ifdef CONFIG_DEBUG_FS > >+static void boottime_debugfs_load(struct seq_file *s, > >+ struct boottime_list *b, > >+ struct boottime_list *p) > >+{ > >+ int i; > >+ unsigned long total_p, total_b; > >+ unsigned long system_total, idle_total, irq_total, iowait_total; > >+ unsigned long system_load, idle_load, irq_load, iowait_load; > >+ > >+ for_each_possible_cpu(i) { > >+ total_b = (b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] + > >+ b->cpu_usage[i].cpustat[CPUTIME_IDLE] + > >+ b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] + > >+ b->cpu_usage[i].cpustat[CPUTIME_IRQ]); > >+ > >+ total_p = (p->cpu_usage[i].cpustat[CPUTIME_SYSTEM] + > >+ p->cpu_usage[i].cpustat[CPUTIME_IDLE] + > >+ p->cpu_usage[i].cpustat[CPUTIME_IOWAIT] + > >+ p->cpu_usage[i].cpustat[CPUTIME_IRQ]); > >+ > >+ if (total_b == total_p) > >+ continue; > >+ > >+ system_total = b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] > >+ - p->cpu_usage[i].cpustat[CPUTIME_SYSTEM]; > >+ idle_total = b->cpu_usage[i].cpustat[CPUTIME_IDLE] > >+ - p->cpu_usage[i].cpustat[CPUTIME_IDLE]; > >+ irq_total = b->cpu_usage[i].cpustat[CPUTIME_IRQ] > >+ - p->cpu_usage[i].cpustat[CPUTIME_IRQ]; > >+ iowait_total = b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] > >+ - p->cpu_usage[i].cpustat[CPUTIME_IOWAIT]; > >+ > >+ system_load = (100 * system_total / (total_b - total_p)); > >+ idle_load = (100 * idle_total / (total_b - total_p)); > >+ irq_load = (100 * irq_total / (total_b - total_p)); > >+ iowait_load = (100 * iowait_total / (total_b - total_p)); > >+ > >+ seq_printf(s, > >+ " cpu%d system: %lu%% idle: %lu%% iowait: %lu%% irq: %lu%%", > >+ i, > >+ system_load, > >+ idle_load, > >+ iowait_load, > >+ irq_load); > >+ } > >+ seq_printf(s, "\n"); > >+} > >+ > >+static void boottime_debugfs_print(struct seq_file *s, > >+ struct boottime_list *b, > >+ struct boottime_list *p) > >+{ > >+ seq_printf(s, "[%5lu.%06lu] calling %s\n", > >+ p->time / 1000000, > >+ (p->time % 1000000), > >+ p->name); > >+ seq_printf(s, "[%5lu.%06lu] initcall %s returned 0 after %ld msecs.", > >+ b->time / 1000000, > >+ (b->time % 1000000), > >+ p->name, (b->time - p->time) / 1000); > >+ > >+ if (p->cpu_load == BOOTTIME_NO_CPU_LOAD || > >+ b->cpu_load == BOOTTIME_NO_CPU_LOAD) { > >+ seq_printf(s, "\n"); > >+ return; > >+ } > >+ > >+ boottime_debugfs_load(s, b, p); > >+} > >+ > >+static int boottime_debugfs_bootgraph_show(struct seq_file *s, void *iter) > >+{ > >+ struct boottime_list *b, *p = NULL, *old_p = NULL; > >+ enum boottime_filter_type filter = (int)s->private; > >+ > >+ list_for_each_entry_reverse(b, &boottime_list, list) { > >+ if (p) { > >+ if (!(filter == BOOTTIME_FILTER_OUT_ZERO && > >+ (b->time - p->time) / 1000 == 0) > >+ && !(filter == BOOTTIME_FILTER_OUT_LESS_100 && > >+ (b->time - p->time) < 100 * 1000)) > >+ boottime_debugfs_print(s, b, p); > >+ old_p = p; > >+ } > >+ p = b; > >+ } > >+ > >+ if (filter == BOOTTIME_FILTER_NOTHING && p) > >+ boottime_debugfs_print(s, p, p); > >+ > >+ if (p) > >+ seq_printf(s, "[%5lu.%06lu] Freeing init memory: 0K\n", > >+ p->time / 1000000, p->time % 1000000); > >+ return 0; > >+} > >+ > >+static int boottime_debugfs_summary_show(struct seq_file *s, void *data) > >+{ > >+ struct boottime_list *b, b_zero; > >+ > >+ if (time_bootloader_done) > >+ seq_printf(s, "bootloader: %ld msecs\n", > >+ time_bootloader_done / 1000); > >+ > >+ seq_printf(s, "kernel: %ld msecs\ntotal: %ld msecs\n", > >+ (time_kernel_done - time_bootloader_done) / 1000, > >+ time_kernel_done / 1000); > >+ seq_printf(s, "kernel:"); > >+ b = list_first_entry(&boottime_list, > >+ struct boottime_list, list); > >+ memset(&b_zero, 0, sizeof(struct boottime_list)); > >+ boottime_debugfs_load(s, b, &b_zero); > >+ > >+ if (time_bootloader_done) > >+ seq_printf(s, > >+ "bootloader: cpu0 system: %d%% idle: %d%% iowait: 0%% irq: 0%%\n", > >+ 100 - boottime_bootloader_idle(), > >+ boottime_bootloader_idle()); > >+ return 0; > >+} > >+ > >+static int boottime_debugfs_bootgraph_open(struct inode *inode, > >+ struct file *file) > >+{ > >+ return single_open(file, > >+ boottime_debugfs_bootgraph_show, > >+ inode->i_private); > >+} > >+ > >+static int boottime_debugfs_summary_open(struct inode *inode, > >+ struct file *file) > >+{ > >+ return single_open(file, > >+ boottime_debugfs_summary_show, > >+ inode->i_private); > >+} > >+ > >+static const struct file_operations boottime_debugfs_bootgraph_operations = { > >+ .open = boottime_debugfs_bootgraph_open, > >+ .read = seq_read, > >+ .llseek = seq_lseek, > >+ .release = single_release, > >+}; > >+ > >+static const struct file_operations boottime_debugfs_summary_operations = { > >+ .open = boottime_debugfs_summary_open, > >+ .read = seq_read, > >+ .llseek = seq_lseek, > >+ .release = single_release, > >+}; > >+ > >+void boottime_debugfs_init(void) > >+{ > >+ struct dentry *dir; > >+ > >+ dir = debugfs_create_dir("boottime", NULL); > >+ > >+ (void) debugfs_create_file("bootgraph", S_IFREG | S_IRUGO, > >+ dir, (void *)BOOTTIME_FILTER_NOTHING, > >+ &boottime_debugfs_bootgraph_operations); > >+ (void) debugfs_create_file("bootgraph_all_except0", S_IFREG | S_IRUGO, > >+ dir, (void *)BOOTTIME_FILTER_OUT_ZERO, > >+ &boottime_debugfs_bootgraph_operations); > >+ (void) debugfs_create_file("bootgraph_larger100", > >+ S_IFREG | S_IRUGO, > >+ dir, (void *)BOOTTIME_FILTER_OUT_LESS_100, > >+ &boottime_debugfs_bootgraph_operations); > >+ (void) debugfs_create_file("summary", S_IFREG | S_IRUGO, > >+ dir, NULL, > >+ &boottime_debugfs_summary_operations); > >+} > >+#else > >+#define boottime_debugfs_init(x) > >+#endif > >+ > >+static ssize_t show_bootloader(struct device *dev, > >+ struct device_attribute *attr, > >+ char *buf) > >+{ > >+ return sprintf(buf, "%ld\n", time_bootloader_done / 1000); > >+} > >+ > >+static ssize_t show_kernel(struct device *dev, > >+ struct device_attribute *attr, > >+ char *buf) > >+{ > >+ return sprintf(buf, "%ld\n", > >+ (time_kernel_done - time_bootloader_done) / 1000); > >+} > >+ > >+DEVICE_ATTR(kernel, 0444, show_kernel, NULL); > >+DEVICE_ATTR(bootloader, 0444, show_bootloader, NULL); > >+ > >+static struct attribute *boottime_sysfs_entries[] = { > >+ &dev_attr_kernel.attr, > >+ &dev_attr_bootloader.attr, > >+ NULL > >+}; > >+ > >+static struct attribute_group boottime_attr_grp = { > >+ .name = NULL, > >+ .attrs = boottime_sysfs_entries, > >+}; > >+ > >+static int __init boottime_init(void) > >+{ > >+ struct kobject *boottime_kobj; > >+ > >+ boottime_kobj = kobject_create_and_add("boottime", NULL); > >+ if (!boottime_kobj) { > >+ printk(KERN_ERR "boottime: out of memory!\n"); > >+ return -ENOMEM; > >+ } > >+ > >+ if (sysfs_create_group(boottime_kobj, &boottime_attr_grp) < 0) { > >+ kobject_put(boottime_kobj); > >+ printk(KERN_ERR "boottime: Failed creating sysfs group\n"); > >+ return -ENOMEM; > >+ } > >+ > >+ boottime_debugfs_init(); > >+ > >+ return 0; > >+} > >+ > >+late_initcall(boottime_init); > >diff --git a/init/main.c b/init/main.c > >index 313360f..c06afd0 100644 > >--- a/init/main.c > >+++ b/init/main.c > >@@ -69,6 +69,7 @@ > > #include <linux/slab.h> > > #include <linux/perf_event.h> > > #include <linux/file.h> > >+#include <linux/boottime.h> > > #include <asm/io.h> > > #include <asm/bugs.h> > >@@ -679,6 +680,8 @@ int __init_or_module do_one_initcall(initcall_t fn) > > int count = preempt_count(); > > int ret; > >+ boottime_mark_symbolic(fn); > >+ > > if (initcall_debug) > > ret = do_one_initcall_debug(fn); > > else > >@@ -804,6 +807,7 @@ static noinline int init_post(void) > > { > > /* need to finish all async __init code before freeing the memory */ > > async_synchronize_full(); > >+ boottime_deactivate(); > > free_initmem(); > > mark_rodata_ro(); > > system_state = SYSTEM_RUNNING; > >@@ -863,6 +867,7 @@ static int __init kernel_init(void * unused) > > do_pre_smp_initcalls(); > > lockup_detector_init(); > >+ boottime_system_up(); > > smp_init(); > > sched_init_smp(); > >@@ -885,6 +890,7 @@ static int __init kernel_init(void * unused) > > if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { > > ramdisk_execute_command = NULL; > >+ boottime_mark("mount+0x0/0x0"); > > prepare_namespace(); > > } > -- Lee Jones Linaro ST-Ericsson Landing Team Lead Linaro.org ? Open source software for ARM SoCs Follow Linaro: Facebook | Twitter | Blog ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time @ 2012-10-12 13:45 ` Lee Jones 0 siblings, 0 replies; 33+ messages in thread From: Lee Jones @ 2012-10-12 13:45 UTC (permalink / raw) To: Dan Murphy Cc: Nishanth Menon, linux-arm-kernel, linux-kernel, Russell King, linus.walleij, Jonas Aaberg, Will Deacon, arnd, Mian Yousaf Kaukab On Fri, 12 Oct 2012, Dan Murphy wrote: > On a note on testing. > > OK this at least compiles on my system with 3.4. > > The weird thing is that I get the same exact kernel boot time for > two different machines. > When I cat the summary for the kernel. > I am not profiling the bootloader time here as it is neglible so no > ATAG argument. > > kernel: 3221288 msecs > total: 3221288 msecs > kernel: cpu0 system: 0% idle: 0% iowait: 0% irq: 0% cpu1 system: 0% > idle: 0% iowait: 0% irq: 0% > > And I get no output from any of the bootgraph entries. That's odd. I get: root@ME:/ cat sys/boottime/bootloader 0 root@ME:/ cat sys/boottime/kernel 4276 root@ME:/ cat /sys/kernel/debug/boottime/summary kernel: 4276 msecs total: 4276 msecs root@ME:/ cat /sys/kernel/debug/boottime/bootgraph [ 0.185254] calling splash+0x0/0x0 [ 2.984335] initcall splash+0x0/0x0 returned 0 after 2799 msecs. [ 2.984335] calling autoboot_delay+0x0/0x0 [ 4.089513] initcall autoboot_delay+0x0/0x0 returned 0 after 1105 msecs. [ 4.089513] calling load_kernel+0x0/0x0 [ 4.239174] initcall load_kernel+0x0/0x0 returned 0 after 149 msecs. [ 4.239174] calling boot_kernel+0x0/0x0 [ 4.276260] initcall boot_kernel+0x0/0x0 returned 0 after 37 msecs. [ 4.276260] calling uncompress_ll_init+0x0/0x0 [ 4.276260] initcall uncompress_ll_init+0x0/0x0 returned 0 after 0 msecs. [ 4.276260] Freeing init memory: 0K > >Author: Lee Jones <lee.jones@linaro.org> > >Date: Wed Jun 30 14:00:40 2010 +0200 > > > > Boottime: A tool for automatic measurement of kernel/bootloader boot time > > The overhead is very low and the results will be found under > > sysfs/bootime, as well as detailed results in debugfs under > > boottime/. The bootgraph* files are compatible with > > scripts/bootgraph.pl. The reason for this patch is to provide > > data (sysfs/boottime) suitable for automatic test-cases as > > well as help for developers to reduce the boot time (debugfs). > > Based heavily on the original driver by Jonas Aaberg. > > Cc: Russell King <linux@arm.linux.org.uk> > > Cc: Will Deacon <will.deacon@arm.com> > > Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com> > > Signed-off-by: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> > > Signed-off-by: Lee Jones <lee.jones@linaro.org> > > Reviewed-by: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com> > > Tested-by: Mian Yousaf KAUKAB <mian.yousaf.kaukab@stericsson.com> > > > >diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile > >index e8a4e58..8522356 100644 > >--- a/arch/arm/common/Makefile > >+++ b/arch/arm/common/Makefile > >@@ -13,3 +13,4 @@ obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o > > obj-$(CONFIG_SHARP_SCOOP) += scoop.o > > obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o > > obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o > >+obj-$(CONFIG_BOOTTIME) += boottime.o > >diff --git a/arch/arm/common/boottime.c b/arch/arm/common/boottime.c > >new file mode 100644 > >index 0000000..73e9e04 > >--- /dev/null > >+++ b/arch/arm/common/boottime.c > >@@ -0,0 +1,46 @@ > >+/* > >+ * Copyright (C) ST-Ericsson SA 2009-2010 > >+ * > >+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson > >+ * > >+ * License terms: GNU General Public License (GPL) version 2 > >+ * > >+ * Store boot times measured during for example u-boot startup. > >+ */ > >+ > >+#include <linux/kernel.h> > >+#include <linux/init.h> > >+#include <linux/boottime.h> > >+#include <linux/string.h> > >+#include <asm/setup.h> > >+ > >+static u32 bootloader_idle; > >+static u32 bootloader_total; > >+ > >+static int __init boottime_parse_tag(const struct tag *tag) > >+{ > >+ int i; > >+ char buff[BOOTTIME_MAX_NAME_LEN]; > >+ > >+ bootloader_idle = tag->u.boottime.idle; > >+ bootloader_total = tag->u.boottime.total; > >+ > >+ for (i = 0; i < tag->u.boottime.num; i++) { > >+ snprintf(buff, BOOTTIME_MAX_NAME_LEN, "%s+0x0/0x0", > >+ tag->u.boottime.entry[i].name); > >+ buff[BOOTTIME_MAX_NAME_LEN - 1] = '\0'; > >+ boottime_mark_wtime(buff, tag->u.boottime.entry[i].time); > >+ } > >+ > >+ return 0; > >+} > >+ > >+__tagtable(ATAG_BOOTTIME, boottime_parse_tag); > >+ > >+int boottime_bootloader_idle(void) > >+{ > >+ if (bootloader_total == 0) > >+ return 0; > >+ > >+ return (int) ((bootloader_idle) / (bootloader_total / 100)); > >+} > >diff --git a/arch/arm/include/asm/setup.h b/arch/arm/include/asm/setup.h > >index 24d284a..e8da062 100644 > >--- a/arch/arm/include/asm/setup.h > >+++ b/arch/arm/include/asm/setup.h > >@@ -143,6 +143,23 @@ struct tag_memclk { > > __u32 fmemclk; > > }; > >+/* for automatic boot timing testcases */ > >+#define ATAG_BOOTTIME 0x41000403 > >+#define BOOTTIME_MAX_NAME_LEN 64 > >+#define BOOTTIME_MAX 10 > >+ > >+struct boottime_entry { > >+ u32 time; /* in us */ > >+ u8 name[BOOTTIME_MAX_NAME_LEN]; > >+}; > >+ > >+struct tag_boottime { > >+ struct boottime_entry entry[BOOTTIME_MAX]; > >+ u32 idle; /* in us */ > >+ u32 total; /* in us */ > >+ u8 num; > >+}; > >+ > > struct tag { > > struct tag_header hdr; > > union { > >@@ -165,6 +182,10 @@ struct tag { > > * DC21285 specific > > */ > > struct tag_memclk memclk; > >+ /* > >+ * Boot time > >+ */ > >+ struct tag_boottime boottime; > > } u; > > }; > >diff --git a/include/linux/boottime.h b/include/linux/boottime.h > >new file mode 100644 > >index 0000000..d330ecd > >--- /dev/null > >+++ b/include/linux/boottime.h > >@@ -0,0 +1,89 @@ > >+/* > >+ * Copyright (C) ST-Ericsson SA 2009-2010 > >+ * > >+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson > >+ * > >+ * License terms: GNU General Public License (GPL) version 2 > >+ * > >+ * boottime is a tool for collecting start-up timing > >+ * information and can together with boot loader support > >+ * display a total system start-up time. > >+ * > >+ */ > >+ > >+#ifndef LINUX_BOOTTIME_H > >+#define LINUX_BOOTTIME_H > >+ > >+#ifdef CONFIG_BOOTTIME > >+#include <linux/kernel.h> > >+ > >+/** > >+ * struct boottime_timer - Callbacks for generic timer. > >+ * @init: Function to call at boottime initialization > >+ * @get_time: Returns the number of us since start-up > >+ * Preferable this is based upon a free running timer. > >+ * This is the only required entry. > >+ * @finalize: Called before init is executed and boottime is done. > >+ */ > >+struct boottime_timer { > >+ int (*init)(void); > >+ unsigned long (*get_time)(void); > >+ void (*finalize)(void); > >+}; > >+ > >+/** > >+ * boottime_mark_wtime() > >+ * Add a sample point with a given time. Useful for adding data collected > >+ * by for example a boot loader. > >+ * @name: The name of the sample point > >+ * @time: The time in us when this point was reached > >+ */ > >+void __init boottime_mark_wtime(char *name, unsigned long time); > >+ > >+/** > >+ * boottime_mark() > >+ * Add a sample point with the current time. > >+ * @name: The name of this sample point > >+ */ > >+void boottime_mark(char *name); > >+ > >+/** > >+ * boottime_mark_symbolic() > >+ * Add a sample point where the name is a symbolic function > >+ * and %pF is needed to get the correct function name. > >+ * @name: function name. > >+ */ > >+void __init boottime_mark_symbolic(void *name); > >+ > >+/** > >+ * boottime_activate() > >+ * Activates boottime and register callbacks. > >+ * @bt: struct with callbacks. > >+ */ > >+void __ref boottime_activate(struct boottime_timer *bt); > >+ > >+/** > >+ * boottime_deactivate() > >+ * This function is called when the kernel boot is done. > >+ * (before "free init memory" is called) > >+ */ > >+void boottime_deactivate(void); > >+ > >+/** > >+ * boottime_system_up() > >+ * A function is called when the basics of the kernel > >+ * is up and running. > >+ */ > >+void __init boottime_system_up(void); > >+ > >+#else > >+ > >+#define boottime_mark_wtime(name, time) > >+#define boottime_mark(name) > >+#define boottime_mark_symbolic(name) > >+#define boottime_activate(bt) > >+#define boottime_deactivate() > >+#define boottime_system_up() > >+#endif > >+ > >+#endif /* LINUX_BOOTTIME_H */ > >diff --git a/init/Kconfig b/init/Kconfig > >index 4c93533..d0df8ff 100644 > >--- a/init/Kconfig > >+++ b/init/Kconfig > >@@ -1464,6 +1464,15 @@ config PROFILING > > Say Y here to enable the extended profiling support mechanisms used > > by profilers such as OProfile. > >+config BOOTTIME > >+ bool "Boot time measurements" > >+ default n > >+ help > >+ Adds sysfs entries (boottime/) with start-up timing information. > >+ If CONFIG_DEBUG_FS is enabled, detailed information about the > >+ boot time, including system load during boot can be extracted. > >+ This information can be visualised with help of the bootgraph script. > >+ > > # > > # Place an empty function call at each tracepoint site. Can be > > # dynamically changed for a probe function. > >diff --git a/init/Makefile b/init/Makefile > >index 7bc47ee..356d529 100644 > >--- a/init/Makefile > >+++ b/init/Makefile > >@@ -9,6 +9,7 @@ else > > obj-$(CONFIG_BLK_DEV_INITRD) += initramfs.o > > endif > > obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o > >+obj-$(CONFIG_BOOTTIME) += boottime.o > > ifneq ($(CONFIG_ARCH_INIT_TASK),y) > > obj-y += init_task.o > >diff --git a/init/boottime.c b/init/boottime.c > >new file mode 100644 > >index 0000000..793c184 > >--- /dev/null > >+++ b/init/boottime.c > >@@ -0,0 +1,475 @@ > >+/* > >+ * Copyright (C) ST-Ericsson SA 2009-2010 > >+ * > >+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson > >+ * > >+ * License terms: GNU General Public License (GPL) version 2 > >+ * > >+ * boottime is a tool for collecting start-up timing > >+ * information and can together with boot loader support > >+ * display a total system start-up time. > >+ * > >+ */ > >+ > >+#include <linux/kernel.h> > >+#include <linux/module.h> > >+#include <linux/list.h> > >+#include <linux/seq_file.h> > >+#include <linux/debugfs.h> > >+#include <linux/spinlock.h> > >+#include <linux/boottime.h> > >+#include <linux/kernel_stat.h> > >+#include <linux/kobject.h> > >+#include <linux/device.h> > >+#include <linux/sysfs.h> > >+#include <linux/slab.h> > >+ > >+/* > >+ * BOOTTIME_MAX_NAME_LEN is defined in arch/arm/include/asm/setup.h to 64. > >+ * No crisis if they don't match. > >+ */ > >+#ifndef BOOTTIME_MAX_NAME_LEN > >+#define BOOTTIME_MAX_NAME_LEN 64 > >+#endif > >+ > >+/* > >+ * We have a few static entries, since it is good to have measure points > >+ * before the system is up and running properly > >+ */ > >+#define NUM_STATIC_BOOTTIME_ENTRIES 32 > >+ > >+struct boottime_list { > >+ struct list_head list; > >+ char name[BOOTTIME_MAX_NAME_LEN]; > >+ /* Time in us since power on, possible including boot loader. */ > >+ unsigned long time; > >+ bool cpu_load; > >+ struct kernel_cpustat cpu_usage[NR_CPUS]; > >+}; > >+ > >+enum boottime_filter_type { > >+ BOOTTIME_FILTER_OUT_ZERO, > >+ BOOTTIME_FILTER_OUT_LESS_100, > >+ BOOTTIME_FILTER_NOTHING, > >+}; > >+ > >+enum boottime_symbolic_print { > >+ BOOTTIME_SYMBOLIC_PRINT, > >+ BOOTTIME_NORMAL_PRINT, > >+}; > >+ > >+enum boottime_cpu_load { > >+ BOOTTIME_CPU_LOAD, > >+ BOOTTIME_NO_CPU_LOAD, > >+}; > >+ > >+static LIST_HEAD(boottime_list); > >+static DEFINE_SPINLOCK(boottime_list_lock); > >+static struct boottime_timer boottime_timer; > >+static int num_const_boottime_list; > >+static struct boottime_list const_boottime_list[NUM_STATIC_BOOTTIME_ENTRIES]; > >+static unsigned long time_kernel_done; > >+static unsigned long time_bootloader_done; > >+static bool system_up; > >+static bool boottime_done; > >+ > >+int __attribute__((weak)) boottime_arch_startup(void) > >+{ > >+ return 0; > >+} > >+ > >+int __attribute__((weak)) boottime_bootloader_idle(void) > >+{ > >+ return 0; > >+} > >+ > >+static void boottime_mark_core(char *name, > >+ unsigned long time, > >+ enum boottime_symbolic_print symbolic, > >+ enum boottime_cpu_load cpu_load) > >+{ > >+ struct boottime_list *b; > >+ unsigned long flags = 0; > >+ int i; > >+ > >+ if (system_up) { > >+ b = kmalloc(sizeof(struct boottime_list), GFP_KERNEL); > >+ if (!b) { > >+ printk(KERN_ERR > >+ "boottime: failed to allocate memory!\n"); > >+ return; > >+ } > >+ > >+ } else { > >+ if (num_const_boottime_list < NUM_STATIC_BOOTTIME_ENTRIES) { > >+ b = &const_boottime_list[num_const_boottime_list]; > >+ num_const_boottime_list++; > >+ } else { > >+ printk(KERN_ERR > >+ "boottime: too many early measure points!\n"); > >+ return; > >+ } > >+ } > >+ > >+ INIT_LIST_HEAD(&b->list); > >+ > >+ if (symbolic == BOOTTIME_SYMBOLIC_PRINT) > >+ snprintf(b->name, BOOTTIME_MAX_NAME_LEN, "%pF", name); > >+ else > >+ strncpy(b->name, name, BOOTTIME_MAX_NAME_LEN); > >+ > >+ b->name[BOOTTIME_MAX_NAME_LEN - 1] = '\0'; > >+ b->time = time; > >+ b->cpu_load = cpu_load; > >+ > >+ if (cpu_load == BOOTTIME_CPU_LOAD && system_up) > >+ for_each_possible_cpu(i) { > >+ b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] = > >+ kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]; > >+ b->cpu_usage[i].cpustat[CPUTIME_IDLE] = > >+ kcpustat_cpu(i).cpustat[CPUTIME_IDLE]; > >+ b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] = > >+ kcpustat_cpu(i).cpustat[CPUTIME_IOWAIT]; > >+ b->cpu_usage[i].cpustat[CPUTIME_IRQ] = > >+ kcpustat_cpu(i).cpustat[CPUTIME_IRQ]; > >+ /* > >+ * TODO: Make sure that user, nice, softirq, steal > >+ * and guest are not used during boot > >+ */ > >+ } > >+ else > >+ b->cpu_load = BOOTTIME_NO_CPU_LOAD; > >+ > >+ if (system_up) { > >+ spin_lock_irqsave(&boottime_list_lock, flags); > >+ list_add(&b->list, &boottime_list); > >+ spin_unlock_irqrestore(&boottime_list_lock, flags); > >+ } else { > >+ list_add(&b->list, &boottime_list); > >+ } > >+} > >+ > >+void __init boottime_mark_wtime(char *name, unsigned long time) > >+{ > >+ boottime_mark_core(name, time, > >+ BOOTTIME_NORMAL_PRINT, > >+ BOOTTIME_NO_CPU_LOAD); > >+} > >+ > >+void __ref boottime_mark_symbolic(void *name) > >+{ > >+ > >+ if (boottime_done) > >+ return; > >+ > >+ if (boottime_timer.get_time) > >+ boottime_mark_core((char *) name, > >+ boottime_timer.get_time(), > >+ BOOTTIME_SYMBOLIC_PRINT, > >+ BOOTTIME_CPU_LOAD); > >+} > >+ > >+void boottime_mark(char *name) > >+{ > >+ if (boottime_timer.get_time) > >+ boottime_mark_core(name, > >+ boottime_timer.get_time(), > >+ BOOTTIME_NORMAL_PRINT, > >+ BOOTTIME_CPU_LOAD); > >+} > >+ > >+void __init boottime_activate(struct boottime_timer *bt) > >+{ > >+ struct boottime_list *b; > >+ int res = 0; > >+ unsigned long flags; > >+ > >+ if (bt == NULL) { > >+ printk(KERN_ERR > >+ "boottime: error: bad configured\n"); > >+ return; > >+ } > >+ > >+ if (bt->get_time == NULL) { > >+ printk(KERN_ERR > >+ "boottime: error: you must provide a get_time() function\n"); > >+ return; > >+ } > >+ memcpy(&boottime_timer, bt, sizeof(struct boottime_timer)); > >+ > >+ if (boottime_timer.init) > >+ res = boottime_timer.init(); > >+ > >+ if (res) { > >+ printk(KERN_ERR "boottime: initialization failed\n"); > >+ return; > >+ } > >+ > >+ if (boottime_arch_startup()) > >+ printk(KERN_ERR > >+ "boottime: arch specfic initialization failed\n"); > >+ > >+ spin_lock_irqsave(&boottime_list_lock, flags); > >+ > >+ if (!list_empty(&boottime_list)) { > >+ > >+ b = list_first_entry(&boottime_list, struct boottime_list, > >+ list); > >+ if (b) > >+ time_bootloader_done = b->time; > >+ } > >+ > >+ spin_unlock_irqrestore(&boottime_list_lock, flags); > >+} > >+ > >+void __init boottime_system_up(void) > >+{ > >+ system_up = true; > >+} > >+ > >+void boottime_deactivate(void) > >+{ > >+ struct boottime_list *b; > >+ unsigned long flags; > >+ > >+ boottime_mark("execute_init+0x0/0x0"); > >+ > >+ boottime_done = true; > >+ > >+ spin_lock_irqsave(&boottime_list_lock, flags); > >+ b = list_first_entry(&boottime_list, struct boottime_list, list); > >+ spin_unlock_irqrestore(&boottime_list_lock, flags); > >+ > >+ time_kernel_done = b->time; > >+ > >+ if (boottime_timer.finalize) > >+ boottime_timer.finalize(); > >+} > >+ > >+#ifdef CONFIG_DEBUG_FS > >+static void boottime_debugfs_load(struct seq_file *s, > >+ struct boottime_list *b, > >+ struct boottime_list *p) > >+{ > >+ int i; > >+ unsigned long total_p, total_b; > >+ unsigned long system_total, idle_total, irq_total, iowait_total; > >+ unsigned long system_load, idle_load, irq_load, iowait_load; > >+ > >+ for_each_possible_cpu(i) { > >+ total_b = (b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] + > >+ b->cpu_usage[i].cpustat[CPUTIME_IDLE] + > >+ b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] + > >+ b->cpu_usage[i].cpustat[CPUTIME_IRQ]); > >+ > >+ total_p = (p->cpu_usage[i].cpustat[CPUTIME_SYSTEM] + > >+ p->cpu_usage[i].cpustat[CPUTIME_IDLE] + > >+ p->cpu_usage[i].cpustat[CPUTIME_IOWAIT] + > >+ p->cpu_usage[i].cpustat[CPUTIME_IRQ]); > >+ > >+ if (total_b == total_p) > >+ continue; > >+ > >+ system_total = b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] > >+ - p->cpu_usage[i].cpustat[CPUTIME_SYSTEM]; > >+ idle_total = b->cpu_usage[i].cpustat[CPUTIME_IDLE] > >+ - p->cpu_usage[i].cpustat[CPUTIME_IDLE]; > >+ irq_total = b->cpu_usage[i].cpustat[CPUTIME_IRQ] > >+ - p->cpu_usage[i].cpustat[CPUTIME_IRQ]; > >+ iowait_total = b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] > >+ - p->cpu_usage[i].cpustat[CPUTIME_IOWAIT]; > >+ > >+ system_load = (100 * system_total / (total_b - total_p)); > >+ idle_load = (100 * idle_total / (total_b - total_p)); > >+ irq_load = (100 * irq_total / (total_b - total_p)); > >+ iowait_load = (100 * iowait_total / (total_b - total_p)); > >+ > >+ seq_printf(s, > >+ " cpu%d system: %lu%% idle: %lu%% iowait: %lu%% irq: %lu%%", > >+ i, > >+ system_load, > >+ idle_load, > >+ iowait_load, > >+ irq_load); > >+ } > >+ seq_printf(s, "\n"); > >+} > >+ > >+static void boottime_debugfs_print(struct seq_file *s, > >+ struct boottime_list *b, > >+ struct boottime_list *p) > >+{ > >+ seq_printf(s, "[%5lu.%06lu] calling %s\n", > >+ p->time / 1000000, > >+ (p->time % 1000000), > >+ p->name); > >+ seq_printf(s, "[%5lu.%06lu] initcall %s returned 0 after %ld msecs.", > >+ b->time / 1000000, > >+ (b->time % 1000000), > >+ p->name, (b->time - p->time) / 1000); > >+ > >+ if (p->cpu_load == BOOTTIME_NO_CPU_LOAD || > >+ b->cpu_load == BOOTTIME_NO_CPU_LOAD) { > >+ seq_printf(s, "\n"); > >+ return; > >+ } > >+ > >+ boottime_debugfs_load(s, b, p); > >+} > >+ > >+static int boottime_debugfs_bootgraph_show(struct seq_file *s, void *iter) > >+{ > >+ struct boottime_list *b, *p = NULL, *old_p = NULL; > >+ enum boottime_filter_type filter = (int)s->private; > >+ > >+ list_for_each_entry_reverse(b, &boottime_list, list) { > >+ if (p) { > >+ if (!(filter == BOOTTIME_FILTER_OUT_ZERO && > >+ (b->time - p->time) / 1000 == 0) > >+ && !(filter == BOOTTIME_FILTER_OUT_LESS_100 && > >+ (b->time - p->time) < 100 * 1000)) > >+ boottime_debugfs_print(s, b, p); > >+ old_p = p; > >+ } > >+ p = b; > >+ } > >+ > >+ if (filter == BOOTTIME_FILTER_NOTHING && p) > >+ boottime_debugfs_print(s, p, p); > >+ > >+ if (p) > >+ seq_printf(s, "[%5lu.%06lu] Freeing init memory: 0K\n", > >+ p->time / 1000000, p->time % 1000000); > >+ return 0; > >+} > >+ > >+static int boottime_debugfs_summary_show(struct seq_file *s, void *data) > >+{ > >+ struct boottime_list *b, b_zero; > >+ > >+ if (time_bootloader_done) > >+ seq_printf(s, "bootloader: %ld msecs\n", > >+ time_bootloader_done / 1000); > >+ > >+ seq_printf(s, "kernel: %ld msecs\ntotal: %ld msecs\n", > >+ (time_kernel_done - time_bootloader_done) / 1000, > >+ time_kernel_done / 1000); > >+ seq_printf(s, "kernel:"); > >+ b = list_first_entry(&boottime_list, > >+ struct boottime_list, list); > >+ memset(&b_zero, 0, sizeof(struct boottime_list)); > >+ boottime_debugfs_load(s, b, &b_zero); > >+ > >+ if (time_bootloader_done) > >+ seq_printf(s, > >+ "bootloader: cpu0 system: %d%% idle: %d%% iowait: 0%% irq: 0%%\n", > >+ 100 - boottime_bootloader_idle(), > >+ boottime_bootloader_idle()); > >+ return 0; > >+} > >+ > >+static int boottime_debugfs_bootgraph_open(struct inode *inode, > >+ struct file *file) > >+{ > >+ return single_open(file, > >+ boottime_debugfs_bootgraph_show, > >+ inode->i_private); > >+} > >+ > >+static int boottime_debugfs_summary_open(struct inode *inode, > >+ struct file *file) > >+{ > >+ return single_open(file, > >+ boottime_debugfs_summary_show, > >+ inode->i_private); > >+} > >+ > >+static const struct file_operations boottime_debugfs_bootgraph_operations = { > >+ .open = boottime_debugfs_bootgraph_open, > >+ .read = seq_read, > >+ .llseek = seq_lseek, > >+ .release = single_release, > >+}; > >+ > >+static const struct file_operations boottime_debugfs_summary_operations = { > >+ .open = boottime_debugfs_summary_open, > >+ .read = seq_read, > >+ .llseek = seq_lseek, > >+ .release = single_release, > >+}; > >+ > >+void boottime_debugfs_init(void) > >+{ > >+ struct dentry *dir; > >+ > >+ dir = debugfs_create_dir("boottime", NULL); > >+ > >+ (void) debugfs_create_file("bootgraph", S_IFREG | S_IRUGO, > >+ dir, (void *)BOOTTIME_FILTER_NOTHING, > >+ &boottime_debugfs_bootgraph_operations); > >+ (void) debugfs_create_file("bootgraph_all_except0", S_IFREG | S_IRUGO, > >+ dir, (void *)BOOTTIME_FILTER_OUT_ZERO, > >+ &boottime_debugfs_bootgraph_operations); > >+ (void) debugfs_create_file("bootgraph_larger100", > >+ S_IFREG | S_IRUGO, > >+ dir, (void *)BOOTTIME_FILTER_OUT_LESS_100, > >+ &boottime_debugfs_bootgraph_operations); > >+ (void) debugfs_create_file("summary", S_IFREG | S_IRUGO, > >+ dir, NULL, > >+ &boottime_debugfs_summary_operations); > >+} > >+#else > >+#define boottime_debugfs_init(x) > >+#endif > >+ > >+static ssize_t show_bootloader(struct device *dev, > >+ struct device_attribute *attr, > >+ char *buf) > >+{ > >+ return sprintf(buf, "%ld\n", time_bootloader_done / 1000); > >+} > >+ > >+static ssize_t show_kernel(struct device *dev, > >+ struct device_attribute *attr, > >+ char *buf) > >+{ > >+ return sprintf(buf, "%ld\n", > >+ (time_kernel_done - time_bootloader_done) / 1000); > >+} > >+ > >+DEVICE_ATTR(kernel, 0444, show_kernel, NULL); > >+DEVICE_ATTR(bootloader, 0444, show_bootloader, NULL); > >+ > >+static struct attribute *boottime_sysfs_entries[] = { > >+ &dev_attr_kernel.attr, > >+ &dev_attr_bootloader.attr, > >+ NULL > >+}; > >+ > >+static struct attribute_group boottime_attr_grp = { > >+ .name = NULL, > >+ .attrs = boottime_sysfs_entries, > >+}; > >+ > >+static int __init boottime_init(void) > >+{ > >+ struct kobject *boottime_kobj; > >+ > >+ boottime_kobj = kobject_create_and_add("boottime", NULL); > >+ if (!boottime_kobj) { > >+ printk(KERN_ERR "boottime: out of memory!\n"); > >+ return -ENOMEM; > >+ } > >+ > >+ if (sysfs_create_group(boottime_kobj, &boottime_attr_grp) < 0) { > >+ kobject_put(boottime_kobj); > >+ printk(KERN_ERR "boottime: Failed creating sysfs group\n"); > >+ return -ENOMEM; > >+ } > >+ > >+ boottime_debugfs_init(); > >+ > >+ return 0; > >+} > >+ > >+late_initcall(boottime_init); > >diff --git a/init/main.c b/init/main.c > >index 313360f..c06afd0 100644 > >--- a/init/main.c > >+++ b/init/main.c > >@@ -69,6 +69,7 @@ > > #include <linux/slab.h> > > #include <linux/perf_event.h> > > #include <linux/file.h> > >+#include <linux/boottime.h> > > #include <asm/io.h> > > #include <asm/bugs.h> > >@@ -679,6 +680,8 @@ int __init_or_module do_one_initcall(initcall_t fn) > > int count = preempt_count(); > > int ret; > >+ boottime_mark_symbolic(fn); > >+ > > if (initcall_debug) > > ret = do_one_initcall_debug(fn); > > else > >@@ -804,6 +807,7 @@ static noinline int init_post(void) > > { > > /* need to finish all async __init code before freeing the memory */ > > async_synchronize_full(); > >+ boottime_deactivate(); > > free_initmem(); > > mark_rodata_ro(); > > system_state = SYSTEM_RUNNING; > >@@ -863,6 +867,7 @@ static int __init kernel_init(void * unused) > > do_pre_smp_initcalls(); > > lockup_detector_init(); > >+ boottime_system_up(); > > smp_init(); > > sched_init_smp(); > >@@ -885,6 +890,7 @@ static int __init kernel_init(void * unused) > > if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { > > ramdisk_execute_command = NULL; > >+ boottime_mark("mount+0x0/0x0"); > > prepare_namespace(); > > } > -- Lee Jones Linaro ST-Ericsson Landing Team Lead Linaro.org │ Open source software for ARM SoCs Follow Linaro: Facebook | Twitter | Blog ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time 2012-10-12 13:45 ` Lee Jones @ 2012-10-12 13:48 ` Russell King - ARM Linux -1 siblings, 0 replies; 33+ messages in thread From: Russell King - ARM Linux @ 2012-10-12 13:48 UTC (permalink / raw) To: linux-arm-kernel On Fri, Oct 12, 2012 at 02:45:42PM +0100, Lee Jones wrote: > That's odd. I get: > > root at ME:/ cat sys/boottime/bootloader > 0 > > root at ME:/ cat sys/boottime/kernel > 4276 > > root at ME:/ cat /sys/kernel/debug/boottime/summary > kernel: 4276 msecs > total: 4276 msecs > > root at ME:/ cat /sys/kernel/debug/boottime/bootgraph > [ 0.185254] calling splash+0x0/0x0 > [ 2.984335] initcall splash+0x0/0x0 returned 0 after 2799 msecs. > [ 2.984335] calling autoboot_delay+0x0/0x0 > [ 4.089513] initcall autoboot_delay+0x0/0x0 returned 0 after 1105 msecs. > [ 4.089513] calling load_kernel+0x0/0x0 > [ 4.239174] initcall load_kernel+0x0/0x0 returned 0 after 149 msecs. > [ 4.239174] calling boot_kernel+0x0/0x0 > [ 4.276260] initcall boot_kernel+0x0/0x0 returned 0 after 37 msecs. > [ 4.276260] calling uncompress_ll_init+0x0/0x0 > [ 4.276260] initcall uncompress_ll_init+0x0/0x0 returned 0 after 0 msecs. > [ 4.276260] Freeing init memory: 0K Umm, what happened to sysfs not becoming procfs v2? I thought we had a fairly strict requirement for "one value per file and not nicely formatted" for sysfs? > > >Author: Lee Jones <lee.jones@linaro.org> > > >Date: Wed Jun 30 14:00:40 2010 +0200 > > > > > > Boottime: A tool for automatic measurement of kernel/bootloader boot time > > > The overhead is very low and the results will be found under > > > sysfs/bootime, as well as detailed results in debugfs under > > > boottime/. The bootgraph* files are compatible with > > > scripts/bootgraph.pl. The reason for this patch is to provide > > > data (sysfs/boottime) suitable for automatic test-cases as > > > well as help for developers to reduce the boot time (debugfs). > > > Based heavily on the original driver by Jonas Aaberg. > > > Cc: Russell King <linux@arm.linux.org.uk> > > > Cc: Will Deacon <will.deacon@arm.com> > > > Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com> > > > Signed-off-by: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> > > > Signed-off-by: Lee Jones <lee.jones@linaro.org> > > > Reviewed-by: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com> > > > Tested-by: Mian Yousaf KAUKAB <mian.yousaf.kaukab@stericsson.com> > > > > > >diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile > > >index e8a4e58..8522356 100644 > > >--- a/arch/arm/common/Makefile > > >+++ b/arch/arm/common/Makefile > > >@@ -13,3 +13,4 @@ obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o > > > obj-$(CONFIG_SHARP_SCOOP) += scoop.o > > > obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o > > > obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o > > >+obj-$(CONFIG_BOOTTIME) += boottime.o > > >diff --git a/arch/arm/common/boottime.c b/arch/arm/common/boottime.c > > >new file mode 100644 > > >index 0000000..73e9e04 > > >--- /dev/null > > >+++ b/arch/arm/common/boottime.c > > >@@ -0,0 +1,46 @@ > > >+/* > > >+ * Copyright (C) ST-Ericsson SA 2009-2010 > > >+ * > > >+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson > > >+ * > > >+ * License terms: GNU General Public License (GPL) version 2 > > >+ * > > >+ * Store boot times measured during for example u-boot startup. > > >+ */ > > >+ > > >+#include <linux/kernel.h> > > >+#include <linux/init.h> > > >+#include <linux/boottime.h> > > >+#include <linux/string.h> > > >+#include <asm/setup.h> > > >+ > > >+static u32 bootloader_idle; > > >+static u32 bootloader_total; > > >+ > > >+static int __init boottime_parse_tag(const struct tag *tag) > > >+{ > > >+ int i; > > >+ char buff[BOOTTIME_MAX_NAME_LEN]; > > >+ > > >+ bootloader_idle = tag->u.boottime.idle; > > >+ bootloader_total = tag->u.boottime.total; > > >+ > > >+ for (i = 0; i < tag->u.boottime.num; i++) { > > >+ snprintf(buff, BOOTTIME_MAX_NAME_LEN, "%s+0x0/0x0", > > >+ tag->u.boottime.entry[i].name); > > >+ buff[BOOTTIME_MAX_NAME_LEN - 1] = '\0'; > > >+ boottime_mark_wtime(buff, tag->u.boottime.entry[i].time); > > >+ } > > >+ > > >+ return 0; > > >+} > > >+ > > >+__tagtable(ATAG_BOOTTIME, boottime_parse_tag); > > >+ > > >+int boottime_bootloader_idle(void) > > >+{ > > >+ if (bootloader_total == 0) > > >+ return 0; > > >+ > > >+ return (int) ((bootloader_idle) / (bootloader_total / 100)); > > >+} > > >diff --git a/arch/arm/include/asm/setup.h b/arch/arm/include/asm/setup.h > > >index 24d284a..e8da062 100644 > > >--- a/arch/arm/include/asm/setup.h > > >+++ b/arch/arm/include/asm/setup.h > > >@@ -143,6 +143,23 @@ struct tag_memclk { > > > __u32 fmemclk; > > > }; > > >+/* for automatic boot timing testcases */ > > >+#define ATAG_BOOTTIME 0x41000403 > > >+#define BOOTTIME_MAX_NAME_LEN 64 > > >+#define BOOTTIME_MAX 10 > > >+ > > >+struct boottime_entry { > > >+ u32 time; /* in us */ > > >+ u8 name[BOOTTIME_MAX_NAME_LEN]; > > >+}; > > >+ > > >+struct tag_boottime { > > >+ struct boottime_entry entry[BOOTTIME_MAX]; > > >+ u32 idle; /* in us */ > > >+ u32 total; /* in us */ > > >+ u8 num; > > >+}; > > >+ > > > struct tag { > > > struct tag_header hdr; > > > union { > > >@@ -165,6 +182,10 @@ struct tag { > > > * DC21285 specific > > > */ > > > struct tag_memclk memclk; > > >+ /* > > >+ * Boot time > > >+ */ > > >+ struct tag_boottime boottime; > > > } u; > > > }; > > >diff --git a/include/linux/boottime.h b/include/linux/boottime.h > > >new file mode 100644 > > >index 0000000..d330ecd > > >--- /dev/null > > >+++ b/include/linux/boottime.h > > >@@ -0,0 +1,89 @@ > > >+/* > > >+ * Copyright (C) ST-Ericsson SA 2009-2010 > > >+ * > > >+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson > > >+ * > > >+ * License terms: GNU General Public License (GPL) version 2 > > >+ * > > >+ * boottime is a tool for collecting start-up timing > > >+ * information and can together with boot loader support > > >+ * display a total system start-up time. > > >+ * > > >+ */ > > >+ > > >+#ifndef LINUX_BOOTTIME_H > > >+#define LINUX_BOOTTIME_H > > >+ > > >+#ifdef CONFIG_BOOTTIME > > >+#include <linux/kernel.h> > > >+ > > >+/** > > >+ * struct boottime_timer - Callbacks for generic timer. > > >+ * @init: Function to call at boottime initialization > > >+ * @get_time: Returns the number of us since start-up > > >+ * Preferable this is based upon a free running timer. > > >+ * This is the only required entry. > > >+ * @finalize: Called before init is executed and boottime is done. > > >+ */ > > >+struct boottime_timer { > > >+ int (*init)(void); > > >+ unsigned long (*get_time)(void); > > >+ void (*finalize)(void); > > >+}; > > >+ > > >+/** > > >+ * boottime_mark_wtime() > > >+ * Add a sample point with a given time. Useful for adding data collected > > >+ * by for example a boot loader. > > >+ * @name: The name of the sample point > > >+ * @time: The time in us when this point was reached > > >+ */ > > >+void __init boottime_mark_wtime(char *name, unsigned long time); > > >+ > > >+/** > > >+ * boottime_mark() > > >+ * Add a sample point with the current time. > > >+ * @name: The name of this sample point > > >+ */ > > >+void boottime_mark(char *name); > > >+ > > >+/** > > >+ * boottime_mark_symbolic() > > >+ * Add a sample point where the name is a symbolic function > > >+ * and %pF is needed to get the correct function name. > > >+ * @name: function name. > > >+ */ > > >+void __init boottime_mark_symbolic(void *name); > > >+ > > >+/** > > >+ * boottime_activate() > > >+ * Activates boottime and register callbacks. > > >+ * @bt: struct with callbacks. > > >+ */ > > >+void __ref boottime_activate(struct boottime_timer *bt); > > >+ > > >+/** > > >+ * boottime_deactivate() > > >+ * This function is called when the kernel boot is done. > > >+ * (before "free init memory" is called) > > >+ */ > > >+void boottime_deactivate(void); > > >+ > > >+/** > > >+ * boottime_system_up() > > >+ * A function is called when the basics of the kernel > > >+ * is up and running. > > >+ */ > > >+void __init boottime_system_up(void); > > >+ > > >+#else > > >+ > > >+#define boottime_mark_wtime(name, time) > > >+#define boottime_mark(name) > > >+#define boottime_mark_symbolic(name) > > >+#define boottime_activate(bt) > > >+#define boottime_deactivate() > > >+#define boottime_system_up() > > >+#endif > > >+ > > >+#endif /* LINUX_BOOTTIME_H */ > > >diff --git a/init/Kconfig b/init/Kconfig > > >index 4c93533..d0df8ff 100644 > > >--- a/init/Kconfig > > >+++ b/init/Kconfig > > >@@ -1464,6 +1464,15 @@ config PROFILING > > > Say Y here to enable the extended profiling support mechanisms used > > > by profilers such as OProfile. > > >+config BOOTTIME > > >+ bool "Boot time measurements" > > >+ default n > > >+ help > > >+ Adds sysfs entries (boottime/) with start-up timing information. > > >+ If CONFIG_DEBUG_FS is enabled, detailed information about the > > >+ boot time, including system load during boot can be extracted. > > >+ This information can be visualised with help of the bootgraph script. > > >+ > > > # > > > # Place an empty function call at each tracepoint site. Can be > > > # dynamically changed for a probe function. > > >diff --git a/init/Makefile b/init/Makefile > > >index 7bc47ee..356d529 100644 > > >--- a/init/Makefile > > >+++ b/init/Makefile > > >@@ -9,6 +9,7 @@ else > > > obj-$(CONFIG_BLK_DEV_INITRD) += initramfs.o > > > endif > > > obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o > > >+obj-$(CONFIG_BOOTTIME) += boottime.o > > > ifneq ($(CONFIG_ARCH_INIT_TASK),y) > > > obj-y += init_task.o > > >diff --git a/init/boottime.c b/init/boottime.c > > >new file mode 100644 > > >index 0000000..793c184 > > >--- /dev/null > > >+++ b/init/boottime.c > > >@@ -0,0 +1,475 @@ > > >+/* > > >+ * Copyright (C) ST-Ericsson SA 2009-2010 > > >+ * > > >+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson > > >+ * > > >+ * License terms: GNU General Public License (GPL) version 2 > > >+ * > > >+ * boottime is a tool for collecting start-up timing > > >+ * information and can together with boot loader support > > >+ * display a total system start-up time. > > >+ * > > >+ */ > > >+ > > >+#include <linux/kernel.h> > > >+#include <linux/module.h> > > >+#include <linux/list.h> > > >+#include <linux/seq_file.h> > > >+#include <linux/debugfs.h> > > >+#include <linux/spinlock.h> > > >+#include <linux/boottime.h> > > >+#include <linux/kernel_stat.h> > > >+#include <linux/kobject.h> > > >+#include <linux/device.h> > > >+#include <linux/sysfs.h> > > >+#include <linux/slab.h> > > >+ > > >+/* > > >+ * BOOTTIME_MAX_NAME_LEN is defined in arch/arm/include/asm/setup.h to 64. > > >+ * No crisis if they don't match. > > >+ */ > > >+#ifndef BOOTTIME_MAX_NAME_LEN > > >+#define BOOTTIME_MAX_NAME_LEN 64 > > >+#endif > > >+ > > >+/* > > >+ * We have a few static entries, since it is good to have measure points > > >+ * before the system is up and running properly > > >+ */ > > >+#define NUM_STATIC_BOOTTIME_ENTRIES 32 > > >+ > > >+struct boottime_list { > > >+ struct list_head list; > > >+ char name[BOOTTIME_MAX_NAME_LEN]; > > >+ /* Time in us since power on, possible including boot loader. */ > > >+ unsigned long time; > > >+ bool cpu_load; > > >+ struct kernel_cpustat cpu_usage[NR_CPUS]; > > >+}; > > >+ > > >+enum boottime_filter_type { > > >+ BOOTTIME_FILTER_OUT_ZERO, > > >+ BOOTTIME_FILTER_OUT_LESS_100, > > >+ BOOTTIME_FILTER_NOTHING, > > >+}; > > >+ > > >+enum boottime_symbolic_print { > > >+ BOOTTIME_SYMBOLIC_PRINT, > > >+ BOOTTIME_NORMAL_PRINT, > > >+}; > > >+ > > >+enum boottime_cpu_load { > > >+ BOOTTIME_CPU_LOAD, > > >+ BOOTTIME_NO_CPU_LOAD, > > >+}; > > >+ > > >+static LIST_HEAD(boottime_list); > > >+static DEFINE_SPINLOCK(boottime_list_lock); > > >+static struct boottime_timer boottime_timer; > > >+static int num_const_boottime_list; > > >+static struct boottime_list const_boottime_list[NUM_STATIC_BOOTTIME_ENTRIES]; > > >+static unsigned long time_kernel_done; > > >+static unsigned long time_bootloader_done; > > >+static bool system_up; > > >+static bool boottime_done; > > >+ > > >+int __attribute__((weak)) boottime_arch_startup(void) > > >+{ > > >+ return 0; > > >+} > > >+ > > >+int __attribute__((weak)) boottime_bootloader_idle(void) > > >+{ > > >+ return 0; > > >+} > > >+ > > >+static void boottime_mark_core(char *name, > > >+ unsigned long time, > > >+ enum boottime_symbolic_print symbolic, > > >+ enum boottime_cpu_load cpu_load) > > >+{ > > >+ struct boottime_list *b; > > >+ unsigned long flags = 0; > > >+ int i; > > >+ > > >+ if (system_up) { > > >+ b = kmalloc(sizeof(struct boottime_list), GFP_KERNEL); > > >+ if (!b) { > > >+ printk(KERN_ERR > > >+ "boottime: failed to allocate memory!\n"); > > >+ return; > > >+ } > > >+ > > >+ } else { > > >+ if (num_const_boottime_list < NUM_STATIC_BOOTTIME_ENTRIES) { > > >+ b = &const_boottime_list[num_const_boottime_list]; > > >+ num_const_boottime_list++; > > >+ } else { > > >+ printk(KERN_ERR > > >+ "boottime: too many early measure points!\n"); > > >+ return; > > >+ } > > >+ } > > >+ > > >+ INIT_LIST_HEAD(&b->list); > > >+ > > >+ if (symbolic == BOOTTIME_SYMBOLIC_PRINT) > > >+ snprintf(b->name, BOOTTIME_MAX_NAME_LEN, "%pF", name); > > >+ else > > >+ strncpy(b->name, name, BOOTTIME_MAX_NAME_LEN); > > >+ > > >+ b->name[BOOTTIME_MAX_NAME_LEN - 1] = '\0'; > > >+ b->time = time; > > >+ b->cpu_load = cpu_load; > > >+ > > >+ if (cpu_load == BOOTTIME_CPU_LOAD && system_up) > > >+ for_each_possible_cpu(i) { > > >+ b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] = > > >+ kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]; > > >+ b->cpu_usage[i].cpustat[CPUTIME_IDLE] = > > >+ kcpustat_cpu(i).cpustat[CPUTIME_IDLE]; > > >+ b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] = > > >+ kcpustat_cpu(i).cpustat[CPUTIME_IOWAIT]; > > >+ b->cpu_usage[i].cpustat[CPUTIME_IRQ] = > > >+ kcpustat_cpu(i).cpustat[CPUTIME_IRQ]; > > >+ /* > > >+ * TODO: Make sure that user, nice, softirq, steal > > >+ * and guest are not used during boot > > >+ */ > > >+ } > > >+ else > > >+ b->cpu_load = BOOTTIME_NO_CPU_LOAD; > > >+ > > >+ if (system_up) { > > >+ spin_lock_irqsave(&boottime_list_lock, flags); > > >+ list_add(&b->list, &boottime_list); > > >+ spin_unlock_irqrestore(&boottime_list_lock, flags); > > >+ } else { > > >+ list_add(&b->list, &boottime_list); > > >+ } > > >+} > > >+ > > >+void __init boottime_mark_wtime(char *name, unsigned long time) > > >+{ > > >+ boottime_mark_core(name, time, > > >+ BOOTTIME_NORMAL_PRINT, > > >+ BOOTTIME_NO_CPU_LOAD); > > >+} > > >+ > > >+void __ref boottime_mark_symbolic(void *name) > > >+{ > > >+ > > >+ if (boottime_done) > > >+ return; > > >+ > > >+ if (boottime_timer.get_time) > > >+ boottime_mark_core((char *) name, > > >+ boottime_timer.get_time(), > > >+ BOOTTIME_SYMBOLIC_PRINT, > > >+ BOOTTIME_CPU_LOAD); > > >+} > > >+ > > >+void boottime_mark(char *name) > > >+{ > > >+ if (boottime_timer.get_time) > > >+ boottime_mark_core(name, > > >+ boottime_timer.get_time(), > > >+ BOOTTIME_NORMAL_PRINT, > > >+ BOOTTIME_CPU_LOAD); > > >+} > > >+ > > >+void __init boottime_activate(struct boottime_timer *bt) > > >+{ > > >+ struct boottime_list *b; > > >+ int res = 0; > > >+ unsigned long flags; > > >+ > > >+ if (bt == NULL) { > > >+ printk(KERN_ERR > > >+ "boottime: error: bad configured\n"); > > >+ return; > > >+ } > > >+ > > >+ if (bt->get_time == NULL) { > > >+ printk(KERN_ERR > > >+ "boottime: error: you must provide a get_time() function\n"); > > >+ return; > > >+ } > > >+ memcpy(&boottime_timer, bt, sizeof(struct boottime_timer)); > > >+ > > >+ if (boottime_timer.init) > > >+ res = boottime_timer.init(); > > >+ > > >+ if (res) { > > >+ printk(KERN_ERR "boottime: initialization failed\n"); > > >+ return; > > >+ } > > >+ > > >+ if (boottime_arch_startup()) > > >+ printk(KERN_ERR > > >+ "boottime: arch specfic initialization failed\n"); > > >+ > > >+ spin_lock_irqsave(&boottime_list_lock, flags); > > >+ > > >+ if (!list_empty(&boottime_list)) { > > >+ > > >+ b = list_first_entry(&boottime_list, struct boottime_list, > > >+ list); > > >+ if (b) > > >+ time_bootloader_done = b->time; > > >+ } > > >+ > > >+ spin_unlock_irqrestore(&boottime_list_lock, flags); > > >+} > > >+ > > >+void __init boottime_system_up(void) > > >+{ > > >+ system_up = true; > > >+} > > >+ > > >+void boottime_deactivate(void) > > >+{ > > >+ struct boottime_list *b; > > >+ unsigned long flags; > > >+ > > >+ boottime_mark("execute_init+0x0/0x0"); > > >+ > > >+ boottime_done = true; > > >+ > > >+ spin_lock_irqsave(&boottime_list_lock, flags); > > >+ b = list_first_entry(&boottime_list, struct boottime_list, list); > > >+ spin_unlock_irqrestore(&boottime_list_lock, flags); > > >+ > > >+ time_kernel_done = b->time; > > >+ > > >+ if (boottime_timer.finalize) > > >+ boottime_timer.finalize(); > > >+} > > >+ > > >+#ifdef CONFIG_DEBUG_FS > > >+static void boottime_debugfs_load(struct seq_file *s, > > >+ struct boottime_list *b, > > >+ struct boottime_list *p) > > >+{ > > >+ int i; > > >+ unsigned long total_p, total_b; > > >+ unsigned long system_total, idle_total, irq_total, iowait_total; > > >+ unsigned long system_load, idle_load, irq_load, iowait_load; > > >+ > > >+ for_each_possible_cpu(i) { > > >+ total_b = (b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] + > > >+ b->cpu_usage[i].cpustat[CPUTIME_IDLE] + > > >+ b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] + > > >+ b->cpu_usage[i].cpustat[CPUTIME_IRQ]); > > >+ > > >+ total_p = (p->cpu_usage[i].cpustat[CPUTIME_SYSTEM] + > > >+ p->cpu_usage[i].cpustat[CPUTIME_IDLE] + > > >+ p->cpu_usage[i].cpustat[CPUTIME_IOWAIT] + > > >+ p->cpu_usage[i].cpustat[CPUTIME_IRQ]); > > >+ > > >+ if (total_b == total_p) > > >+ continue; > > >+ > > >+ system_total = b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] > > >+ - p->cpu_usage[i].cpustat[CPUTIME_SYSTEM]; > > >+ idle_total = b->cpu_usage[i].cpustat[CPUTIME_IDLE] > > >+ - p->cpu_usage[i].cpustat[CPUTIME_IDLE]; > > >+ irq_total = b->cpu_usage[i].cpustat[CPUTIME_IRQ] > > >+ - p->cpu_usage[i].cpustat[CPUTIME_IRQ]; > > >+ iowait_total = b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] > > >+ - p->cpu_usage[i].cpustat[CPUTIME_IOWAIT]; > > >+ > > >+ system_load = (100 * system_total / (total_b - total_p)); > > >+ idle_load = (100 * idle_total / (total_b - total_p)); > > >+ irq_load = (100 * irq_total / (total_b - total_p)); > > >+ iowait_load = (100 * iowait_total / (total_b - total_p)); > > >+ > > >+ seq_printf(s, > > >+ " cpu%d system: %lu%% idle: %lu%% iowait: %lu%% irq: %lu%%", > > >+ i, > > >+ system_load, > > >+ idle_load, > > >+ iowait_load, > > >+ irq_load); > > >+ } > > >+ seq_printf(s, "\n"); > > >+} > > >+ > > >+static void boottime_debugfs_print(struct seq_file *s, > > >+ struct boottime_list *b, > > >+ struct boottime_list *p) > > >+{ > > >+ seq_printf(s, "[%5lu.%06lu] calling %s\n", > > >+ p->time / 1000000, > > >+ (p->time % 1000000), > > >+ p->name); > > >+ seq_printf(s, "[%5lu.%06lu] initcall %s returned 0 after %ld msecs.", > > >+ b->time / 1000000, > > >+ (b->time % 1000000), > > >+ p->name, (b->time - p->time) / 1000); > > >+ > > >+ if (p->cpu_load == BOOTTIME_NO_CPU_LOAD || > > >+ b->cpu_load == BOOTTIME_NO_CPU_LOAD) { > > >+ seq_printf(s, "\n"); > > >+ return; > > >+ } > > >+ > > >+ boottime_debugfs_load(s, b, p); > > >+} > > >+ > > >+static int boottime_debugfs_bootgraph_show(struct seq_file *s, void *iter) > > >+{ > > >+ struct boottime_list *b, *p = NULL, *old_p = NULL; > > >+ enum boottime_filter_type filter = (int)s->private; > > >+ > > >+ list_for_each_entry_reverse(b, &boottime_list, list) { > > >+ if (p) { > > >+ if (!(filter == BOOTTIME_FILTER_OUT_ZERO && > > >+ (b->time - p->time) / 1000 == 0) > > >+ && !(filter == BOOTTIME_FILTER_OUT_LESS_100 && > > >+ (b->time - p->time) < 100 * 1000)) > > >+ boottime_debugfs_print(s, b, p); > > >+ old_p = p; > > >+ } > > >+ p = b; > > >+ } > > >+ > > >+ if (filter == BOOTTIME_FILTER_NOTHING && p) > > >+ boottime_debugfs_print(s, p, p); > > >+ > > >+ if (p) > > >+ seq_printf(s, "[%5lu.%06lu] Freeing init memory: 0K\n", > > >+ p->time / 1000000, p->time % 1000000); > > >+ return 0; > > >+} > > >+ > > >+static int boottime_debugfs_summary_show(struct seq_file *s, void *data) > > >+{ > > >+ struct boottime_list *b, b_zero; > > >+ > > >+ if (time_bootloader_done) > > >+ seq_printf(s, "bootloader: %ld msecs\n", > > >+ time_bootloader_done / 1000); > > >+ > > >+ seq_printf(s, "kernel: %ld msecs\ntotal: %ld msecs\n", > > >+ (time_kernel_done - time_bootloader_done) / 1000, > > >+ time_kernel_done / 1000); > > >+ seq_printf(s, "kernel:"); > > >+ b = list_first_entry(&boottime_list, > > >+ struct boottime_list, list); > > >+ memset(&b_zero, 0, sizeof(struct boottime_list)); > > >+ boottime_debugfs_load(s, b, &b_zero); > > >+ > > >+ if (time_bootloader_done) > > >+ seq_printf(s, > > >+ "bootloader: cpu0 system: %d%% idle: %d%% iowait: 0%% irq: 0%%\n", > > >+ 100 - boottime_bootloader_idle(), > > >+ boottime_bootloader_idle()); > > >+ return 0; > > >+} > > >+ > > >+static int boottime_debugfs_bootgraph_open(struct inode *inode, > > >+ struct file *file) > > >+{ > > >+ return single_open(file, > > >+ boottime_debugfs_bootgraph_show, > > >+ inode->i_private); > > >+} > > >+ > > >+static int boottime_debugfs_summary_open(struct inode *inode, > > >+ struct file *file) > > >+{ > > >+ return single_open(file, > > >+ boottime_debugfs_summary_show, > > >+ inode->i_private); > > >+} > > >+ > > >+static const struct file_operations boottime_debugfs_bootgraph_operations = { > > >+ .open = boottime_debugfs_bootgraph_open, > > >+ .read = seq_read, > > >+ .llseek = seq_lseek, > > >+ .release = single_release, > > >+}; > > >+ > > >+static const struct file_operations boottime_debugfs_summary_operations = { > > >+ .open = boottime_debugfs_summary_open, > > >+ .read = seq_read, > > >+ .llseek = seq_lseek, > > >+ .release = single_release, > > >+}; > > >+ > > >+void boottime_debugfs_init(void) > > >+{ > > >+ struct dentry *dir; > > >+ > > >+ dir = debugfs_create_dir("boottime", NULL); > > >+ > > >+ (void) debugfs_create_file("bootgraph", S_IFREG | S_IRUGO, > > >+ dir, (void *)BOOTTIME_FILTER_NOTHING, > > >+ &boottime_debugfs_bootgraph_operations); > > >+ (void) debugfs_create_file("bootgraph_all_except0", S_IFREG | S_IRUGO, > > >+ dir, (void *)BOOTTIME_FILTER_OUT_ZERO, > > >+ &boottime_debugfs_bootgraph_operations); > > >+ (void) debugfs_create_file("bootgraph_larger100", > > >+ S_IFREG | S_IRUGO, > > >+ dir, (void *)BOOTTIME_FILTER_OUT_LESS_100, > > >+ &boottime_debugfs_bootgraph_operations); > > >+ (void) debugfs_create_file("summary", S_IFREG | S_IRUGO, > > >+ dir, NULL, > > >+ &boottime_debugfs_summary_operations); > > >+} > > >+#else > > >+#define boottime_debugfs_init(x) > > >+#endif > > >+ > > >+static ssize_t show_bootloader(struct device *dev, > > >+ struct device_attribute *attr, > > >+ char *buf) > > >+{ > > >+ return sprintf(buf, "%ld\n", time_bootloader_done / 1000); > > >+} > > >+ > > >+static ssize_t show_kernel(struct device *dev, > > >+ struct device_attribute *attr, > > >+ char *buf) > > >+{ > > >+ return sprintf(buf, "%ld\n", > > >+ (time_kernel_done - time_bootloader_done) / 1000); > > >+} > > >+ > > >+DEVICE_ATTR(kernel, 0444, show_kernel, NULL); > > >+DEVICE_ATTR(bootloader, 0444, show_bootloader, NULL); > > >+ > > >+static struct attribute *boottime_sysfs_entries[] = { > > >+ &dev_attr_kernel.attr, > > >+ &dev_attr_bootloader.attr, > > >+ NULL > > >+}; > > >+ > > >+static struct attribute_group boottime_attr_grp = { > > >+ .name = NULL, > > >+ .attrs = boottime_sysfs_entries, > > >+}; > > >+ > > >+static int __init boottime_init(void) > > >+{ > > >+ struct kobject *boottime_kobj; > > >+ > > >+ boottime_kobj = kobject_create_and_add("boottime", NULL); > > >+ if (!boottime_kobj) { > > >+ printk(KERN_ERR "boottime: out of memory!\n"); > > >+ return -ENOMEM; > > >+ } > > >+ > > >+ if (sysfs_create_group(boottime_kobj, &boottime_attr_grp) < 0) { > > >+ kobject_put(boottime_kobj); > > >+ printk(KERN_ERR "boottime: Failed creating sysfs group\n"); > > >+ return -ENOMEM; > > >+ } > > >+ > > >+ boottime_debugfs_init(); > > >+ > > >+ return 0; > > >+} > > >+ > > >+late_initcall(boottime_init); > > >diff --git a/init/main.c b/init/main.c > > >index 313360f..c06afd0 100644 > > >--- a/init/main.c > > >+++ b/init/main.c > > >@@ -69,6 +69,7 @@ > > > #include <linux/slab.h> > > > #include <linux/perf_event.h> > > > #include <linux/file.h> > > >+#include <linux/boottime.h> > > > #include <asm/io.h> > > > #include <asm/bugs.h> > > >@@ -679,6 +680,8 @@ int __init_or_module do_one_initcall(initcall_t fn) > > > int count = preempt_count(); > > > int ret; > > >+ boottime_mark_symbolic(fn); > > >+ > > > if (initcall_debug) > > > ret = do_one_initcall_debug(fn); > > > else > > >@@ -804,6 +807,7 @@ static noinline int init_post(void) > > > { > > > /* need to finish all async __init code before freeing the memory */ > > > async_synchronize_full(); > > >+ boottime_deactivate(); > > > free_initmem(); > > > mark_rodata_ro(); > > > system_state = SYSTEM_RUNNING; > > >@@ -863,6 +867,7 @@ static int __init kernel_init(void * unused) > > > do_pre_smp_initcalls(); > > > lockup_detector_init(); > > >+ boottime_system_up(); > > > smp_init(); > > > sched_init_smp(); > > >@@ -885,6 +890,7 @@ static int __init kernel_init(void * unused) > > > if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { > > > ramdisk_execute_command = NULL; > > >+ boottime_mark("mount+0x0/0x0"); > > > prepare_namespace(); > > > } > > > > -- > Lee Jones > Linaro ST-Ericsson Landing Team Lead > Linaro.org ? Open source software for ARM SoCs > Follow Linaro: Facebook | Twitter | Blog ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time @ 2012-10-12 13:48 ` Russell King - ARM Linux 0 siblings, 0 replies; 33+ messages in thread From: Russell King - ARM Linux @ 2012-10-12 13:48 UTC (permalink / raw) To: Lee Jones, Greg Kroah-Hartman Cc: Dan Murphy, Nishanth Menon, linux-arm-kernel, linux-kernel, linus.walleij, Jonas Aaberg, Will Deacon, arnd, Mian Yousaf Kaukab On Fri, Oct 12, 2012 at 02:45:42PM +0100, Lee Jones wrote: > That's odd. I get: > > root@ME:/ cat sys/boottime/bootloader > 0 > > root@ME:/ cat sys/boottime/kernel > 4276 > > root@ME:/ cat /sys/kernel/debug/boottime/summary > kernel: 4276 msecs > total: 4276 msecs > > root@ME:/ cat /sys/kernel/debug/boottime/bootgraph > [ 0.185254] calling splash+0x0/0x0 > [ 2.984335] initcall splash+0x0/0x0 returned 0 after 2799 msecs. > [ 2.984335] calling autoboot_delay+0x0/0x0 > [ 4.089513] initcall autoboot_delay+0x0/0x0 returned 0 after 1105 msecs. > [ 4.089513] calling load_kernel+0x0/0x0 > [ 4.239174] initcall load_kernel+0x0/0x0 returned 0 after 149 msecs. > [ 4.239174] calling boot_kernel+0x0/0x0 > [ 4.276260] initcall boot_kernel+0x0/0x0 returned 0 after 37 msecs. > [ 4.276260] calling uncompress_ll_init+0x0/0x0 > [ 4.276260] initcall uncompress_ll_init+0x0/0x0 returned 0 after 0 msecs. > [ 4.276260] Freeing init memory: 0K Umm, what happened to sysfs not becoming procfs v2? I thought we had a fairly strict requirement for "one value per file and not nicely formatted" for sysfs? > > >Author: Lee Jones <lee.jones@linaro.org> > > >Date: Wed Jun 30 14:00:40 2010 +0200 > > > > > > Boottime: A tool for automatic measurement of kernel/bootloader boot time > > > The overhead is very low and the results will be found under > > > sysfs/bootime, as well as detailed results in debugfs under > > > boottime/. The bootgraph* files are compatible with > > > scripts/bootgraph.pl. The reason for this patch is to provide > > > data (sysfs/boottime) suitable for automatic test-cases as > > > well as help for developers to reduce the boot time (debugfs). > > > Based heavily on the original driver by Jonas Aaberg. > > > Cc: Russell King <linux@arm.linux.org.uk> > > > Cc: Will Deacon <will.deacon@arm.com> > > > Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com> > > > Signed-off-by: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> > > > Signed-off-by: Lee Jones <lee.jones@linaro.org> > > > Reviewed-by: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com> > > > Tested-by: Mian Yousaf KAUKAB <mian.yousaf.kaukab@stericsson.com> > > > > > >diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile > > >index e8a4e58..8522356 100644 > > >--- a/arch/arm/common/Makefile > > >+++ b/arch/arm/common/Makefile > > >@@ -13,3 +13,4 @@ obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o > > > obj-$(CONFIG_SHARP_SCOOP) += scoop.o > > > obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o > > > obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o > > >+obj-$(CONFIG_BOOTTIME) += boottime.o > > >diff --git a/arch/arm/common/boottime.c b/arch/arm/common/boottime.c > > >new file mode 100644 > > >index 0000000..73e9e04 > > >--- /dev/null > > >+++ b/arch/arm/common/boottime.c > > >@@ -0,0 +1,46 @@ > > >+/* > > >+ * Copyright (C) ST-Ericsson SA 2009-2010 > > >+ * > > >+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson > > >+ * > > >+ * License terms: GNU General Public License (GPL) version 2 > > >+ * > > >+ * Store boot times measured during for example u-boot startup. > > >+ */ > > >+ > > >+#include <linux/kernel.h> > > >+#include <linux/init.h> > > >+#include <linux/boottime.h> > > >+#include <linux/string.h> > > >+#include <asm/setup.h> > > >+ > > >+static u32 bootloader_idle; > > >+static u32 bootloader_total; > > >+ > > >+static int __init boottime_parse_tag(const struct tag *tag) > > >+{ > > >+ int i; > > >+ char buff[BOOTTIME_MAX_NAME_LEN]; > > >+ > > >+ bootloader_idle = tag->u.boottime.idle; > > >+ bootloader_total = tag->u.boottime.total; > > >+ > > >+ for (i = 0; i < tag->u.boottime.num; i++) { > > >+ snprintf(buff, BOOTTIME_MAX_NAME_LEN, "%s+0x0/0x0", > > >+ tag->u.boottime.entry[i].name); > > >+ buff[BOOTTIME_MAX_NAME_LEN - 1] = '\0'; > > >+ boottime_mark_wtime(buff, tag->u.boottime.entry[i].time); > > >+ } > > >+ > > >+ return 0; > > >+} > > >+ > > >+__tagtable(ATAG_BOOTTIME, boottime_parse_tag); > > >+ > > >+int boottime_bootloader_idle(void) > > >+{ > > >+ if (bootloader_total == 0) > > >+ return 0; > > >+ > > >+ return (int) ((bootloader_idle) / (bootloader_total / 100)); > > >+} > > >diff --git a/arch/arm/include/asm/setup.h b/arch/arm/include/asm/setup.h > > >index 24d284a..e8da062 100644 > > >--- a/arch/arm/include/asm/setup.h > > >+++ b/arch/arm/include/asm/setup.h > > >@@ -143,6 +143,23 @@ struct tag_memclk { > > > __u32 fmemclk; > > > }; > > >+/* for automatic boot timing testcases */ > > >+#define ATAG_BOOTTIME 0x41000403 > > >+#define BOOTTIME_MAX_NAME_LEN 64 > > >+#define BOOTTIME_MAX 10 > > >+ > > >+struct boottime_entry { > > >+ u32 time; /* in us */ > > >+ u8 name[BOOTTIME_MAX_NAME_LEN]; > > >+}; > > >+ > > >+struct tag_boottime { > > >+ struct boottime_entry entry[BOOTTIME_MAX]; > > >+ u32 idle; /* in us */ > > >+ u32 total; /* in us */ > > >+ u8 num; > > >+}; > > >+ > > > struct tag { > > > struct tag_header hdr; > > > union { > > >@@ -165,6 +182,10 @@ struct tag { > > > * DC21285 specific > > > */ > > > struct tag_memclk memclk; > > >+ /* > > >+ * Boot time > > >+ */ > > >+ struct tag_boottime boottime; > > > } u; > > > }; > > >diff --git a/include/linux/boottime.h b/include/linux/boottime.h > > >new file mode 100644 > > >index 0000000..d330ecd > > >--- /dev/null > > >+++ b/include/linux/boottime.h > > >@@ -0,0 +1,89 @@ > > >+/* > > >+ * Copyright (C) ST-Ericsson SA 2009-2010 > > >+ * > > >+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson > > >+ * > > >+ * License terms: GNU General Public License (GPL) version 2 > > >+ * > > >+ * boottime is a tool for collecting start-up timing > > >+ * information and can together with boot loader support > > >+ * display a total system start-up time. > > >+ * > > >+ */ > > >+ > > >+#ifndef LINUX_BOOTTIME_H > > >+#define LINUX_BOOTTIME_H > > >+ > > >+#ifdef CONFIG_BOOTTIME > > >+#include <linux/kernel.h> > > >+ > > >+/** > > >+ * struct boottime_timer - Callbacks for generic timer. > > >+ * @init: Function to call at boottime initialization > > >+ * @get_time: Returns the number of us since start-up > > >+ * Preferable this is based upon a free running timer. > > >+ * This is the only required entry. > > >+ * @finalize: Called before init is executed and boottime is done. > > >+ */ > > >+struct boottime_timer { > > >+ int (*init)(void); > > >+ unsigned long (*get_time)(void); > > >+ void (*finalize)(void); > > >+}; > > >+ > > >+/** > > >+ * boottime_mark_wtime() > > >+ * Add a sample point with a given time. Useful for adding data collected > > >+ * by for example a boot loader. > > >+ * @name: The name of the sample point > > >+ * @time: The time in us when this point was reached > > >+ */ > > >+void __init boottime_mark_wtime(char *name, unsigned long time); > > >+ > > >+/** > > >+ * boottime_mark() > > >+ * Add a sample point with the current time. > > >+ * @name: The name of this sample point > > >+ */ > > >+void boottime_mark(char *name); > > >+ > > >+/** > > >+ * boottime_mark_symbolic() > > >+ * Add a sample point where the name is a symbolic function > > >+ * and %pF is needed to get the correct function name. > > >+ * @name: function name. > > >+ */ > > >+void __init boottime_mark_symbolic(void *name); > > >+ > > >+/** > > >+ * boottime_activate() > > >+ * Activates boottime and register callbacks. > > >+ * @bt: struct with callbacks. > > >+ */ > > >+void __ref boottime_activate(struct boottime_timer *bt); > > >+ > > >+/** > > >+ * boottime_deactivate() > > >+ * This function is called when the kernel boot is done. > > >+ * (before "free init memory" is called) > > >+ */ > > >+void boottime_deactivate(void); > > >+ > > >+/** > > >+ * boottime_system_up() > > >+ * A function is called when the basics of the kernel > > >+ * is up and running. > > >+ */ > > >+void __init boottime_system_up(void); > > >+ > > >+#else > > >+ > > >+#define boottime_mark_wtime(name, time) > > >+#define boottime_mark(name) > > >+#define boottime_mark_symbolic(name) > > >+#define boottime_activate(bt) > > >+#define boottime_deactivate() > > >+#define boottime_system_up() > > >+#endif > > >+ > > >+#endif /* LINUX_BOOTTIME_H */ > > >diff --git a/init/Kconfig b/init/Kconfig > > >index 4c93533..d0df8ff 100644 > > >--- a/init/Kconfig > > >+++ b/init/Kconfig > > >@@ -1464,6 +1464,15 @@ config PROFILING > > > Say Y here to enable the extended profiling support mechanisms used > > > by profilers such as OProfile. > > >+config BOOTTIME > > >+ bool "Boot time measurements" > > >+ default n > > >+ help > > >+ Adds sysfs entries (boottime/) with start-up timing information. > > >+ If CONFIG_DEBUG_FS is enabled, detailed information about the > > >+ boot time, including system load during boot can be extracted. > > >+ This information can be visualised with help of the bootgraph script. > > >+ > > > # > > > # Place an empty function call at each tracepoint site. Can be > > > # dynamically changed for a probe function. > > >diff --git a/init/Makefile b/init/Makefile > > >index 7bc47ee..356d529 100644 > > >--- a/init/Makefile > > >+++ b/init/Makefile > > >@@ -9,6 +9,7 @@ else > > > obj-$(CONFIG_BLK_DEV_INITRD) += initramfs.o > > > endif > > > obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o > > >+obj-$(CONFIG_BOOTTIME) += boottime.o > > > ifneq ($(CONFIG_ARCH_INIT_TASK),y) > > > obj-y += init_task.o > > >diff --git a/init/boottime.c b/init/boottime.c > > >new file mode 100644 > > >index 0000000..793c184 > > >--- /dev/null > > >+++ b/init/boottime.c > > >@@ -0,0 +1,475 @@ > > >+/* > > >+ * Copyright (C) ST-Ericsson SA 2009-2010 > > >+ * > > >+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson > > >+ * > > >+ * License terms: GNU General Public License (GPL) version 2 > > >+ * > > >+ * boottime is a tool for collecting start-up timing > > >+ * information and can together with boot loader support > > >+ * display a total system start-up time. > > >+ * > > >+ */ > > >+ > > >+#include <linux/kernel.h> > > >+#include <linux/module.h> > > >+#include <linux/list.h> > > >+#include <linux/seq_file.h> > > >+#include <linux/debugfs.h> > > >+#include <linux/spinlock.h> > > >+#include <linux/boottime.h> > > >+#include <linux/kernel_stat.h> > > >+#include <linux/kobject.h> > > >+#include <linux/device.h> > > >+#include <linux/sysfs.h> > > >+#include <linux/slab.h> > > >+ > > >+/* > > >+ * BOOTTIME_MAX_NAME_LEN is defined in arch/arm/include/asm/setup.h to 64. > > >+ * No crisis if they don't match. > > >+ */ > > >+#ifndef BOOTTIME_MAX_NAME_LEN > > >+#define BOOTTIME_MAX_NAME_LEN 64 > > >+#endif > > >+ > > >+/* > > >+ * We have a few static entries, since it is good to have measure points > > >+ * before the system is up and running properly > > >+ */ > > >+#define NUM_STATIC_BOOTTIME_ENTRIES 32 > > >+ > > >+struct boottime_list { > > >+ struct list_head list; > > >+ char name[BOOTTIME_MAX_NAME_LEN]; > > >+ /* Time in us since power on, possible including boot loader. */ > > >+ unsigned long time; > > >+ bool cpu_load; > > >+ struct kernel_cpustat cpu_usage[NR_CPUS]; > > >+}; > > >+ > > >+enum boottime_filter_type { > > >+ BOOTTIME_FILTER_OUT_ZERO, > > >+ BOOTTIME_FILTER_OUT_LESS_100, > > >+ BOOTTIME_FILTER_NOTHING, > > >+}; > > >+ > > >+enum boottime_symbolic_print { > > >+ BOOTTIME_SYMBOLIC_PRINT, > > >+ BOOTTIME_NORMAL_PRINT, > > >+}; > > >+ > > >+enum boottime_cpu_load { > > >+ BOOTTIME_CPU_LOAD, > > >+ BOOTTIME_NO_CPU_LOAD, > > >+}; > > >+ > > >+static LIST_HEAD(boottime_list); > > >+static DEFINE_SPINLOCK(boottime_list_lock); > > >+static struct boottime_timer boottime_timer; > > >+static int num_const_boottime_list; > > >+static struct boottime_list const_boottime_list[NUM_STATIC_BOOTTIME_ENTRIES]; > > >+static unsigned long time_kernel_done; > > >+static unsigned long time_bootloader_done; > > >+static bool system_up; > > >+static bool boottime_done; > > >+ > > >+int __attribute__((weak)) boottime_arch_startup(void) > > >+{ > > >+ return 0; > > >+} > > >+ > > >+int __attribute__((weak)) boottime_bootloader_idle(void) > > >+{ > > >+ return 0; > > >+} > > >+ > > >+static void boottime_mark_core(char *name, > > >+ unsigned long time, > > >+ enum boottime_symbolic_print symbolic, > > >+ enum boottime_cpu_load cpu_load) > > >+{ > > >+ struct boottime_list *b; > > >+ unsigned long flags = 0; > > >+ int i; > > >+ > > >+ if (system_up) { > > >+ b = kmalloc(sizeof(struct boottime_list), GFP_KERNEL); > > >+ if (!b) { > > >+ printk(KERN_ERR > > >+ "boottime: failed to allocate memory!\n"); > > >+ return; > > >+ } > > >+ > > >+ } else { > > >+ if (num_const_boottime_list < NUM_STATIC_BOOTTIME_ENTRIES) { > > >+ b = &const_boottime_list[num_const_boottime_list]; > > >+ num_const_boottime_list++; > > >+ } else { > > >+ printk(KERN_ERR > > >+ "boottime: too many early measure points!\n"); > > >+ return; > > >+ } > > >+ } > > >+ > > >+ INIT_LIST_HEAD(&b->list); > > >+ > > >+ if (symbolic == BOOTTIME_SYMBOLIC_PRINT) > > >+ snprintf(b->name, BOOTTIME_MAX_NAME_LEN, "%pF", name); > > >+ else > > >+ strncpy(b->name, name, BOOTTIME_MAX_NAME_LEN); > > >+ > > >+ b->name[BOOTTIME_MAX_NAME_LEN - 1] = '\0'; > > >+ b->time = time; > > >+ b->cpu_load = cpu_load; > > >+ > > >+ if (cpu_load == BOOTTIME_CPU_LOAD && system_up) > > >+ for_each_possible_cpu(i) { > > >+ b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] = > > >+ kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]; > > >+ b->cpu_usage[i].cpustat[CPUTIME_IDLE] = > > >+ kcpustat_cpu(i).cpustat[CPUTIME_IDLE]; > > >+ b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] = > > >+ kcpustat_cpu(i).cpustat[CPUTIME_IOWAIT]; > > >+ b->cpu_usage[i].cpustat[CPUTIME_IRQ] = > > >+ kcpustat_cpu(i).cpustat[CPUTIME_IRQ]; > > >+ /* > > >+ * TODO: Make sure that user, nice, softirq, steal > > >+ * and guest are not used during boot > > >+ */ > > >+ } > > >+ else > > >+ b->cpu_load = BOOTTIME_NO_CPU_LOAD; > > >+ > > >+ if (system_up) { > > >+ spin_lock_irqsave(&boottime_list_lock, flags); > > >+ list_add(&b->list, &boottime_list); > > >+ spin_unlock_irqrestore(&boottime_list_lock, flags); > > >+ } else { > > >+ list_add(&b->list, &boottime_list); > > >+ } > > >+} > > >+ > > >+void __init boottime_mark_wtime(char *name, unsigned long time) > > >+{ > > >+ boottime_mark_core(name, time, > > >+ BOOTTIME_NORMAL_PRINT, > > >+ BOOTTIME_NO_CPU_LOAD); > > >+} > > >+ > > >+void __ref boottime_mark_symbolic(void *name) > > >+{ > > >+ > > >+ if (boottime_done) > > >+ return; > > >+ > > >+ if (boottime_timer.get_time) > > >+ boottime_mark_core((char *) name, > > >+ boottime_timer.get_time(), > > >+ BOOTTIME_SYMBOLIC_PRINT, > > >+ BOOTTIME_CPU_LOAD); > > >+} > > >+ > > >+void boottime_mark(char *name) > > >+{ > > >+ if (boottime_timer.get_time) > > >+ boottime_mark_core(name, > > >+ boottime_timer.get_time(), > > >+ BOOTTIME_NORMAL_PRINT, > > >+ BOOTTIME_CPU_LOAD); > > >+} > > >+ > > >+void __init boottime_activate(struct boottime_timer *bt) > > >+{ > > >+ struct boottime_list *b; > > >+ int res = 0; > > >+ unsigned long flags; > > >+ > > >+ if (bt == NULL) { > > >+ printk(KERN_ERR > > >+ "boottime: error: bad configured\n"); > > >+ return; > > >+ } > > >+ > > >+ if (bt->get_time == NULL) { > > >+ printk(KERN_ERR > > >+ "boottime: error: you must provide a get_time() function\n"); > > >+ return; > > >+ } > > >+ memcpy(&boottime_timer, bt, sizeof(struct boottime_timer)); > > >+ > > >+ if (boottime_timer.init) > > >+ res = boottime_timer.init(); > > >+ > > >+ if (res) { > > >+ printk(KERN_ERR "boottime: initialization failed\n"); > > >+ return; > > >+ } > > >+ > > >+ if (boottime_arch_startup()) > > >+ printk(KERN_ERR > > >+ "boottime: arch specfic initialization failed\n"); > > >+ > > >+ spin_lock_irqsave(&boottime_list_lock, flags); > > >+ > > >+ if (!list_empty(&boottime_list)) { > > >+ > > >+ b = list_first_entry(&boottime_list, struct boottime_list, > > >+ list); > > >+ if (b) > > >+ time_bootloader_done = b->time; > > >+ } > > >+ > > >+ spin_unlock_irqrestore(&boottime_list_lock, flags); > > >+} > > >+ > > >+void __init boottime_system_up(void) > > >+{ > > >+ system_up = true; > > >+} > > >+ > > >+void boottime_deactivate(void) > > >+{ > > >+ struct boottime_list *b; > > >+ unsigned long flags; > > >+ > > >+ boottime_mark("execute_init+0x0/0x0"); > > >+ > > >+ boottime_done = true; > > >+ > > >+ spin_lock_irqsave(&boottime_list_lock, flags); > > >+ b = list_first_entry(&boottime_list, struct boottime_list, list); > > >+ spin_unlock_irqrestore(&boottime_list_lock, flags); > > >+ > > >+ time_kernel_done = b->time; > > >+ > > >+ if (boottime_timer.finalize) > > >+ boottime_timer.finalize(); > > >+} > > >+ > > >+#ifdef CONFIG_DEBUG_FS > > >+static void boottime_debugfs_load(struct seq_file *s, > > >+ struct boottime_list *b, > > >+ struct boottime_list *p) > > >+{ > > >+ int i; > > >+ unsigned long total_p, total_b; > > >+ unsigned long system_total, idle_total, irq_total, iowait_total; > > >+ unsigned long system_load, idle_load, irq_load, iowait_load; > > >+ > > >+ for_each_possible_cpu(i) { > > >+ total_b = (b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] + > > >+ b->cpu_usage[i].cpustat[CPUTIME_IDLE] + > > >+ b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] + > > >+ b->cpu_usage[i].cpustat[CPUTIME_IRQ]); > > >+ > > >+ total_p = (p->cpu_usage[i].cpustat[CPUTIME_SYSTEM] + > > >+ p->cpu_usage[i].cpustat[CPUTIME_IDLE] + > > >+ p->cpu_usage[i].cpustat[CPUTIME_IOWAIT] + > > >+ p->cpu_usage[i].cpustat[CPUTIME_IRQ]); > > >+ > > >+ if (total_b == total_p) > > >+ continue; > > >+ > > >+ system_total = b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] > > >+ - p->cpu_usage[i].cpustat[CPUTIME_SYSTEM]; > > >+ idle_total = b->cpu_usage[i].cpustat[CPUTIME_IDLE] > > >+ - p->cpu_usage[i].cpustat[CPUTIME_IDLE]; > > >+ irq_total = b->cpu_usage[i].cpustat[CPUTIME_IRQ] > > >+ - p->cpu_usage[i].cpustat[CPUTIME_IRQ]; > > >+ iowait_total = b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] > > >+ - p->cpu_usage[i].cpustat[CPUTIME_IOWAIT]; > > >+ > > >+ system_load = (100 * system_total / (total_b - total_p)); > > >+ idle_load = (100 * idle_total / (total_b - total_p)); > > >+ irq_load = (100 * irq_total / (total_b - total_p)); > > >+ iowait_load = (100 * iowait_total / (total_b - total_p)); > > >+ > > >+ seq_printf(s, > > >+ " cpu%d system: %lu%% idle: %lu%% iowait: %lu%% irq: %lu%%", > > >+ i, > > >+ system_load, > > >+ idle_load, > > >+ iowait_load, > > >+ irq_load); > > >+ } > > >+ seq_printf(s, "\n"); > > >+} > > >+ > > >+static void boottime_debugfs_print(struct seq_file *s, > > >+ struct boottime_list *b, > > >+ struct boottime_list *p) > > >+{ > > >+ seq_printf(s, "[%5lu.%06lu] calling %s\n", > > >+ p->time / 1000000, > > >+ (p->time % 1000000), > > >+ p->name); > > >+ seq_printf(s, "[%5lu.%06lu] initcall %s returned 0 after %ld msecs.", > > >+ b->time / 1000000, > > >+ (b->time % 1000000), > > >+ p->name, (b->time - p->time) / 1000); > > >+ > > >+ if (p->cpu_load == BOOTTIME_NO_CPU_LOAD || > > >+ b->cpu_load == BOOTTIME_NO_CPU_LOAD) { > > >+ seq_printf(s, "\n"); > > >+ return; > > >+ } > > >+ > > >+ boottime_debugfs_load(s, b, p); > > >+} > > >+ > > >+static int boottime_debugfs_bootgraph_show(struct seq_file *s, void *iter) > > >+{ > > >+ struct boottime_list *b, *p = NULL, *old_p = NULL; > > >+ enum boottime_filter_type filter = (int)s->private; > > >+ > > >+ list_for_each_entry_reverse(b, &boottime_list, list) { > > >+ if (p) { > > >+ if (!(filter == BOOTTIME_FILTER_OUT_ZERO && > > >+ (b->time - p->time) / 1000 == 0) > > >+ && !(filter == BOOTTIME_FILTER_OUT_LESS_100 && > > >+ (b->time - p->time) < 100 * 1000)) > > >+ boottime_debugfs_print(s, b, p); > > >+ old_p = p; > > >+ } > > >+ p = b; > > >+ } > > >+ > > >+ if (filter == BOOTTIME_FILTER_NOTHING && p) > > >+ boottime_debugfs_print(s, p, p); > > >+ > > >+ if (p) > > >+ seq_printf(s, "[%5lu.%06lu] Freeing init memory: 0K\n", > > >+ p->time / 1000000, p->time % 1000000); > > >+ return 0; > > >+} > > >+ > > >+static int boottime_debugfs_summary_show(struct seq_file *s, void *data) > > >+{ > > >+ struct boottime_list *b, b_zero; > > >+ > > >+ if (time_bootloader_done) > > >+ seq_printf(s, "bootloader: %ld msecs\n", > > >+ time_bootloader_done / 1000); > > >+ > > >+ seq_printf(s, "kernel: %ld msecs\ntotal: %ld msecs\n", > > >+ (time_kernel_done - time_bootloader_done) / 1000, > > >+ time_kernel_done / 1000); > > >+ seq_printf(s, "kernel:"); > > >+ b = list_first_entry(&boottime_list, > > >+ struct boottime_list, list); > > >+ memset(&b_zero, 0, sizeof(struct boottime_list)); > > >+ boottime_debugfs_load(s, b, &b_zero); > > >+ > > >+ if (time_bootloader_done) > > >+ seq_printf(s, > > >+ "bootloader: cpu0 system: %d%% idle: %d%% iowait: 0%% irq: 0%%\n", > > >+ 100 - boottime_bootloader_idle(), > > >+ boottime_bootloader_idle()); > > >+ return 0; > > >+} > > >+ > > >+static int boottime_debugfs_bootgraph_open(struct inode *inode, > > >+ struct file *file) > > >+{ > > >+ return single_open(file, > > >+ boottime_debugfs_bootgraph_show, > > >+ inode->i_private); > > >+} > > >+ > > >+static int boottime_debugfs_summary_open(struct inode *inode, > > >+ struct file *file) > > >+{ > > >+ return single_open(file, > > >+ boottime_debugfs_summary_show, > > >+ inode->i_private); > > >+} > > >+ > > >+static const struct file_operations boottime_debugfs_bootgraph_operations = { > > >+ .open = boottime_debugfs_bootgraph_open, > > >+ .read = seq_read, > > >+ .llseek = seq_lseek, > > >+ .release = single_release, > > >+}; > > >+ > > >+static const struct file_operations boottime_debugfs_summary_operations = { > > >+ .open = boottime_debugfs_summary_open, > > >+ .read = seq_read, > > >+ .llseek = seq_lseek, > > >+ .release = single_release, > > >+}; > > >+ > > >+void boottime_debugfs_init(void) > > >+{ > > >+ struct dentry *dir; > > >+ > > >+ dir = debugfs_create_dir("boottime", NULL); > > >+ > > >+ (void) debugfs_create_file("bootgraph", S_IFREG | S_IRUGO, > > >+ dir, (void *)BOOTTIME_FILTER_NOTHING, > > >+ &boottime_debugfs_bootgraph_operations); > > >+ (void) debugfs_create_file("bootgraph_all_except0", S_IFREG | S_IRUGO, > > >+ dir, (void *)BOOTTIME_FILTER_OUT_ZERO, > > >+ &boottime_debugfs_bootgraph_operations); > > >+ (void) debugfs_create_file("bootgraph_larger100", > > >+ S_IFREG | S_IRUGO, > > >+ dir, (void *)BOOTTIME_FILTER_OUT_LESS_100, > > >+ &boottime_debugfs_bootgraph_operations); > > >+ (void) debugfs_create_file("summary", S_IFREG | S_IRUGO, > > >+ dir, NULL, > > >+ &boottime_debugfs_summary_operations); > > >+} > > >+#else > > >+#define boottime_debugfs_init(x) > > >+#endif > > >+ > > >+static ssize_t show_bootloader(struct device *dev, > > >+ struct device_attribute *attr, > > >+ char *buf) > > >+{ > > >+ return sprintf(buf, "%ld\n", time_bootloader_done / 1000); > > >+} > > >+ > > >+static ssize_t show_kernel(struct device *dev, > > >+ struct device_attribute *attr, > > >+ char *buf) > > >+{ > > >+ return sprintf(buf, "%ld\n", > > >+ (time_kernel_done - time_bootloader_done) / 1000); > > >+} > > >+ > > >+DEVICE_ATTR(kernel, 0444, show_kernel, NULL); > > >+DEVICE_ATTR(bootloader, 0444, show_bootloader, NULL); > > >+ > > >+static struct attribute *boottime_sysfs_entries[] = { > > >+ &dev_attr_kernel.attr, > > >+ &dev_attr_bootloader.attr, > > >+ NULL > > >+}; > > >+ > > >+static struct attribute_group boottime_attr_grp = { > > >+ .name = NULL, > > >+ .attrs = boottime_sysfs_entries, > > >+}; > > >+ > > >+static int __init boottime_init(void) > > >+{ > > >+ struct kobject *boottime_kobj; > > >+ > > >+ boottime_kobj = kobject_create_and_add("boottime", NULL); > > >+ if (!boottime_kobj) { > > >+ printk(KERN_ERR "boottime: out of memory!\n"); > > >+ return -ENOMEM; > > >+ } > > >+ > > >+ if (sysfs_create_group(boottime_kobj, &boottime_attr_grp) < 0) { > > >+ kobject_put(boottime_kobj); > > >+ printk(KERN_ERR "boottime: Failed creating sysfs group\n"); > > >+ return -ENOMEM; > > >+ } > > >+ > > >+ boottime_debugfs_init(); > > >+ > > >+ return 0; > > >+} > > >+ > > >+late_initcall(boottime_init); > > >diff --git a/init/main.c b/init/main.c > > >index 313360f..c06afd0 100644 > > >--- a/init/main.c > > >+++ b/init/main.c > > >@@ -69,6 +69,7 @@ > > > #include <linux/slab.h> > > > #include <linux/perf_event.h> > > > #include <linux/file.h> > > >+#include <linux/boottime.h> > > > #include <asm/io.h> > > > #include <asm/bugs.h> > > >@@ -679,6 +680,8 @@ int __init_or_module do_one_initcall(initcall_t fn) > > > int count = preempt_count(); > > > int ret; > > >+ boottime_mark_symbolic(fn); > > >+ > > > if (initcall_debug) > > > ret = do_one_initcall_debug(fn); > > > else > > >@@ -804,6 +807,7 @@ static noinline int init_post(void) > > > { > > > /* need to finish all async __init code before freeing the memory */ > > > async_synchronize_full(); > > >+ boottime_deactivate(); > > > free_initmem(); > > > mark_rodata_ro(); > > > system_state = SYSTEM_RUNNING; > > >@@ -863,6 +867,7 @@ static int __init kernel_init(void * unused) > > > do_pre_smp_initcalls(); > > > lockup_detector_init(); > > >+ boottime_system_up(); > > > smp_init(); > > > sched_init_smp(); > > >@@ -885,6 +890,7 @@ static int __init kernel_init(void * unused) > > > if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { > > > ramdisk_execute_command = NULL; > > >+ boottime_mark("mount+0x0/0x0"); > > > prepare_namespace(); > > > } > > > > -- > Lee Jones > Linaro ST-Ericsson Landing Team Lead > Linaro.org │ Open source software for ARM SoCs > Follow Linaro: Facebook | Twitter | Blog ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time 2012-10-12 13:48 ` Russell King - ARM Linux @ 2012-10-12 13:53 ` Arnd Bergmann -1 siblings, 0 replies; 33+ messages in thread From: Arnd Bergmann @ 2012-10-12 13:53 UTC (permalink / raw) To: linux-arm-kernel On Friday 12 October 2012, Russell King - ARM Linux wrote: > > root at ME:/ cat /sys/kernel/debug/boottime/bootgraph > > [ 0.185254] calling splash+0x0/0x0 > > [ 2.984335] initcall splash+0x0/0x0 returned 0 after 2799 msecs. > > [ 2.984335] calling autoboot_delay+0x0/0x0 > > [ 4.089513] initcall autoboot_delay+0x0/0x0 returned 0 after 1105 msecs. > > [ 4.089513] calling load_kernel+0x0/0x0 > > [ 4.239174] initcall load_kernel+0x0/0x0 returned 0 after 149 msecs. > > [ 4.239174] calling boot_kernel+0x0/0x0 > > [ 4.276260] initcall boot_kernel+0x0/0x0 returned 0 after 37 msecs. > > [ 4.276260] calling uncompress_ll_init+0x0/0x0 > > [ 4.276260] initcall uncompress_ll_init+0x0/0x0 returned 0 after 0 msecs. > > [ 4.276260] Freeing init memory: 0K > > Umm, what happened to sysfs not becoming procfs v2? I thought we had > a fairly strict requirement for "one value per file and not nicely > formatted" for sysfs? > I was thinking the same thing at first, but then I noticed it's actually debugfs, which has no such rules. Arnd ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time @ 2012-10-12 13:53 ` Arnd Bergmann 0 siblings, 0 replies; 33+ messages in thread From: Arnd Bergmann @ 2012-10-12 13:53 UTC (permalink / raw) To: Russell King - ARM Linux Cc: Lee Jones, Greg Kroah-Hartman, Dan Murphy, Nishanth Menon, linux-arm-kernel, linux-kernel, linus.walleij, Jonas Aaberg, Will Deacon, Mian Yousaf Kaukab On Friday 12 October 2012, Russell King - ARM Linux wrote: > > root@ME:/ cat /sys/kernel/debug/boottime/bootgraph > > [ 0.185254] calling splash+0x0/0x0 > > [ 2.984335] initcall splash+0x0/0x0 returned 0 after 2799 msecs. > > [ 2.984335] calling autoboot_delay+0x0/0x0 > > [ 4.089513] initcall autoboot_delay+0x0/0x0 returned 0 after 1105 msecs. > > [ 4.089513] calling load_kernel+0x0/0x0 > > [ 4.239174] initcall load_kernel+0x0/0x0 returned 0 after 149 msecs. > > [ 4.239174] calling boot_kernel+0x0/0x0 > > [ 4.276260] initcall boot_kernel+0x0/0x0 returned 0 after 37 msecs. > > [ 4.276260] calling uncompress_ll_init+0x0/0x0 > > [ 4.276260] initcall uncompress_ll_init+0x0/0x0 returned 0 after 0 msecs. > > [ 4.276260] Freeing init memory: 0K > > Umm, what happened to sysfs not becoming procfs v2? I thought we had > a fairly strict requirement for "one value per file and not nicely > formatted" for sysfs? > I was thinking the same thing at first, but then I noticed it's actually debugfs, which has no such rules. Arnd ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time 2012-10-12 13:53 ` Arnd Bergmann @ 2012-10-12 14:01 ` Lee Jones -1 siblings, 0 replies; 33+ messages in thread From: Lee Jones @ 2012-10-12 14:01 UTC (permalink / raw) To: linux-arm-kernel On Fri, 12 Oct 2012, Arnd Bergmann wrote: > On Friday 12 October 2012, Russell King - ARM Linux wrote: > > > root at ME:/ cat /sys/kernel/debug/boottime/bootgraph > > > [ 0.185254] calling splash+0x0/0x0 > > > [ 2.984335] initcall splash+0x0/0x0 returned 0 after 2799 msecs. > > > [ 2.984335] calling autoboot_delay+0x0/0x0 > > > [ 4.089513] initcall autoboot_delay+0x0/0x0 returned 0 after 1105 msecs. > > > [ 4.089513] calling load_kernel+0x0/0x0 > > > [ 4.239174] initcall load_kernel+0x0/0x0 returned 0 after 149 msecs. > > > [ 4.239174] calling boot_kernel+0x0/0x0 > > > [ 4.276260] initcall boot_kernel+0x0/0x0 returned 0 after 37 msecs. > > > [ 4.276260] calling uncompress_ll_init+0x0/0x0 > > > [ 4.276260] initcall uncompress_ll_init+0x0/0x0 returned 0 after 0 msecs. > > > [ 4.276260] Freeing init memory: 0K > > > > Umm, what happened to sysfs not becoming procfs v2? I thought we had > > a fairly strict requirement for "one value per file and not nicely > > formatted" for sysfs? > > > > I was thinking the same thing at first, but then I noticed it's actually > debugfs, which has no such rules. Right. :) -- Lee Jones Linaro ST-Ericsson Landing Team Lead Linaro.org ? Open source software for ARM SoCs Follow Linaro: Facebook | Twitter | Blog ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time @ 2012-10-12 14:01 ` Lee Jones 0 siblings, 0 replies; 33+ messages in thread From: Lee Jones @ 2012-10-12 14:01 UTC (permalink / raw) To: Arnd Bergmann Cc: Russell King - ARM Linux, Greg Kroah-Hartman, Dan Murphy, Nishanth Menon, linux-arm-kernel, linux-kernel, linus.walleij, Jonas Aaberg, Will Deacon, Mian Yousaf Kaukab On Fri, 12 Oct 2012, Arnd Bergmann wrote: > On Friday 12 October 2012, Russell King - ARM Linux wrote: > > > root@ME:/ cat /sys/kernel/debug/boottime/bootgraph > > > [ 0.185254] calling splash+0x0/0x0 > > > [ 2.984335] initcall splash+0x0/0x0 returned 0 after 2799 msecs. > > > [ 2.984335] calling autoboot_delay+0x0/0x0 > > > [ 4.089513] initcall autoboot_delay+0x0/0x0 returned 0 after 1105 msecs. > > > [ 4.089513] calling load_kernel+0x0/0x0 > > > [ 4.239174] initcall load_kernel+0x0/0x0 returned 0 after 149 msecs. > > > [ 4.239174] calling boot_kernel+0x0/0x0 > > > [ 4.276260] initcall boot_kernel+0x0/0x0 returned 0 after 37 msecs. > > > [ 4.276260] calling uncompress_ll_init+0x0/0x0 > > > [ 4.276260] initcall uncompress_ll_init+0x0/0x0 returned 0 after 0 msecs. > > > [ 4.276260] Freeing init memory: 0K > > > > Umm, what happened to sysfs not becoming procfs v2? I thought we had > > a fairly strict requirement for "one value per file and not nicely > > formatted" for sysfs? > > > > I was thinking the same thing at first, but then I noticed it's actually > debugfs, which has no such rules. Right. :) -- Lee Jones Linaro ST-Ericsson Landing Team Lead Linaro.org │ Open source software for ARM SoCs Follow Linaro: Facebook | Twitter | Blog ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time 2012-10-12 14:01 ` Lee Jones @ 2012-10-12 16:36 ` Dan Murphy -1 siblings, 0 replies; 33+ messages in thread From: Dan Murphy @ 2012-10-12 16:36 UTC (permalink / raw) To: linux-arm-kernel On 10/12/2012 09:01 AM, Lee Jones wrote: > On Fri, 12 Oct 2012, Arnd Bergmann wrote: > >> On Friday 12 October 2012, Russell King - ARM Linux wrote: >>>> root at ME:/ cat /sys/kernel/debug/boottime/bootgraph >>>> [ 0.185254] calling splash+0x0/0x0 >>>> [ 2.984335] initcall splash+0x0/0x0 returned 0 after 2799 msecs. >>>> [ 2.984335] calling autoboot_delay+0x0/0x0 >>>> [ 4.089513] initcall autoboot_delay+0x0/0x0 returned 0 after 1105 msecs. >>>> [ 4.089513] calling load_kernel+0x0/0x0 >>>> [ 4.239174] initcall load_kernel+0x0/0x0 returned 0 after 149 msecs. >>>> [ 4.239174] calling boot_kernel+0x0/0x0 >>>> [ 4.276260] initcall boot_kernel+0x0/0x0 returned 0 after 37 msecs. >>>> [ 4.276260] calling uncompress_ll_init+0x0/0x0 >>>> [ 4.276260] initcall uncompress_ll_init+0x0/0x0 returned 0 after 0 msecs. >>>> [ 4.276260] Freeing init memory: 0K >>> Umm, what happened to sysfs not becoming procfs v2? I thought we had >>> a fairly strict requirement for "one value per file and not nicely >>> formatted" for sysfs? >>> >> I was thinking the same thing at first, but then I noticed it's actually >> debugfs, which has no such rules. > Right. :) > OK I don't see when boottime_activate is called. Where would this call actually be made from? I see the call to deactivate but no call to activate. Dan ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time @ 2012-10-12 16:36 ` Dan Murphy 0 siblings, 0 replies; 33+ messages in thread From: Dan Murphy @ 2012-10-12 16:36 UTC (permalink / raw) To: Lee Jones Cc: Arnd Bergmann, Russell King - ARM Linux, Greg Kroah-Hartman, Nishanth Menon, linux-arm-kernel, linux-kernel, linus.walleij, Jonas Aaberg, Will Deacon, Mian Yousaf Kaukab On 10/12/2012 09:01 AM, Lee Jones wrote: > On Fri, 12 Oct 2012, Arnd Bergmann wrote: > >> On Friday 12 October 2012, Russell King - ARM Linux wrote: >>>> root@ME:/ cat /sys/kernel/debug/boottime/bootgraph >>>> [ 0.185254] calling splash+0x0/0x0 >>>> [ 2.984335] initcall splash+0x0/0x0 returned 0 after 2799 msecs. >>>> [ 2.984335] calling autoboot_delay+0x0/0x0 >>>> [ 4.089513] initcall autoboot_delay+0x0/0x0 returned 0 after 1105 msecs. >>>> [ 4.089513] calling load_kernel+0x0/0x0 >>>> [ 4.239174] initcall load_kernel+0x0/0x0 returned 0 after 149 msecs. >>>> [ 4.239174] calling boot_kernel+0x0/0x0 >>>> [ 4.276260] initcall boot_kernel+0x0/0x0 returned 0 after 37 msecs. >>>> [ 4.276260] calling uncompress_ll_init+0x0/0x0 >>>> [ 4.276260] initcall uncompress_ll_init+0x0/0x0 returned 0 after 0 msecs. >>>> [ 4.276260] Freeing init memory: 0K >>> Umm, what happened to sysfs not becoming procfs v2? I thought we had >>> a fairly strict requirement for "one value per file and not nicely >>> formatted" for sysfs? >>> >> I was thinking the same thing at first, but then I noticed it's actually >> debugfs, which has no such rules. > Right. :) > OK I don't see when boottime_activate is called. Where would this call actually be made from? I see the call to deactivate but no call to activate. Dan ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time 2012-10-12 16:36 ` Dan Murphy @ 2012-10-12 16:42 ` Lee Jones -1 siblings, 0 replies; 33+ messages in thread From: Lee Jones @ 2012-10-12 16:42 UTC (permalink / raw) To: linux-arm-kernel On Fri, 12 Oct 2012, Dan Murphy wrote: > > On 10/12/2012 09:01 AM, Lee Jones wrote: > >On Fri, 12 Oct 2012, Arnd Bergmann wrote: > > > >>On Friday 12 October 2012, Russell King - ARM Linux wrote: > >>>>root at ME:/ cat /sys/kernel/debug/boottime/bootgraph > >>>>[ 0.185254] calling splash+0x0/0x0 > >>>>[ 2.984335] initcall splash+0x0/0x0 returned 0 after 2799 msecs. > >>>>[ 2.984335] calling autoboot_delay+0x0/0x0 > >>>>[ 4.089513] initcall autoboot_delay+0x0/0x0 returned 0 after 1105 msecs. > >>>>[ 4.089513] calling load_kernel+0x0/0x0 > >>>>[ 4.239174] initcall load_kernel+0x0/0x0 returned 0 after 149 msecs. > >>>>[ 4.239174] calling boot_kernel+0x0/0x0 > >>>>[ 4.276260] initcall boot_kernel+0x0/0x0 returned 0 after 37 msecs. > >>>>[ 4.276260] calling uncompress_ll_init+0x0/0x0 > >>>>[ 4.276260] initcall uncompress_ll_init+0x0/0x0 returned 0 after 0 msecs. > >>>>[ 4.276260] Freeing init memory: 0K > >>>Umm, what happened to sysfs not becoming procfs v2? I thought we had > >>>a fairly strict requirement for "one value per file and not nicely > >>>formatted" for sysfs? > >>> > >>I was thinking the same thing at first, but then I noticed it's actually > >>debugfs, which has no such rules. > >Right. :) > > > OK I don't see when boottime_activate is called. > > Where would this call actually be made from? > > I see the call to deactivate but no call to activate. Here perhaps (Jonas, alerted me to the missing patch): commit 4c49a18bcfd2d041cbad7f41c6e6b39d90008382 (HEAD, refs/heads/dt-snowball-pre-rc1) Author: Jonas Aaberg <jonas.aberg@stericsson.com> Date: Wed Sep 14 09:29:20 2011 +0200 drivers: clocksource: dbx500-prcmu: Add boottime support Change-Id: I9b5e3d050131c08c08786ae84cb76619c0525049 Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32055 diff --git a/drivers/clocksource/clksrc-dbx500-prcmu.c b/drivers/clocksource/clksrc-dbx500-prcmu.c index c26c369..0069cd9 100644 --- a/drivers/clocksource/clksrc-dbx500-prcmu.c +++ b/drivers/clocksource/clksrc-dbx500-prcmu.c @@ -14,6 +14,7 @@ */ #include <linux/clockchips.h> #include <linux/clksrc-dbx500-prcmu.h> +#include <linux/boottime.h> #include <asm/sched_clock.h> @@ -68,6 +69,23 @@ static u32 notrace dbx500_prcmu_sched_clock_read(void) #endif +#ifdef CONFIG_BOOTTIME +static unsigned long __init boottime_get_time(void) +{ + return div_s64(clocksource_cyc2ns(clocksource_dbx500_prcmu.read( + &clocksource_dbx500_prcmu), + clocksource_dbx500_prcmu.mult, + clocksource_dbx500_prcmu.shift), + 1000); +} + +static struct boottime_timer __initdata boottime_timer = { + .init = NULL, + .get_time = boottime_get_time, + .finalize = NULL, +}; +#endif + void __init clksrc_dbx500_prcmu_init(void __iomem *base) { clksrc_dbx500_timer_base = base; @@ -90,4 +108,6 @@ void __init clksrc_dbx500_prcmu_init(void __iomem *base) 32, RATE_32K); #endif clocksource_register_hz(&clocksource_dbx500_prcmu, RATE_32K); + + boottime_activate(&boottime_timer); } -- Lee Jones Linaro ST-Ericsson Landing Team Lead Linaro.org ? Open source software for ARM SoCs Follow Linaro: Facebook | Twitter | Blog ^ permalink raw reply related [flat|nested] 33+ messages in thread
* Re: [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time @ 2012-10-12 16:42 ` Lee Jones 0 siblings, 0 replies; 33+ messages in thread From: Lee Jones @ 2012-10-12 16:42 UTC (permalink / raw) To: Dan Murphy Cc: Arnd Bergmann, Russell King - ARM Linux, Greg Kroah-Hartman, Nishanth Menon, linux-arm-kernel, linux-kernel, linus.walleij, Jonas Aaberg, Will Deacon, Mian Yousaf Kaukab On Fri, 12 Oct 2012, Dan Murphy wrote: > > On 10/12/2012 09:01 AM, Lee Jones wrote: > >On Fri, 12 Oct 2012, Arnd Bergmann wrote: > > > >>On Friday 12 October 2012, Russell King - ARM Linux wrote: > >>>>root@ME:/ cat /sys/kernel/debug/boottime/bootgraph > >>>>[ 0.185254] calling splash+0x0/0x0 > >>>>[ 2.984335] initcall splash+0x0/0x0 returned 0 after 2799 msecs. > >>>>[ 2.984335] calling autoboot_delay+0x0/0x0 > >>>>[ 4.089513] initcall autoboot_delay+0x0/0x0 returned 0 after 1105 msecs. > >>>>[ 4.089513] calling load_kernel+0x0/0x0 > >>>>[ 4.239174] initcall load_kernel+0x0/0x0 returned 0 after 149 msecs. > >>>>[ 4.239174] calling boot_kernel+0x0/0x0 > >>>>[ 4.276260] initcall boot_kernel+0x0/0x0 returned 0 after 37 msecs. > >>>>[ 4.276260] calling uncompress_ll_init+0x0/0x0 > >>>>[ 4.276260] initcall uncompress_ll_init+0x0/0x0 returned 0 after 0 msecs. > >>>>[ 4.276260] Freeing init memory: 0K > >>>Umm, what happened to sysfs not becoming procfs v2? I thought we had > >>>a fairly strict requirement for "one value per file and not nicely > >>>formatted" for sysfs? > >>> > >>I was thinking the same thing at first, but then I noticed it's actually > >>debugfs, which has no such rules. > >Right. :) > > > OK I don't see when boottime_activate is called. > > Where would this call actually be made from? > > I see the call to deactivate but no call to activate. Here perhaps (Jonas, alerted me to the missing patch): commit 4c49a18bcfd2d041cbad7f41c6e6b39d90008382 (HEAD, refs/heads/dt-snowball-pre-rc1) Author: Jonas Aaberg <jonas.aberg@stericsson.com> Date: Wed Sep 14 09:29:20 2011 +0200 drivers: clocksource: dbx500-prcmu: Add boottime support Change-Id: I9b5e3d050131c08c08786ae84cb76619c0525049 Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32055 diff --git a/drivers/clocksource/clksrc-dbx500-prcmu.c b/drivers/clocksource/clksrc-dbx500-prcmu.c index c26c369..0069cd9 100644 --- a/drivers/clocksource/clksrc-dbx500-prcmu.c +++ b/drivers/clocksource/clksrc-dbx500-prcmu.c @@ -14,6 +14,7 @@ */ #include <linux/clockchips.h> #include <linux/clksrc-dbx500-prcmu.h> +#include <linux/boottime.h> #include <asm/sched_clock.h> @@ -68,6 +69,23 @@ static u32 notrace dbx500_prcmu_sched_clock_read(void) #endif +#ifdef CONFIG_BOOTTIME +static unsigned long __init boottime_get_time(void) +{ + return div_s64(clocksource_cyc2ns(clocksource_dbx500_prcmu.read( + &clocksource_dbx500_prcmu), + clocksource_dbx500_prcmu.mult, + clocksource_dbx500_prcmu.shift), + 1000); +} + +static struct boottime_timer __initdata boottime_timer = { + .init = NULL, + .get_time = boottime_get_time, + .finalize = NULL, +}; +#endif + void __init clksrc_dbx500_prcmu_init(void __iomem *base) { clksrc_dbx500_timer_base = base; @@ -90,4 +108,6 @@ void __init clksrc_dbx500_prcmu_init(void __iomem *base) 32, RATE_32K); #endif clocksource_register_hz(&clocksource_dbx500_prcmu, RATE_32K); + + boottime_activate(&boottime_timer); } -- Lee Jones Linaro ST-Ericsson Landing Team Lead Linaro.org │ Open source software for ARM SoCs Follow Linaro: Facebook | Twitter | Blog ^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time 2012-10-12 16:42 ` Lee Jones @ 2012-10-15 15:52 ` Dan Murphy -1 siblings, 0 replies; 33+ messages in thread From: Dan Murphy @ 2012-10-15 15:52 UTC (permalink / raw) To: linux-arm-kernel On 10/12/2012 11:42 AM, Lee Jones wrote: > On Fri, 12 Oct 2012, Dan Murphy wrote: > >> On 10/12/2012 09:01 AM, Lee Jones wrote: >>> On Fri, 12 Oct 2012, Arnd Bergmann wrote: >>> >>>> On Friday 12 October 2012, Russell King - ARM Linux wrote: >>>>>> root at ME:/ cat /sys/kernel/debug/boottime/bootgraph >>>>>> [ 0.185254] calling splash+0x0/0x0 >>>>>> [ 2.984335] initcall splash+0x0/0x0 returned 0 after 2799 msecs. >>>>>> [ 2.984335] calling autoboot_delay+0x0/0x0 >>>>>> [ 4.089513] initcall autoboot_delay+0x0/0x0 returned 0 after 1105 msecs. >>>>>> [ 4.089513] calling load_kernel+0x0/0x0 >>>>>> [ 4.239174] initcall load_kernel+0x0/0x0 returned 0 after 149 msecs. >>>>>> [ 4.239174] calling boot_kernel+0x0/0x0 >>>>>> [ 4.276260] initcall boot_kernel+0x0/0x0 returned 0 after 37 msecs. >>>>>> [ 4.276260] calling uncompress_ll_init+0x0/0x0 >>>>>> [ 4.276260] initcall uncompress_ll_init+0x0/0x0 returned 0 after 0 msecs. >>>>>> [ 4.276260] Freeing init memory: 0K >>>>> Umm, what happened to sysfs not becoming procfs v2? I thought we had >>>>> a fairly strict requirement for "one value per file and not nicely >>>>> formatted" for sysfs? >>>>> >>>> I was thinking the same thing at first, but then I noticed it's actually >>>> debugfs, which has no such rules. >>> Right. :) >>> >> OK I don't see when boottime_activate is called. >> >> Where would this call actually be made from? >> >> I see the call to deactivate but no call to activate. > Here perhaps (Jonas, alerted me to the missing patch): > > commit 4c49a18bcfd2d041cbad7f41c6e6b39d90008382 (HEAD, refs/heads/dt-snowball-pre-rc1) > Author: Jonas Aaberg<jonas.aberg@stericsson.com> > Date: Wed Sep 14 09:29:20 2011 +0200 > > drivers: clocksource: dbx500-prcmu: Add boottime support > > Change-Id: I9b5e3d050131c08c08786ae84cb76619c0525049 > Signed-off-by: Jonas Aaberg<jonas.aberg@stericsson.com> > Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32055 > > diff --git a/drivers/clocksource/clksrc-dbx500-prcmu.c b/drivers/clocksource/clksrc-dbx500-prcmu.c > index c26c369..0069cd9 100644 > --- a/drivers/clocksource/clksrc-dbx500-prcmu.c > +++ b/drivers/clocksource/clksrc-dbx500-prcmu.c > @@ -14,6 +14,7 @@ > */ > #include<linux/clockchips.h> > #include<linux/clksrc-dbx500-prcmu.h> > +#include<linux/boottime.h> > > #include<asm/sched_clock.h> > > @@ -68,6 +69,23 @@ static u32 notrace dbx500_prcmu_sched_clock_read(void) > > #endif > > +#ifdef CONFIG_BOOTTIME > +static unsigned long __init boottime_get_time(void) > +{ > + return div_s64(clocksource_cyc2ns(clocksource_dbx500_prcmu.read( > + &clocksource_dbx500_prcmu), > + clocksource_dbx500_prcmu.mult, > + clocksource_dbx500_prcmu.shift), > + 1000); > +} > + > +static struct boottime_timer __initdata boottime_timer = { > + .init = NULL, > + .get_time = boottime_get_time, > + .finalize = NULL, > +}; > +#endif > + > void __init clksrc_dbx500_prcmu_init(void __iomem *base) > { > clksrc_dbx500_timer_base = base; > @@ -90,4 +108,6 @@ void __init clksrc_dbx500_prcmu_init(void __iomem *base) > 32, RATE_32K); > #endif > clocksource_register_hz(&clocksource_dbx500_prcmu, RATE_32K); > + > + boottime_activate(&boottime_timer); > } > OK So has this been validated and tested on any other IC then a ST part? This boottime_activate patch seems to be only for the dbx500. If this is supposed to be a generic solution I would expect a generic patch for ARM to enable boot time activation. Have you profiled how much time the boot time logging has added to the over all boot? Also the boottime commit message should explain a little more what it is measuring. i.e. This patch enables logging the boot time from point x to point y for the kernel only. This does not include init. What about adding the boot time from the init sequence to init complete? ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time @ 2012-10-15 15:52 ` Dan Murphy 0 siblings, 0 replies; 33+ messages in thread From: Dan Murphy @ 2012-10-15 15:52 UTC (permalink / raw) To: Lee Jones Cc: Arnd Bergmann, Russell King - ARM Linux, Greg Kroah-Hartman, Nishanth Menon, linux-arm-kernel, linux-kernel, linus.walleij, Jonas Aaberg, Will Deacon, Mian Yousaf Kaukab On 10/12/2012 11:42 AM, Lee Jones wrote: > On Fri, 12 Oct 2012, Dan Murphy wrote: > >> On 10/12/2012 09:01 AM, Lee Jones wrote: >>> On Fri, 12 Oct 2012, Arnd Bergmann wrote: >>> >>>> On Friday 12 October 2012, Russell King - ARM Linux wrote: >>>>>> root@ME:/ cat /sys/kernel/debug/boottime/bootgraph >>>>>> [ 0.185254] calling splash+0x0/0x0 >>>>>> [ 2.984335] initcall splash+0x0/0x0 returned 0 after 2799 msecs. >>>>>> [ 2.984335] calling autoboot_delay+0x0/0x0 >>>>>> [ 4.089513] initcall autoboot_delay+0x0/0x0 returned 0 after 1105 msecs. >>>>>> [ 4.089513] calling load_kernel+0x0/0x0 >>>>>> [ 4.239174] initcall load_kernel+0x0/0x0 returned 0 after 149 msecs. >>>>>> [ 4.239174] calling boot_kernel+0x0/0x0 >>>>>> [ 4.276260] initcall boot_kernel+0x0/0x0 returned 0 after 37 msecs. >>>>>> [ 4.276260] calling uncompress_ll_init+0x0/0x0 >>>>>> [ 4.276260] initcall uncompress_ll_init+0x0/0x0 returned 0 after 0 msecs. >>>>>> [ 4.276260] Freeing init memory: 0K >>>>> Umm, what happened to sysfs not becoming procfs v2? I thought we had >>>>> a fairly strict requirement for "one value per file and not nicely >>>>> formatted" for sysfs? >>>>> >>>> I was thinking the same thing at first, but then I noticed it's actually >>>> debugfs, which has no such rules. >>> Right. :) >>> >> OK I don't see when boottime_activate is called. >> >> Where would this call actually be made from? >> >> I see the call to deactivate but no call to activate. > Here perhaps (Jonas, alerted me to the missing patch): > > commit 4c49a18bcfd2d041cbad7f41c6e6b39d90008382 (HEAD, refs/heads/dt-snowball-pre-rc1) > Author: Jonas Aaberg<jonas.aberg@stericsson.com> > Date: Wed Sep 14 09:29:20 2011 +0200 > > drivers: clocksource: dbx500-prcmu: Add boottime support > > Change-Id: I9b5e3d050131c08c08786ae84cb76619c0525049 > Signed-off-by: Jonas Aaberg<jonas.aberg@stericsson.com> > Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32055 > > diff --git a/drivers/clocksource/clksrc-dbx500-prcmu.c b/drivers/clocksource/clksrc-dbx500-prcmu.c > index c26c369..0069cd9 100644 > --- a/drivers/clocksource/clksrc-dbx500-prcmu.c > +++ b/drivers/clocksource/clksrc-dbx500-prcmu.c > @@ -14,6 +14,7 @@ > */ > #include<linux/clockchips.h> > #include<linux/clksrc-dbx500-prcmu.h> > +#include<linux/boottime.h> > > #include<asm/sched_clock.h> > > @@ -68,6 +69,23 @@ static u32 notrace dbx500_prcmu_sched_clock_read(void) > > #endif > > +#ifdef CONFIG_BOOTTIME > +static unsigned long __init boottime_get_time(void) > +{ > + return div_s64(clocksource_cyc2ns(clocksource_dbx500_prcmu.read( > + &clocksource_dbx500_prcmu), > + clocksource_dbx500_prcmu.mult, > + clocksource_dbx500_prcmu.shift), > + 1000); > +} > + > +static struct boottime_timer __initdata boottime_timer = { > + .init = NULL, > + .get_time = boottime_get_time, > + .finalize = NULL, > +}; > +#endif > + > void __init clksrc_dbx500_prcmu_init(void __iomem *base) > { > clksrc_dbx500_timer_base = base; > @@ -90,4 +108,6 @@ void __init clksrc_dbx500_prcmu_init(void __iomem *base) > 32, RATE_32K); > #endif > clocksource_register_hz(&clocksource_dbx500_prcmu, RATE_32K); > + > + boottime_activate(&boottime_timer); > } > OK So has this been validated and tested on any other IC then a ST part? This boottime_activate patch seems to be only for the dbx500. If this is supposed to be a generic solution I would expect a generic patch for ARM to enable boot time activation. Have you profiled how much time the boot time logging has added to the over all boot? Also the boottime commit message should explain a little more what it is measuring. i.e. This patch enables logging the boot time from point x to point y for the kernel only. This does not include init. What about adding the boot time from the init sequence to init complete? ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time 2012-10-15 15:52 ` Dan Murphy @ 2012-10-16 7:34 ` Lee Jones -1 siblings, 0 replies; 33+ messages in thread From: Lee Jones @ 2012-10-16 7:34 UTC (permalink / raw) To: linux-arm-kernel > >>>>>>root at ME:/ cat /sys/kernel/debug/boottime/bootgraph > >>>>>>[ 0.185254] calling splash+0x0/0x0 > >>>>>>[ 2.984335] initcall splash+0x0/0x0 returned 0 after 2799 msecs. > >>>>>>[ 2.984335] calling autoboot_delay+0x0/0x0 > >>>>>>[ 4.089513] initcall autoboot_delay+0x0/0x0 returned 0 after 1105 msecs. > >>>>>>[ 4.089513] calling load_kernel+0x0/0x0 > >>>>>>[ 4.239174] initcall load_kernel+0x0/0x0 returned 0 after 149 msecs. > >>>>>>[ 4.239174] calling boot_kernel+0x0/0x0 > >>>>>>[ 4.276260] initcall boot_kernel+0x0/0x0 returned 0 after 37 msecs. > >>>>>>[ 4.276260] calling uncompress_ll_init+0x0/0x0 > >>>>>>[ 4.276260] initcall uncompress_ll_init+0x0/0x0 returned 0 after 0 msecs. > >>>>>>[ 4.276260] Freeing init memory: 0K > >commit 4c49a18bcfd2d041cbad7f41c6e6b39d90008382 (HEAD, refs/heads/dt-snowball-pre-rc1) > >Author: Jonas Aaberg<jonas.aberg@stericsson.com> > >Date: Wed Sep 14 09:29:20 2011 +0200 > > > > drivers: clocksource: dbx500-prcmu: Add boottime support > > > > Change-Id: I9b5e3d050131c08c08786ae84cb76619c0525049 > > Signed-off-by: Jonas Aaberg<jonas.aberg@stericsson.com> > > Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32055 > > > >diff --git a/drivers/clocksource/clksrc-dbx500-prcmu.c b/drivers/clocksource/clksrc-dbx500-prcmu.c > >index c26c369..0069cd9 100644 > >--- a/drivers/clocksource/clksrc-dbx500-prcmu.c > >+++ b/drivers/clocksource/clksrc-dbx500-prcmu.c > >@@ -14,6 +14,7 @@ > > */ > > #include<linux/clockchips.h> > > #include<linux/clksrc-dbx500-prcmu.h> > >+#include<linux/boottime.h> > > > > #include<asm/sched_clock.h> > > > >@@ -68,6 +69,23 @@ static u32 notrace dbx500_prcmu_sched_clock_read(void) > > > > #endif > > > >+#ifdef CONFIG_BOOTTIME > >+static unsigned long __init boottime_get_time(void) > >+{ > >+ return div_s64(clocksource_cyc2ns(clocksource_dbx500_prcmu.read( > >+ &clocksource_dbx500_prcmu), > >+ clocksource_dbx500_prcmu.mult, > >+ clocksource_dbx500_prcmu.shift), > >+ 1000); > >+} > >+ > >+static struct boottime_timer __initdata boottime_timer = { > >+ .init = NULL, > >+ .get_time = boottime_get_time, > >+ .finalize = NULL, > >+}; > >+#endif > >+ > > void __init clksrc_dbx500_prcmu_init(void __iomem *base) > > { > > clksrc_dbx500_timer_base = base; > >@@ -90,4 +108,6 @@ void __init clksrc_dbx500_prcmu_init(void __iomem *base) > > 32, RATE_32K); > > #endif > > clocksource_register_hz(&clocksource_dbx500_prcmu, RATE_32K); > >+ > >+ boottime_activate(&boottime_timer); > > } > > > > OK So has this been validated and tested on any other IC then a ST part? > This boottime_activate patch seems to be only for the dbx500. If > this is supposed to be a generic solution I would expect > a generic patch for ARM to enable boot time activation. No, this is essentially an STE patch. We don't have access to any other hardware, so we'd be reliant on kindhearted soles to test on their h/w. I guess something like Beagleboard and/or Panda would be ideal. Know anyone? ;) > Have you profiled how much time the boot time logging has added to > the over all boot? > Also the boottime commit message should explain a little more what > it is measuring. > i.e. This patch enables logging the boot time from point x to point > y for the kernel only. This does not include init. > > What about adding the boot time from the init sequence to init complete? I don't know the answers to these questions, but I'll endeavour to find out and get back to you posthaste. -- Lee Jones Linaro ST-Ericsson Landing Team Lead Linaro.org ? Open source software for ARM SoCs Follow Linaro: Facebook | Twitter | Blog ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time @ 2012-10-16 7:34 ` Lee Jones 0 siblings, 0 replies; 33+ messages in thread From: Lee Jones @ 2012-10-16 7:34 UTC (permalink / raw) To: Dan Murphy Cc: Arnd Bergmann, Russell King - ARM Linux, Greg Kroah-Hartman, Nishanth Menon, linux-arm-kernel, linux-kernel, linus.walleij, Jonas Aaberg, Will Deacon, Mian Yousaf Kaukab > >>>>>>root@ME:/ cat /sys/kernel/debug/boottime/bootgraph > >>>>>>[ 0.185254] calling splash+0x0/0x0 > >>>>>>[ 2.984335] initcall splash+0x0/0x0 returned 0 after 2799 msecs. > >>>>>>[ 2.984335] calling autoboot_delay+0x0/0x0 > >>>>>>[ 4.089513] initcall autoboot_delay+0x0/0x0 returned 0 after 1105 msecs. > >>>>>>[ 4.089513] calling load_kernel+0x0/0x0 > >>>>>>[ 4.239174] initcall load_kernel+0x0/0x0 returned 0 after 149 msecs. > >>>>>>[ 4.239174] calling boot_kernel+0x0/0x0 > >>>>>>[ 4.276260] initcall boot_kernel+0x0/0x0 returned 0 after 37 msecs. > >>>>>>[ 4.276260] calling uncompress_ll_init+0x0/0x0 > >>>>>>[ 4.276260] initcall uncompress_ll_init+0x0/0x0 returned 0 after 0 msecs. > >>>>>>[ 4.276260] Freeing init memory: 0K > >commit 4c49a18bcfd2d041cbad7f41c6e6b39d90008382 (HEAD, refs/heads/dt-snowball-pre-rc1) > >Author: Jonas Aaberg<jonas.aberg@stericsson.com> > >Date: Wed Sep 14 09:29:20 2011 +0200 > > > > drivers: clocksource: dbx500-prcmu: Add boottime support > > > > Change-Id: I9b5e3d050131c08c08786ae84cb76619c0525049 > > Signed-off-by: Jonas Aaberg<jonas.aberg@stericsson.com> > > Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32055 > > > >diff --git a/drivers/clocksource/clksrc-dbx500-prcmu.c b/drivers/clocksource/clksrc-dbx500-prcmu.c > >index c26c369..0069cd9 100644 > >--- a/drivers/clocksource/clksrc-dbx500-prcmu.c > >+++ b/drivers/clocksource/clksrc-dbx500-prcmu.c > >@@ -14,6 +14,7 @@ > > */ > > #include<linux/clockchips.h> > > #include<linux/clksrc-dbx500-prcmu.h> > >+#include<linux/boottime.h> > > > > #include<asm/sched_clock.h> > > > >@@ -68,6 +69,23 @@ static u32 notrace dbx500_prcmu_sched_clock_read(void) > > > > #endif > > > >+#ifdef CONFIG_BOOTTIME > >+static unsigned long __init boottime_get_time(void) > >+{ > >+ return div_s64(clocksource_cyc2ns(clocksource_dbx500_prcmu.read( > >+ &clocksource_dbx500_prcmu), > >+ clocksource_dbx500_prcmu.mult, > >+ clocksource_dbx500_prcmu.shift), > >+ 1000); > >+} > >+ > >+static struct boottime_timer __initdata boottime_timer = { > >+ .init = NULL, > >+ .get_time = boottime_get_time, > >+ .finalize = NULL, > >+}; > >+#endif > >+ > > void __init clksrc_dbx500_prcmu_init(void __iomem *base) > > { > > clksrc_dbx500_timer_base = base; > >@@ -90,4 +108,6 @@ void __init clksrc_dbx500_prcmu_init(void __iomem *base) > > 32, RATE_32K); > > #endif > > clocksource_register_hz(&clocksource_dbx500_prcmu, RATE_32K); > >+ > >+ boottime_activate(&boottime_timer); > > } > > > > OK So has this been validated and tested on any other IC then a ST part? > This boottime_activate patch seems to be only for the dbx500. If > this is supposed to be a generic solution I would expect > a generic patch for ARM to enable boot time activation. No, this is essentially an STE patch. We don't have access to any other hardware, so we'd be reliant on kindhearted soles to test on their h/w. I guess something like Beagleboard and/or Panda would be ideal. Know anyone? ;) > Have you profiled how much time the boot time logging has added to > the over all boot? > Also the boottime commit message should explain a little more what > it is measuring. > i.e. This patch enables logging the boot time from point x to point > y for the kernel only. This does not include init. > > What about adding the boot time from the init sequence to init complete? I don't know the answers to these questions, but I'll endeavour to find out and get back to you posthaste. -- Lee Jones Linaro ST-Ericsson Landing Team Lead Linaro.org │ Open source software for ARM SoCs Follow Linaro: Facebook | Twitter | Blog ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time 2012-10-15 15:52 ` Dan Murphy @ 2012-10-23 7:19 ` Lee Jones -1 siblings, 0 replies; 33+ messages in thread From: Lee Jones @ 2012-10-23 7:19 UTC (permalink / raw) To: linux-arm-kernel I have a few answers for you. > >>>>>>root at ME:/ cat /sys/kernel/debug/boottime/bootgraph > >>>>>>[ 0.185254] calling splash+0x0/0x0 > >>>>>>[ 2.984335] initcall splash+0x0/0x0 returned 0 after 2799 msecs. > >>>>>>[ 2.984335] calling autoboot_delay+0x0/0x0 > >>>>>>[ 4.089513] initcall autoboot_delay+0x0/0x0 returned 0 after 1105 msecs. > >>>>>>[ 4.089513] calling load_kernel+0x0/0x0 > >>>>>>[ 4.239174] initcall load_kernel+0x0/0x0 returned 0 after 149 msecs. > >>>>>>[ 4.239174] calling boot_kernel+0x0/0x0 > >>>>>>[ 4.276260] initcall boot_kernel+0x0/0x0 returned 0 after 37 msecs. > >>>>>>[ 4.276260] calling uncompress_ll_init+0x0/0x0 > >>>>>>[ 4.276260] initcall uncompress_ll_init+0x0/0x0 returned 0 after 0 msecs. > >>>>>>[ 4.276260] Freeing init memory: 0K > OK So has this been validated and tested on any other IC then a ST part? No, it has not. As I believe I mentioned before, this is a tool written for and used by ST-Ericsson. Extending it to use on other platforms sounds like a great idea, but won't be done by ST-Ericsson due to a lack of hardware and software assets. > This boottime_activate patch seems to be only for the dbx500. If > this is supposed to be a generic solution I would expect > a generic patch for ARM to enable boot time activation. Apparently a generic boottime extention would be fairly trivial with the use of ktime_get(). > Have you profiled how much time the boot time logging has added to > the over all boot? This has not been completed, but the original author suspects it would be <100ms. > Also the boottime commit message should explain a little more what > it is measuring. > i.e. This patch enables logging the boot time from point x to point > y for the kernel only. This does not include init. > What about adding the boot time from the init sequence to init complete? This sounds like a good idea, but again won't be extended upon by us. It appears that this driver is interesting and possibly very useful for quite a lot of people out there. This patch should therefore be seen as the foundations for other to then extend in the ways alluded to above and ported to work on other more generic platforms. I think these should be extra patches on top of the one submitted here. >From a personal perspective, I am happy to change a few bits and pieces, but I don't have the time to satisfy all of the suggestions made, or adapt the driver in any substantial manor. -- Lee Jones Linaro ST-Ericsson Landing Team Lead Linaro.org ? Open source software for ARM SoCs Follow Linaro: Facebook | Twitter | Blog ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time @ 2012-10-23 7:19 ` Lee Jones 0 siblings, 0 replies; 33+ messages in thread From: Lee Jones @ 2012-10-23 7:19 UTC (permalink / raw) To: Dan Murphy Cc: Arnd Bergmann, Russell King - ARM Linux, Greg Kroah-Hartman, Nishanth Menon, linux-arm-kernel, linux-kernel, linus.walleij, Jonas Aaberg, Will Deacon, Mian Yousaf Kaukab I have a few answers for you. > >>>>>>root@ME:/ cat /sys/kernel/debug/boottime/bootgraph > >>>>>>[ 0.185254] calling splash+0x0/0x0 > >>>>>>[ 2.984335] initcall splash+0x0/0x0 returned 0 after 2799 msecs. > >>>>>>[ 2.984335] calling autoboot_delay+0x0/0x0 > >>>>>>[ 4.089513] initcall autoboot_delay+0x0/0x0 returned 0 after 1105 msecs. > >>>>>>[ 4.089513] calling load_kernel+0x0/0x0 > >>>>>>[ 4.239174] initcall load_kernel+0x0/0x0 returned 0 after 149 msecs. > >>>>>>[ 4.239174] calling boot_kernel+0x0/0x0 > >>>>>>[ 4.276260] initcall boot_kernel+0x0/0x0 returned 0 after 37 msecs. > >>>>>>[ 4.276260] calling uncompress_ll_init+0x0/0x0 > >>>>>>[ 4.276260] initcall uncompress_ll_init+0x0/0x0 returned 0 after 0 msecs. > >>>>>>[ 4.276260] Freeing init memory: 0K > OK So has this been validated and tested on any other IC then a ST part? No, it has not. As I believe I mentioned before, this is a tool written for and used by ST-Ericsson. Extending it to use on other platforms sounds like a great idea, but won't be done by ST-Ericsson due to a lack of hardware and software assets. > This boottime_activate patch seems to be only for the dbx500. If > this is supposed to be a generic solution I would expect > a generic patch for ARM to enable boot time activation. Apparently a generic boottime extention would be fairly trivial with the use of ktime_get(). > Have you profiled how much time the boot time logging has added to > the over all boot? This has not been completed, but the original author suspects it would be <100ms. > Also the boottime commit message should explain a little more what > it is measuring. > i.e. This patch enables logging the boot time from point x to point > y for the kernel only. This does not include init. > What about adding the boot time from the init sequence to init complete? This sounds like a good idea, but again won't be extended upon by us. It appears that this driver is interesting and possibly very useful for quite a lot of people out there. This patch should therefore be seen as the foundations for other to then extend in the ways alluded to above and ported to work on other more generic platforms. I think these should be extra patches on top of the one submitted here. >From a personal perspective, I am happy to change a few bits and pieces, but I don't have the time to satisfy all of the suggestions made, or adapt the driver in any substantial manor. -- Lee Jones Linaro ST-Ericsson Landing Team Lead Linaro.org │ Open source software for ARM SoCs Follow Linaro: Facebook | Twitter | Blog ^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time 2012-10-23 7:19 ` Lee Jones (?) @ 2013-06-09 8:17 ` Steve Liu -1 siblings, 0 replies; 33+ messages in thread From: Steve Liu @ 2013-06-09 8:17 UTC (permalink / raw) To: linux-kernel how about this patch, do we really need this patch ^ permalink raw reply [flat|nested] 33+ messages in thread
end of thread, other threads:[~2013-06-09 8:45 UTC | newest] Thread overview: 33+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2012-10-11 14:42 [PATCH] Boottime: A tool for automatic measurement of kernel/bootloader boot time Lee Jones 2012-10-11 14:42 ` Lee Jones 2012-10-11 15:24 ` Christian Gmeiner 2012-10-11 15:24 ` Christian Gmeiner 2012-10-11 15:36 ` Lee Jones 2012-10-11 15:36 ` Lee Jones 2012-10-11 20:17 ` Nishanth Menon 2012-10-11 20:17 ` Nishanth Menon 2012-10-12 9:37 ` Lee Jones 2012-10-12 9:37 ` Lee Jones 2012-10-12 9:51 ` Lee Jones 2012-10-12 9:51 ` Lee Jones 2012-10-12 13:35 ` Dan Murphy 2012-10-12 13:35 ` Dan Murphy 2012-10-12 13:45 ` Lee Jones 2012-10-12 13:45 ` Lee Jones 2012-10-12 13:48 ` Russell King - ARM Linux 2012-10-12 13:48 ` Russell King - ARM Linux 2012-10-12 13:53 ` Arnd Bergmann 2012-10-12 13:53 ` Arnd Bergmann 2012-10-12 14:01 ` Lee Jones 2012-10-12 14:01 ` Lee Jones 2012-10-12 16:36 ` Dan Murphy 2012-10-12 16:36 ` Dan Murphy 2012-10-12 16:42 ` Lee Jones 2012-10-12 16:42 ` Lee Jones 2012-10-15 15:52 ` Dan Murphy 2012-10-15 15:52 ` Dan Murphy 2012-10-16 7:34 ` Lee Jones 2012-10-16 7:34 ` Lee Jones 2012-10-23 7:19 ` Lee Jones 2012-10-23 7:19 ` Lee Jones 2013-06-09 8:17 ` Steve Liu
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.