s390/pageattr: do a single TLB flush for change_page_attr
authorMartin Schwidefsky <schwidefsky@de.ibm.com>
Thu, 4 Feb 2016 11:24:46 +0000 (12:24 +0100)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Tue, 23 Feb 2016 07:56:17 +0000 (08:56 +0100)
The change of the access rights for an address range in the kernel
address space is currently done with a loop of IPTE + a store of the
modified PTE. Between the IPTE and the store the PTE will be invalid,
this intermediate state can cause problems with concurrent accesses.

Consider a change of a kernel area from read-write to read-only, a
concurrent reader of that area should be fine but with the invalid
PTE it might get an unexpected exception.

Remove the IPTEs for each PTE and do a global flush after all PTEs
have been modified.

Reviewed-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/mm/pageattr.c

index 749c98407b41f02e37e1aec174dcc2ca51674a35..f2a5c29a97e9f455fba6cc81bad7399a80354594 100644 (file)
@@ -65,19 +65,17 @@ static pte_t *walk_page_table(unsigned long addr)
 static void change_page_attr(unsigned long addr, int numpages,
                             pte_t (*set) (pte_t))
 {
-       pte_t *ptep, pte;
+       pte_t *ptep;
        int i;
 
        for (i = 0; i < numpages; i++) {
                ptep = walk_page_table(addr);
                if (WARN_ON_ONCE(!ptep))
                        break;
-               pte = *ptep;
-               pte = set(pte);
-               __ptep_ipte(addr, ptep);
-               *ptep = pte;
+               *ptep = set(*ptep);
                addr += PAGE_SIZE;
        }
+       __tlb_flush_kernel();
 }
 
 int set_memory_ro(unsigned long addr, int numpages)