Commit 89d637a8 authored by Ingo Molnar's avatar Ingo Molnar Committed by Linus Torvalds

[PATCH] ldt-fix-2.5.32-A3

this is an updated version of the LDT fixes. It fixes the following kinds
of problems:

 - fix a possible gcc optimization causing a race causing the loading of a
   corrupt LDT descriptor upon context switch. [this fix got simplified
   over previous versions.]

 - remove an unconditional OOM printk, and there's no need to set ->size
   in the OOM path.

 - fix preemption bugs, load_LDT()/clear_LDT() was not preemption-safe,
   when it was used outside of spinlocks.

the context-switch race is the following. 'LDT modification' is the
following operation: the seg->ldt pointer is modified, then seg->size is
modified. In theory gcc is free to reschedule the two modifications, and
first modify ->size, then ->ldt. Thus if this modification is not
synchronized with context-switches, another thread might see a temporary
state of the new ->size [which was increased], but still the old pointer.
Ie.:

	CPU0				CPU1

	pc->size = newsize;
					load_LDT(); // (oldptr, newsize)
	pc->ldt = newptr;

the corrupt LDT is loaded until the SMP cross-call is sent, leaving the
window open for many usecs.

the fix is to put a wmb() after ->ldt modifications. [this is also in
preparation of not-write-ordered SMP x86 designs.]
parent e5d588fe
......@@ -49,17 +49,20 @@ static int alloc_ldt(mm_context_t *pc, int mincount, int reload)
memcpy(newldt, pc->ldt, oldsize*LDT_ENTRY_SIZE);
oldldt = pc->ldt;
memset(newldt+oldsize*LDT_ENTRY_SIZE, 0, (mincount-oldsize)*LDT_ENTRY_SIZE);
wmb();
pc->ldt = newldt;
wmb();
pc->size = mincount;
wmb();
if (reload) {
load_LDT(pc);
#ifdef CONFIG_SMP
if (current->mm->cpu_vm_mask != (1<<smp_processor_id()))
preempt_disable();
if (current->mm->cpu_vm_mask != (1 << smp_processor_id()))
smp_call_function(flush_ldt, 0, 1, 1);
preempt_enable();
#endif
}
wmb();
if (oldsize) {
if (oldsize*LDT_ENTRY_SIZE > PAGE_SIZE)
vfree(oldldt);
......@@ -72,11 +75,8 @@ static int alloc_ldt(mm_context_t *pc, int mincount, int reload)
static inline int copy_ldt(mm_context_t *new, mm_context_t *old)
{
int err = alloc_ldt(new, old->size, 0);
if (err < 0) {
printk(KERN_WARNING "ldt allocation failed\n");
new->size = 0;
if (err < 0)
return err;
}
memcpy(new->ldt, old->ldt, old->size*LDT_ENTRY_SIZE);
return 0;
}
......
......@@ -86,14 +86,17 @@ static inline void load_TLS(struct thread_struct *t, unsigned int cpu)
static inline void clear_LDT(void)
{
set_ldt_desc(smp_processor_id(), &default_ldt[0], 5);
int cpu = get_cpu();
set_ldt_desc(cpu, &default_ldt[0], 5);
load_LDT_desc();
put_cpu();
}
/*
* load one particular LDT into the current CPU
*/
static inline void load_LDT (mm_context_t *pc)
static inline void load_LDT_nolock(mm_context_t *pc, int cpu)
{
void *segments = pc->ldt;
int count = pc->size;
......@@ -103,10 +106,17 @@ static inline void load_LDT (mm_context_t *pc)
count = 5;
}
set_ldt_desc(smp_processor_id(), segments, count);
set_ldt_desc(cpu, segments, count);
load_LDT_desc();
}
static inline void load_LDT(mm_context_t *pc)
{
int cpu = get_cpu();
load_LDT_nolock(pc, cpu);
put_cpu();
}
#endif /* !__ASSEMBLY__ */
#endif
......@@ -10,7 +10,7 @@
typedef struct {
int size;
struct semaphore sem;
void * ldt;
void *ldt;
} mm_context_t;
#endif
......@@ -44,7 +44,7 @@ static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, str
* load the LDT, if the LDT is different:
*/
if (unlikely(prev->context.ldt != next->context.ldt))
load_LDT(&next->context);
load_LDT_nolock(&next->context, cpu);
}
#ifdef CONFIG_SMP
else {
......@@ -56,7 +56,7 @@ static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, str
* tlb flush IPI delivery. We must reload %cr3.
*/
load_cr3(next->pgd);
load_LDT(&next->context);
load_LDT_nolock(&next->context, cpu);
}
}
#endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment