ldt optimization
PATCH 1/1 ldt optimization
linuxthreads uses the ldt to identify the threads in a process. This results in a modify_ldt call, which causes a 64kB vmalloc call, for every process that is linked against libpthread. vmalloc space is often tight, which limits the number of processes that can run concurrently. I remember one report of failures with 80 users on a multiuser kde x-server. The attached patch changes the ldt allocation to try to use kmalloc if possible, and to limit the initial allocation size to 2 kB. Additionally, the patch adds proper failure returns. The curent code just prinkt's the error and continues the fork() syscall [and silently drops the ldt].
2.6 contains a similar patch, and it was (is?) in several distro and ac kernels.
Marcelo, could you add it to your tree?
--
Manfred
// $Header$
// Kernel Version:
// VERSION = 2 // PATCHLEVEL = 4 // SUBLEVEL = 20 // EXTRAVERSION = -pre4
arch/i386/kernel/process.c +5 -48
@@ -466,23 +466,6 @@ }/* -* No need to lock the MM as we are the last user -*/ -void release_segments (struct mm_struct *mm) -{ -void * ldt = mm->context.segments; - -/* -* free the LDT -*/ -if (ldt) { - mm->context.segments =NULL ; -clear_LDT (); -vfree (ldt); - } -} - -/* * Create a kernel thread */ int kernel_thread (int (*fn)(void *),void * arg,unsigned long flags)
@@ -534,45 +517,19 @@void release_thread (struct task_struct *dead_task) {if (dead_task->mm) { -void * ldt = dead_task->mm->context.segments; -// temporary debugging check -if (ldt) { -printk ("WARNING: dead process %8s still has LDT? <%p>\n" , - dead_task->comm, ldt); +if (dead_task->mm->context.size) { +printk ("WARNING: dead process %8s still has LDT? <%p/%d>\n" , + dead_task->comm, + dead_task->mm->context.ldt, + dead_task->mm->context.size);BUG (); } } -release_x86_irqs (dead_task); }/* -* we do not have to muck with descriptors here, that is -* done in switch_mm() as needed. -*/ -void copy_segments (struct task_struct *p,struct mm_struct *new_mm) -{ -struct mm_struct * old_mm; -void *old_ldt, *ldt; - - ldt =NULL ; - old_mm = current->mm; -if (old_mm && (old_ldt = old_mm->context.segments) !=NULL ) { -/* -* Completely new LDT, we initialize it from the parent: -*/ - ldt =vmalloc (LDT_ENTRIES *LDT_ENTRY_SIZE ); -if (!ldt) -printk (KERN_WARNING "ldt allocation failed\n" ); -else -memcpy (ldt, old_ldt,LDT_ENTRIES *LDT_ENTRY_SIZE ); - } - new_mm->context.segments = ldt; - new_mm->context.cpuvalid = ~0UL ;/* valid on all CPU's - they can't have stale data */ -} - -/* * Save a segment. */ #define savesegment (seg,value) \
arch/i386/kernel/ldt.c +121 -29
196 lines not on this page · raw message
include/asm-i386/mmu.h +5 -2
@@ -4,10 +4,13 @@/* * The i386 doesn't have a mmu context, but * we put the segment information here. +* +* cpu_vm_mask is used to optimize ldt flushing. */ typedef struct { -void *segments; -unsigned long cpuvalid; +int size; +struct semaphore sem; +void * ldt; }mm_context_t ;#endif
include/asm-i386/desc.h +4 -4
@@ -79,13 +79,13 @@/* * load one particular LDT into the current CPU */ -static inline void load_LDT (struct mm_struct *mm) +static inline void load_LDT (mm_context_t *pc) {int cpu =smp_processor_id (); -void *segments = mm->context.segments; -int count =LDT_ENTRIES ; +void *segments = pc->ldt; +int count = pc->size; -if (!segments) { +if (!count) { segments = &default_ldt[0 ]; count =5 ; }
include/asm-i386/mmu_context.h +11 -11
@@ -7,10 +7,12 @@#include <asm/pgalloc.h> /* -* possibly do the LDT unload here? +* hooks to add arch specific data into the mm struct. +* Note that destroy_context is called even if init_new_context +* fails. */ -#define destroy_context (mm) do { } while(0) -#define init_new_context (tsk,mm) 0 +int init_new_context (struct task_struct *tsk,struct mm_struct *mm); +void destroy_context (struct mm_struct *mm);#ifdef CONFIG_SMP
@@ -30,19 +32,18 @@if (prev != next) {/* stop flush ipis for the previous mm */ clear_bit (cpu, &prev->cpu_vm_mask); -/* -* Re-load LDT if necessary -*/ -if (prev->context.segments != next->context.segments) -load_LDT (next);#ifdef CONFIG_SMP cpu_tlbstate[cpu].state =TLBSTATE_OK ; cpu_tlbstate[cpu].active_mm = next;#endif set_bit (cpu, &next->cpu_vm_mask); -set_bit (cpu, &next->context.cpuvalid);/* Re-load page tables */ load_cr3 (next->pgd); +/* load_LDT, if either the previous or next thread +* has a non-default LDT. +*/ +if (next->context.size+prev->context.size) +load_LDT (&next->context); }#ifdef CONFIG_SMP else {
@@ -54,9 +55,8 @@* tlb flush IPI delivery. We must reload %cr3. */ load_cr3 (next->pgd); + load_LDT(&next->context); } -if (!test_and_set_bit (cpu, &next->context.cpuvalid)) -load_LDT (next); }#endif }
include/asm-i386/processor.h +6 -3
@@ -435,9 +435,12 @@*/ extern int kernel_thread (int (*fn)(void *),void * arg,unsigned long flags); -/* Copy and release all segment info associated with a VM */ -extern void copy_segments (struct task_struct *p,struct mm_struct * mm); -extern void release_segments (struct mm_struct * mm); +/* Copy and release all segment info associated with a VM +* Unusable due to lack of error handling, use {init_new,destroy}_context +* instead. +*/ +static inline void copy_segments (struct task_struct *p,struct mm_struct * mm) { } +static inline void release_segments (struct mm_struct * mm) { }/* * Return saved PC of a blocked thread.
arch/i386/math-emu/fpu_system.h +1 -1
@@ -20,7 +20,7 @@of the stack frame of math_emulate() */ #define SETUP_DATA_AREA (arg) FPU_info = (struct info *) &arg -#define LDT_DESCRIPTOR (s) (((struct desc_struct *)current->mm->context.segments)[(s) >> 3]) +#define LDT_DESCRIPTOR (s) (((struct desc_struct *)current->mm->context.ldt)[(s) >> 3])#define SEG_D_SIZE (x) ((x).b & (3 << 21))#define SEG_G_BIT (x) ((x).b & (1 << 23))#define SEG_GRANULARITY (x) (((x).b & (1 << 23)) ? 4096 : 1)