--- include/linux/sysctl.h.org 2005-06-13 16:05:17.000000000 +0300 +++ include/linux/sysctl.h 2005-06-25 15:05:06.000000000 +0300 @@ -136,6 +136,7 @@ enum KERN_UNKNOWN_NMI_PANIC=66, /* int: unknown nmi panic flag */ KERN_BOOTLOADER_TYPE=67, /* int: boot loader type */ KERN_RANDOMIZE=68, /* int: randomize virtual address space */ + KERN_PROFILE=69, /* int: profile on/off */ }; --- include/linux/profile.h.org 2005-03-02 09:38:08.000000000 +0200 +++ include/linux/profile.h 2005-06-26 01:56:46.000000000 +0300 @@ -6,14 +6,20 @@ #include #include #include +#include #include #include #define CPU_PROFILING 1 #define SCHED_PROFILING 2 +struct ctl_table; +struct file; struct proc_dir_entry; struct pt_regs; +int profile_sysctl_handler(ctl_table *table, int write, + struct file *file, void __user *buffer, size_t +*length, loff_t *ppos); /* init basic kernel profiler */ void __init profile_init(void); --- kernel/profile.c.org 2005-06-13 16:05:23.000000000 +0300 +++ kernel/profile.c 2005-06-26 21:34:28.000000000 +0300 @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -37,9 +36,11 @@ struct profile_hit { /* Oprofile timer tick hook */ int (*timer_hook)(struct pt_regs *); +int profile_params[2] = {0, 0}; static atomic_t *prof_buffer; static unsigned long prof_len, prof_shift; -static int prof_on; +static int prof_on = 0; +static int prof_booton = 0; static cpumask_t prof_cpu_mask = CPU_MASK_ALL; #ifdef CONFIG_SMP static DEFINE_PER_CPU(struct profile_hit *[2], cpu_profile_hits); @@ -80,6 +81,7 @@ void __init profile_init(void) /* only text is profiled */ prof_len = (_etext - _stext) >> prof_shift; prof_buffer = alloc_bootmem(prof_len*sizeof(atomic_t)); + prof_booton = 1; } /* Profile event notifications */ @@ -367,6 +369,12 @@ static int __devinit profile_cpu_callbac } return NOTIFY_OK; } +static struct notifier_block profile_cpu_notifier = +{ + .notifier_call = profile_cpu_callback, + .priority = 0, +}; + #endif /* CONFIG_HOTPLUG_CPU */ #else /* !CONFIG_SMP */ #define profile_flip_buffers() do { } while (0) @@ -548,6 +556,96 @@ out_cleanup: #define create_hash_tables() ({ 0; }) #endif +#ifdef CONFIG_SMP +static int remove_hash_tables(void) +{ + int cpu; + + smp_mb(); + on_each_cpu(profile_nop, NULL, 0, 1); + for_each_online_cpu(cpu) { + struct page *page; + + if (per_cpu(cpu_profile_hits, cpu)[0]) { + page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[0]); + per_cpu(cpu_profile_hits, cpu)[0] = NULL; + __free_page(page); + } + if (per_cpu(cpu_profile_hits, cpu)[1]) { + page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[1]); + per_cpu(cpu_profile_hits, cpu)[1] = NULL; + __free_page(page); + } + } + return -1; +} +#else +#define remove_hash_tables() ({ 0; }) +#endif + +int profile_sysctl_handler(ctl_table *table, int write, + struct file *file, void __user *buffer, size_t *length, loff_t *ppos) +{ + int err; + struct proc_dir_entry *entry; + + if (prof_booton && write) return 0; + err=proc_dointvec(table, write, file, buffer, length, ppos); + if ((err >= 0) && write) { + prof_shift = profile_params[1]; + switch(profile_params[0]) + { + case 0: + if (prof_on) { + prof_on = 0; + remove_proc_entry("profile",NULL); +#ifdef CONFIG_HOTPLUG_CPU + unregister_cpu_notifier(&profile_cpu_notifier); +#endif + remove_hash_tables(); + vfree(prof_buffer); + printk(KERN_INFO "kernel profiling disabled\n"); + } + break; + case SCHED_PROFILING || CPU_PROFILING: + if (prof_on) return -1; + prof_len = (_etext - _stext) >> prof_shift; + prof_buffer = vmalloc(prof_len*sizeof(atomic_t)); + if (!prof_buffer) return(-ENOMEM); + if (create_hash_tables()) { + vfree(prof_buffer); + return -1; + } + prof_on = profile_params[0]; + if (!(entry = create_proc_entry("profile", S_IWUSR | S_IRUGO, NULL))) { + remove_hash_tables(); + vfree(prof_buffer); + return 0; + } + entry->proc_fops = &proc_profile_operations; + entry->size = (1+prof_len) * sizeof(atomic_t); +#ifdef CONFIG_HOTPLUG_CPU + register_cpu_notifier(&profile_cpu_notifier); +#endif + profile_discard_flip_buffers(); + memset(prof_buffer, 0, prof_len * sizeof(atomic_t)); + switch(prof_on) + { + case SCHED_PROFILING:printk(KERN_INFO + "kernel schedule profiling enabled (shift: %ld)\n", + prof_shift); + break; + case CPU_PROFILING:printk(KERN_INFO + "kernel profiling enabled (shift: %ld)\n", + prof_shift); + break; + } + break; + } + } + return 0; +} + static int __init create_proc_profile(void) { struct proc_dir_entry *entry; @@ -560,7 +658,11 @@ static int __init create_proc_profile(vo return 0; entry->proc_fops = &proc_profile_operations; entry->size = (1+prof_len) * sizeof(atomic_t); - hotcpu_notifier(profile_cpu_callback, 0); +#ifdef CONFIG_HOTPLUG_CPU + register_cpu_notifier(&profile_cpu_notifier); +#endif + profile_params[0] = prof_on; + profile_params[1] = prof_shift; return 0; } module_init(create_proc_profile); --- kernel/sysctl.c.org 2005-06-13 16:05:23.000000000 +0300 +++ kernel/sysctl.c 2005-06-26 02:06:23.000000000 +0300 @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,7 @@ extern int min_free_kbytes; extern int printk_ratelimit_jiffies; extern int printk_ratelimit_burst; extern int pid_max_min, pid_max_max; +extern int profile_params[]; #if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_X86) int unknown_nmi_panic; @@ -642,7 +644,15 @@ static ctl_table kern_table[] = { .mode = 0644, .proc_handler = &proc_dointvec, }, - + { + .ctl_name = KERN_PROFILE, + .procname = "profile", + .data = &profile_params, + .maxlen = 2*sizeof(int), + .mode = 0644, + .proc_handler = &profile_sysctl_handler, + .strategy = &sysctl_intvec, + }, { .ctl_name = 0 } };