diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/Makefile | 2 | ||||
-rw-r--r-- | kernel/irq/handle.c | 1 | ||||
-rw-r--r-- | kernel/itimer.c | 6 | ||||
-rw-r--r-- | kernel/kallsyms.c | 13 | ||||
-rw-r--r-- | kernel/kprobes.c | 142 | ||||
-rw-r--r-- | kernel/power/main.c | 6 | ||||
-rw-r--r-- | kernel/printk.c | 72 | ||||
-rw-r--r-- | kernel/profile.c | 16 | ||||
-rw-r--r-- | kernel/sched.c | 2 | ||||
-rw-r--r-- | kernel/sys.c | 2 |
10 files changed, 201 insertions, 61 deletions
diff --git a/kernel/Makefile b/kernel/Makefile index eb88b44..b01d26f 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -29,7 +29,7 @@ obj-$(CONFIG_SYSFS) += ksysfs.o obj-$(CONFIG_GENERIC_HARDIRQS) += irq/ obj-$(CONFIG_SECCOMP) += seccomp.o -ifneq ($(CONFIG_IA64),y) +ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y) # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is # needed for x86 only. Why this used to be enabled for all architectures is beyond # me. I suspect most platforms don't need this, but until we know that for sure diff --git a/kernel/irq/handle.c b/kernel/irq/handle.c index 2fb0e46..06b5a63 100644 --- a/kernel/irq/handle.c +++ b/kernel/irq/handle.c @@ -30,6 +30,7 @@ */ irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = { [0 ... NR_IRQS-1] = { + .status = IRQ_DISABLED, .handler = &no_irq_type, .lock = SPIN_LOCK_UNLOCKED } diff --git a/kernel/itimer.c b/kernel/itimer.c index e9a40e9..1dc988e 100644 --- a/kernel/itimer.c +++ b/kernel/itimer.c @@ -123,7 +123,11 @@ static inline void it_real_arm(struct task_struct *p, unsigned long interval) return; if (interval > (unsigned long) LONG_MAX) interval = LONG_MAX; - p->signal->real_timer.expires = jiffies + interval; + /* the "+ 1" below makes sure that the timer doesn't go off before + * the interval requested. This could happen if + * time requested % (usecs per jiffy) is more than the usecs left + * in the current jiffy */ + p->signal->real_timer.expires = jiffies + interval + 1; add_timer(&p->signal->real_timer); } diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c index 1627f8d..13bcec1 100644 --- a/kernel/kallsyms.c +++ b/kernel/kallsyms.c @@ -46,6 +46,14 @@ static inline int is_kernel_inittext(unsigned long addr) return 0; } +static inline int is_kernel_extratext(unsigned long addr) +{ + if (addr >= (unsigned long)_sextratext + && addr <= (unsigned long)_eextratext) + return 1; + return 0; +} + static inline int is_kernel_text(unsigned long addr) { if (addr >= (unsigned long)_stext && addr <= (unsigned long)_etext) @@ -169,8 +177,9 @@ const char *kallsyms_lookup(unsigned long addr, namebuf[0] = 0; if ((all_var && is_kernel(addr)) || - (!all_var && (is_kernel_text(addr) || is_kernel_inittext(addr)))) { - unsigned long symbol_end=0; + (!all_var && (is_kernel_text(addr) || is_kernel_inittext(addr) || + is_kernel_extratext(addr)))) { + unsigned long symbol_end = 0; /* do a binary search on the sorted kallsyms_addresses array */ low = 0; diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 1d5dd13..037142b7 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -44,6 +44,7 @@ static struct hlist_head kprobe_table[KPROBE_TABLE_SIZE]; unsigned int kprobe_cpu = NR_CPUS; static DEFINE_SPINLOCK(kprobe_lock); +static struct kprobe *curr_kprobe; /* Locks kprobe: irqs must be disabled */ void lock_kprobes(void) @@ -73,22 +74,139 @@ struct kprobe *get_kprobe(void *addr) return NULL; } +/* + * Aggregate handlers for multiple kprobes support - these handlers + * take care of invoking the individual kprobe handlers on p->list + */ +int aggr_pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct kprobe *kp; + + list_for_each_entry(kp, &p->list, list) { + if (kp->pre_handler) { + curr_kprobe = kp; + kp->pre_handler(kp, regs); + curr_kprobe = NULL; + } + } + return 0; +} + +void aggr_post_handler(struct kprobe *p, struct pt_regs *regs, + unsigned long flags) +{ + struct kprobe *kp; + + list_for_each_entry(kp, &p->list, list) { + if (kp->post_handler) { + curr_kprobe = kp; + kp->post_handler(kp, regs, flags); + curr_kprobe = NULL; + } + } + return; +} + +int aggr_fault_handler(struct kprobe *p, struct pt_regs *regs, int trapnr) +{ + /* + * if we faulted "during" the execution of a user specified + * probe handler, invoke just that probe's fault handler + */ + if (curr_kprobe && curr_kprobe->fault_handler) { + if (curr_kprobe->fault_handler(curr_kprobe, regs, trapnr)) + return 1; + } + return 0; +} + +/* + * Fill in the required fields of the "manager kprobe". Replace the + * earlier kprobe in the hlist with the manager kprobe + */ +static inline void add_aggr_kprobe(struct kprobe *ap, struct kprobe *p) +{ + ap->addr = p->addr; + ap->opcode = p->opcode; + memcpy(&ap->ainsn, &p->ainsn, sizeof(struct arch_specific_insn)); + + ap->pre_handler = aggr_pre_handler; + ap->post_handler = aggr_post_handler; + ap->fault_handler = aggr_fault_handler; + + INIT_LIST_HEAD(&ap->list); + list_add(&p->list, &ap->list); + + INIT_HLIST_NODE(&ap->hlist); + hlist_del(&p->hlist); + hlist_add_head(&ap->hlist, + &kprobe_table[hash_ptr(ap->addr, KPROBE_HASH_BITS)]); +} + +/* + * This is the second or subsequent kprobe at the address - handle + * the intricacies + * TODO: Move kcalloc outside the spinlock + */ +static int register_aggr_kprobe(struct kprobe *old_p, struct kprobe *p) +{ + int ret = 0; + struct kprobe *ap; + + if (old_p->break_handler || p->break_handler) { + ret = -EEXIST; /* kprobe and jprobe can't (yet) coexist */ + } else if (old_p->pre_handler == aggr_pre_handler) { + list_add(&p->list, &old_p->list); + } else { + ap = kcalloc(1, sizeof(struct kprobe), GFP_ATOMIC); + if (!ap) + return -ENOMEM; + add_aggr_kprobe(ap, old_p); + list_add(&p->list, &ap->list); + } + return ret; +} + +/* kprobe removal house-keeping routines */ +static inline void cleanup_kprobe(struct kprobe *p, unsigned long flags) +{ + *p->addr = p->opcode; + hlist_del(&p->hlist); + flush_icache_range((unsigned long) p->addr, + (unsigned long) p->addr + sizeof(kprobe_opcode_t)); + spin_unlock_irqrestore(&kprobe_lock, flags); + arch_remove_kprobe(p); +} + +static inline void cleanup_aggr_kprobe(struct kprobe *old_p, + struct kprobe *p, unsigned long flags) +{ + list_del(&p->list); + if (list_empty(&old_p->list)) { + cleanup_kprobe(old_p, flags); + kfree(old_p); + } else + spin_unlock_irqrestore(&kprobe_lock, flags); +} + int register_kprobe(struct kprobe *p) { int ret = 0; unsigned long flags = 0; + struct kprobe *old_p; if ((ret = arch_prepare_kprobe(p)) != 0) { goto rm_kprobe; } spin_lock_irqsave(&kprobe_lock, flags); - INIT_HLIST_NODE(&p->hlist); - if (get_kprobe(p->addr)) { - ret = -EEXIST; + old_p = get_kprobe(p->addr); + if (old_p) { + ret = register_aggr_kprobe(old_p, p); goto out; } - arch_copy_kprobe(p); + arch_copy_kprobe(p); + INIT_HLIST_NODE(&p->hlist); hlist_add_head(&p->hlist, &kprobe_table[hash_ptr(p->addr, KPROBE_HASH_BITS)]); @@ -107,13 +225,17 @@ rm_kprobe: void unregister_kprobe(struct kprobe *p) { unsigned long flags; - arch_remove_kprobe(p); + struct kprobe *old_p; + spin_lock_irqsave(&kprobe_lock, flags); - *p->addr = p->opcode; - hlist_del(&p->hlist); - flush_icache_range((unsigned long) p->addr, - (unsigned long) p->addr + sizeof(kprobe_opcode_t)); - spin_unlock_irqrestore(&kprobe_lock, flags); + old_p = get_kprobe(p->addr); + if (old_p) { + if (old_p->pre_handler == aggr_pre_handler) + cleanup_aggr_kprobe(old_p, p, flags); + else + cleanup_kprobe(p, flags); + } else + spin_unlock_irqrestore(&kprobe_lock, flags); } static struct notifier_block kprobe_exceptions_nb = { diff --git a/kernel/power/main.c b/kernel/power/main.c index 7960ddf..4cdebc9 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -156,14 +156,14 @@ static int enter_state(suspend_state_t state) goto Unlock; } - pr_debug("PM: Preparing system for suspend\n"); + pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]); if ((error = suspend_prepare(state))) goto Unlock; - pr_debug("PM: Entering state.\n"); + pr_debug("PM: Entering %s sleep\n", pm_states[state]); error = suspend_enter(state); - pr_debug("PM: Finishing up.\n"); + pr_debug("PM: Finishing wakeup.\n"); suspend_finish(state); Unlock: up(&pm_sem); diff --git a/kernel/printk.c b/kernel/printk.c index 290a07c..01b58d7 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -160,42 +160,6 @@ static int __init console_setup(char *str) __setup("console=", console_setup); -/** - * add_preferred_console - add a device to the list of preferred consoles. - * - * The last preferred console added will be used for kernel messages - * and stdin/out/err for init. Normally this is used by console_setup - * above to handle user-supplied console arguments; however it can also - * be used by arch-specific code either to override the user or more - * commonly to provide a default console (ie from PROM variables) when - * the user has not supplied one. - */ -int __init add_preferred_console(char *name, int idx, char *options) -{ - struct console_cmdline *c; - int i; - - /* - * See if this tty is not yet registered, and - * if we have a slot free. - */ - for(i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++) - if (strcmp(console_cmdline[i].name, name) == 0 && - console_cmdline[i].index == idx) { - selected_console = i; - return 0; - } - if (i == MAX_CMDLINECONSOLES) - return -E2BIG; - selected_console = i; - c = &console_cmdline[i]; - memcpy(c->name, name, sizeof(c->name)); - c->name[sizeof(c->name) - 1] = 0; - c->options = options; - c->index = idx; - return 0; -} - static int __init log_buf_len_setup(char *str) { unsigned long size = memparse(str, &str); @@ -671,6 +635,42 @@ static void call_console_drivers(unsigned long start, unsigned long end) {} #endif /** + * add_preferred_console - add a device to the list of preferred consoles. + * + * The last preferred console added will be used for kernel messages + * and stdin/out/err for init. Normally this is used by console_setup + * above to handle user-supplied console arguments; however it can also + * be used by arch-specific code either to override the user or more + * commonly to provide a default console (ie from PROM variables) when + * the user has not supplied one. + */ +int __init add_preferred_console(char *name, int idx, char *options) +{ + struct console_cmdline *c; + int i; + + /* + * See if this tty is not yet registered, and + * if we have a slot free. + */ + for(i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++) + if (strcmp(console_cmdline[i].name, name) == 0 && + console_cmdline[i].index == idx) { + selected_console = i; + return 0; + } + if (i == MAX_CMDLINECONSOLES) + return -E2BIG; + selected_console = i; + c = &console_cmdline[i]; + memcpy(c->name, name, sizeof(c->name)); + c->name[sizeof(c->name) - 1] = 0; + c->options = options; + c->index = idx; + return 0; +} + +/** * acquire_console_sem - lock the console system for exclusive use. * * Acquires a semaphore which guarantees that the caller has diff --git a/kernel/profile.c b/kernel/profile.c index 0221a50..ad8cbb7 100644 --- a/kernel/profile.c +++ b/kernel/profile.c @@ -49,15 +49,19 @@ static DECLARE_MUTEX(profile_flip_mutex); static int __init profile_setup(char * str) { + static char __initdata schedstr[] = "schedule"; int par; - if (!strncmp(str, "schedule", 8)) { + if (!strncmp(str, schedstr, strlen(schedstr))) { prof_on = SCHED_PROFILING; - printk(KERN_INFO "kernel schedule profiling enabled\n"); - if (str[7] == ',') - str += 8; - } - if (get_option(&str,&par)) { + if (str[strlen(schedstr)] == ',') + str += strlen(schedstr) + 1; + if (get_option(&str, &par)) + prof_shift = par; + printk(KERN_INFO + "kernel schedule profiling enabled (shift: %ld)\n", + prof_shift); + } else if (get_option(&str, &par)) { prof_shift = par; prof_on = CPU_PROFILING; printk(KERN_INFO "kernel profiling enabled (shift: %ld)\n", diff --git a/kernel/sched.c b/kernel/sched.c index 0dc3158..66b2ed7 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -4243,7 +4243,7 @@ static void move_task_off_dead_cpu(int dead_cpu, struct task_struct *tsk) /* No more Mr. Nice Guy. */ if (dest_cpu == NR_CPUS) { - tsk->cpus_allowed = cpuset_cpus_allowed(tsk); + cpus_setall(tsk->cpus_allowed); dest_cpu = any_online_cpu(tsk->cpus_allowed); /* diff --git a/kernel/sys.c b/kernel/sys.c index f64e97c..f006632 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -1195,7 +1195,7 @@ static int groups_from_user(struct group_info *group_info, return 0; } -/* a simple shell-metzner sort */ +/* a simple Shell sort */ static void groups_sort(struct group_info *group_info) { int base, max, stride; |