Merge tag 'drm-next-2020-02-04' of git://anongit.freedesktop.org/drm/drm
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 4 Feb 2020 07:21:04 +0000 (07:21 +0000)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 4 Feb 2020 07:21:04 +0000 (07:21 +0000)
Pull drm ttm/mm updates from Dave Airlie:
 "Thomas Hellstrom has some more changes to the TTM layer that needed a
  patch to the mm subsystem.

  This adds a new mm API vmf_insert_mixed_prot to avoid an ugly hack
  that has limitations in the TTM layer"

* tag 'drm-next-2020-02-04' of git://anongit.freedesktop.org/drm/drm:
  mm, drm/ttm: Fix vm page protection handling
  mm: Add a vmf_insert_mixed_prot() function

1  2 
include/linux/mm.h
include/linux/mm_types.h
mm/memory.c

diff --combined include/linux/mm.h
@@@ -70,6 -70,11 +70,6 @@@ static inline void totalram_pages_add(l
        atomic_long_add(count, &_totalram_pages);
  }
  
 -static inline void totalram_pages_set(long val)
 -{
 -      atomic_long_set(&_totalram_pages, val);
 -}
 -
  extern void * high_memory;
  extern int page_cluster;
  
@@@ -620,19 -625,24 +620,19 @@@ unsigned long vmalloc_to_pfn(const voi
   * On nommu, vmalloc/vfree wrap through kmalloc/kfree directly, so there
   * is no special casing required.
   */
 -static inline bool is_vmalloc_addr(const void *x)
 -{
 -#ifdef CONFIG_MMU
 -      unsigned long addr = (unsigned long)x;
 -
 -      return addr >= VMALLOC_START && addr < VMALLOC_END;
 -#else
 -      return false;
 -#endif
 -}
  
  #ifndef is_ioremap_addr
  #define is_ioremap_addr(x) is_vmalloc_addr(x)
  #endif
  
  #ifdef CONFIG_MMU
 +extern bool is_vmalloc_addr(const void *x);
  extern int is_vmalloc_or_module_addr(const void *x);
  #else
 +static inline bool is_vmalloc_addr(const void *x)
 +{
 +      return false;
 +}
  static inline int is_vmalloc_or_module_addr(const void *x)
  {
        return 0;
@@@ -911,6 -921,10 +911,6 @@@ vm_fault_t finish_mkwrite_fault(struct 
  
  #define ZONEID_PGSHIFT                (ZONEID_PGOFF * (ZONEID_SHIFT != 0))
  
 -#if SECTIONS_WIDTH+NODES_WIDTH+ZONES_WIDTH > BITS_PER_LONG - NR_PAGEFLAGS
 -#error SECTIONS_WIDTH+NODES_WIDTH+ZONES_WIDTH > BITS_PER_LONG - NR_PAGEFLAGS
 -#endif
 -
  #define ZONES_MASK            ((1UL << ZONES_WIDTH) - 1)
  #define NODES_MASK            ((1UL << NODES_WIDTH) - 1)
  #define SECTIONS_MASK         ((1UL << SECTIONS_WIDTH) - 1)
@@@ -938,10 -952,9 +938,10 @@@ static inline bool is_zone_device_page(
  #endif
  
  #ifdef CONFIG_DEV_PAGEMAP_OPS
 -void __put_devmap_managed_page(struct page *page);
 +void free_devmap_managed_page(struct page *page);
  DECLARE_STATIC_KEY_FALSE(devmap_managed_key);
 -static inline bool put_devmap_managed_page(struct page *page)
 +
 +static inline bool page_is_devmap_managed(struct page *page)
  {
        if (!static_branch_unlikely(&devmap_managed_key))
                return false;
        switch (page->pgmap->type) {
        case MEMORY_DEVICE_PRIVATE:
        case MEMORY_DEVICE_FS_DAX:
 -              __put_devmap_managed_page(page);
                return true;
        default:
                break;
        return false;
  }
  
 +void put_devmap_managed_page(struct page *page);
 +
  #else /* CONFIG_DEV_PAGEMAP_OPS */
 -static inline bool put_devmap_managed_page(struct page *page)
 +static inline bool page_is_devmap_managed(struct page *page)
  {
        return false;
  }
 +
 +static inline void put_devmap_managed_page(struct page *page)
 +{
 +}
  #endif /* CONFIG_DEV_PAGEMAP_OPS */
  
  static inline bool is_device_private_page(const struct page *page)
@@@ -1020,37 -1028,37 +1020,37 @@@ static inline void put_page(struct pag
         * need to inform the device driver through callback. See
         * include/linux/memremap.h and HMM for details.
         */
 -      if (put_devmap_managed_page(page))
 +      if (page_is_devmap_managed(page)) {
 +              put_devmap_managed_page(page);
                return;
 +      }
  
        if (put_page_testzero(page))
                __put_page(page);
  }
  
  /**
 - * put_user_page() - release a gup-pinned page
 + * unpin_user_page() - release a gup-pinned page
   * @page:            pointer to page to be released
   *
 - * Pages that were pinned via get_user_pages*() must be released via
 - * either put_user_page(), or one of the put_user_pages*() routines
 - * below. This is so that eventually, pages that are pinned via
 - * get_user_pages*() can be separately tracked and uniquely handled. In
 - * particular, interactions with RDMA and filesystems need special
 - * handling.
 + * Pages that were pinned via pin_user_pages*() must be released via either
 + * unpin_user_page(), or one of the unpin_user_pages*() routines. This is so
 + * that eventually such pages can be separately tracked and uniquely handled. In
 + * particular, interactions with RDMA and filesystems need special handling.
   *
 - * put_user_page() and put_page() are not interchangeable, despite this early
 - * implementation that makes them look the same. put_user_page() calls must
 - * be perfectly matched up with get_user_page() calls.
 + * unpin_user_page() and put_page() are not interchangeable, despite this early
 + * implementation that makes them look the same. unpin_user_page() calls must
 + * be perfectly matched up with pin*() calls.
   */
 -static inline void put_user_page(struct page *page)
 +static inline void unpin_user_page(struct page *page)
  {
        put_page(page);
  }
  
 -void put_user_pages_dirty_lock(struct page **pages, unsigned long npages,
 -                             bool make_dirty);
 +void unpin_user_pages_dirty_lock(struct page **pages, unsigned long npages,
 +                               bool make_dirty);
  
 -void put_user_pages(struct page **pages, unsigned long npages);
 +void unpin_user_pages(struct page **pages, unsigned long npages);
  
  #if defined(CONFIG_SPARSEMEM) && !defined(CONFIG_SPARSEMEM_VMEMMAP)
  #define SECTION_IN_PAGE_FLAGS
@@@ -1498,16 -1506,9 +1498,16 @@@ long get_user_pages_remote(struct task_
                            unsigned long start, unsigned long nr_pages,
                            unsigned int gup_flags, struct page **pages,
                            struct vm_area_struct **vmas, int *locked);
 +long pin_user_pages_remote(struct task_struct *tsk, struct mm_struct *mm,
 +                         unsigned long start, unsigned long nr_pages,
 +                         unsigned int gup_flags, struct page **pages,
 +                         struct vm_area_struct **vmas, int *locked);
  long get_user_pages(unsigned long start, unsigned long nr_pages,
                            unsigned int gup_flags, struct page **pages,
                            struct vm_area_struct **vmas);
 +long pin_user_pages(unsigned long start, unsigned long nr_pages,
 +                  unsigned int gup_flags, struct page **pages,
 +                  struct vm_area_struct **vmas);
  long get_user_pages_locked(unsigned long start, unsigned long nr_pages,
                    unsigned int gup_flags, struct page **pages, int *locked);
  long get_user_pages_unlocked(unsigned long start, unsigned long nr_pages,
  
  int get_user_pages_fast(unsigned long start, int nr_pages,
                        unsigned int gup_flags, struct page **pages);
 +int pin_user_pages_fast(unsigned long start, int nr_pages,
 +                      unsigned int gup_flags, struct page **pages);
  
  int account_locked_vm(struct mm_struct *mm, unsigned long pages, bool inc);
  int __account_locked_vm(struct mm_struct *mm, unsigned long pages, bool inc,
@@@ -2329,7 -2328,6 +2329,7 @@@ extern int __do_munmap(struct mm_struc
                       struct list_head *uf, bool downgrade);
  extern int do_munmap(struct mm_struct *, unsigned long, size_t,
                     struct list_head *uf);
 +extern int do_madvise(unsigned long start, size_t len_in, int behavior);
  
  static inline unsigned long
  do_mmap_pgoff(struct file *file, unsigned long addr,
@@@ -2535,6 -2533,8 +2535,8 @@@ vm_fault_t vmf_insert_pfn_prot(struct v
                        unsigned long pfn, pgprot_t pgprot);
  vm_fault_t vmf_insert_mixed(struct vm_area_struct *vma, unsigned long addr,
                        pfn_t pfn);
+ vm_fault_t vmf_insert_mixed_prot(struct vm_area_struct *vma, unsigned long addr,
+                       pfn_t pfn, pgprot_t pgprot);
  vm_fault_t vmf_insert_mixed_mkwrite(struct vm_area_struct *vma,
                unsigned long addr, pfn_t pfn);
  int vm_iomap_memory(struct vm_area_struct *vma, phys_addr_t start, unsigned long len);
@@@ -2581,15 -2581,13 +2583,15 @@@ struct page *follow_page(struct vm_area
  #define FOLL_ANON     0x8000  /* don't do file mappings */
  #define FOLL_LONGTERM 0x10000 /* mapping lifetime is indefinite: see below */
  #define FOLL_SPLIT_PMD        0x20000 /* split huge pmd before returning */
 +#define FOLL_PIN      0x40000 /* pages must be released via unpin_user_page */
  
  /*
 - * NOTE on FOLL_LONGTERM:
 + * FOLL_PIN and FOLL_LONGTERM may be used in various combinations with each
 + * other. Here is what they mean, and how to use them:
   *
   * FOLL_LONGTERM indicates that the page will be held for an indefinite time
 - * period _often_ under userspace control.  This is contrasted with
 - * iov_iter_get_pages() where usages which are transient.
 + * period _often_ under userspace control.  This is in contrast to
 + * iov_iter_get_pages(), whose usages are transient.
   *
   * FIXME: For pages which are part of a filesystem, mappings are subject to the
   * lifetime enforced by the filesystem and we need guarantees that longterm
   * Currently only get_user_pages() and get_user_pages_fast() support this flag
   * and calls to get_user_pages_[un]locked are specifically not allowed.  This
   * is due to an incompatibility with the FS DAX check and
 - * FAULT_FLAG_ALLOW_RETRY
 + * FAULT_FLAG_ALLOW_RETRY.
   *
 - * In the CMA case: longterm pins in a CMA region would unnecessarily fragment
 - * that region.  And so CMA attempts to migrate the page before pinning when
 + * In the CMA case: long term pins in a CMA region would unnecessarily fragment
 + * that region.  And so, CMA attempts to migrate the page before pinning, when
   * FOLL_LONGTERM is specified.
 + *
 + * FOLL_PIN indicates that a special kind of tracking (not just page->_refcount,
 + * but an additional pin counting system) will be invoked. This is intended for
 + * anything that gets a page reference and then touches page data (for example,
 + * Direct IO). This lets the filesystem know that some non-file-system entity is
 + * potentially changing the pages' data. In contrast to FOLL_GET (whose pages
 + * are released via put_page()), FOLL_PIN pages must be released, ultimately, by
 + * a call to unpin_user_page().
 + *
 + * FOLL_PIN is similar to FOLL_GET: both of these pin pages. They use different
 + * and separate refcounting mechanisms, however, and that means that each has
 + * its own acquire and release mechanisms:
 + *
 + *     FOLL_GET: get_user_pages*() to acquire, and put_page() to release.
 + *
 + *     FOLL_PIN: pin_user_pages*() to acquire, and unpin_user_pages to release.
 + *
 + * FOLL_PIN and FOLL_GET are mutually exclusive for a given function call.
 + * (The underlying pages may experience both FOLL_GET-based and FOLL_PIN-based
 + * calls applied to them, and that's perfectly OK. This is a constraint on the
 + * callers, not on the pages.)
 + *
 + * FOLL_PIN should be set internally by the pin_user_pages*() APIs, never
 + * directly by the caller. That's in order to help avoid mismatches when
 + * releasing pages: get_user_pages*() pages must be released via put_page(),
 + * while pin_user_pages*() pages must be released via unpin_user_page().
 + *
 + * Please see Documentation/vm/pin_user_pages.rst for more information.
   */
  
  static inline int vm_fault_to_errno(vm_fault_t vm_fault, int foll_flags)
diff --combined include/linux/mm_types.h
@@@ -312,7 -312,12 +312,12 @@@ struct vm_area_struct 
        /* Second cache line starts here. */
  
        struct mm_struct *vm_mm;        /* The address space we belong to. */
-       pgprot_t vm_page_prot;          /* Access permissions of this VMA. */
+       /*
+        * Access permissions of this VMA.
+        * See vmf_insert_mixed_prot() for discussion.
+        */
+       pgprot_t vm_page_prot;
        unsigned long vm_flags;         /* Flags, see mm.h. */
  
        /*
@@@ -490,7 -495,7 +495,7 @@@ struct mm_struct 
                /* store ref to file /proc/<pid>/exe symlink points to */
                struct file __rcu *exe_file;
  #ifdef CONFIG_MMU_NOTIFIER
 -              struct mmu_notifier_mm *mmu_notifier_mm;
 +              struct mmu_notifier_subscriptions *notifier_subscriptions;
  #endif
  #if defined(CONFIG_TRANSPARENT_HUGEPAGE) && !USE_SPLIT_PMD_PTLOCKS
                pgtable_t pmd_huge_pte; /* protected by page_table_lock */
diff --combined mm/memory.c
@@@ -1664,6 -1664,9 +1664,9 @@@ out_unlock
   * vmf_insert_pfn_prot should only be used if using multiple VMAs is
   * impractical.
   *
+  * See vmf_insert_mixed_prot() for a discussion of the implication of using
+  * a value of @pgprot different from that of @vma->vm_page_prot.
+  *
   * Context: Process context.  May allocate using %GFP_KERNEL.
   * Return: vm_fault_t value.
   */
@@@ -1737,9 -1740,9 +1740,9 @@@ static bool vm_mixed_ok(struct vm_area_
  }
  
  static vm_fault_t __vm_insert_mixed(struct vm_area_struct *vma,
-               unsigned long addr, pfn_t pfn, bool mkwrite)
+               unsigned long addr, pfn_t pfn, pgprot_t pgprot,
+               bool mkwrite)
  {
-       pgprot_t pgprot = vma->vm_page_prot;
        int err;
  
        BUG_ON(!vm_mixed_ok(vma, pfn));
        return VM_FAULT_NOPAGE;
  }
  
+ /**
+  * vmf_insert_mixed_prot - insert single pfn into user vma with specified pgprot
+  * @vma: user vma to map to
+  * @addr: target user address of this page
+  * @pfn: source kernel pfn
+  * @pgprot: pgprot flags for the inserted page
+  *
+  * This is exactly like vmf_insert_mixed(), except that it allows drivers to
+  * to override pgprot on a per-page basis.
+  *
+  * Typically this function should be used by drivers to set caching- and
+  * encryption bits different than those of @vma->vm_page_prot, because
+  * the caching- or encryption mode may not be known at mmap() time.
+  * This is ok as long as @vma->vm_page_prot is not used by the core vm
+  * to set caching and encryption bits for those vmas (except for COW pages).
+  * This is ensured by core vm only modifying these page table entries using
+  * functions that don't touch caching- or encryption bits, using pte_modify()
+  * if needed. (See for example mprotect()).
+  * Also when new page-table entries are created, this is only done using the
+  * fault() callback, and never using the value of vma->vm_page_prot,
+  * except for page-table entries that point to anonymous pages as the result
+  * of COW.
+  *
+  * Context: Process context.  May allocate using %GFP_KERNEL.
+  * Return: vm_fault_t value.
+  */
+ vm_fault_t vmf_insert_mixed_prot(struct vm_area_struct *vma, unsigned long addr,
+                                pfn_t pfn, pgprot_t pgprot)
+ {
+       return __vm_insert_mixed(vma, addr, pfn, pgprot, false);
+ }
+ EXPORT_SYMBOL(vmf_insert_mixed_prot);
  vm_fault_t vmf_insert_mixed(struct vm_area_struct *vma, unsigned long addr,
                pfn_t pfn)
  {
-       return __vm_insert_mixed(vma, addr, pfn, false);
+       return __vm_insert_mixed(vma, addr, pfn, vma->vm_page_prot, false);
  }
  EXPORT_SYMBOL(vmf_insert_mixed);
  
  vm_fault_t vmf_insert_mixed_mkwrite(struct vm_area_struct *vma,
                unsigned long addr, pfn_t pfn)
  {
-       return __vm_insert_mixed(vma, addr, pfn, true);
+       return __vm_insert_mixed(vma, addr, pfn, vma->vm_page_prot, true);
  }
  EXPORT_SYMBOL(vmf_insert_mixed_mkwrite);
  
@@@ -2203,7 -2239,7 +2239,7 @@@ static inline int pte_unmap_same(struc
                                pte_t *page_table, pte_t orig_pte)
  {
        int same = 1;
 -#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT)
 +#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPTION)
        if (sizeof(pte_t) > sizeof(unsigned long)) {
                spinlock_t *ptl = pte_lockptr(mm, pmd);
                spin_lock(ptl);