All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V2 0/3] PTE fixes for arm and arm64
@ 2014-02-25 11:38 Steve Capper
  2014-02-25 11:38 ` [PATCH V2 1/3] arm: mm: Double logical invert for pte accessors Steve Capper
                   ` (4 more replies)
  0 siblings, 5 replies; 15+ messages in thread
From: Steve Capper @ 2014-02-25 11:38 UTC (permalink / raw)
  To: linux-arm-kernel

This series fixes a couple of problems that I have come across:
 1) Some pte accessors for arm and arm64 can have their results
    cancelled out by a downcast. This is addressed by the first two
    patches.

 2) On arm with LPAE it is impossible to distinguish between clean
    writable ptes and read only ptes. This is addressed by the third
    patch.

As the third patch depends on the first patch, all three patches are
bundled together in the same series.

I have reviewed Will and Russell's patch:
 b6ccb9803e90 ("ARM: 7954/1: mm: remove remaining domain support
	from ARMv6")
And I believe that the L_PTE_WRITE logic should be compatible with this
patch. It applies cleanly and compiles; but I would be greatful if
someone could please give it a test.

Cheers,
-- 
Steve

Steve Capper (3):
  arm: mm: Double logical invert for pte accessors
  arm64: mm: Add double logical invert to pte accessors
  arm: mm: Switch back to L_PTE_WRITE

 arch/arm/include/asm/pgtable-2level.h |  2 +-
 arch/arm/include/asm/pgtable-3level.h |  1 +
 arch/arm/include/asm/pgtable.h        | 42 +++++++++++++++++------------------
 arch/arm/mm/dump.c                    |  8 +++----
 arch/arm/mm/mmu.c                     | 25 +++++++++++----------
 arch/arm/mm/proc-macros.S             | 16 ++++++-------
 arch/arm/mm/proc-v7-2level.S          |  6 ++---
 arch/arm/mm/proc-v7-3level.S          |  4 +++-
 arch/arm/mm/proc-xscale.S             |  4 ++--
 arch/arm64/include/asm/pgtable.h      | 10 ++++-----
 10 files changed, 61 insertions(+), 57 deletions(-)

-- 
1.8.1.4

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH V2 1/3] arm: mm: Double logical invert for pte accessors
  2014-02-25 11:38 [PATCH V2 0/3] PTE fixes for arm and arm64 Steve Capper
@ 2014-02-25 11:38 ` Steve Capper
  2014-02-28 15:45   ` Catalin Marinas
  2014-03-26 11:01   ` Catalin Marinas
  2014-02-25 11:38 ` [PATCH V2 2/3] arm64: mm: Add double logical invert to " Steve Capper
                   ` (3 subsequent siblings)
  4 siblings, 2 replies; 15+ messages in thread
From: Steve Capper @ 2014-02-25 11:38 UTC (permalink / raw)
  To: linux-arm-kernel

Long descriptors on ARM are 64 bits, and some pte functions such as
pte_dirty return a bitwise-and of a flag with the pte value. If the
flag to be tested resides in the upper 32 bits of the pte, then we run
into the danger of the result being dropped if downcast.

For example:
        gather_stats(page, md, pte_dirty(*pte), 1);
where pte_dirty(*pte) is downcast to an int.

Functions such as huge_pte_write also perform a downcast to unsigned
long (which is 32 bits).

This patch adds a double logical invert to all the pte_ accessors to
ensure predictable downcasting.

Signed-off-by: Steve Capper <steve.capper@linaro.org>
---
Changes in V2: drop the pte_isset macro, just add the !!
---
 arch/arm/include/asm/pgtable.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/arch/arm/include/asm/pgtable.h b/arch/arm/include/asm/pgtable.h
index 7d59b52..02ee408 100644
--- a/arch/arm/include/asm/pgtable.h
+++ b/arch/arm/include/asm/pgtable.h
@@ -215,10 +215,10 @@ static inline pte_t *pmd_page_vaddr(pmd_t pmd)
 #define pte_clear(mm,addr,ptep)	set_pte_ext(ptep, __pte(0), 0)
 
 #define pte_none(pte)		(!pte_val(pte))
-#define pte_present(pte)	(pte_val(pte) & L_PTE_PRESENT)
+#define pte_present(pte)	(!!(pte_val(pte) & L_PTE_PRESENT))
 #define pte_write(pte)		(!(pte_val(pte) & L_PTE_RDONLY))
-#define pte_dirty(pte)		(pte_val(pte) & L_PTE_DIRTY)
-#define pte_young(pte)		(pte_val(pte) & L_PTE_YOUNG)
+#define pte_dirty(pte)		(!!(pte_val(pte) & L_PTE_DIRTY))
+#define pte_young(pte)		(!!(pte_val(pte) & L_PTE_YOUNG))
 #define pte_exec(pte)		(!(pte_val(pte) & L_PTE_XN))
 #define pte_special(pte)	(0)
 
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH V2 2/3] arm64: mm: Add double logical invert to pte accessors
  2014-02-25 11:38 [PATCH V2 0/3] PTE fixes for arm and arm64 Steve Capper
  2014-02-25 11:38 ` [PATCH V2 1/3] arm: mm: Double logical invert for pte accessors Steve Capper
@ 2014-02-25 11:38 ` Steve Capper
  2014-02-28 15:46   ` Catalin Marinas
  2014-02-25 11:38 ` [PATCH V2 3/3] arm: mm: Switch back to L_PTE_WRITE Steve Capper
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 15+ messages in thread
From: Steve Capper @ 2014-02-25 11:38 UTC (permalink / raw)
  To: linux-arm-kernel

Page table entries on ARM64 are 64 bits, and some pte functions such as
pte_dirty return a bitwise-and of a flag with the pte value. If the
flag to be tested resides in the upper 32 bits of the pte, then we run
into the danger of the result being dropped if downcast.

For example:
	gather_stats(page, md, pte_dirty(*pte), 1);
where pte_dirty(*pte) is downcast to an int.

This patch adds a double logical invert to all the pte_ accessors to
ensure predictable downcasting.

Signed-off-by: Steve Capper <steve.capper@linaro.org>
---
Changes in V2: drop the pte_isset macro, just add the !!
---
 arch/arm64/include/asm/pgtable.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index b524dcd..aa3917c 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -136,11 +136,11 @@ extern struct page *empty_zero_page;
 /*
  * The following only work if pte_present(). Undefined behaviour otherwise.
  */
-#define pte_present(pte)	(pte_val(pte) & (PTE_VALID | PTE_PROT_NONE))
-#define pte_dirty(pte)		(pte_val(pte) & PTE_DIRTY)
-#define pte_young(pte)		(pte_val(pte) & PTE_AF)
-#define pte_special(pte)	(pte_val(pte) & PTE_SPECIAL)
-#define pte_write(pte)		(pte_val(pte) & PTE_WRITE)
+#define pte_present(pte)	(!!(pte_val(pte) & (PTE_VALID | PTE_PROT_NONE)))
+#define pte_dirty(pte)		(!!(pte_val(pte) & PTE_DIRTY))
+#define pte_young(pte)		(!!(pte_val(pte) & PTE_AF))
+#define pte_special(pte)	(!!(pte_val(pte) & PTE_SPECIAL))
+#define pte_write(pte)		(!!(pte_val(pte) & PTE_WRITE))
 #define pte_exec(pte)		(!(pte_val(pte) & PTE_UXN))
 
 #define pte_valid_user(pte) \
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH V2 3/3] arm: mm: Switch back to L_PTE_WRITE
  2014-02-25 11:38 [PATCH V2 0/3] PTE fixes for arm and arm64 Steve Capper
  2014-02-25 11:38 ` [PATCH V2 1/3] arm: mm: Double logical invert for pte accessors Steve Capper
  2014-02-25 11:38 ` [PATCH V2 2/3] arm64: mm: Add double logical invert to " Steve Capper
@ 2014-02-25 11:38 ` Steve Capper
  2014-03-26 11:00   ` Catalin Marinas
  2014-02-25 15:22 ` [PATCH V2 0/3] PTE fixes for arm and arm64 Will Deacon
  2014-03-26 10:23 ` Steve Capper
  4 siblings, 1 reply; 15+ messages in thread
From: Steve Capper @ 2014-02-25 11:38 UTC (permalink / raw)
  To: linux-arm-kernel

For LPAE, we have the following means for encoding writable or dirty
ptes:
                              L_PTE_DIRTY       L_PTE_RDONLY
    !pte_dirty && !pte_write        0               1
    !pte_dirty && pte_write         0               1
    pte_dirty && !pte_write         1               1
    pte_dirty && pte_write          1               0

So we can't distinguish between writable clean ptes and read only
ptes. This can cause problems with ptes being incorrectly flagged as
read only when they are writable but not dirty.

This patch re-introduces the L_PTE_WRITE bit for both short descriptors
and long descriptors, by reverting
  36bb94b ARM: pgtable: provide RDONLY page table bit rather than WRITE bit

For short descriptors the L_PTE_RDONLY bit is renamed to L_PTE_WRITE
and the pertinent logic changed. For long descriptors, L_PTE_WRITE is
implemented as a new software bit.

Signed-off-by: Steve Capper <steve.capper@linaro.org>
---
Changes in V2: L_PTE_SPECIAL preserved, L_PTE_WRITE moved to bit #58
for LPAE, retained the L_PTE_RDONLY definition.

Rebased against 3.14-rc4 (fixed minor merge conflict in mmu.c)
---
 arch/arm/include/asm/pgtable-2level.h |  2 +-
 arch/arm/include/asm/pgtable-3level.h |  1 +
 arch/arm/include/asm/pgtable.h        | 36 +++++++++++++++++------------------
 arch/arm/mm/dump.c                    |  8 ++++----
 arch/arm/mm/mmu.c                     | 25 ++++++++++++------------
 arch/arm/mm/proc-macros.S             | 16 ++++++++--------
 arch/arm/mm/proc-v7-2level.S          |  6 +++---
 arch/arm/mm/proc-v7-3level.S          |  4 +++-
 arch/arm/mm/proc-xscale.S             |  4 ++--
 9 files changed, 53 insertions(+), 49 deletions(-)

diff --git a/arch/arm/include/asm/pgtable-2level.h b/arch/arm/include/asm/pgtable-2level.h
index dfff709..ca43b84 100644
--- a/arch/arm/include/asm/pgtable-2level.h
+++ b/arch/arm/include/asm/pgtable-2level.h
@@ -120,7 +120,7 @@
 #define L_PTE_YOUNG		(_AT(pteval_t, 1) << 1)
 #define L_PTE_FILE		(_AT(pteval_t, 1) << 2)	/* only when !PRESENT */
 #define L_PTE_DIRTY		(_AT(pteval_t, 1) << 6)
-#define L_PTE_RDONLY		(_AT(pteval_t, 1) << 7)
+#define L_PTE_WRITE		(_AT(pteval_t, 1) << 7)
 #define L_PTE_USER		(_AT(pteval_t, 1) << 8)
 #define L_PTE_XN		(_AT(pteval_t, 1) << 9)
 #define L_PTE_SHARED		(_AT(pteval_t, 1) << 10)	/* shared(v6), coherent(xsc3) */
diff --git a/arch/arm/include/asm/pgtable-3level.h b/arch/arm/include/asm/pgtable-3level.h
index 85c60ad..e30c98b 100644
--- a/arch/arm/include/asm/pgtable-3level.h
+++ b/arch/arm/include/asm/pgtable-3level.h
@@ -86,6 +86,7 @@
 #define L_PTE_DIRTY		(_AT(pteval_t, 1) << 55)	/* unused */
 #define L_PTE_SPECIAL		(_AT(pteval_t, 1) << 56)	/* unused */
 #define L_PTE_NONE		(_AT(pteval_t, 1) << 57)	/* PROT_NONE */
+#define L_PTE_WRITE		(_AT(pteval_t, 1) << 58)
 
 #define PMD_SECT_VALID		(_AT(pmdval_t, 1) << 0)
 #define PMD_SECT_DIRTY		(_AT(pmdval_t, 1) << 55)
diff --git a/arch/arm/include/asm/pgtable.h b/arch/arm/include/asm/pgtable.h
index 02ee408..0d24469 100644
--- a/arch/arm/include/asm/pgtable.h
+++ b/arch/arm/include/asm/pgtable.h
@@ -88,13 +88,13 @@ extern pgprot_t		pgprot_s2_device;
 
 #define _MOD_PROT(p, b)	__pgprot(pgprot_val(p) | (b))
 
-#define PAGE_NONE		_MOD_PROT(pgprot_user, L_PTE_XN | L_PTE_RDONLY | L_PTE_NONE)
-#define PAGE_SHARED		_MOD_PROT(pgprot_user, L_PTE_USER | L_PTE_XN)
-#define PAGE_SHARED_EXEC	_MOD_PROT(pgprot_user, L_PTE_USER)
-#define PAGE_COPY		_MOD_PROT(pgprot_user, L_PTE_USER | L_PTE_RDONLY | L_PTE_XN)
-#define PAGE_COPY_EXEC		_MOD_PROT(pgprot_user, L_PTE_USER | L_PTE_RDONLY)
-#define PAGE_READONLY		_MOD_PROT(pgprot_user, L_PTE_USER | L_PTE_RDONLY | L_PTE_XN)
-#define PAGE_READONLY_EXEC	_MOD_PROT(pgprot_user, L_PTE_USER | L_PTE_RDONLY)
+#define PAGE_NONE		_MOD_PROT(pgprot_user, L_PTE_XN | L_PTE_NONE)
+#define PAGE_SHARED		_MOD_PROT(pgprot_user, L_PTE_USER | L_PTE_WRITE | L_PTE_XN)
+#define PAGE_SHARED_EXEC	_MOD_PROT(pgprot_user, L_PTE_USER | L_PTE_WRITE)
+#define PAGE_COPY		_MOD_PROT(pgprot_user, L_PTE_USER | L_PTE_XN)
+#define PAGE_COPY_EXEC		_MOD_PROT(pgprot_user, L_PTE_USER)
+#define PAGE_READONLY		_MOD_PROT(pgprot_user, L_PTE_USER | L_PTE_XN)
+#define PAGE_READONLY_EXEC	_MOD_PROT(pgprot_user, L_PTE_USER)
 #define PAGE_KERNEL		_MOD_PROT(pgprot_kernel, L_PTE_XN)
 #define PAGE_KERNEL_EXEC	pgprot_kernel
 #define PAGE_HYP		_MOD_PROT(pgprot_kernel, L_PTE_HYP)
@@ -102,13 +102,13 @@ extern pgprot_t		pgprot_s2_device;
 #define PAGE_S2			_MOD_PROT(pgprot_s2, L_PTE_S2_RDONLY)
 #define PAGE_S2_DEVICE		_MOD_PROT(pgprot_s2_device, L_PTE_S2_RDWR)
 
-#define __PAGE_NONE		__pgprot(_L_PTE_DEFAULT | L_PTE_RDONLY | L_PTE_XN | L_PTE_NONE)
-#define __PAGE_SHARED		__pgprot(_L_PTE_DEFAULT | L_PTE_USER | L_PTE_XN)
-#define __PAGE_SHARED_EXEC	__pgprot(_L_PTE_DEFAULT | L_PTE_USER)
-#define __PAGE_COPY		__pgprot(_L_PTE_DEFAULT | L_PTE_USER | L_PTE_RDONLY | L_PTE_XN)
-#define __PAGE_COPY_EXEC	__pgprot(_L_PTE_DEFAULT | L_PTE_USER | L_PTE_RDONLY)
-#define __PAGE_READONLY		__pgprot(_L_PTE_DEFAULT | L_PTE_USER | L_PTE_RDONLY | L_PTE_XN)
-#define __PAGE_READONLY_EXEC	__pgprot(_L_PTE_DEFAULT | L_PTE_USER | L_PTE_RDONLY)
+#define __PAGE_NONE		__pgprot(_L_PTE_DEFAULT | L_PTE_XN | L_PTE_NONE)
+#define __PAGE_SHARED		__pgprot(_L_PTE_DEFAULT | L_PTE_USER | L_PTE_WRITE | L_PTE_XN)
+#define __PAGE_SHARED_EXEC	__pgprot(_L_PTE_DEFAULT | L_PTE_USER | L_PTE_WRITE)
+#define __PAGE_COPY		__pgprot(_L_PTE_DEFAULT | L_PTE_USER | L_PTE_XN)
+#define __PAGE_COPY_EXEC	__pgprot(_L_PTE_DEFAULT | L_PTE_USER)
+#define __PAGE_READONLY		__pgprot(_L_PTE_DEFAULT | L_PTE_USER | L_PTE_XN)
+#define __PAGE_READONLY_EXEC	__pgprot(_L_PTE_DEFAULT | L_PTE_USER)
 
 #define __pgprot_modify(prot,mask,bits)		\
 	__pgprot((pgprot_val(prot) & ~(mask)) | (bits))
@@ -216,7 +216,7 @@ static inline pte_t *pmd_page_vaddr(pmd_t pmd)
 
 #define pte_none(pte)		(!pte_val(pte))
 #define pte_present(pte)	(!!(pte_val(pte) & L_PTE_PRESENT))
-#define pte_write(pte)		(!(pte_val(pte) & L_PTE_RDONLY))
+#define pte_write(pte)		(!!(pte_val(pte) & L_PTE_WRITE))
 #define pte_dirty(pte)		(!!(pte_val(pte) & L_PTE_DIRTY))
 #define pte_young(pte)		(!!(pte_val(pte) & L_PTE_YOUNG))
 #define pte_exec(pte)		(!(pte_val(pte) & L_PTE_XN))
@@ -248,8 +248,8 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
 #define PTE_BIT_FUNC(fn,op) \
 static inline pte_t pte_##fn(pte_t pte) { pte_val(pte) op; return pte; }
 
-PTE_BIT_FUNC(wrprotect, |= L_PTE_RDONLY);
-PTE_BIT_FUNC(mkwrite,   &= ~L_PTE_RDONLY);
+PTE_BIT_FUNC(wrprotect, &= ~L_PTE_WRITE);
+PTE_BIT_FUNC(mkwrite,   |= L_PTE_WRITE);
 PTE_BIT_FUNC(mkclean,   &= ~L_PTE_DIRTY);
 PTE_BIT_FUNC(mkdirty,   |= L_PTE_DIRTY);
 PTE_BIT_FUNC(mkold,     &= ~L_PTE_YOUNG);
@@ -261,7 +261,7 @@ static inline pte_t pte_mkspecial(pte_t pte) { return pte; }
 
 static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
 {
-	const pteval_t mask = L_PTE_XN | L_PTE_RDONLY | L_PTE_USER |
+	const pteval_t mask = L_PTE_XN | L_PTE_WRITE | L_PTE_USER |
 		L_PTE_NONE | L_PTE_VALID;
 	pte_val(pte) = (pte_val(pte) & ~mask) | (pgprot_val(newprot) & mask);
 	return pte;
diff --git a/arch/arm/mm/dump.c b/arch/arm/mm/dump.c
index 2b3a564..715ca93 100644
--- a/arch/arm/mm/dump.c
+++ b/arch/arm/mm/dump.c
@@ -58,10 +58,10 @@ static const struct prot_bits pte_bits[] = {
 		.set	= "USR",
 		.clear	= "   ",
 	}, {
-		.mask	= L_PTE_RDONLY,
-		.val	= L_PTE_RDONLY,
-		.set	= "ro",
-		.clear	= "RW",
+		.mask	= L_PTE_WRITE,
+		.val	= L_PTE_WRITE,
+		.set	= "RW",
+		.clear	= "ro",
 	}, {
 		.mask	= L_PTE_XN,
 		.val	= L_PTE_XN,
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index a623cb3..03cf9e9 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -231,7 +231,7 @@ __setup("noalign", noalign_setup);
 
 #endif /* ifdef CONFIG_CPU_CP15 / else */
 
-#define PROT_PTE_DEVICE		L_PTE_PRESENT|L_PTE_YOUNG|L_PTE_DIRTY|L_PTE_XN
+#define PROT_PTE_DEVICE		L_PTE_PRESENT|L_PTE_YOUNG|L_PTE_DIRTY|L_PTE_WRITE|L_PTE_XN
 #define PROT_PTE_S2_DEVICE	PROT_PTE_DEVICE
 #define PROT_SECT_DEVICE	PMD_TYPE_SECT|PMD_SECT_AP_WRITE
 
@@ -281,26 +281,26 @@ static struct mem_type mem_types[] = {
 	},
 #endif
 	[MT_LOW_VECTORS] = {
-		.prot_pte  = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
-				L_PTE_RDONLY,
+		.prot_pte  = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY,
 		.prot_l1   = PMD_TYPE_TABLE,
 		.domain    = DOMAIN_USER,
 	},
 	[MT_HIGH_VECTORS] = {
 		.prot_pte  = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
-				L_PTE_USER | L_PTE_RDONLY,
+				L_PTE_USER,
 		.prot_l1   = PMD_TYPE_TABLE,
 		.domain    = DOMAIN_USER,
 	},
 	[MT_MEMORY_RWX] = {
-		.prot_pte  = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY,
+		.prot_pte  = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
+				L_PTE_WRITE,
 		.prot_l1   = PMD_TYPE_TABLE,
 		.prot_sect = PMD_TYPE_SECT | PMD_SECT_AP_WRITE,
 		.domain    = DOMAIN_KERNEL,
 	},
 	[MT_MEMORY_RW] = {
 		.prot_pte  = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
-			     L_PTE_XN,
+				L_PTE_WRITE | L_PTE_XN,
 		.prot_l1   = PMD_TYPE_TABLE,
 		.prot_sect = PMD_TYPE_SECT | PMD_SECT_AP_WRITE,
 		.domain    = DOMAIN_KERNEL,
@@ -311,26 +311,27 @@ static struct mem_type mem_types[] = {
 	},
 	[MT_MEMORY_RWX_NONCACHED] = {
 		.prot_pte  = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
-				L_PTE_MT_BUFFERABLE,
+				L_PTE_WRITE | L_PTE_MT_BUFFERABLE,
 		.prot_l1   = PMD_TYPE_TABLE,
 		.prot_sect = PMD_TYPE_SECT | PMD_SECT_AP_WRITE,
 		.domain    = DOMAIN_KERNEL,
 	},
 	[MT_MEMORY_RW_DTCM] = {
 		.prot_pte  = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
-				L_PTE_XN,
+				L_PTE_WRITE | L_PTE_XN,
 		.prot_l1   = PMD_TYPE_TABLE,
 		.prot_sect = PMD_TYPE_SECT | PMD_SECT_XN,
 		.domain    = DOMAIN_KERNEL,
 	},
 	[MT_MEMORY_RWX_ITCM] = {
-		.prot_pte  = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY,
+		.prot_pte  = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
+				L_PTE_WRITE,
 		.prot_l1   = PMD_TYPE_TABLE,
 		.domain    = DOMAIN_KERNEL,
 	},
 	[MT_MEMORY_RW_SO] = {
 		.prot_pte  = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
-				L_PTE_MT_UNCACHED | L_PTE_XN,
+				L_PTE_MT_UNCACHED | L_PTE_WRITE | L_PTE_XN,
 		.prot_l1   = PMD_TYPE_TABLE,
 		.prot_sect = PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_S |
 				PMD_SECT_UNCACHED | PMD_SECT_XN,
@@ -338,7 +339,7 @@ static struct mem_type mem_types[] = {
 	},
 	[MT_MEMORY_DMA_READY] = {
 		.prot_pte  = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
-				L_PTE_XN,
+				L_PTE_WRITE | L_PTE_XN,
 		.prot_l1   = PMD_TYPE_TABLE,
 		.domain    = DOMAIN_KERNEL,
 	},
@@ -593,7 +594,7 @@ static void __init build_mem_type_table(void)
 
 	pgprot_user   = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | user_pgprot);
 	pgprot_kernel = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG |
-				 L_PTE_DIRTY | kern_pgprot);
+				 L_PTE_DIRTY | L_PTE_WRITE | kern_pgprot);
 	pgprot_s2  = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | s2_pgprot);
 	pgprot_s2_device  = __pgprot(s2_device_pgprot);
 	pgprot_hyp_device  = __pgprot(hyp_device_pgprot);
diff --git a/arch/arm/mm/proc-macros.S b/arch/arm/mm/proc-macros.S
index e3c48a3..c62fd89 100644
--- a/arch/arm/mm/proc-macros.S
+++ b/arch/arm/mm/proc-macros.S
@@ -97,7 +97,7 @@
 #error PTE shared bit mismatch
 #endif
 #if !defined (CONFIG_ARM_LPAE) && \
-	(L_PTE_XN+L_PTE_USER+L_PTE_RDONLY+L_PTE_DIRTY+L_PTE_YOUNG+\
+	(L_PTE_XN+L_PTE_USER+L_PTE_WRITE+L_PTE_DIRTY+L_PTE_YOUNG+\
 	 L_PTE_FILE+L_PTE_PRESENT) > L_PTE_SHARED
 #error Invalid Linux PTE bit settings
 #endif
@@ -152,9 +152,9 @@
 	and	r2, r1, #L_PTE_MT_MASK
 	ldr	r2, [ip, r2]
 
-	eor	r1, r1, #L_PTE_DIRTY
-	tst	r1, #L_PTE_DIRTY|L_PTE_RDONLY
-	orrne	r3, r3, #PTE_EXT_APX
+	tst	r1, #L_PTE_WRITE
+	tstne	r1, #L_PTE_DIRTY
+	orreq	r3, r3, #PTE_EXT_APX
 
 	tst	r1, #L_PTE_USER
 	orrne	r3, r3, #PTE_EXT_AP1
@@ -199,7 +199,7 @@
 	.macro	armv3_set_pte_ext wc_disable=1
 	str	r1, [r0], #2048			@ linux version
 
-	eor	r3, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY
+	eor	r3, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY
 
 	bic	r2, r1, #PTE_SMALL_AP_MASK	@ keep C, B bits
 	bic	r2, r2, #PTE_TYPE_MASK
@@ -208,7 +208,7 @@
 	tst	r3, #L_PTE_USER			@ user?
 	orrne	r2, r2, #PTE_SMALL_AP_URO_SRW
 
-	tst	r3, #L_PTE_RDONLY | L_PTE_DIRTY	@ write and dirty?
+	tst	r3, #L_PTE_WRITE | L_PTE_DIRTY	@ write and dirty?
 	orreq	r2, r2, #PTE_SMALL_AP_UNO_SRW
 
 	tst	r3, #L_PTE_PRESENT | L_PTE_YOUNG	@ present and young?
@@ -242,7 +242,7 @@
 	.macro	xscale_set_pte_ext_prologue
 	str	r1, [r0]			@ linux version
 
-	eor	r3, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY
+	eor	r3, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY
 
 	bic	r2, r1, #PTE_SMALL_AP_MASK	@ keep C, B bits
 	orr	r2, r2, #PTE_TYPE_EXT		@ extended page
@@ -250,7 +250,7 @@
 	tst	r3, #L_PTE_USER			@ user?
 	orrne	r2, r2, #PTE_EXT_AP_URO_SRW	@ yes -> user r/o, system r/w
 
-	tst	r3, #L_PTE_RDONLY | L_PTE_DIRTY	@ write and dirty?
+	tst	r3, #L_PTE_WRITE | L_PTE_DIRTY	@ write and dirty?
 	orreq	r2, r2, #PTE_EXT_AP_UNO_SRW	@ yes -> user n/a, system r/w
 						@ combined with user -> user r/w
 	.endm
diff --git a/arch/arm/mm/proc-v7-2level.S b/arch/arm/mm/proc-v7-2level.S
index bdd3be4..297fccf 100644
--- a/arch/arm/mm/proc-v7-2level.S
+++ b/arch/arm/mm/proc-v7-2level.S
@@ -84,9 +84,9 @@ ENTRY(cpu_v7_set_pte_ext)
 	tst	r1, #1 << 4
 	orrne	r3, r3, #PTE_EXT_TEX(1)
 
-	eor	r1, r1, #L_PTE_DIRTY
-	tst	r1, #L_PTE_RDONLY | L_PTE_DIRTY
-	orrne	r3, r3, #PTE_EXT_APX
+	tst	r1, #L_PTE_WRITE
+	tstne	r1, #L_PTE_DIRTY
+	orreq	r3, r3, #PTE_EXT_APX
 
 	tst	r1, #L_PTE_USER
 	orrne	r3, r3, #PTE_EXT_AP1
diff --git a/arch/arm/mm/proc-v7-3level.S b/arch/arm/mm/proc-v7-3level.S
index 01a719e..7726edd 100644
--- a/arch/arm/mm/proc-v7-3level.S
+++ b/arch/arm/mm/proc-v7-3level.S
@@ -78,7 +78,9 @@ ENTRY(cpu_v7_set_pte_ext)
 	tst	r3, #1 << (57 - 32)		@ L_PTE_NONE
 	bicne	r2, #L_PTE_VALID
 	bne	1f
-	tst	r3, #1 << (55 - 32)		@ L_PTE_DIRTY
+	bic	r2, #L_PTE_RDONLY
+	tst	r3, #1 << (58 - 32)		@ L_PTE_WRITE
+	tstne	r3, #1 << (55 - 32)		@ L_PTE_DIRTY
 	orreq	r2, #L_PTE_RDONLY
 1:	strd	r2, r3, [r0]
 	ALT_SMP(W(nop))
diff --git a/arch/arm/mm/proc-xscale.S b/arch/arm/mm/proc-xscale.S
index d19b1cf..d5b23e8 100644
--- a/arch/arm/mm/proc-xscale.S
+++ b/arch/arm/mm/proc-xscale.S
@@ -510,8 +510,8 @@ ENTRY(cpu_xscale_set_pte_ext)
 	@
 	@ Erratum 40: must set memory to write-through for user read-only pages
 	@
-	and	ip, r1, #(L_PTE_MT_MASK | L_PTE_USER | L_PTE_RDONLY) & ~(4 << 2)
-	teq	ip, #L_PTE_MT_WRITEBACK | L_PTE_USER | L_PTE_RDONLY
+	and	ip, r1, #(L_PTE_MT_MASK | L_PTE_USER | L_PTE_WRITE) & ~(4 << 2)
+	teq	ip, #L_PTE_MT_WRITEBACK | L_PTE_USER
 
 	moveq	r1, #L_PTE_MT_WRITETHROUGH
 	and	r1, r1, #L_PTE_MT_MASK
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH V2 0/3] PTE fixes for arm and arm64
  2014-02-25 11:38 [PATCH V2 0/3] PTE fixes for arm and arm64 Steve Capper
                   ` (2 preceding siblings ...)
  2014-02-25 11:38 ` [PATCH V2 3/3] arm: mm: Switch back to L_PTE_WRITE Steve Capper
@ 2014-02-25 15:22 ` Will Deacon
  2014-02-25 17:08   ` Steve Capper
  2014-03-26 10:23 ` Steve Capper
  4 siblings, 1 reply; 15+ messages in thread
From: Will Deacon @ 2014-02-25 15:22 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Feb 25, 2014 at 11:38:51AM +0000, Steve Capper wrote:
> This series fixes a couple of problems that I have come across:
>  1) Some pte accessors for arm and arm64 can have their results
>     cancelled out by a downcast. This is addressed by the first two
>     patches.
> 
>  2) On arm with LPAE it is impossible to distinguish between clean
>     writable ptes and read only ptes. This is addressed by the third
>     patch.
> 
> As the third patch depends on the first patch, all three patches are
> bundled together in the same series.
> 
> I have reviewed Will and Russell's patch:
>  b6ccb9803e90 ("ARM: 7954/1: mm: remove remaining domain support
> 	from ARMv6")
> And I believe that the L_PTE_WRITE logic should be compatible with this
> patch. It applies cleanly and compiles; but I would be greatful if
> someone could please give it a test.

There's an 1136 on my desk with your name on it :)

Will

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH V2 0/3] PTE fixes for arm and arm64
  2014-02-25 15:22 ` [PATCH V2 0/3] PTE fixes for arm and arm64 Will Deacon
@ 2014-02-25 17:08   ` Steve Capper
  0 siblings, 0 replies; 15+ messages in thread
From: Steve Capper @ 2014-02-25 17:08 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Feb 25, 2014 at 03:22:36PM +0000, Will Deacon wrote:
> On Tue, Feb 25, 2014 at 11:38:51AM +0000, Steve Capper wrote:
> > This series fixes a couple of problems that I have come across:
> >  1) Some pte accessors for arm and arm64 can have their results
> >     cancelled out by a downcast. This is addressed by the first two
> >     patches.
> > 
> >  2) On arm with LPAE it is impossible to distinguish between clean
> >     writable ptes and read only ptes. This is addressed by the third
> >     patch.
> > 
> > As the third patch depends on the first patch, all three patches are
> > bundled together in the same series.
> > 
> > I have reviewed Will and Russell's patch:
> >  b6ccb9803e90 ("ARM: 7954/1: mm: remove remaining domain support
> > 	from ARMv6")
> > And I believe that the L_PTE_WRITE logic should be compatible with this
> > patch. It applies cleanly and compiles; but I would be greatful if
> > someone could please give it a test.
> 
> There's an 1136 on my desk with your name on it :)

The 1136 appears to run fine with this series and your domain removal
patch applied on top. Test writes to the vectors page from user space
were unsuccessful (i.e. the board didn't explode when userspace
attempted to write over the vectors page).

Cheers,
-- 
Steve

> 
> Will

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH V2 1/3] arm: mm: Double logical invert for pte accessors
  2014-02-25 11:38 ` [PATCH V2 1/3] arm: mm: Double logical invert for pte accessors Steve Capper
@ 2014-02-28 15:45   ` Catalin Marinas
  2014-03-26 11:01   ` Catalin Marinas
  1 sibling, 0 replies; 15+ messages in thread
From: Catalin Marinas @ 2014-02-28 15:45 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Feb 25, 2014 at 11:38:52AM +0000, Steve Capper wrote:
> Long descriptors on ARM are 64 bits, and some pte functions such as
> pte_dirty return a bitwise-and of a flag with the pte value. If the
> flag to be tested resides in the upper 32 bits of the pte, then we run
> into the danger of the result being dropped if downcast.
> 
> For example:
>         gather_stats(page, md, pte_dirty(*pte), 1);
> where pte_dirty(*pte) is downcast to an int.
> 
> Functions such as huge_pte_write also perform a downcast to unsigned
> long (which is 32 bits).
> 
> This patch adds a double logical invert to all the pte_ accessors to
> ensure predictable downcasting.
> 
> Signed-off-by: Steve Capper <steve.capper@linaro.org>

Acked-by: Catalin Marinas <catalin.marinas@arm.com>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH V2 2/3] arm64: mm: Add double logical invert to pte accessors
  2014-02-25 11:38 ` [PATCH V2 2/3] arm64: mm: Add double logical invert to " Steve Capper
@ 2014-02-28 15:46   ` Catalin Marinas
  0 siblings, 0 replies; 15+ messages in thread
From: Catalin Marinas @ 2014-02-28 15:46 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Feb 25, 2014 at 11:38:53AM +0000, Steve Capper wrote:
> Page table entries on ARM64 are 64 bits, and some pte functions such as
> pte_dirty return a bitwise-and of a flag with the pte value. If the
> flag to be tested resides in the upper 32 bits of the pte, then we run
> into the danger of the result being dropped if downcast.
> 
> For example:
> 	gather_stats(page, md, pte_dirty(*pte), 1);
> where pte_dirty(*pte) is downcast to an int.
> 
> This patch adds a double logical invert to all the pte_ accessors to
> ensure predictable downcasting.
> 
> Signed-off-by: Steve Capper <steve.capper@linaro.org>

Applied and added cc stable as well. Thanks.

-- 
Catalin

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH V2 0/3] PTE fixes for arm and arm64
  2014-02-25 11:38 [PATCH V2 0/3] PTE fixes for arm and arm64 Steve Capper
                   ` (3 preceding siblings ...)
  2014-02-25 15:22 ` [PATCH V2 0/3] PTE fixes for arm and arm64 Will Deacon
@ 2014-03-26 10:23 ` Steve Capper
  2014-03-26 11:01   ` Russell King - ARM Linux
  4 siblings, 1 reply; 15+ messages in thread
From: Steve Capper @ 2014-03-26 10:23 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Feb 25, 2014 at 11:38:51AM +0000, Steve Capper wrote:
> This series fixes a couple of problems that I have come across:
>  1) Some pte accessors for arm and arm64 can have their results
>     cancelled out by a downcast. This is addressed by the first two
>     patches.
> 
>  2) On arm with LPAE it is impossible to distinguish between clean
>     writable ptes and read only ptes. This is addressed by the third
>     patch.
> 
> As the third patch depends on the first patch, all three patches are
> bundled together in the same series.
> 
> I have reviewed Will and Russell's patch:
>  b6ccb9803e90 ("ARM: 7954/1: mm: remove remaining domain support
> 	from ARMv6")
> And I believe that the L_PTE_WRITE logic should be compatible with this
> patch. It applies cleanly and compiles; but I would be greatful if
> someone could please give it a test.

If there are no objections, I was going to put the following into
Russell's patch system:
	arm: mm: Double logical invert for pte accessors
	arm: mm: Switch back to L_PTE_WRITE

Cheers,
-- 
Steve

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH V2 3/3] arm: mm: Switch back to L_PTE_WRITE
  2014-02-25 11:38 ` [PATCH V2 3/3] arm: mm: Switch back to L_PTE_WRITE Steve Capper
@ 2014-03-26 11:00   ` Catalin Marinas
  0 siblings, 0 replies; 15+ messages in thread
From: Catalin Marinas @ 2014-03-26 11:00 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Feb 25, 2014 at 11:38:54AM +0000, Steve Capper wrote:
> --- a/arch/arm/include/asm/pgtable-3level.h
> +++ b/arch/arm/include/asm/pgtable-3level.h
> @@ -86,6 +86,7 @@
>  #define L_PTE_DIRTY            (_AT(pteval_t, 1) << 55)        /* unused */
>  #define L_PTE_SPECIAL          (_AT(pteval_t, 1) << 56)        /* unused */
>  #define L_PTE_NONE             (_AT(pteval_t, 1) << 57)        /* PROT_NONE */
> +#define L_PTE_WRITE            (_AT(pteval_t, 1) << 58)

I think we could rename L_PTE_RDONLY just to PTE_RDONLY since it's a
hardware bit and only used by LPAE.

Otherwise:

Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH V2 1/3] arm: mm: Double logical invert for pte accessors
  2014-02-25 11:38 ` [PATCH V2 1/3] arm: mm: Double logical invert for pte accessors Steve Capper
  2014-02-28 15:45   ` Catalin Marinas
@ 2014-03-26 11:01   ` Catalin Marinas
  1 sibling, 0 replies; 15+ messages in thread
From: Catalin Marinas @ 2014-03-26 11:01 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Feb 25, 2014 at 11:38:52AM +0000, Steve Capper wrote:
> Long descriptors on ARM are 64 bits, and some pte functions such as
> pte_dirty return a bitwise-and of a flag with the pte value. If the
> flag to be tested resides in the upper 32 bits of the pte, then we run
> into the danger of the result being dropped if downcast.
> 
> For example:
>         gather_stats(page, md, pte_dirty(*pte), 1);
> where pte_dirty(*pte) is downcast to an int.
> 
> Functions such as huge_pte_write also perform a downcast to unsigned
> long (which is 32 bits).
> 
> This patch adds a double logical invert to all the pte_ accessors to
> ensure predictable downcasting.
> 
> Signed-off-by: Steve Capper <steve.capper@linaro.org>

Acked-by: Catalin Marinas <catalin.marinas@arm.com>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH V2 0/3] PTE fixes for arm and arm64
  2014-03-26 10:23 ` Steve Capper
@ 2014-03-26 11:01   ` Russell King - ARM Linux
  2014-03-26 13:23     ` Steve Capper
  0 siblings, 1 reply; 15+ messages in thread
From: Russell King - ARM Linux @ 2014-03-26 11:01 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Mar 26, 2014 at 10:23:19AM +0000, Steve Capper wrote:
> If there are no objections, I was going to put the following into
> Russell's patch system:
> 	arm: mm: Double logical invert for pte accessors
> 	arm: mm: Switch back to L_PTE_WRITE

I'm not all that happy with double inversions - I think they just serve
to cause confusion (and it was confusing, which is why I removed it.)
I'll only take them if you have a really good reason why you want to
bring it back.

-- 
FTTC broadband for 0.8mile line: now at 9.7Mbps down 460kbps up... slowly
improving, and getting towards what was expected from it.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH V2 0/3] PTE fixes for arm and arm64
  2014-03-26 11:01   ` Russell King - ARM Linux
@ 2014-03-26 13:23     ` Steve Capper
  2014-03-26 13:48       ` Russell King - ARM Linux
  0 siblings, 1 reply; 15+ messages in thread
From: Steve Capper @ 2014-03-26 13:23 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Mar 26, 2014 at 11:01:41AM +0000, Russell King - ARM Linux wrote:
> On Wed, Mar 26, 2014 at 10:23:19AM +0000, Steve Capper wrote:
> > If there are no objections, I was going to put the following into
> > Russell's patch system:
> > 	arm: mm: Double logical invert for pte accessors
> > 	arm: mm: Switch back to L_PTE_WRITE
> 
> I'm not all that happy with double inversions - I think they just serve
> to cause confusion (and it was confusing, which is why I removed it.)
> I'll only take them if you have a really good reason why you want to
> bring it back.

Hi Russell,
The problem I'm trying to solve is for LPAE, where we have flags in the
upper 32 bits of a page table entry that are tested for with a bitwise
and, then subsequently downcast by a store to 32-bit integer:

	gather_stats(page, md, pte_dirty(*pte), 1);
and,

	static inline unsigned long huge_pte_write(pte_t pte)
	{
		return pte_write(pte);
	}

(and other cases that may arise in future).

We erroneously get false returned in situations where we shouldn't, and
it's tricky to track this down.

My reasoning was that applying double logical inverts to all the pte
accessors that don't already have one ensures that we get values that
are safe to downcast, and this is safe for cases where new accessors
are added and the convention is followed and for where bit layouts are
changed.

I agree that this is confusing (and does look rather ugly), and I would
happily try alternatives.

I had tried to create a helper macro, pte_isset, but this didn't attract
any positive comments:
http://lists.infradead.org/pipermail/linux-arm-kernel/2014-February/235380.html

I tried bool types, but it looked a little too cumbersome to me.

Would the double logical invert be more acceptable if it were better
commented?

Cheers,
-- 
Steve

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH V2 0/3] PTE fixes for arm and arm64
  2014-03-26 13:23     ` Steve Capper
@ 2014-03-26 13:48       ` Russell King - ARM Linux
  2014-03-27 10:01         ` Steve Capper
  0 siblings, 1 reply; 15+ messages in thread
From: Russell King - ARM Linux @ 2014-03-26 13:48 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Mar 26, 2014 at 01:23:19PM +0000, Steve Capper wrote:
> On Wed, Mar 26, 2014 at 11:01:41AM +0000, Russell King - ARM Linux wrote:
> > On Wed, Mar 26, 2014 at 10:23:19AM +0000, Steve Capper wrote:
> > > If there are no objections, I was going to put the following into
> > > Russell's patch system:
> > > 	arm: mm: Double logical invert for pte accessors
> > > 	arm: mm: Switch back to L_PTE_WRITE
> > 
> > I'm not all that happy with double inversions - I think they just serve
> > to cause confusion (and it was confusing, which is why I removed it.)
> > I'll only take them if you have a really good reason why you want to
> > bring it back.
> 
> Hi Russell,
> The problem I'm trying to solve is for LPAE, where we have flags in the
> upper 32 bits of a page table entry that are tested for with a bitwise
> and, then subsequently downcast by a store to 32-bit integer:
> 
> 	gather_stats(page, md, pte_dirty(*pte), 1);
> and,
> 
> 	static inline unsigned long huge_pte_write(pte_t pte)
> 	{
> 		return pte_write(pte);
> 	}
> 
> (and other cases that may arise in future).

I think I have already said that these cases should be dealt with by
ensuring that they return sensible values in such cases.  The official
return type for pte_write() and pte_dirty() if they aren't a macro is
"int", and that makes a 64-bit AND operation returning a bit set in
the high 32-bits incorrect behaviour.

So, the return value from all these functions must fit within "int" and
be of the appropriate true/false indication according to C rules depending
on the test.

While we can use the shortcut of doing a 32-bit AND to test a bit in the
32-bit case, we can't use this with LPAE nor 64-bit PTEs where "int" is
not 64-bit - in that case, these functions must adjust the value
appropriately.

> I had tried to create a helper macro, pte_isset, but this didn't attract
> any positive comments:
> http://lists.infradead.org/pipermail/linux-arm-kernel/2014-February/235380.html

It imposes overhead for _everyone_ whether they need that overhead or not.

The problem with !!value is that the compiler has to generate more code
to convert "value" into a one-or-zero in /every/ case, because by doing
that, you've told the compiler not "I want a true/false" value but "I
want a one or zero value".  So, what you end up with in the 32-bit case
is:

	load pte
	test pte bit
	set another register to 0 if test was zero
	set register to 1 if test was non-zero
	test register for zero or non-zero
	... do something ...

which is rather inefficient when you're doing that lots of times.  As I
say, we only need this if the bit being tested is not representable
within 32-bit.

So, rather than:

+#define pte_isset(pte, val)    (!!(pte_val(pte) & (val)))

maybe:

#define pte_isset(pte, val) ((u32)(val) == (val) ? pte_val(pte) & (val) : !!(pte_val(pte) & (val)))

What this says is, if the bit fits within 32-bit, use our existing logic,
otherwise use the new logic.  Since "val" will always be a constant, the
compiler should be able to optimise this, completely eliminating one or
other branches.  It would be worth checking the assembly from the above
on 32-bit LPAE, because the compiler will probably do a 64-bit test even
for values which fit in 32-bit - this may create even better code:

#define pte_isset(pte, val) ((u32)(val) == (val) ? (u32)pte_val(pte) & (u32)(val) : !!(pte_val(pte) & (val)))

but again, it needs the assembly read to work out how it behaves.

Also, it may be worth considering a pte_isclear() macro, since we don't
need the logic in that case - it can just be a plain and simple:

#define pte_isclear(pte, val) (!(pte_val(pte) & (val)))

since we always need the negation.  Again, as per the above, it may be
better on 32-bit LPAE whether a similar trick here would be worth it -
there's no point testing both halves of a 64-bit register pair when you
know that one half is always zero.

-- 
FTTC broadband for 0.8mile line: now at 9.7Mbps down 460kbps up... slowly
improving, and getting towards what was expected from it.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH V2 0/3] PTE fixes for arm and arm64
  2014-03-26 13:48       ` Russell King - ARM Linux
@ 2014-03-27 10:01         ` Steve Capper
  0 siblings, 0 replies; 15+ messages in thread
From: Steve Capper @ 2014-03-27 10:01 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Mar 26, 2014 at 01:48:03PM +0000, Russell King - ARM Linux wrote:
> On Wed, Mar 26, 2014 at 01:23:19PM +0000, Steve Capper wrote:
> > On Wed, Mar 26, 2014 at 11:01:41AM +0000, Russell King - ARM Linux wrote:
> > > On Wed, Mar 26, 2014 at 10:23:19AM +0000, Steve Capper wrote:
> > > > If there are no objections, I was going to put the following into
> > > > Russell's patch system:
> > > > 	arm: mm: Double logical invert for pte accessors
> > > > 	arm: mm: Switch back to L_PTE_WRITE
> > > 
> > > I'm not all that happy with double inversions - I think they just serve
> > > to cause confusion (and it was confusing, which is why I removed it.)
> > > I'll only take them if you have a really good reason why you want to
> > > bring it back.
> > 
> > Hi Russell,
> > The problem I'm trying to solve is for LPAE, where we have flags in the
> > upper 32 bits of a page table entry that are tested for with a bitwise
> > and, then subsequently downcast by a store to 32-bit integer:
> > 
> > 	gather_stats(page, md, pte_dirty(*pte), 1);
> > and,
> > 
> > 	static inline unsigned long huge_pte_write(pte_t pte)
> > 	{
> > 		return pte_write(pte);
> > 	}
> > 
> > (and other cases that may arise in future).
> 
> I think I have already said that these cases should be dealt with by
> ensuring that they return sensible values in such cases.  The official
> return type for pte_write() and pte_dirty() if they aren't a macro is
> "int", and that makes a 64-bit AND operation returning a bit set in
> the high 32-bits incorrect behaviour.
> 
> So, the return value from all these functions must fit within "int" and
> be of the appropriate true/false indication according to C rules depending
> on the test.
> 
> While we can use the shortcut of doing a 32-bit AND to test a bit in the
> 32-bit case, we can't use this with LPAE nor 64-bit PTEs where "int" is
> not 64-bit - in that case, these functions must adjust the value
> appropriately.
> 
> > I had tried to create a helper macro, pte_isset, but this didn't attract
> > any positive comments:
> > http://lists.infradead.org/pipermail/linux-arm-kernel/2014-February/235380.html
> 
> It imposes overhead for _everyone_ whether they need that overhead or not.
> 
> The problem with !!value is that the compiler has to generate more code
> to convert "value" into a one-or-zero in /every/ case, because by doing
> that, you've told the compiler not "I want a true/false" value but "I
> want a one or zero value".  So, what you end up with in the 32-bit case
> is:
> 
> 	load pte
> 	test pte bit
> 	set another register to 0 if test was zero
> 	set register to 1 if test was non-zero
> 	test register for zero or non-zero
> 	... do something ...
> 
> which is rather inefficient when you're doing that lots of times.  As I
> say, we only need this if the bit being tested is not representable
> within 32-bit.
> 
> So, rather than:
> 
> +#define pte_isset(pte, val)    (!!(pte_val(pte) & (val)))
> 
> maybe:
> 
> #define pte_isset(pte, val) ((u32)(val) == (val) ? pte_val(pte) & (val) : !!(pte_val(pte) & (val)))
> 
> What this says is, if the bit fits within 32-bit, use our existing logic,
> otherwise use the new logic.  Since "val" will always be a constant, the
> compiler should be able to optimise this, completely eliminating one or
> other branches.  It would be worth checking the assembly from the above
> on 32-bit LPAE, because the compiler will probably do a 64-bit test even
> for values which fit in 32-bit - this may create even better code:
> 
> #define pte_isset(pte, val) ((u32)(val) == (val) ? (u32)pte_val(pte) & (u32)(val) : !!(pte_val(pte) & (val)))
> 
> but again, it needs the assembly read to work out how it behaves.
> 
> Also, it may be worth considering a pte_isclear() macro, since we don't
> need the logic in that case - it can just be a plain and simple:
> 
> #define pte_isclear(pte, val) (!(pte_val(pte) & (val)))
> 
> since we always need the negation.  Again, as per the above, it may be
> better on 32-bit LPAE whether a similar trick here would be worth it -
> there's no point testing both halves of a 64-bit register pair when you
> know that one half is always zero.

Thanks Russell,
I will get a pte_isset and pte_isclear coded up and will look at the
codegen for 32/64 bit ptes.

Cheers,
-- 
Steve

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2014-03-27 10:01 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-02-25 11:38 [PATCH V2 0/3] PTE fixes for arm and arm64 Steve Capper
2014-02-25 11:38 ` [PATCH V2 1/3] arm: mm: Double logical invert for pte accessors Steve Capper
2014-02-28 15:45   ` Catalin Marinas
2014-03-26 11:01   ` Catalin Marinas
2014-02-25 11:38 ` [PATCH V2 2/3] arm64: mm: Add double logical invert to " Steve Capper
2014-02-28 15:46   ` Catalin Marinas
2014-02-25 11:38 ` [PATCH V2 3/3] arm: mm: Switch back to L_PTE_WRITE Steve Capper
2014-03-26 11:00   ` Catalin Marinas
2014-02-25 15:22 ` [PATCH V2 0/3] PTE fixes for arm and arm64 Will Deacon
2014-02-25 17:08   ` Steve Capper
2014-03-26 10:23 ` Steve Capper
2014-03-26 11:01   ` Russell King - ARM Linux
2014-03-26 13:23     ` Steve Capper
2014-03-26 13:48       ` Russell King - ARM Linux
2014-03-27 10:01         ` Steve Capper

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.