Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 72118
b: refs/heads/master
c: ba8a922
h: refs/heads/master
v: v3
  • Loading branch information
Martin Schwidefsky committed Oct 22, 2007
1 parent 17125a3 commit 878e590
Show file tree
Hide file tree
Showing 6 changed files with 246 additions and 151 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: e3d3683d1402c1737687cb698451d545f57c32a7
refs/heads/master: ba8a9229ab9e80278c28ad68b15053f65b2b0a7c
2 changes: 1 addition & 1 deletion trunk/arch/s390/kernel/smp.c
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ static void smp_ext_bitcall(int cpu, ec_bit_sig sig)
*/
void smp_ptlb_callback(void *info)
{
local_flush_tlb();
__tlb_flush_local();
}

void smp_ptlb_all(void)
Expand Down
17 changes: 0 additions & 17 deletions trunk/include/asm-s390/pgalloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ static inline void pgd_free(pgd_t *pgd)
*/
#define pmd_alloc_one(mm,address) ({ BUG(); ((pmd_t *)2); })
#define pmd_free(x) do { } while (0)
#define __pmd_free_tlb(tlb,x) do { } while (0)
#define pgd_populate(mm, pmd, pte) BUG()
#define pgd_populate_kernel(mm, pmd, pte) BUG()
#else /* __s390x__ */
Expand Down Expand Up @@ -118,12 +117,6 @@ static inline void pmd_free (pmd_t *pmd)
free_pages((unsigned long) pmd, PMD_ALLOC_ORDER);
}

#define __pmd_free_tlb(tlb,pmd) \
do { \
tlb_flush_mmu(tlb, 0, 0); \
pmd_free(pmd); \
} while (0)

static inline void
pgd_populate_kernel(struct mm_struct *mm, pgd_t *pgd, pmd_t *pmd)
{
Expand Down Expand Up @@ -224,14 +217,4 @@ static inline void pte_free(struct page *pte)
__free_page(pte);
}

#define __pte_free_tlb(tlb, pte) \
({ \
struct mmu_gather *__tlb = (tlb); \
struct page *__pte = (pte); \
struct page *shadow_page = get_shadow_page(__pte); \
if (shadow_page) \
tlb_remove_page(__tlb, shadow_page); \
tlb_remove_page(__tlb, __pte); \
})

#endif /* _S390_PGALLOC_H */
99 changes: 71 additions & 28 deletions trunk/include/asm-s390/pgtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,8 @@ static inline pgd_t *get_shadow_pgd(pgd_t *pgdp)
* within a page table are directly modified. Thus, the following
* hook is made available.
*/
static inline void set_pte(pte_t *pteptr, pte_t pteval)
static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
pte_t *pteptr, pte_t pteval)
{
pte_t *shadow_pte = get_shadow_pte(pteptr);

Expand All @@ -437,7 +438,6 @@ static inline void set_pte(pte_t *pteptr, pte_t pteval)
pte_val(*shadow_pte) = _PAGE_TYPE_EMPTY;
}
}
#define set_pte_at(mm,addr,ptep,pteval) set_pte(ptep,pteval)

/*
* pgd/pmd/pte query functions
Expand Down Expand Up @@ -508,7 +508,8 @@ static inline int pte_file(pte_t pte)
return (pte_val(pte) & mask) == _PAGE_TYPE_FILE;
}

#define pte_same(a,b) (pte_val(a) == pte_val(b))
#define __HAVE_ARCH_PTE_SAME
#define pte_same(a,b) (pte_val(a) == pte_val(b))

/*
* query functions pte_write/pte_dirty/pte_young only work if
Expand Down Expand Up @@ -663,24 +664,19 @@ static inline pte_t pte_mkyoung(pte_t pte)
return pte;
}

static inline int ptep_test_and_clear_young(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep)
#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG
static inline int ptep_test_and_clear_young(struct vm_area_struct *vma,
unsigned long addr, pte_t *ptep)
{
return 0;
}

static inline int
ptep_clear_flush_young(struct vm_area_struct *vma,
unsigned long address, pte_t *ptep)
#define __HAVE_ARCH_PTEP_CLEAR_YOUNG_FLUSH
static inline int ptep_clear_flush_young(struct vm_area_struct *vma,
unsigned long address, pte_t *ptep)
{
/* No need to flush TLB; bits are in storage key */
return ptep_test_and_clear_young(vma, address, ptep);
}

static inline pte_t ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
{
pte_t pte = *ptep;
pte_clear(mm, addr, ptep);
return pte;
return 0;
}

static inline void __ptep_ipte(unsigned long address, pte_t *ptep)
Expand Down Expand Up @@ -709,6 +705,32 @@ static inline void ptep_invalidate(unsigned long address, pte_t *ptep)
__ptep_ipte(address, ptep);
}

/*
* This is hard to understand. ptep_get_and_clear and ptep_clear_flush
* both clear the TLB for the unmapped pte. The reason is that
* ptep_get_and_clear is used in common code (e.g. change_pte_range)
* to modify an active pte. The sequence is
* 1) ptep_get_and_clear
* 2) set_pte_at
* 3) flush_tlb_range
* On s390 the tlb needs to get flushed with the modification of the pte
* if the pte is active. The only way how this can be implemented is to
* have ptep_get_and_clear do the tlb flush. In exchange flush_tlb_range
* is a nop.
*/
#define __HAVE_ARCH_PTEP_GET_AND_CLEAR
#define ptep_get_and_clear(__mm, __address, __ptep) \
({ \
pte_t __pte = *(__ptep); \
if (atomic_read(&(__mm)->mm_users) > 1 || \
(__mm) != current->active_mm) \
ptep_invalidate(__address, __ptep); \
else \
pte_clear((__mm), (__address), (__ptep)); \
__pte; \
})

#define __HAVE_ARCH_PTEP_CLEAR_FLUSH
static inline pte_t ptep_clear_flush(struct vm_area_struct *vma,
unsigned long address, pte_t *ptep)
{
Expand All @@ -717,12 +739,40 @@ static inline pte_t ptep_clear_flush(struct vm_area_struct *vma,
return pte;
}

static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
/*
* The batched pte unmap code uses ptep_get_and_clear_full to clear the
* ptes. Here an optimization is possible. tlb_gather_mmu flushes all
* tlbs of an mm if it can guarantee that the ptes of the mm_struct
* cannot be accessed while the batched unmap is running. In this case
* full==1 and a simple pte_clear is enough. See tlb.h.
*/
#define __HAVE_ARCH_PTEP_GET_AND_CLEAR_FULL
static inline pte_t ptep_get_and_clear_full(struct mm_struct *mm,
unsigned long addr,
pte_t *ptep, int full)
{
pte_t old_pte = *ptep;
set_pte_at(mm, addr, ptep, pte_wrprotect(old_pte));
pte_t pte = *ptep;

if (full)
pte_clear(mm, addr, ptep);
else
ptep_invalidate(addr, ptep);
return pte;
}

#define __HAVE_ARCH_PTEP_SET_WRPROTECT
#define ptep_set_wrprotect(__mm, __addr, __ptep) \
({ \
pte_t __pte = *(__ptep); \
if (pte_write(__pte)) { \
if (atomic_read(&(__mm)->mm_users) > 1 || \
(__mm) != current->active_mm) \
ptep_invalidate(__addr, __ptep); \
set_pte_at(__mm, __addr, __ptep, pte_wrprotect(__pte)); \
} \
})

#define __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS
#define ptep_set_access_flags(__vma, __addr, __ptep, __entry, __dirty) \
({ \
int __changed = !pte_same(*(__ptep), __entry); \
Expand All @@ -740,11 +790,13 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr,
* should therefore only be called if it is not mapped in any
* address space.
*/
#define __HAVE_ARCH_PAGE_TEST_DIRTY
static inline int page_test_dirty(struct page *page)
{
return (page_get_storage_key(page_to_phys(page)) & _PAGE_CHANGED) != 0;
}

#define __HAVE_ARCH_PAGE_CLEAR_DIRTY
static inline void page_clear_dirty(struct page *page)
{
page_set_storage_key(page_to_phys(page), PAGE_DEFAULT_KEY);
Expand All @@ -753,6 +805,7 @@ static inline void page_clear_dirty(struct page *page)
/*
* Test and clear referenced bit in storage key.
*/
#define __HAVE_ARCH_PAGE_TEST_AND_CLEAR_YOUNG
static inline int page_test_and_clear_young(struct page *page)
{
unsigned long physpage = page_to_phys(page);
Expand Down Expand Up @@ -930,16 +983,6 @@ extern int remove_shared_memory(unsigned long start, unsigned long size);
#define __HAVE_ARCH_MEMMAP_INIT
extern void memmap_init(unsigned long, int, unsigned long, unsigned long);

#define __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS
#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG
#define __HAVE_ARCH_PTEP_CLEAR_YOUNG_FLUSH
#define __HAVE_ARCH_PTEP_GET_AND_CLEAR
#define __HAVE_ARCH_PTEP_CLEAR_FLUSH
#define __HAVE_ARCH_PTEP_SET_WRPROTECT
#define __HAVE_ARCH_PTE_SAME
#define __HAVE_ARCH_PAGE_TEST_DIRTY
#define __HAVE_ARCH_PAGE_CLEAR_DIRTY
#define __HAVE_ARCH_PAGE_TEST_AND_CLEAR_YOUNG
#include <asm-generic/pgtable.h>

#endif /* _S390_PAGE_H */
Expand Down
127 changes: 118 additions & 9 deletions trunk/include/asm-s390/tlb.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,128 @@
#define _S390_TLB_H

/*
* s390 doesn't need any special per-pte or
* per-vma handling..
* TLB flushing on s390 is complicated. The following requirement
* from the principles of operation is the most arduous:
*
* "A valid table entry must not be changed while it is attached
* to any CPU and may be used for translation by that CPU except to
* (1) invalidate the entry by using INVALIDATE PAGE TABLE ENTRY,
* or INVALIDATE DAT TABLE ENTRY, (2) alter bits 56-63 of a page
* table entry, or (3) make a change by means of a COMPARE AND SWAP
* AND PURGE instruction that purges the TLB."
*
* The modification of a pte of an active mm struct therefore is
* a two step process: i) invalidate the pte, ii) store the new pte.
* This is true for the page protection bit as well.
* The only possible optimization is to flush at the beginning of
* a tlb_gather_mmu cycle if the mm_struct is currently not in use.
*
* Pages used for the page tables is a different story. FIXME: more
*/
#define tlb_start_vma(tlb, vma) do { } while (0)
#define tlb_end_vma(tlb, vma) do { } while (0)
#define __tlb_remove_tlb_entry(tlb, ptep, address) do { } while (0)

#include <linux/mm.h>
#include <linux/swap.h>
#include <asm/processor.h>
#include <asm/pgalloc.h>
#include <asm/smp.h>
#include <asm/tlbflush.h>

#ifndef CONFIG_SMP
#define TLB_NR_PTRS 1
#else
#define TLB_NR_PTRS 508
#endif

struct mmu_gather {
struct mm_struct *mm;
unsigned int fullmm;
unsigned int nr_ptes;
unsigned int nr_pmds;
void *array[TLB_NR_PTRS];
};

DECLARE_PER_CPU(struct mmu_gather, mmu_gathers);

static inline struct mmu_gather *tlb_gather_mmu(struct mm_struct *mm,
unsigned int full_mm_flush)
{
struct mmu_gather *tlb = &get_cpu_var(mmu_gathers);

tlb->mm = mm;
tlb->fullmm = full_mm_flush || (num_online_cpus() == 1) ||
(atomic_read(&mm->mm_users) <= 1 && mm == current->active_mm);
tlb->nr_ptes = 0;
tlb->nr_pmds = TLB_NR_PTRS;
if (tlb->fullmm)
__tlb_flush_mm(mm);
return tlb;
}

static inline void tlb_flush_mmu(struct mmu_gather *tlb,
unsigned long start, unsigned long end)
{
if (!tlb->fullmm && (tlb->nr_ptes > 0 || tlb->nr_pmds < TLB_NR_PTRS))
__tlb_flush_mm(tlb->mm);
while (tlb->nr_ptes > 0)
pte_free(tlb->array[--tlb->nr_ptes]);
while (tlb->nr_pmds < TLB_NR_PTRS)
pmd_free((pmd_t *) tlb->array[tlb->nr_pmds++]);
}

static inline void tlb_finish_mmu(struct mmu_gather *tlb,
unsigned long start, unsigned long end)
{
tlb_flush_mmu(tlb, start, end);

/* keep the page table cache within bounds */
check_pgt_cache();

put_cpu_var(mmu_gathers);
}

/*
* .. because we flush the whole mm when it
* fills up.
* Release the page cache reference for a pte removed by
* tlb_ptep_clear_flush. In both flush modes the tlb fo a page cache page
* has already been freed, so just do free_page_and_swap_cache.
*/
#define tlb_flush(tlb) flush_tlb_mm((tlb)->mm)
static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page)
{
free_page_and_swap_cache(page);
}

#include <asm-generic/tlb.h>
/*
* pte_free_tlb frees a pte table and clears the CRSTE for the
* page table from the tlb.
*/
static inline void pte_free_tlb(struct mmu_gather *tlb, struct page *page)
{
if (!tlb->fullmm) {
tlb->array[tlb->nr_ptes++] = page;
if (tlb->nr_ptes >= tlb->nr_pmds)
tlb_flush_mmu(tlb, 0, 0);
} else
pte_free(page);
}

/*
* pmd_free_tlb frees a pmd table and clears the CRSTE for the
* segment table entry from the tlb.
*/
static inline void pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd)
{
#ifdef __s390x__
if (!tlb->fullmm) {
tlb->array[--tlb->nr_pmds] = (struct page *) pmd;
if (tlb->nr_ptes >= tlb->nr_pmds)
tlb_flush_mmu(tlb, 0, 0);
} else
pmd_free(pmd);
#endif
}

#define tlb_start_vma(tlb, vma) do { } while (0)
#define tlb_end_vma(tlb, vma) do { } while (0)
#define tlb_remove_tlb_entry(tlb, ptep, addr) do { } while (0)
#define tlb_migrate_finish(mm) do { } while (0)

#endif /* _S390_TLB_H */
Loading

0 comments on commit 878e590

Please sign in to comment.