avoid vmalloc in modify_ldt
Subsystems (3)
PATCH 1/1 avoid vmalloc in modify_ldt
modify_ldt always allocates a 64 kB vmalloc block, regardless of how many ldt entries are actually needed.
All apps linked to libpthread now use the ldt to access thread-local variables, and an 8192 entry ldt is both a waste of memory and of the vmalloc area.
The attached patch:
* dynamically allocs and reallocs the ldt, starting with a 512 entry (4 kB) ldt allocated with kmalloc.
* returns all 5 entries of the default ldt if modify_ldt(2,,) is called.
* moves copy_segments into init_new_context, and adds error handling.
* deletes copy_segments on i386.
* removes the call to copy_segments from kernel/fork.c. No other platform uses it.
* reuses cpu_vm_valid for the LDT reload: there is no need to maintain 2 cpu bitmasks, one set_bit removed from every thread switch.
Tested on i386 SMP, both with libpthread apps and a small test app.
--
Manfred
// $Header$
// Kernel Version:
// VERSION = 2 // PATCHLEVEL = 5 // SUBLEVEL = 1 // EXTRAVERSION =-pre10
./arch/i386/kernel/process.c +5 -47
@@ -465,23 +465,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)
@@ -533,40 +516,15 @@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 (); } } -} - -/* -* 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 */ }/*
./arch/i386/kernel/ldt.c +119 -29
194 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 +8 -9
@@ -10,7 +10,7 @@* possibly do the LDT unload here? */ #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);#ifdef CONFIG_SMP
@@ -30,19 +30,19 @@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 */ asmvolatile ("movl %0,%%cr3" : :"r" (__pa (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 +54,8 @@* tlb flush IPI delivery. We must flush our tlb. */ local_flush_tlb (); + load_LDT(&next->context); } -if (!test_and_set_bit (cpu, &next->context.cpuvalid)) -load_LDT (next); }#endif }
kernel/fork.c +0 -5
@@ -340,11 +340,6 @@if (retval) goto free_pt; -/* -* child gets a private LDT (if there was an LDT in the parent) -*/ -copy_segments (tsk, mm); -if (init_new_context (tsk,mm)) goto free_pt;
include/asm-i386/processor.h +1 -2
@@ -431,8 +431,7 @@*/ 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); +/* Release all segment info associated with a VM */ extern void release_segments (struct mm_struct * mm);/*