* hmm_pfn_t - HMM uses its own pfn type to keep several flags per page
  *
  * Flags:
- * HMM_PFN_VALID: pfn is valid
- * HMM_PFN_READ:  CPU page table has read permission set
+ * HMM_PFN_VALID: pfn is valid. It has, at least, read permission.
  * HMM_PFN_WRITE: CPU page table has write permission set
  * HMM_PFN_ERROR: corresponding CPU page table entry points to poisoned memory
  * HMM_PFN_EMPTY: corresponding CPU page table entry is pte_none()
 typedef unsigned long hmm_pfn_t;
 
 #define HMM_PFN_VALID (1 << 0)
-#define HMM_PFN_READ (1 << 1)
-#define HMM_PFN_WRITE (1 << 2)
-#define HMM_PFN_ERROR (1 << 3)
-#define HMM_PFN_EMPTY (1 << 4)
-#define HMM_PFN_SPECIAL (1 << 5)
-#define HMM_PFN_DEVICE_UNADDRESSABLE (1 << 6)
-#define HMM_PFN_SHIFT 7
+#define HMM_PFN_WRITE (1 << 1)
+#define HMM_PFN_ERROR (1 << 2)
+#define HMM_PFN_EMPTY (1 << 3)
+#define HMM_PFN_SPECIAL (1 << 4)
+#define HMM_PFN_DEVICE_UNADDRESSABLE (1 << 5)
+#define HMM_PFN_SHIFT 6
 
 /*
  * hmm_pfn_t_to_page() - return struct page pointed to by a valid hmm_pfn_t
 
        hmm_pfn_t *pfns = range->pfns;
        unsigned long addr = start, i;
        bool write_fault;
-       hmm_pfn_t flag;
        pte_t *ptep;
 
        i = (addr - range->start) >> PAGE_SHIFT;
-       flag = vma->vm_flags & VM_READ ? HMM_PFN_READ : 0;
        write_fault = hmm_vma_walk->fault & hmm_vma_walk->write;
 
 again:
 
        if (pmd_devmap(*pmdp) || pmd_trans_huge(*pmdp)) {
                unsigned long pfn;
+               hmm_pfn_t flag = 0;
                pmd_t pmd;
 
                /*
                                } else if (write_fault)
                                        goto fault;
                                pfns[i] |= HMM_PFN_DEVICE_UNADDRESSABLE;
-                               pfns[i] |= flag;
                        } else if (is_migration_entry(entry)) {
                                if (hmm_vma_walk->fault) {
                                        pte_unmap(ptep);
                if (write_fault && !pte_write(pte))
                        goto fault;
 
-               pfns[i] = hmm_pfn_t_from_pfn(pte_pfn(pte)) | flag;
+               pfns[i] = hmm_pfn_t_from_pfn(pte_pfn(pte));
                pfns[i] |= pte_write(pte) ? HMM_PFN_WRITE : 0;
                continue;
 
 /*
  * hmm_vma_get_pfns() - snapshot CPU page table for a range of virtual addresses
  * @range: range being snapshotted
- * Returns: -EINVAL if invalid argument, -ENOMEM out of memory, 0 success
+ * Returns: -EINVAL if invalid argument, -ENOMEM out of memory, -EPERM invalid
+ *          vma permission, 0 success
  *
  * This snapshots the CPU page table for a range of virtual addresses. Snapshot
  * validity is tracked by range struct. See hmm_vma_range_done() for further
        if (!hmm->mmu_notifier.ops)
                return -EINVAL;
 
+       if (!(vma->vm_flags & VM_READ)) {
+               /*
+                * If vma do not allow read access, then assume that it does
+                * not allow write access, either. Architecture that allow
+                * write without read access are not supported by HMM, because
+                * operations such has atomic access would not work.
+                */
+               hmm_pfns_clear(range->pfns, range->start, range->end);
+               return -EPERM;
+       }
+
        /* Initialize range to track CPU page table update */
        spin_lock(&hmm->lock);
        range->valid = true;
  *     goto retry;
  *   case 0:
  *     break;
+ *   case -ENOMEM:
+ *   case -EINVAL:
+ *   case -EPERM:
  *   default:
  *     // Handle error !
  *     up_read(&mm->mmap_sem)
        if (!hmm->mmu_notifier.ops)
                return -EINVAL;
 
-       /* Initialize range to track CPU page table update */
-       spin_lock(&hmm->lock);
-       range->valid = true;
-       list_add_rcu(&range->list, &hmm->ranges);
-       spin_unlock(&hmm->lock);
+       if (!(vma->vm_flags & VM_READ)) {
+               /*
+                * If vma do not allow read access, then assume that it does
+                * not allow write access, either. Architecture that allow
+                * write without read access are not supported by HMM, because
+                * operations such has atomic access would not work.
+                */
+               hmm_pfns_clear(range->pfns, range->start, range->end);
+               return -EPERM;
+       }
 
        /* FIXME support hugetlb fs */
        if (is_vm_hugetlb_page(vma) || (vma->vm_flags & VM_SPECIAL)) {
                return 0;
        }
 
+       /* Initialize range to track CPU page table update */
+       spin_lock(&hmm->lock);
+       range->valid = true;
+       list_add_rcu(&range->list, &hmm->ranges);
+       spin_unlock(&hmm->lock);
+
        hmm_vma_walk.fault = true;
        hmm_vma_walk.write = write;
        hmm_vma_walk.block = block;