#include <asm/pgtable.h>
 
 
+static pte_basic_t pte_update_delta(pte_t *ptep, unsigned long addr,
+                                   unsigned long old, unsigned long new)
+{
+       return pte_update(&init_mm, addr, ptep, old & ~new, new & ~old, 0);
+}
+
 /*
- * Updates the attributes of a page in three steps:
- *
- * 1. take the page_table_lock
- * 2. install the new entry with the updated attributes
- * 3. flush the TLB
+ * Updates the attributes of a page atomically.
  *
  * This sequence is safe against concurrent updates, and also allows updating the
  * attributes of a page currently being executed or accessed.
 static int change_page_attr(pte_t *ptep, unsigned long addr, void *data)
 {
        long action = (long)data;
-       pte_t pte;
-
-       spin_lock(&init_mm.page_table_lock);
-
-       pte = ptep_get(ptep);
 
-       /* modify the PTE bits as desired, then apply */
+       /* modify the PTE bits as desired */
        switch (action) {
        case SET_MEMORY_RO:
-               pte = pte_wrprotect(pte);
+               /* Don't clear DIRTY bit */
+               pte_update_delta(ptep, addr, _PAGE_KERNEL_RW & ~_PAGE_DIRTY, _PAGE_KERNEL_RO);
                break;
        case SET_MEMORY_RW:
-               pte = pte_mkwrite(pte_mkdirty(pte));
+               pte_update_delta(ptep, addr, _PAGE_KERNEL_RO, _PAGE_KERNEL_RW);
                break;
        case SET_MEMORY_NX:
-               pte = pte_exprotect(pte);
+               pte_update_delta(ptep, addr, _PAGE_KERNEL_ROX, _PAGE_KERNEL_RO);
                break;
        case SET_MEMORY_X:
-               pte = pte_mkexec(pte);
+               pte_update_delta(ptep, addr, _PAGE_KERNEL_RO, _PAGE_KERNEL_ROX);
                break;
        default:
                WARN_ON_ONCE(1);
                break;
        }
 
-       pte_update(&init_mm, addr, ptep, ~0UL, pte_val(pte), 0);
-
        /* See ptesync comment in radix__set_pte_at() */
        if (radix_enabled())
                asm volatile("ptesync": : :"memory");
 
        flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
 
-       spin_unlock(&init_mm.page_table_lock);
-
        return 0;
 }