(1/3) cleanup thread info on x86
PATCH 1/1 (1/3) cleanup thread info on x86
These patches have been in Alan's tree for the last week with no problems.
This is a port of work Ben LaHaise did around 2.5.20 time. I split it up and updated it for the new preempt_count semantics.
* clean thread info infrastructure (1/3)
- take out all instances of things like (8191&esp) to get current stack address.
Has a few unused things that will be used in the following two patches.
Signature · 2 lines
Dave Hansen [email protected]
# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas:
# ChangeSet 1.855 -> 1.856
# arch/i386/kernel/head.S 1.17 -> 1.18
# include/asm-i386/thread_info.h 1.7 -> 1.8
# include/asm-i386/page.h 1.19 -> 1.20
# arch/i386/kernel/entry.S 1.37.1.2 -> 1.38.2.1
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/10/31 haveblue@elm3b96.(none) 1.856
# Merge elm3b96.(none):/work/dave/bk/linux-2.5
# into elm3b96.(none):/work/dave/bk/linux-2.5-thread_info_infra # -------------------------------------------- # diff -Nru a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S --- a/arch/i386/kernel/entry.S Thu Oct 31 11:10:55 2002 +++ b/arch/i386/kernel/entry.S Thu Oct 31 11:10:55 2002 @@ -136,7 +136,7 @@ movl %ecx,CS(%esp) # movl %esp, %ebx pushl %ebx - andl $-8192, %ebx # GET_THREAD_INFO + GET_THREAD_INFO_WITH_ESP(%ebx) # GET_THREAD_INFO movl TI_EXEC_DOMAIN(%ebx), %edx # Get the execution domain movl 4(%edx), %edx # Get the lcall7 handler for the domain pushl $0x7 @@ -158,7 +158,7 @@ movl %ecx,CS(%esp) # movl %esp, %ebx pushl %ebx - andl $-8192, %ebx # GET_THREAD_INFO + GET_THREAD_INFO_WITH_ESP(%ebx) # GET_THREAD_INFO movl TI_EXEC_DOMAIN(%ebx), %edx # Get the execution domain movl 4(%edx), %edx # Get the lcall7 handler for the domain pushl $0x27 diff -Nru a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S --- a/arch/i386/kernel/head.S Thu Oct 31 11:10:55 2002 +++ b/arch/i386/kernel/head.S Thu Oct 31 11:10:55 2002 @@ -15,6 +15,7 @@ #include <asm/page.h> #include <asm/pgtable.h> #include <asm/desc.h> +#include <asm/thread_info.h>
#define OLD_CL_MAGIC_ADDR 0x90020
#define OLD_CL_MAGIC 0xA33F
@@ -305,7 +306,7 @@
ret
ENTRY(stack_start)
- .long init_thread_union+8192
+ .long init_thread_union+THREAD_SIZE
.long __KERNEL_DS
/* This is the default interrupt "handler" :-) */
include/asm-i386/page.h +5 -1
@@ -3,7 +3,11 @@/* PAGE_SHIFT determines the page size */ #define PAGE_SHIFT 12 -#define PAGE_SIZE (1UL << PAGE_SHIFT) +#ifndef __ASSEMBLY__ +#define PAGE_SIZE (1UL << PAGE_SHIFT) +#else +#define PAGE_SIZE (1 << PAGE_SHIFT) +#endif #define PAGE_MASK (~(PAGE_SIZE-1))#define LARGE_PAGE_MASK (~(LARGE_PAGE_SIZE-1))
include/asm-i386/thread_info.h +21 -11
@@ -9,6 +9,7 @@#ifdef __KERNEL__ +#include <asm/page.h> #ifndef __ASSEMBLY__#include <asm/processor.h> #endif
@@ -54,10 +55,13 @@* * preempt_count needs to be 1 initially, until the scheduler is functional. */ +#define THREAD_ORDER 1 +#define INIT_THREAD_SIZE THREAD_SIZE +#ifndef __ASSEMBLY__#define INIT_THREAD_INFO (tsk) \ { \ - .task = &tsk, \ + .task = &tsk, \ .exec_domain = &default_exec_domain, \ .flags = 0, \ .cpu = 0, \
@@ -68,30 +72,36 @@#define init_thread_info (init_thread_union.thread_info)#define init_stack (init_thread_union.stack) +/* thread information allocation */ +#define THREAD_SIZE (PAGE_SIZE << THREAD_ORDER) +#define alloc_thread_info () ((struct thread_info *) __get_free_pages(GFP_KERNEL,THREAD_ORDER)) +#define free_thread_info (ti) free_pages((unsigned long) (ti), THREAD_ORDER) +#define get_thread_info (ti) get_task_struct((ti)->task) +#define put_thread_info (ti) put_task_struct((ti)->task) +/* how to get the thread information struct from C */ static inline struct thread_info *current_thread_info (void ) {struct thread_info *ti; - __asm__("andl %%esp,%0; " :"=r" (ti) :"0" (~8191UL )); + __asm__("andl %%esp,%0; " :"=r" (ti) :"0" (~(THREAD_SIZE -1 )));return ti; } -/* thread information allocation */ -#define THREAD_SIZE (2*PAGE_SIZE) -#define alloc_thread_info () ((struct thread_info *) __get_free_pages(GFP_KERNEL,1)) -#define free_thread_info (ti) free_pages((unsigned long) (ti), 1) -#define get_thread_info (ti) get_task_struct((ti)->task) -#define put_thread_info (ti) put_task_struct((ti)->task) -#else /* !__ASSEMBLY__ */ +#define THREAD_SIZE (PAGE_SIZE << THREAD_ORDER) +/* how to get the thread information struct from ASM */ #define GET_THREAD_INFO (reg) \ - movl $-8192, reg; \ + movl $-THREAD_SIZE, reg; \ andl %esp, reg -#endif +/* use this one if reg already contains %esp */ +#define GET_THREAD_INFO_WITH_ESP (reg) \ + andl $-THREAD_SIZE, reg +#endif +/* * thread information flags * - these are process state flags that various assembly files may need to access
PATCH 1/1 (2/3) per-cpu interrupt stacks for x86
* interrupt stacks (2/3)
- allocate per-cpu interrupt stacks. upon entry to common_interrupt, switch to the current cpu's stack.
- inherit the interrupted task's preempt count, and pass back relevant task flags
Depends on the previously sent thread_info cleanup
Signature · 2 lines
Dave Hansen [email protected]
# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas:
# ChangeSet 1.856 -> 1.857
# arch/i386/kernel/process.c 1.32 -> 1.32.1.1
# arch/i386/kernel/irq.c 1.21.1.2 -> 1.23.1.1
# include/asm-i386/thread_info.h 1.8 -> 1.10
# arch/i386/kernel/entry.S 1.38.2.1 -> 1.38.1.3
# arch/i386/kernel/smpboot.c 1.36 -> 1.37
# arch/i386/kernel/init_task.c 1.6 -> 1.6.1.1
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/10/31 haveblue@elm3b96.(none) 1.857
# Merge
# --------------------------------------------
#
arch/i386/kernel/entry.S +38 -0
@@ -334,7 +334,45 @@ ALIGN common_interrupt: SAVE_ALL + + + GET_THREAD_INFO(%ebx) + movl TI_IRQ_STACK(%ebx),%ecx + movl TI_TASK(%ebx),%edx + movl %esp,%eax + leal (THREAD_SIZE-4)(%ecx),%esi # %ecx+THREAD_SIZE is next stack + # -4 keeps us in the right one + testl %ecx,%ecx # is there a valid irq_stack? + + # switch to the irq stack +#ifdef CONFIG_X86_HAVE_CMOV + cmovnz %esi,%esp +#else + jz 1f + mov %esi,%esp +1: +#endif + + # update the task pointer in the irq stack + GET_THREAD_INFO(%esi) + movl %edx,TI_TASK(%esi) + + # update the preempt count in the irq stack + movl TI_PRE_COUNT(%ebx),%ecx + movl %ecx,TI_PRE_COUNT(%esi) + call do_IRQ + + movl %eax,%esp # potentially restore non-irq stack + + # copy flags from the irq stack back into the task's thread_info + # %esi is saved over the do_IRQ call and contains the irq stack + # thread_info pointer + # %ebx contains the original thread_info pointer + movl TI_FLAGS(%esi),%eax + movl $0,TI_FLAGS(%esi) + LOCK orl %eax,TI_FLAGS(%ebx) + jmp ret_from_intr #define BUILD_INTERRUPT(name, nr) \
arch/i386/kernel/init_task.c +4 -0
@@ -13,6 +13,10 @@static struct signal_struct init_signals =INIT_SIGNALS (init_signals);struct mm_struct init_mm =INIT_MM (init_mm); +union thread_union init_irq_union + __attribute__((__section__(".data.init_task"))); + +/* * Initial thread structure. *
arch/i386/kernel/irq.c +5 -4
@@ -311,7 +311,8 @@* SMP cross-CPU interrupts have their own specific * handlers). */ -asmlinkage unsigned int do_IRQ(struct pt_regs regs) +struct pt_regs *do_IRQ (struct pt_regs *regs) __attribute__((regparm (1 ))); +struct pt_regs *do_IRQ (struct pt_regs *regs) {/* * We ack quickly, we don't want the irq controller
@@ -323,7 +324,7 @@* 0 return value means that this irq is already being * handled by some other CPU. (or is disabled) */ -int irq = regs.orig_eax &0xff ;/* high bits used in ret_from_ code */ +int irq = regs->orig_eax &0xff ;/* high bits used in ret_from_ code */ int cpu =smp_processor_id ();irq_desc_t *desc = irq_desc + irq;struct irqaction * action;
@@ -388,7 +389,7 @@*/ for (;;) {spin_unlock (&desc->lock); -handle_IRQ_event (irq, ®s, action); +handle_IRQ_event (irq, regs, action);spin_lock (&desc->lock);if (likely (!(desc->status &IRQ_PENDING )))
@@ -407,7 +408,7 @@irq_exit (); -return 1 ; +return regs; }/**
arch/i386/kernel/process.c +1 -0
@@ -413,6 +413,7 @@/* never put a printk in __switch_to... printk() calls wake_up*() indirectly */ + next_p->thread_info->irq_stack = prev_p->thread_info->irq_stack;unlazy_fpu (prev_p);/*
arch/i386/kernel/smpboot.c +36 -1
@@ -70,6 +70,11 @@/* Per CPU bogomips and other parameters */ struct cpuinfo_x86 cpu_data[NR_CPUS] __cacheline_aligned; +/* Per CPU interrupt stacks */ +extern union thread_union init_irq_union; +union thread_union *irq_stacks[NR_CPUS] __cacheline_aligned = + { &init_irq_union, }; +/* Set when the idlers are all forked */ int smp_threads_ready;
@@ -764,6 +769,28 @@return (send_status | accept_status); } +static void __init setup_irq_stack (struct task_struct *p,int cpu) +{ +unsigned long stk; + + stk =__get_free_pages (GFP_KERNEL ,THREAD_ORDER +1 ); +if (!stk) +panic ("I can't seem to allocate my irq stack. Oh well, giving up." ); + + irq_stacks[cpu] = (void *)stk; +memset (irq_stacks[cpu],0 ,THREAD_SIZE ); + irq_stacks[cpu]->thread_info.cpu = cpu; + irq_stacks[cpu]->thread_info.preempt_count =1 ; +/* interrupts are not preemptable */ + p->thread_info->irq_stack = irq_stacks[cpu]; + +/* If we want to make the irq stack more than one unit +* deep, we can chain then off of the irq_stack pointer +* here. +*/ +} + +extern unsigned long cpu_initialized;static void __init do_boot_cpu (int apicid)
@@ -787,6 +814,8 @@if (IS_ERR (idle))panic ("failed fork for CPU %d" , cpu); +setup_irq_stack (idle, cpu); +/* * We remove it from the pidhash and the runqueue * once we got the process:
@@ -804,7 +833,13 @@/* So we see what's up */ printk ("Booting processor %d/%d eip %lx\n" , cpu, apicid, start_eip); - stack_start.esp = (void *) (1024 +PAGE_SIZE + (char *)idle->thread_info); + +/* The -4 is to correct for the fact that the stack pointer +* is used to find the location of the thread_info structure +* by masking off several of the LSBs. Without the -4, esp +* is pointing to the page after the one the stack is on. +*/ + stack_start.esp = (void *)(THREAD_SIZE -4 + (char *)idle->thread_info);/* * This grunge runs the startup process for
include/asm-i386/thread_info.h +4 -0
@@ -29,9 +29,11 @@__s32 preempt_count;/* 0 => preemptable, <0 => BUG */ mm_segment_t addr_limit;/* thread address space: +0 for interrupts: illegal 0-0xBFFFFFFF for user-thead 0-0xFFFFFFFF for kernel-thread */ +struct thread_info *irq_stack;/* pointer to cpu irq stack */ __u8 supervisor_stack[0 ]; };
@@ -45,6 +47,7 @@#define TI_CPU 0x0000000C#define TI_PRE_COUNT 0x00000010#define TI_ADDR_LIMIT 0x00000014 +#define TI_IRQ_STACK 0x00000018#endif
@@ -67,6 +70,7 @@ .cpu = 0, \ .preempt_count = 1, \ .addr_limit = KERNEL_DS, \ + .irq_stack = &init_irq_union \ }#define init_thread_info (init_thread_union.thread_info)
PATCH 1/1 (3/3) stack overflow checking for x86
In reply to David C. Hansen* stack checking (3/3)
- use gcc's profiling features to check for stack overflows upon entry to functions.
- Warn if the task goes over 4k.
- Panic if the stack gets within 512 bytes of overflowing.
- use kksymoops information, if available
This won't apply cleanly without the irqstack patch, but the conflict is easy to resolve. It requires the thread_info cleanup.
Signature · 2 lines
Dave Hansen [email protected]
# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas:
# ChangeSet 1.857.1.1 -> 1.859
# arch/i386/kernel/process.c 1.32.1.1 -> 1.36
# arch/i386/kernel/irq.c 1.23.1.1 -> 1.25
# Makefile 1.326.2.11 -> 1.329
# include/asm-i386/thread_info.h 1.10 -> 1.11
# arch/i386/kernel/entry.S 1.38.1.3 -> 1.46
# arch/i386/Makefile 1.24.2.3 -> 1.27
# arch/i386/boot/compressed/misc.c 1.9 -> 1.10
# arch/i386/kernel/init_task.c 1.6.1.1 -> 1.8
# arch/i386/kernel/i386_ksyms.c 1.36.2.3 -> 1.39
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/10/31 haveblue@elm3b96.(none) 1.859
# Merge elm3b96.(none):/work/dave/bk/linux-2.5-irq-stack
# into elm3b96.(none):/work/dave/bk/linux-2.5-irq-stack+overflow-detect # -------------------------------------------- # diff -Nru a/Makefile b/Makefile --- a/Makefile Thu Oct 31 11:13:28 2002 +++ b/Makefile Thu Oct 31 11:13:28 2002 @@ -168,7 +168,8 @@
CPPFLAGS := -D__KERNEL__ -Iinclude
CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -Wno-trigraphs -O2 \
- -fomit-frame-pointer -fno-strict-aliasing -fno-common + -fno-strict-aliasing -fno-common
+
AFLAGS := -D__ASSEMBLY__ $(CPPFLAGS)
export VERSION PATCHLEVEL SUBLEVEL EXTRAVERSION KERNELRELEASE ARCH \
@@ -254,6 +255,10 @@
ifdef CONFIG_MODULES
export EXPORT_FLAGS := -DEXPORT_SYMTAB
+endif
+
+ifneq ($(CONFIG_FRAME_POINTER),y)
+CFLAGS += -fomit-frame-pointer
endif
#
arch/i386/Makefile +4 -0
@@ -51,6 +51,10 @@ MACHINE := mach-generic endif +ifdef CONFIG_X86_STACK_CHECK +CFLAGS += -p +endif + HEAD := arch/i386/kernel/head.o arch/i386/kernel/init_task.o libs-y += arch/i386/lib/
arch/i386/boot/compressed/misc.c +4 -0
@@ -377,3 +377,7 @@if (high_loaded)close_output_buffer_if_we_run_high (mv);return high_loaded; } + +/* We don't actually check for stack overflows this early. */ +__asm__(".globl mcount ; mcount: ret\n" ); +
arch/i386/kernel/entry.S +55 -0
61 lines not on this page · raw message
arch/i386/kernel/i386_ksyms.c +5 -0
@@ -211,3 +211,8 @@EXPORT_SYMBOL (edd);EXPORT_SYMBOL (eddnr);#endif + +#ifdef CONFIG_X86_STACK_CHECK +extern void mcount (void ); +EXPORT_SYMBOL (mcount); +#endif
arch/i386/kernel/init_task.c +4 -0
@@ -16,6 +16,10 @@ union thread_union init_irq_union __attribute__((__section__(".data.init_task"))); +#ifdef CONFIG_X86_STACK_CHECK +union thread_union stack_overflow_stack + __attribute__((__section__(".data.init_task"))); +#endif /* * Initial thread structure.
arch/i386/kernel/process.c +19 -1
@@ -156,7 +156,25 @@__setup ("idle=" , idle_setup); -void show_regs (struct pt_regs * regs) +void stack_overflow (unsigned long esp,unsigned long eip) +{ +int panicing = ((esp&(THREAD_SIZE -1 )) <=STACK_PANIC ); + +printk ("esp: 0x%x masked: 0x%x STACK_PANIC:0x%x %d %d\n" , + esp, (esp&(THREAD_SIZE -1 )),STACK_PANIC , (((esp&(THREAD_SIZE -1 )) <=STACK_PANIC )), panicing ); + +if (panicing) +print_symbol ("stack overflow from %s\n" , eip); +else +print_symbol ("excessive stack use from %s\n" , eip); +printk ("esp: %p\n" , (void *)esp); +show_trace ((void *)esp); + +if (panicing) +panic ("stack overflow\n" ); +} + +asmlinkage void show_regs(struct pt_regs * regs) {unsigned long cr0 =0L , cr2 =0L , cr3 =0L , cr4 =0L ;
include/asm-i386/thread_info.h +2 -0
@@ -60,6 +60,8 @@*/ #define THREAD_ORDER 1#define INIT_THREAD_SIZE THREAD_SIZE +#define STACK_PANIC 0x200ul +#define STACK_WARN ((THREAD_SIZE)>>1)#ifndef __ASSEMBLY__#define INIT_THREAD_INFO (tsk) \
Re: [PATCH] (3/3) stack overflow checking for x86
Quoted 9 lines · David C. Hansen
* stack checking (3/3)
- use gcc's profiling features to check for stack overflows upon entry to functions.
- Warn if the task goes over 4k.
- Panic if the stack gets within 512 bytes of overflowing.
- use kksymoops information, if availableThis won't apply cleanly without the irqstack patch, but the conflict is easy to resolve. It requires the
thread_infocleanup.
I'm wondering about interaction between this patch and the already merged CONFIG_DEBUG_STACKOVERFLOW ?
Dave
Signature · 1 line
| Dave Jones. http://www.codemonkey.org.uk
Re: [PATCH] (3/3) stack overflow checking for x86
Quoted 8 lines · Dave Jones
Quoted 7 lines · David C. Hansen
* stack checking (3/3)
- use gcc's profiling features to check for stack overflows upon entry to functions.
- Warn if the task goes over 4k.
- Panic if the stack gets within 512 bytes of overflowing.
- use kksymoops information, if available
Quoted 5 lines
Quoted 2 lines
This won't apply cleanly without the irqstack patch, but the conflict is easy to resolve. It requires the
thread_infocleanup.I'm wondering about interaction between this patch and the already merged
CONFIG_DEBUG_STACKOVERFLOW?
The currently merged one is very, very simple, but relatively worthless. There are no guarantees about catching an overflow, especially if it happens in a long call chain _after_ do_IRQ with interrupts disabled. Ben's version checks on entrance to every function, making it _much_ more likely to be caught, even during an interrupt. But, the currently merged one doesn't have any strange compiler requirements, like adding the -p option.
The irq stack patch would make the current check pretty worthless because the check happens just after the switch to a fresh irqstack would have happened.
But, if they both get in, it can be cleaned up later because even if both are turned on, nothing will blow up.
Signature · 2 lines
Dave Hansen [email protected]
Re: [PATCH] (3/3) stack overflow checking for x86
In reply to Dave JonesQuoted 5 lines · Dave Jones
Quoted 2 lines
This won't apply cleanly without the irqstack patch, but the conflict is easy to resolve. It requires the
thread_infocleanup.I'm wondering about interaction between this patch and the already merged
CONFIG_DEBUG_STACKOVERFLOW?
It replaces it and actually makes it useful since IRQ usage is now bounded and defined relative to non IRQ usage. Without IRQ stacks you don't have a hope in hell of catching overflows that depend on an irq occuring at the right moment
Re: [PATCH] (3/3) stack overflow checking for x86
Quoted 11 lines · Alan Cox
Quoted 5 lines · Dave Jones
Quoted 2 lines
This won't apply cleanly without the irqstack patch, but the conflict is easy to resolve. It requires the
thread_infocleanup.I'm wondering about interaction between this patch and the already merged
CONFIG_DEBUG_STACKOVERFLOW?It replaces it and actually makes it useful since IRQ usage is now bounded and defined relative to non IRQ usage. Without IRQ stacks you don't have a hope in hell of catching overflows that depend on an irq occuring at the right moment
Yeah, I figured it worked better, but wondered why the patch didn't remove the existing implementation.
Dave
Signature · 1 line
| Dave Jones. http://www.codemonkey.org.uk