static int alloc_ldt(mm_context_t *pc, int mincount, int reload) { void *oldldt, *newldt; int oldsize; if (mincount <= pc->size) return 0; oldsize = pc->size; mincount = (mincount + (PAGE_SIZE / LDT_ENTRY_SIZE - 1)) & (~(PAGE_SIZE / LDT_ENTRY_SIZE - 1)); if (mincount * LDT_ENTRY_SIZE > PAGE_SIZE) newldt = vmalloc(mincount * LDT_ENTRY_SIZE); else newldt = (void *)__get_free_page(GFP_KERNEL); if (!newldt) return -ENOMEM; if (oldsize) memcpy(newldt, pc->ldt, oldsize * LDT_ENTRY_SIZE); oldldt = pc->ldt; memset(newldt + oldsize * LDT_ENTRY_SIZE, 0, (mincount - oldsize) * LDT_ENTRY_SIZE); paravirt_alloc_ldt(newldt, mincount); #ifdef CONFIG_X86_64 /* CHECKME: Do we really need this ? */ wmb(); #endif pc->ldt = newldt; wmb(); pc->size = mincount; wmb(); if (reload) { #ifdef CONFIG_SMP preempt_disable(); load_LDT(pc); if (!cpumask_equal(mm_cpumask(current->mm), cpumask_of(smp_processor_id()))) smp_call_function(flush_ldt, current->mm, 1); preempt_enable(); #else load_LDT(pc); #endif } if (oldsize) { paravirt_free_ldt(oldldt, oldsize); if (oldsize * LDT_ENTRY_SIZE > PAGE_SIZE) vfree(oldldt); else put_page(virt_to_page(oldldt)); } return 0; }
/* After calling this, the LDT is immutable. */ static void finalize_ldt_struct(struct ldt_struct *ldt) { paravirt_alloc_ldt(ldt->entries, ldt->size); }