aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2011-05-23 10:24:39 +0200
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2011-05-23 10:24:31 +0200
commit2d42552d1c1659b014851cf449ad2fe458509128 (patch)
treeb9ef22867ce52e23b5249a7ad38637eec40363b8 /arch/s390
parentc26001d4e9133fe45e47eee18cfd826219e71fb9 (diff)
downloadkernel_samsung_crespo-2d42552d1c1659b014851cf449ad2fe458509128.zip
kernel_samsung_crespo-2d42552d1c1659b014851cf449ad2fe458509128.tar.gz
kernel_samsung_crespo-2d42552d1c1659b014851cf449ad2fe458509128.tar.bz2
[S390] merge page_test_dirty and page_clear_dirty
The page_clear_dirty primitive always sets the default storage key which resets the access control bits and the fetch protection bit. That will surprise a KVM guest that sets non-zero access control bits or the fetch protection bit. Merge page_test_dirty and page_clear_dirty back to a single function and only clear the dirty bit from the storage key. In addition move the function page_test_and_clear_dirty and page_test_and_clear_young to page.h where they belong. This requires to change the parameter from a struct page * to a page frame number. Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390')
-rw-r--r--arch/s390/include/asm/page.h56
-rw-r--r--arch/s390/include/asm/pgtable.h58
2 files changed, 59 insertions, 55 deletions
diff --git a/arch/s390/include/asm/page.h b/arch/s390/include/asm/page.h
index 3c987e9..81ee277 100644
--- a/arch/s390/include/asm/page.h
+++ b/arch/s390/include/asm/page.h
@@ -107,8 +107,8 @@ typedef pte_t *pgtable_t;
#define __pgd(x) ((pgd_t) { (x) } )
#define __pgprot(x) ((pgprot_t) { (x) } )
-static inline void
-page_set_storage_key(unsigned long addr, unsigned int skey, int mapped)
+static inline void page_set_storage_key(unsigned long addr,
+ unsigned char skey, int mapped)
{
if (!mapped)
asm volatile(".insn rrf,0xb22b0000,%0,%1,8,0"
@@ -117,15 +117,59 @@ page_set_storage_key(unsigned long addr, unsigned int skey, int mapped)
asm volatile("sske %0,%1" : : "d" (skey), "a" (addr));
}
-static inline unsigned int
-page_get_storage_key(unsigned long addr)
+static inline unsigned char page_get_storage_key(unsigned long addr)
{
- unsigned int skey;
+ unsigned char skey;
- asm volatile("iske %0,%1" : "=d" (skey) : "a" (addr), "0" (0));
+ asm volatile("iske %0,%1" : "=d" (skey) : "a" (addr));
return skey;
}
+static inline int page_reset_referenced(unsigned long addr)
+{
+ unsigned int ipm;
+
+ asm volatile(
+ " rrbe 0,%1\n"
+ " ipm %0\n"
+ : "=d" (ipm) : "a" (addr) : "cc");
+ return !!(ipm & 0x20000000);
+}
+
+/* Bits int the storage key */
+#define _PAGE_CHANGED 0x02 /* HW changed bit */
+#define _PAGE_REFERENCED 0x04 /* HW referenced bit */
+#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.
+ */
+#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.
+ */
+#define __HAVE_ARCH_PAGE_TEST_AND_CLEAR_YOUNG
+static inline int page_test_and_clear_young(unsigned long pfn)
+{
+ return page_reset_referenced(pfn << PAGE_SHIFT);
+}
+
struct page;
void arch_free_page(struct page *page, int order);
void arch_alloc_page(struct page *page, int order);
diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h
index 763620e..4ca4dd2 100644
--- a/arch/s390/include/asm/pgtable.h
+++ b/arch/s390/include/asm/pgtable.h
@@ -373,10 +373,6 @@ extern unsigned long VMALLOC_START;
#define _ASCE_USER_BITS (_ASCE_SPACE_SWITCH | _ASCE_PRIVATE_SPACE | \
_ASCE_ALT_EVENT)
-/* Bits int the storage key */
-#define _PAGE_CHANGED 0x02 /* HW changed bit */
-#define _PAGE_REFERENCED 0x04 /* HW referenced bit */
-
/*
* Page protection definitions.
*/
@@ -555,8 +551,6 @@ static inline void rcp_unlock(pte_t *ptep)
#endif
}
-/* forward declaration for SetPageUptodate in page-flags.h*/
-static inline void page_clear_dirty(struct page *page, int mapped);
#include <linux/page-flags.h>
static inline void ptep_rcp_copy(pte_t *ptep)
@@ -566,7 +560,7 @@ static inline void ptep_rcp_copy(pte_t *ptep)
unsigned int skey;
unsigned long *pgste = (unsigned long *) (ptep + PTRS_PER_PTE);
- skey = page_get_storage_key(page_to_phys(page));
+ skey = page_get_storage_key(pte_val(*ptep) >> PAGE_SHIFT);
if (skey & _PAGE_CHANGED) {
set_bit_simple(RCP_GC_BIT, pgste);
set_bit_simple(KVM_UD_BIT, pgste);
@@ -760,6 +754,7 @@ static inline int kvm_s390_test_and_clear_page_dirty(struct mm_struct *mm,
{
int dirty;
unsigned long *pgste;
+ unsigned long pfn;
struct page *page;
unsigned int skey;
@@ -767,8 +762,9 @@ static inline int kvm_s390_test_and_clear_page_dirty(struct mm_struct *mm,
return -EINVAL;
rcp_lock(ptep);
pgste = (unsigned long *) (ptep + PTRS_PER_PTE);
- page = virt_to_page(pte_val(*ptep));
- skey = page_get_storage_key(page_to_phys(page));
+ pfn = pte_val(*ptep) >> PAGE_SHIFT;
+ page = pfn_to_page(pfn);
+ skey = page_get_storage_key(pfn);
if (skey & _PAGE_CHANGED) {
set_bit_simple(RCP_GC_BIT, pgste);
set_bit_simple(KVM_UD_BIT, pgste);
@@ -779,7 +775,7 @@ static inline int kvm_s390_test_and_clear_page_dirty(struct mm_struct *mm,
}
dirty = test_and_clear_bit_simple(KVM_UD_BIT, pgste);
if (skey & _PAGE_CHANGED)
- page_clear_dirty(page, 1);
+ page_set_storage_key(pfn, skey & ~_PAGE_CHANGED, 1);
rcp_unlock(ptep);
return dirty;
}
@@ -790,16 +786,16 @@ static inline int ptep_test_and_clear_young(struct vm_area_struct *vma,
unsigned long addr, pte_t *ptep)
{
#ifdef CONFIG_PGSTE
- unsigned long physpage;
+ unsigned long pfn;
int young;
unsigned long *pgste;
if (!vma->vm_mm->context.has_pgste)
return 0;
- physpage = pte_val(*ptep) & PAGE_MASK;
+ pfn = pte_val(*ptep) >> PAGE_SHIFT;
pgste = (unsigned long *) (ptep + PTRS_PER_PTE);
- young = ((page_get_storage_key(physpage) & _PAGE_REFERENCED) != 0);
+ young = ((page_get_storage_key(pfn) & _PAGE_REFERENCED) != 0);
rcp_lock(ptep);
if (young)
set_bit_simple(RCP_GR_BIT, pgste);
@@ -937,42 +933,6 @@ static inline pte_t ptep_get_and_clear_full(struct mm_struct *mm,
})
/*
- * 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.
- */
-#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, int mapped)
-{
- page_set_storage_key(page_to_phys(page), PAGE_DEFAULT_KEY, mapped);
-}
-
-/*
- * 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);
- int ccode;
-
- asm volatile(
- " rrbe 0,%1\n"
- " ipm %0\n"
- " srl %0,28\n"
- : "=d" (ccode) : "a" (physpage) : "cc" );
- return ccode & 2;
-}
-
-/*
* Conversion functions: convert a page and protection to a page entry,
* and a page entry and page directory to the page they refer to.
*/