mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 12:42:02 +00:00
[S390] use generic RCU page-table freeing code
Replace the s390 specific rcu page-table freeing code with the generic variant. This requires to duplicate the definition for the struct mmu_table_batch as s390 does not use the generic tlb flush code. While we are at it remove the restriction that page table fragments can not be reused after a single fragment has been freed with rcu and split out allocation and freeing of page tables with pgstes. Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
parent
3ec90878ba
commit
36409f6353
@ -89,6 +89,7 @@ config S390
|
||||
select HAVE_GET_USER_PAGES_FAST
|
||||
select HAVE_ARCH_MUTEX_CPU_RELAX
|
||||
select HAVE_ARCH_JUMP_LABEL if !MARCH_G5
|
||||
select HAVE_RCU_TABLE_FREE if SMP
|
||||
select ARCH_INLINE_SPIN_TRYLOCK
|
||||
select ARCH_INLINE_SPIN_TRYLOCK_BH
|
||||
select ARCH_INLINE_SPIN_LOCK
|
||||
|
@ -17,15 +17,15 @@
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
#define check_pgt_cache() do {} while (0)
|
||||
|
||||
unsigned long *crst_table_alloc(struct mm_struct *);
|
||||
void crst_table_free(struct mm_struct *, unsigned long *);
|
||||
void crst_table_free_rcu(struct mm_struct *, unsigned long *);
|
||||
|
||||
unsigned long *page_table_alloc(struct mm_struct *);
|
||||
void page_table_free(struct mm_struct *, unsigned long *);
|
||||
void page_table_free_rcu(struct mm_struct *, unsigned long *);
|
||||
#ifdef CONFIG_HAVE_RCU_TABLE_FREE
|
||||
void page_table_free_rcu(struct mmu_gather *, unsigned long *);
|
||||
void __tlb_remove_table(void *_table);
|
||||
#endif
|
||||
|
||||
static inline void clear_table(unsigned long *s, unsigned long val, size_t n)
|
||||
{
|
||||
|
@ -26,67 +26,60 @@
|
||||
#include <linux/swap.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/pgalloc.h>
|
||||
#include <asm/smp.h>
|
||||
#include <asm/tlbflush.h>
|
||||
|
||||
struct mmu_gather {
|
||||
struct mm_struct *mm;
|
||||
#ifdef CONFIG_HAVE_RCU_TABLE_FREE
|
||||
struct mmu_table_batch *batch;
|
||||
#endif
|
||||
unsigned int fullmm;
|
||||
unsigned int nr_ptes;
|
||||
unsigned int nr_pxds;
|
||||
unsigned int max;
|
||||
void **array;
|
||||
void *local[8];
|
||||
unsigned int need_flush;
|
||||
};
|
||||
|
||||
static inline void __tlb_alloc_page(struct mmu_gather *tlb)
|
||||
{
|
||||
unsigned long addr = __get_free_pages(GFP_NOWAIT | __GFP_NOWARN, 0);
|
||||
#ifdef CONFIG_HAVE_RCU_TABLE_FREE
|
||||
struct mmu_table_batch {
|
||||
struct rcu_head rcu;
|
||||
unsigned int nr;
|
||||
void *tables[0];
|
||||
};
|
||||
|
||||
if (addr) {
|
||||
tlb->array = (void *) addr;
|
||||
tlb->max = PAGE_SIZE / sizeof(void *);
|
||||
}
|
||||
}
|
||||
#define MAX_TABLE_BATCH \
|
||||
((PAGE_SIZE - sizeof(struct mmu_table_batch)) / sizeof(void *))
|
||||
|
||||
extern void tlb_table_flush(struct mmu_gather *tlb);
|
||||
extern void tlb_remove_table(struct mmu_gather *tlb, void *table);
|
||||
#endif
|
||||
|
||||
static inline void tlb_gather_mmu(struct mmu_gather *tlb,
|
||||
struct mm_struct *mm,
|
||||
unsigned int full_mm_flush)
|
||||
{
|
||||
tlb->mm = mm;
|
||||
tlb->max = ARRAY_SIZE(tlb->local);
|
||||
tlb->array = tlb->local;
|
||||
tlb->fullmm = full_mm_flush;
|
||||
tlb->need_flush = 0;
|
||||
#ifdef CONFIG_HAVE_RCU_TABLE_FREE
|
||||
tlb->batch = NULL;
|
||||
#endif
|
||||
if (tlb->fullmm)
|
||||
__tlb_flush_mm(mm);
|
||||
else
|
||||
__tlb_alloc_page(tlb);
|
||||
tlb->nr_ptes = 0;
|
||||
tlb->nr_pxds = tlb->max;
|
||||
}
|
||||
|
||||
static inline void tlb_flush_mmu(struct mmu_gather *tlb)
|
||||
{
|
||||
if (!tlb->fullmm && (tlb->nr_ptes > 0 || tlb->nr_pxds < tlb->max))
|
||||
__tlb_flush_mm(tlb->mm);
|
||||
while (tlb->nr_ptes > 0)
|
||||
page_table_free_rcu(tlb->mm, tlb->array[--tlb->nr_ptes]);
|
||||
while (tlb->nr_pxds < tlb->max)
|
||||
crst_table_free_rcu(tlb->mm, tlb->array[tlb->nr_pxds++]);
|
||||
if (!tlb->need_flush)
|
||||
return;
|
||||
tlb->need_flush = 0;
|
||||
__tlb_flush_mm(tlb->mm);
|
||||
#ifdef CONFIG_HAVE_RCU_TABLE_FREE
|
||||
tlb_table_flush(tlb);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void tlb_finish_mmu(struct mmu_gather *tlb,
|
||||
unsigned long start, unsigned long end)
|
||||
{
|
||||
tlb_flush_mmu(tlb);
|
||||
|
||||
rcu_table_freelist_finish();
|
||||
|
||||
/* keep the page table cache within bounds */
|
||||
check_pgt_cache();
|
||||
|
||||
if (tlb->array != tlb->local)
|
||||
free_pages((unsigned long) tlb->array, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -112,12 +105,11 @@ static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page)
|
||||
static inline void pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
|
||||
unsigned long address)
|
||||
{
|
||||
if (!tlb->fullmm) {
|
||||
tlb->array[tlb->nr_ptes++] = pte;
|
||||
if (tlb->nr_ptes >= tlb->nr_pxds)
|
||||
tlb_flush_mmu(tlb);
|
||||
} else
|
||||
page_table_free(tlb->mm, (unsigned long *) pte);
|
||||
#ifdef CONFIG_HAVE_RCU_TABLE_FREE
|
||||
if (!tlb->fullmm)
|
||||
return page_table_free_rcu(tlb, (unsigned long *) pte);
|
||||
#endif
|
||||
page_table_free(tlb->mm, (unsigned long *) pte);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -133,12 +125,11 @@ static inline void pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd,
|
||||
#ifdef __s390x__
|
||||
if (tlb->mm->context.asce_limit <= (1UL << 31))
|
||||
return;
|
||||
if (!tlb->fullmm) {
|
||||
tlb->array[--tlb->nr_pxds] = pmd;
|
||||
if (tlb->nr_ptes >= tlb->nr_pxds)
|
||||
tlb_flush_mmu(tlb);
|
||||
} else
|
||||
crst_table_free(tlb->mm, (unsigned long *) pmd);
|
||||
#ifdef CONFIG_HAVE_RCU_TABLE_FREE
|
||||
if (!tlb->fullmm)
|
||||
return tlb_remove_table(tlb, pmd);
|
||||
#endif
|
||||
crst_table_free(tlb->mm, (unsigned long *) pmd);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -155,12 +146,11 @@ static inline void pud_free_tlb(struct mmu_gather *tlb, pud_t *pud,
|
||||
#ifdef __s390x__
|
||||
if (tlb->mm->context.asce_limit <= (1UL << 42))
|
||||
return;
|
||||
if (!tlb->fullmm) {
|
||||
tlb->array[--tlb->nr_pxds] = pud;
|
||||
if (tlb->nr_ptes >= tlb->nr_pxds)
|
||||
tlb_flush_mmu(tlb);
|
||||
} else
|
||||
crst_table_free(tlb->mm, (unsigned long *) pud);
|
||||
#ifdef CONFIG_HAVE_RCU_TABLE_FREE
|
||||
if (!tlb->fullmm)
|
||||
return tlb_remove_table(tlb, pud);
|
||||
#endif
|
||||
crst_table_free(tlb->mm, (unsigned long *) pud);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -24,94 +24,12 @@
|
||||
#include <asm/tlbflush.h>
|
||||
#include <asm/mmu_context.h>
|
||||
|
||||
struct rcu_table_freelist {
|
||||
struct rcu_head rcu;
|
||||
struct mm_struct *mm;
|
||||
unsigned int pgt_index;
|
||||
unsigned int crst_index;
|
||||
unsigned long *table[0];
|
||||
};
|
||||
|
||||
#define RCU_FREELIST_SIZE \
|
||||
((PAGE_SIZE - sizeof(struct rcu_table_freelist)) \
|
||||
/ sizeof(unsigned long))
|
||||
|
||||
static DEFINE_PER_CPU(struct rcu_table_freelist *, rcu_table_freelist);
|
||||
|
||||
static void __page_table_free(struct mm_struct *mm, unsigned long *table);
|
||||
|
||||
static struct rcu_table_freelist *rcu_table_freelist_get(struct mm_struct *mm)
|
||||
{
|
||||
struct rcu_table_freelist **batchp = &__get_cpu_var(rcu_table_freelist);
|
||||
struct rcu_table_freelist *batch = *batchp;
|
||||
|
||||
if (batch)
|
||||
return batch;
|
||||
batch = (struct rcu_table_freelist *) __get_free_page(GFP_ATOMIC);
|
||||
if (batch) {
|
||||
batch->mm = mm;
|
||||
batch->pgt_index = 0;
|
||||
batch->crst_index = RCU_FREELIST_SIZE;
|
||||
*batchp = batch;
|
||||
}
|
||||
return batch;
|
||||
}
|
||||
|
||||
static void rcu_table_freelist_callback(struct rcu_head *head)
|
||||
{
|
||||
struct rcu_table_freelist *batch =
|
||||
container_of(head, struct rcu_table_freelist, rcu);
|
||||
|
||||
while (batch->pgt_index > 0)
|
||||
__page_table_free(batch->mm, batch->table[--batch->pgt_index]);
|
||||
while (batch->crst_index < RCU_FREELIST_SIZE)
|
||||
crst_table_free(batch->mm, batch->table[batch->crst_index++]);
|
||||
free_page((unsigned long) batch);
|
||||
}
|
||||
|
||||
void rcu_table_freelist_finish(void)
|
||||
{
|
||||
struct rcu_table_freelist **batchp = &get_cpu_var(rcu_table_freelist);
|
||||
struct rcu_table_freelist *batch = *batchp;
|
||||
|
||||
if (!batch)
|
||||
goto out;
|
||||
call_rcu(&batch->rcu, rcu_table_freelist_callback);
|
||||
*batchp = NULL;
|
||||
out:
|
||||
put_cpu_var(rcu_table_freelist);
|
||||
}
|
||||
|
||||
static void smp_sync(void *arg)
|
||||
{
|
||||
}
|
||||
|
||||
#ifndef CONFIG_64BIT
|
||||
#define ALLOC_ORDER 1
|
||||
#define TABLES_PER_PAGE 4
|
||||
#define FRAG_MASK 15UL
|
||||
#define SECOND_HALVES 10UL
|
||||
|
||||
void clear_table_pgstes(unsigned long *table)
|
||||
{
|
||||
clear_table(table, _PAGE_TYPE_EMPTY, PAGE_SIZE/4);
|
||||
memset(table + 256, 0, PAGE_SIZE/4);
|
||||
clear_table(table + 512, _PAGE_TYPE_EMPTY, PAGE_SIZE/4);
|
||||
memset(table + 768, 0, PAGE_SIZE/4);
|
||||
}
|
||||
|
||||
#define FRAG_MASK 0x0f
|
||||
#else
|
||||
#define ALLOC_ORDER 2
|
||||
#define TABLES_PER_PAGE 2
|
||||
#define FRAG_MASK 3UL
|
||||
#define SECOND_HALVES 2UL
|
||||
|
||||
void clear_table_pgstes(unsigned long *table)
|
||||
{
|
||||
clear_table(table, _PAGE_TYPE_EMPTY, PAGE_SIZE/2);
|
||||
memset(table + 256, 0, PAGE_SIZE/2);
|
||||
}
|
||||
|
||||
#define FRAG_MASK 0x03
|
||||
#endif
|
||||
|
||||
unsigned long VMALLOC_START = VMALLOC_END - VMALLOC_SIZE;
|
||||
@ -140,29 +58,6 @@ void crst_table_free(struct mm_struct *mm, unsigned long *table)
|
||||
free_pages((unsigned long) table, ALLOC_ORDER);
|
||||
}
|
||||
|
||||
void crst_table_free_rcu(struct mm_struct *mm, unsigned long *table)
|
||||
{
|
||||
struct rcu_table_freelist *batch;
|
||||
|
||||
preempt_disable();
|
||||
if (atomic_read(&mm->mm_users) < 2 &&
|
||||
cpumask_equal(mm_cpumask(mm), cpumask_of(smp_processor_id()))) {
|
||||
crst_table_free(mm, table);
|
||||
goto out;
|
||||
}
|
||||
batch = rcu_table_freelist_get(mm);
|
||||
if (!batch) {
|
||||
smp_call_function(smp_sync, NULL, 1);
|
||||
crst_table_free(mm, table);
|
||||
goto out;
|
||||
}
|
||||
batch->table[--batch->crst_index] = table;
|
||||
if (batch->pgt_index >= batch->crst_index)
|
||||
rcu_table_freelist_finish();
|
||||
out:
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
int crst_table_upgrade(struct mm_struct *mm, unsigned long limit)
|
||||
{
|
||||
@ -238,124 +133,175 @@ void crst_table_downgrade(struct mm_struct *mm, unsigned long limit)
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline unsigned int atomic_xor_bits(atomic_t *v, unsigned int bits)
|
||||
{
|
||||
unsigned int old, new;
|
||||
|
||||
do {
|
||||
old = atomic_read(v);
|
||||
new = old ^ bits;
|
||||
} while (atomic_cmpxchg(v, old, new) != old);
|
||||
return new;
|
||||
}
|
||||
|
||||
/*
|
||||
* page table entry allocation/free routines.
|
||||
*/
|
||||
#ifdef CONFIG_PGSTE
|
||||
static inline unsigned long *page_table_alloc_pgste(struct mm_struct *mm)
|
||||
{
|
||||
struct page *page;
|
||||
unsigned long *table;
|
||||
|
||||
page = alloc_page(GFP_KERNEL|__GFP_REPEAT);
|
||||
if (!page)
|
||||
return NULL;
|
||||
pgtable_page_ctor(page);
|
||||
atomic_set(&page->_mapcount, 3);
|
||||
table = (unsigned long *) page_to_phys(page);
|
||||
clear_table(table, _PAGE_TYPE_EMPTY, PAGE_SIZE/2);
|
||||
clear_table(table + PTRS_PER_PTE, 0, PAGE_SIZE/2);
|
||||
return table;
|
||||
}
|
||||
|
||||
static inline void page_table_free_pgste(unsigned long *table)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
page = pfn_to_page(__pa(table) >> PAGE_SHIFT);
|
||||
pgtable_page_ctor(page);
|
||||
atomic_set(&page->_mapcount, -1);
|
||||
__free_page(page);
|
||||
}
|
||||
#endif
|
||||
|
||||
unsigned long *page_table_alloc(struct mm_struct *mm)
|
||||
{
|
||||
struct page *page;
|
||||
unsigned long *table;
|
||||
unsigned long bits;
|
||||
unsigned int mask, bit;
|
||||
|
||||
bits = (mm->context.has_pgste) ? 3UL : 1UL;
|
||||
#ifdef CONFIG_PGSTE
|
||||
if (mm_has_pgste(mm))
|
||||
return page_table_alloc_pgste(mm);
|
||||
#endif
|
||||
/* Allocate fragments of a 4K page as 1K/2K page table */
|
||||
spin_lock_bh(&mm->context.list_lock);
|
||||
page = NULL;
|
||||
mask = FRAG_MASK;
|
||||
if (!list_empty(&mm->context.pgtable_list)) {
|
||||
page = list_first_entry(&mm->context.pgtable_list,
|
||||
struct page, lru);
|
||||
if ((page->flags & FRAG_MASK) == ((1UL << TABLES_PER_PAGE) - 1))
|
||||
page = NULL;
|
||||
table = (unsigned long *) page_to_phys(page);
|
||||
mask = atomic_read(&page->_mapcount);
|
||||
mask = mask | (mask >> 4);
|
||||
}
|
||||
if (!page) {
|
||||
if ((mask & FRAG_MASK) == FRAG_MASK) {
|
||||
spin_unlock_bh(&mm->context.list_lock);
|
||||
page = alloc_page(GFP_KERNEL|__GFP_REPEAT);
|
||||
if (!page)
|
||||
return NULL;
|
||||
pgtable_page_ctor(page);
|
||||
page->flags &= ~FRAG_MASK;
|
||||
atomic_set(&page->_mapcount, 1);
|
||||
table = (unsigned long *) page_to_phys(page);
|
||||
if (mm->context.has_pgste)
|
||||
clear_table_pgstes(table);
|
||||
else
|
||||
clear_table(table, _PAGE_TYPE_EMPTY, PAGE_SIZE);
|
||||
clear_table(table, _PAGE_TYPE_EMPTY, PAGE_SIZE);
|
||||
spin_lock_bh(&mm->context.list_lock);
|
||||
list_add(&page->lru, &mm->context.pgtable_list);
|
||||
} else {
|
||||
for (bit = 1; mask & bit; bit <<= 1)
|
||||
table += PTRS_PER_PTE;
|
||||
mask = atomic_xor_bits(&page->_mapcount, bit);
|
||||
if ((mask & FRAG_MASK) == FRAG_MASK)
|
||||
list_del(&page->lru);
|
||||
}
|
||||
table = (unsigned long *) page_to_phys(page);
|
||||
while (page->flags & bits) {
|
||||
table += 256;
|
||||
bits <<= 1;
|
||||
}
|
||||
page->flags |= bits;
|
||||
if ((page->flags & FRAG_MASK) == ((1UL << TABLES_PER_PAGE) - 1))
|
||||
list_move_tail(&page->lru, &mm->context.pgtable_list);
|
||||
spin_unlock_bh(&mm->context.list_lock);
|
||||
return table;
|
||||
}
|
||||
|
||||
static void __page_table_free(struct mm_struct *mm, unsigned long *table)
|
||||
{
|
||||
struct page *page;
|
||||
unsigned long bits;
|
||||
|
||||
bits = ((unsigned long) table) & 15;
|
||||
table = (unsigned long *)(((unsigned long) table) ^ bits);
|
||||
page = pfn_to_page(__pa(table) >> PAGE_SHIFT);
|
||||
page->flags ^= bits;
|
||||
if (!(page->flags & FRAG_MASK)) {
|
||||
pgtable_page_dtor(page);
|
||||
__free_page(page);
|
||||
}
|
||||
}
|
||||
|
||||
void page_table_free(struct mm_struct *mm, unsigned long *table)
|
||||
{
|
||||
struct page *page;
|
||||
unsigned long bits;
|
||||
unsigned int bit, mask;
|
||||
|
||||
bits = (mm->context.has_pgste) ? 3UL : 1UL;
|
||||
bits <<= (__pa(table) & (PAGE_SIZE - 1)) / 256 / sizeof(unsigned long);
|
||||
#ifdef CONFIG_PGSTE
|
||||
if (mm_has_pgste(mm))
|
||||
return page_table_free_pgste(table);
|
||||
#endif
|
||||
/* Free 1K/2K page table fragment of a 4K page */
|
||||
page = pfn_to_page(__pa(table) >> PAGE_SHIFT);
|
||||
bit = 1 << ((__pa(table) & ~PAGE_MASK)/(PTRS_PER_PTE*sizeof(pte_t)));
|
||||
spin_lock_bh(&mm->context.list_lock);
|
||||
page->flags ^= bits;
|
||||
if (page->flags & FRAG_MASK) {
|
||||
/* Page now has some free pgtable fragments. */
|
||||
if (!list_empty(&page->lru))
|
||||
list_move(&page->lru, &mm->context.pgtable_list);
|
||||
page = NULL;
|
||||
} else
|
||||
/* All fragments of the 4K page have been freed. */
|
||||
if ((atomic_read(&page->_mapcount) & FRAG_MASK) != FRAG_MASK)
|
||||
list_del(&page->lru);
|
||||
mask = atomic_xor_bits(&page->_mapcount, bit);
|
||||
if (mask & FRAG_MASK)
|
||||
list_add(&page->lru, &mm->context.pgtable_list);
|
||||
spin_unlock_bh(&mm->context.list_lock);
|
||||
if (page) {
|
||||
if (mask == 0) {
|
||||
pgtable_page_dtor(page);
|
||||
atomic_set(&page->_mapcount, -1);
|
||||
__free_page(page);
|
||||
}
|
||||
}
|
||||
|
||||
void page_table_free_rcu(struct mm_struct *mm, unsigned long *table)
|
||||
{
|
||||
struct rcu_table_freelist *batch;
|
||||
struct page *page;
|
||||
unsigned long bits;
|
||||
#ifdef CONFIG_HAVE_RCU_TABLE_FREE
|
||||
|
||||
preempt_disable();
|
||||
if (atomic_read(&mm->mm_users) < 2 &&
|
||||
cpumask_equal(mm_cpumask(mm), cpumask_of(smp_processor_id()))) {
|
||||
page_table_free(mm, table);
|
||||
goto out;
|
||||
static void __page_table_free_rcu(void *table, unsigned bit)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
#ifdef CONFIG_PGSTE
|
||||
if (bit == FRAG_MASK)
|
||||
return page_table_free_pgste(table);
|
||||
#endif
|
||||
/* Free 1K/2K page table fragment of a 4K page */
|
||||
page = pfn_to_page(__pa(table) >> PAGE_SHIFT);
|
||||
if (atomic_xor_bits(&page->_mapcount, bit) == 0) {
|
||||
pgtable_page_dtor(page);
|
||||
atomic_set(&page->_mapcount, -1);
|
||||
__free_page(page);
|
||||
}
|
||||
batch = rcu_table_freelist_get(mm);
|
||||
if (!batch) {
|
||||
smp_call_function(smp_sync, NULL, 1);
|
||||
page_table_free(mm, table);
|
||||
goto out;
|
||||
}
|
||||
|
||||
void page_table_free_rcu(struct mmu_gather *tlb, unsigned long *table)
|
||||
{
|
||||
struct mm_struct *mm;
|
||||
struct page *page;
|
||||
unsigned int bit, mask;
|
||||
|
||||
mm = tlb->mm;
|
||||
#ifdef CONFIG_PGSTE
|
||||
if (mm_has_pgste(mm)) {
|
||||
table = (unsigned long *) (__pa(table) | FRAG_MASK);
|
||||
tlb_remove_table(tlb, table);
|
||||
return;
|
||||
}
|
||||
bits = (mm->context.has_pgste) ? 3UL : 1UL;
|
||||
bits <<= (__pa(table) & (PAGE_SIZE - 1)) / 256 / sizeof(unsigned long);
|
||||
#endif
|
||||
bit = 1 << ((__pa(table) & ~PAGE_MASK) / (PTRS_PER_PTE*sizeof(pte_t)));
|
||||
page = pfn_to_page(__pa(table) >> PAGE_SHIFT);
|
||||
spin_lock_bh(&mm->context.list_lock);
|
||||
/* Delayed freeing with rcu prevents reuse of pgtable fragments */
|
||||
list_del_init(&page->lru);
|
||||
if ((atomic_read(&page->_mapcount) & FRAG_MASK) != FRAG_MASK)
|
||||
list_del(&page->lru);
|
||||
mask = atomic_xor_bits(&page->_mapcount, bit | (bit << 4));
|
||||
if (mask & FRAG_MASK)
|
||||
list_add_tail(&page->lru, &mm->context.pgtable_list);
|
||||
spin_unlock_bh(&mm->context.list_lock);
|
||||
table = (unsigned long *)(((unsigned long) table) | bits);
|
||||
batch->table[batch->pgt_index++] = table;
|
||||
if (batch->pgt_index >= batch->crst_index)
|
||||
rcu_table_freelist_finish();
|
||||
out:
|
||||
preempt_enable();
|
||||
table = (unsigned long *) (__pa(table) | (bit << 4));
|
||||
tlb_remove_table(tlb, table);
|
||||
}
|
||||
|
||||
void __tlb_remove_table(void *_table)
|
||||
{
|
||||
void *table = (void *)((unsigned long) _table & PAGE_MASK);
|
||||
unsigned type = (unsigned long) _table & ~PAGE_MASK;
|
||||
|
||||
if (type)
|
||||
__page_table_free_rcu(table, type);
|
||||
else
|
||||
free_pages((unsigned long) table, ALLOC_ORDER);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* switch on pgstes for its userspace process (for kvm)
|
||||
*/
|
||||
@ -369,7 +315,7 @@ int s390_enable_sie(void)
|
||||
return -EINVAL;
|
||||
|
||||
/* Do we have pgstes? if yes, we are done */
|
||||
if (tsk->mm->context.has_pgste)
|
||||
if (mm_has_pgste(tsk->mm))
|
||||
return 0;
|
||||
|
||||
/* lets check if we are allowed to replace the mm */
|
||||
|
Loading…
Reference in New Issue
Block a user