@@ -2945,3 +2945,44 @@ static int __init filelock_init(void)
return 0;
}
core_initcall(filelock_init);
+
+/**
+ * mapping_inode_has_layout()
+ * @page page we are trying to GUP
+ *
+ * This should only be called on DAX pages. DAX pages which are mapped through
+ * FS DAX do not use the page cache. As a result they require the user to take
+ * a LAYOUT lease on them prior to be able to pin them for longterm use.
+ * This allows the user to opt-into the fact that truncation operations will
+ * fail for the duration of the pin.
+ *
+ * @Return true if the page has a LAYOUT lease associated with it's file.
+ */
+bool mapping_inode_has_layout(struct page *page)
+{
+ bool ret = false;
+ struct inode *inode;
+ struct file_lock *fl;
+ struct file_lock_context *ctx;
+
+ if (WARN_ON(PageAnon(page)) ||
+ WARN_ON(!page) ||
+ WARN_ON(!page->mapping) ||
+ WARN_ON(!page->mapping->host))
+ return false;
+
+ inode = page->mapping->host;
+
+ ctx = locks_get_lock_context(inode, F_RDLCK);
+ spin_lock(&ctx->flc_lock);
+ list_for_each_entry(fl, &ctx->flc_lease, fl_list) {
+ if (fl->fl_flags & FL_LAYOUT) {
+ ret = true;
+ break;
+ }
+ }
+ spin_unlock(&ctx->flc_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mapping_inode_has_layout);
@@ -1630,6 +1630,8 @@ 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);
+bool mapping_inode_has_layout(struct page *page);
+
/* Container for pinned pfns / pages */
struct frame_vector {
unsigned int nr_allocated; /* Number of frames we have space for */
@@ -361,6 +361,13 @@ static struct page *follow_page_pte(struct vm_area_struct *vma,
page = pte_page(pte);
else
goto no_page;
+
+ if (unlikely(flags & FOLL_LONGTERM) &&
+ (*pgmap)->type == MEMORY_DEVICE_FS_DAX &&
+ !mapping_inode_has_layout(page)) {
+ page = ERR_PTR(-EPERM);
+ goto out;
+ }
} else if (unlikely(!page)) {
if (flags & FOLL_DUMP) {
/* Avoid special (like zero) pages in core dumps */
@@ -1905,6 +1912,16 @@ static int gup_pte_range(pmd_t pmd, unsigned long addr, unsigned long end,
VM_BUG_ON_PAGE(compound_head(page) != head, page);
+ if (pte_devmap(pte) &&
+ unlikely(flags & FOLL_LONGTERM) &&
+ pgmap->type == MEMORY_DEVICE_FS_DAX &&
+ !mapping_inode_has_layout(head)) {
+ mod_node_page_state(page_pgdat(head),
+ NR_GUP_FAST_PAGE_BACKOFFS, 1);
+ put_user_page(head);
+ goto pte_unmap;
+ }
+
SetPageReferenced(page);
pages[*nr] = page;
(*nr)++;
@@ -1955,6 +1972,14 @@ static int __gup_device_huge(unsigned long pfn, unsigned long addr,
}
SetPageReferenced(page);
pages[*nr] = page;
+
+ if (unlikely(flags & FOLL_LONGTERM) &&
+ pgmap->type == MEMORY_DEVICE_FS_DAX &&
+ !mapping_inode_has_layout(page)) {
+ undo_dev_pagemap(nr, nr_start, pages);
+ return 0;
+ }
+
if (try_get_gup_pin_page(page, NR_GUP_FAST_PAGES_REQUESTED)) {
undo_dev_pagemap(nr, nr_start, pages);
return 0;
@@ -950,6 +950,12 @@ struct page *follow_devmap_pmd(struct vm_area_struct *vma, unsigned long addr,
if (!*pgmap)
return ERR_PTR(-EFAULT);
page = pfn_to_page(pfn);
+
+ if (unlikely(flags & FOLL_LONGTERM) &&
+ (*pgmap)->type == MEMORY_DEVICE_FS_DAX &&
+ !mapping_inode_has_layout(page))
+ return ERR_PTR(-EPERM);
+
if (unlikely(!try_get_gup_pin_page(page,
NR_GUP_SLOW_PAGES_REQUESTED)))
page = ERR_PTR(-ENOMEM);
@@ -1092,6 +1098,12 @@ struct page *follow_devmap_pud(struct vm_area_struct *vma, unsigned long addr,
if (!*pgmap)
return ERR_PTR(-EFAULT);
page = pfn_to_page(pfn);
+
+ if (unlikely(flags & FOLL_LONGTERM) &&
+ (*pgmap)->type == MEMORY_DEVICE_FS_DAX &&
+ !mapping_inode_has_layout(page))
+ return ERR_PTR(-EPERM);
+
if (unlikely(!try_get_gup_pin_page(page,
NR_GUP_SLOW_PAGES_REQUESTED)))
page = ERR_PTR(-ENOMEM);