Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 356137
b: refs/heads/master
c: abf09be
h: refs/heads/master
i:
  356135: 43daed8
v: v3
  • Loading branch information
Martin Schwidefsky committed Feb 14, 2013
1 parent e897442 commit 83bbf41
Show file tree
Hide file tree
Showing 13 changed files with 113 additions and 141 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: 486c0a0bc80d370471b21662bf03f04fbb37cdc6
refs/heads/master: abf09bed3cceadd809f0356065c2ada6cee90d4a
22 changes: 0 additions & 22 deletions trunk/arch/s390/include/asm/page.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,28 +154,6 @@ static inline int page_reset_referenced(unsigned long addr)
#define _PAGE_FP_BIT 0x08 /* HW fetch protection bit */
#define _PAGE_ACC_BITS 0xf0 /* HW access control bits */

/*
* Test and clear dirty bit in storage key.
* We can't clear the changed bit atomically. This is a potential
* race against modification of the referenced bit. This function
* should therefore only be called if it is not mapped in any
* address space.
*
* Note that the bit gets set whenever page content is changed. That means
* also when the page is modified by DMA or from inside the kernel.
*/
#define __HAVE_ARCH_PAGE_TEST_AND_CLEAR_DIRTY
static inline int page_test_and_clear_dirty(unsigned long pfn, int mapped)
{
unsigned char skey;

skey = page_get_storage_key(pfn << PAGE_SHIFT);
if (!(skey & _PAGE_CHANGED))
return 0;
page_set_storage_key(pfn << PAGE_SHIFT, skey & ~_PAGE_CHANGED, mapped);
return 1;
}

/*
* Test and clear referenced bit in storage key.
*/
Expand Down
131 changes: 88 additions & 43 deletions trunk/arch/s390/include/asm/pgtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#ifndef __ASSEMBLY__
#include <linux/sched.h>
#include <linux/mm_types.h>
#include <linux/page-flags.h>
#include <asm/bug.h>
#include <asm/page.h>

Expand Down Expand Up @@ -221,13 +222,15 @@ extern unsigned long MODULES_END;
/* Software bits in the page table entry */
#define _PAGE_SWT 0x001 /* SW pte type bit t */
#define _PAGE_SWX 0x002 /* SW pte type bit x */
#define _PAGE_SWC 0x004 /* SW pte changed bit (for KVM) */
#define _PAGE_SWR 0x008 /* SW pte referenced bit (for KVM) */
#define _PAGE_SPECIAL 0x010 /* SW associated with special page */
#define _PAGE_SWC 0x004 /* SW pte changed bit */
#define _PAGE_SWR 0x008 /* SW pte referenced bit */
#define _PAGE_SWW 0x010 /* SW pte write bit */
#define _PAGE_SPECIAL 0x020 /* SW associated with special page */
#define __HAVE_ARCH_PTE_SPECIAL

/* Set of bits not changed in pte_modify */
#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_SPECIAL | _PAGE_SWC | _PAGE_SWR)
#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_SPECIAL | _PAGE_CO | \
_PAGE_SWC | _PAGE_SWR)

/* Six different types of pages. */
#define _PAGE_TYPE_EMPTY 0x400
Expand Down Expand Up @@ -321,6 +324,7 @@ extern unsigned long MODULES_END;

/* Bits in the region table entry */
#define _REGION_ENTRY_ORIGIN ~0xfffUL/* region/segment table origin */
#define _REGION_ENTRY_RO 0x200 /* region protection bit */
#define _REGION_ENTRY_INV 0x20 /* invalid region table entry */
#define _REGION_ENTRY_TYPE_MASK 0x0c /* region/segment table type mask */
#define _REGION_ENTRY_TYPE_R1 0x0c /* region first table type */
Expand Down Expand Up @@ -382,9 +386,10 @@ extern unsigned long MODULES_END;
*/
#define PAGE_NONE __pgprot(_PAGE_TYPE_NONE)
#define PAGE_RO __pgprot(_PAGE_TYPE_RO)
#define PAGE_RW __pgprot(_PAGE_TYPE_RW)
#define PAGE_RW __pgprot(_PAGE_TYPE_RO | _PAGE_SWW)
#define PAGE_RWC __pgprot(_PAGE_TYPE_RW | _PAGE_SWW | _PAGE_SWC)

#define PAGE_KERNEL PAGE_RW
#define PAGE_KERNEL PAGE_RWC
#define PAGE_SHARED PAGE_KERNEL
#define PAGE_COPY PAGE_RO

Expand Down Expand Up @@ -632,23 +637,23 @@ static inline pgste_t pgste_update_all(pte_t *ptep, pgste_t pgste)
bits = skey & (_PAGE_CHANGED | _PAGE_REFERENCED);
/* Clear page changed & referenced bit in the storage key */
if (bits & _PAGE_CHANGED)
page_set_storage_key(address, skey ^ bits, 1);
page_set_storage_key(address, skey ^ bits, 0);
else if (bits)
page_reset_referenced(address);
/* Transfer page changed & referenced bit to guest bits in pgste */
pgste_val(pgste) |= bits << 48; /* RCP_GR_BIT & RCP_GC_BIT */
/* Get host changed & referenced bits from pgste */
bits |= (pgste_val(pgste) & (RCP_HR_BIT | RCP_HC_BIT)) >> 52;
/* Clear host bits in pgste. */
/* Transfer page changed & referenced bit to kvm user bits */
pgste_val(pgste) |= bits << 45; /* KVM_UR_BIT & KVM_UC_BIT */
/* Clear relevant host bits in pgste. */
pgste_val(pgste) &= ~(RCP_HR_BIT | RCP_HC_BIT);
pgste_val(pgste) &= ~(RCP_ACC_BITS | RCP_FP_BIT);
/* Copy page access key and fetch protection bit to pgste */
pgste_val(pgste) |=
(unsigned long) (skey & (_PAGE_ACC_BITS | _PAGE_FP_BIT)) << 56;
/* Transfer changed and referenced to kvm user bits */
pgste_val(pgste) |= bits << 45; /* KVM_UR_BIT & KVM_UC_BIT */
/* Transfer changed & referenced to pte sofware bits */
pte_val(*ptep) |= bits << 1; /* _PAGE_SWR & _PAGE_SWC */
/* Transfer referenced bit to pte */
pte_val(*ptep) |= (bits & _PAGE_REFERENCED) << 1;
#endif
return pgste;

Expand All @@ -661,20 +666,25 @@ static inline pgste_t pgste_update_young(pte_t *ptep, pgste_t pgste)

if (!pte_present(*ptep))
return pgste;
/* Get referenced bit from storage key */
young = page_reset_referenced(pte_val(*ptep) & PAGE_MASK);
/* Transfer page referenced bit to pte software bit (host view) */
if (young || (pgste_val(pgste) & RCP_HR_BIT))
if (young)
pgste_val(pgste) |= RCP_GR_BIT;
/* Get host referenced bit from pgste */
if (pgste_val(pgste) & RCP_HR_BIT) {
pgste_val(pgste) &= ~RCP_HR_BIT;
young = 1;
}
/* Transfer referenced bit to kvm user bits and pte */
if (young) {
pgste_val(pgste) |= KVM_UR_BIT;
pte_val(*ptep) |= _PAGE_SWR;
/* Clear host referenced bit in pgste. */
pgste_val(pgste) &= ~RCP_HR_BIT;
/* Transfer page referenced bit to guest bit in pgste */
pgste_val(pgste) |= (unsigned long) young << 50; /* set RCP_GR_BIT */
}
#endif
return pgste;

}

static inline void pgste_set_pte(pte_t *ptep, pgste_t pgste, pte_t entry)
static inline void pgste_set_key(pte_t *ptep, pgste_t pgste, pte_t entry)
{
#ifdef CONFIG_PGSTE
unsigned long address;
Expand All @@ -688,10 +698,23 @@ static inline void pgste_set_pte(pte_t *ptep, pgste_t pgste, pte_t entry)
/* Set page access key and fetch protection bit from pgste */
nkey |= (pgste_val(pgste) & (RCP_ACC_BITS | RCP_FP_BIT)) >> 56;
if (okey != nkey)
page_set_storage_key(address, nkey, 1);
page_set_storage_key(address, nkey, 0);
#endif
}

static inline void pgste_set_pte(pte_t *ptep, pte_t entry)
{
if (!MACHINE_HAS_ESOP && (pte_val(entry) & _PAGE_SWW)) {
/*
* Without enhanced suppression-on-protection force
* the dirty bit on for all writable ptes.
*/
pte_val(entry) |= _PAGE_SWC;
pte_val(entry) &= ~_PAGE_RO;
}
*ptep = entry;
}

/**
* struct gmap_struct - guest address space
* @mm: pointer to the parent mm_struct
Expand Down Expand Up @@ -750,11 +773,14 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,

if (mm_has_pgste(mm)) {
pgste = pgste_get_lock(ptep);
pgste_set_pte(ptep, pgste, entry);
*ptep = entry;
pgste_set_key(ptep, pgste, entry);
pgste_set_pte(ptep, entry);
pgste_set_unlock(ptep, pgste);
} else
} else {
if (!(pte_val(entry) & _PAGE_INVALID) && MACHINE_HAS_EDAT1)
pte_val(entry) |= _PAGE_CO;
*ptep = entry;
}
}

/*
Expand All @@ -763,16 +789,12 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
*/
static inline int pte_write(pte_t pte)
{
return (pte_val(pte) & _PAGE_RO) == 0;
return (pte_val(pte) & _PAGE_SWW) != 0;
}

static inline int pte_dirty(pte_t pte)
{
#ifdef CONFIG_PGSTE
if (pte_val(pte) & _PAGE_SWC)
return 1;
#endif
return 0;
return (pte_val(pte) & _PAGE_SWC) != 0;
}

static inline int pte_young(pte_t pte)
Expand Down Expand Up @@ -822,11 +844,14 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
{
pte_val(pte) &= _PAGE_CHG_MASK;
pte_val(pte) |= pgprot_val(newprot);
if ((pte_val(pte) & _PAGE_SWC) && (pte_val(pte) & _PAGE_SWW))
pte_val(pte) &= ~_PAGE_RO;
return pte;
}

static inline pte_t pte_wrprotect(pte_t pte)
{
pte_val(pte) &= ~_PAGE_SWW;
/* Do not clobber _PAGE_TYPE_NONE pages! */
if (!(pte_val(pte) & _PAGE_INVALID))
pte_val(pte) |= _PAGE_RO;
Expand All @@ -835,20 +860,26 @@ static inline pte_t pte_wrprotect(pte_t pte)

static inline pte_t pte_mkwrite(pte_t pte)
{
pte_val(pte) &= ~_PAGE_RO;
pte_val(pte) |= _PAGE_SWW;
if (pte_val(pte) & _PAGE_SWC)
pte_val(pte) &= ~_PAGE_RO;
return pte;
}

static inline pte_t pte_mkclean(pte_t pte)
{
#ifdef CONFIG_PGSTE
pte_val(pte) &= ~_PAGE_SWC;
#endif
/* Do not clobber _PAGE_TYPE_NONE pages! */
if (!(pte_val(pte) & _PAGE_INVALID))
pte_val(pte) |= _PAGE_RO;
return pte;
}

static inline pte_t pte_mkdirty(pte_t pte)
{
pte_val(pte) |= _PAGE_SWC;
if (pte_val(pte) & _PAGE_SWW)
pte_val(pte) &= ~_PAGE_RO;
return pte;
}

Expand Down Expand Up @@ -886,10 +917,10 @@ static inline pte_t pte_mkhuge(pte_t pte)
pte_val(pte) |= _SEGMENT_ENTRY_INV;
}
/*
* Clear SW pte bits SWT and SWX, there are no SW bits in a segment
* table entry.
* Clear SW pte bits, there are no SW bits in a segment table entry.
*/
pte_val(pte) &= ~(_PAGE_SWT | _PAGE_SWX);
pte_val(pte) &= ~(_PAGE_SWT | _PAGE_SWX | _PAGE_SWC |
_PAGE_SWR | _PAGE_SWW);
/*
* Also set the change-override bit because we don't need dirty bit
* tracking for hugetlbfs pages.
Expand Down Expand Up @@ -1041,9 +1072,11 @@ static inline void ptep_modify_prot_commit(struct mm_struct *mm,
unsigned long address,
pte_t *ptep, pte_t pte)
{
*ptep = pte;
if (mm_has_pgste(mm))
if (mm_has_pgste(mm)) {
pgste_set_pte(ptep, pte);
pgste_set_unlock(ptep, *(pgste_t *)(ptep + PTRS_PER_PTE));
} else
*ptep = pte;
}

#define __HAVE_ARCH_PTEP_CLEAR_FLUSH
Expand Down Expand Up @@ -1111,10 +1144,13 @@ static inline pte_t ptep_set_wrprotect(struct mm_struct *mm,

if (!mm_exclusive(mm))
__ptep_ipte(address, ptep);
*ptep = pte_wrprotect(pte);
pte = pte_wrprotect(pte);

if (mm_has_pgste(mm))
if (mm_has_pgste(mm)) {
pgste_set_pte(ptep, pte);
pgste_set_unlock(ptep, pgste);
} else
*ptep = pte;
}
return pte;
}
Expand All @@ -1132,10 +1168,12 @@ static inline int ptep_set_access_flags(struct vm_area_struct *vma,
pgste = pgste_get_lock(ptep);

__ptep_ipte(address, ptep);
*ptep = entry;

if (mm_has_pgste(vma->vm_mm))
if (mm_has_pgste(vma->vm_mm)) {
pgste_set_pte(ptep, entry);
pgste_set_unlock(ptep, pgste);
} else
*ptep = entry;
return 1;
}

Expand All @@ -1153,8 +1191,13 @@ static inline pte_t mk_pte_phys(unsigned long physpage, pgprot_t pgprot)
static inline pte_t mk_pte(struct page *page, pgprot_t pgprot)
{
unsigned long physpage = page_to_phys(page);
pte_t __pte = mk_pte_phys(physpage, pgprot);

return mk_pte_phys(physpage, pgprot);
if ((pte_val(__pte) & _PAGE_SWW) && PageDirty(page)) {
pte_val(__pte) |= _PAGE_SWC;
pte_val(__pte) &= ~_PAGE_RO;
}
return __pte;
}

#define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD-1))
Expand Down Expand Up @@ -1246,6 +1289,8 @@ static inline int pmd_trans_splitting(pmd_t pmd)
static inline void set_pmd_at(struct mm_struct *mm, unsigned long addr,
pmd_t *pmdp, pmd_t entry)
{
if (!(pmd_val(entry) & _SEGMENT_ENTRY_INV) && MACHINE_HAS_EDAT1)
pmd_val(entry) |= _SEGMENT_ENTRY_CO;
*pmdp = entry;
}

Expand Down
1 change: 0 additions & 1 deletion trunk/arch/s390/include/asm/sclp.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ int sclp_cpu_deconfigure(u8 cpu);
void sclp_facilities_detect(void);
unsigned long long sclp_get_rnmax(void);
unsigned long long sclp_get_rzm(void);
u8 sclp_get_fac85(void);
int sclp_sdias_blk_count(void);
int sclp_sdias_copy(void *dest, int blk_num, int nr_blks);
int sclp_chp_configure(struct chp_id chpid);
Expand Down
16 changes: 9 additions & 7 deletions trunk/arch/s390/include/asm/setup.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,14 @@ extern unsigned int s390_user_mode;

#define MACHINE_FLAG_VM (1UL << 0)
#define MACHINE_FLAG_IEEE (1UL << 1)
#define MACHINE_FLAG_CSP (1UL << 3)
#define MACHINE_FLAG_MVPG (1UL << 4)
#define MACHINE_FLAG_DIAG44 (1UL << 5)
#define MACHINE_FLAG_IDTE (1UL << 6)
#define MACHINE_FLAG_DIAG9C (1UL << 7)
#define MACHINE_FLAG_MVCOS (1UL << 8)
#define MACHINE_FLAG_KVM (1UL << 9)
#define MACHINE_FLAG_CSP (1UL << 2)
#define MACHINE_FLAG_MVPG (1UL << 3)
#define MACHINE_FLAG_DIAG44 (1UL << 4)
#define MACHINE_FLAG_IDTE (1UL << 5)
#define MACHINE_FLAG_DIAG9C (1UL << 6)
#define MACHINE_FLAG_MVCOS (1UL << 7)
#define MACHINE_FLAG_KVM (1UL << 8)
#define MACHINE_FLAG_ESOP (1UL << 9)
#define MACHINE_FLAG_EDAT1 (1UL << 10)
#define MACHINE_FLAG_EDAT2 (1UL << 11)
#define MACHINE_FLAG_LPAR (1UL << 12)
Expand All @@ -84,6 +85,7 @@ extern unsigned int s390_user_mode;
#define MACHINE_IS_LPAR (S390_lowcore.machine_flags & MACHINE_FLAG_LPAR)

#define MACHINE_HAS_DIAG9C (S390_lowcore.machine_flags & MACHINE_FLAG_DIAG9C)
#define MACHINE_HAS_ESOP (S390_lowcore.machine_flags & MACHINE_FLAG_ESOP)
#define MACHINE_HAS_PFMF MACHINE_HAS_EDAT1
#define MACHINE_HAS_HPAGE MACHINE_HAS_EDAT1

Expand Down
2 changes: 1 addition & 1 deletion trunk/arch/s390/kvm/kvm-s390.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ int kvm_dev_ioctl_check_extension(long ext)
r = KVM_MAX_VCPUS;
break;
case KVM_CAP_S390_COW:
r = sclp_get_fac85() & 0x2;
r = MACHINE_HAS_ESOP;
break;
default:
r = 0;
Expand Down
2 changes: 1 addition & 1 deletion trunk/arch/s390/lib/uaccess_pt.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ static __always_inline unsigned long follow_table(struct mm_struct *mm,
ptep = pte_offset_map(pmd, addr);
if (!pte_present(*ptep))
return -0x11UL;
if (write && !pte_write(*ptep))
if (write && (!pte_write(*ptep) || !pte_dirty(*ptep)))
return -0x04UL;

return (pte_val(*ptep) & PAGE_MASK) + (addr & ~PAGE_MASK);
Expand Down
Loading

0 comments on commit 83bbf41

Please sign in to comment.