aboutsummaryrefslogtreecommitdiffstats
path: root/include/linux/memremap.h
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2016-01-15 19:56:49 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2016-01-15 20:56:32 -0500
commit5c2c2587b13235bf8b5c9027589f22eff68bdf49 (patch)
tree718d85e1921e2d408bb207261f3000a286815aaa /include/linux/memremap.h
parent468ded03c07e0f2b5f05332bc255add47b1b0dee (diff)
mm, dax, pmem: introduce {get|put}_dev_pagemap() for dax-gup
get_dev_page() enables paths like get_user_pages() to pin a dynamically mapped pfn-range (devm_memremap_pages()) while the resulting struct page objects are in use. Unlike get_page() it may fail if the device is, or is in the process of being, disabled. While the initial lookup of the range may be an expensive list walk, the result is cached to speed up subsequent lookups which are likely to be in the same mapped range. devm_memremap_pages() now requires a reference counter to be specified at init time. For pmem this means moving request_queue allocation into pmem_alloc() so the existing queue usage counter can track "device pages". ZONE_DEVICE pages always have an elevated count and will never be on an lru reclaim list. That space in 'struct page' can be redirected for other uses, but for safety introduce a poison value that will always trip __list_add() to assert. This allows half of the struct list_head storage to be reclaimed with some assurance to back up the assumption that the page count never goes to zero and a list_add() is never attempted. Signed-off-by: Dan Williams <dan.j.williams@intel.com> Tested-by: Logan Gunthorpe <logang@deltatee.com> Cc: Dave Hansen <dave@sr71.net> Cc: Matthew Wilcox <willy@linux.intel.com> Cc: Ross Zwisler <ross.zwisler@linux.intel.com> Cc: Alexander Viro <viro@zeniv.linux.org.uk> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'include/linux/memremap.h')
-rw-r--r--include/linux/memremap.h49
1 files changed, 47 insertions, 2 deletions
diff --git a/include/linux/memremap.h b/include/linux/memremap.h
index aa3e82a80d7b..bcaa634139a9 100644
--- a/include/linux/memremap.h
+++ b/include/linux/memremap.h
@@ -1,6 +1,8 @@
1#ifndef _LINUX_MEMREMAP_H_ 1#ifndef _LINUX_MEMREMAP_H_
2#define _LINUX_MEMREMAP_H_ 2#define _LINUX_MEMREMAP_H_
3#include <linux/mm.h> 3#include <linux/mm.h>
4#include <linux/ioport.h>
5#include <linux/percpu-refcount.h>
4 6
5struct resource; 7struct resource;
6struct device; 8struct device;
@@ -36,21 +38,25 @@ static inline struct vmem_altmap *to_vmem_altmap(unsigned long memmap_start)
36/** 38/**
37 * struct dev_pagemap - metadata for ZONE_DEVICE mappings 39 * struct dev_pagemap - metadata for ZONE_DEVICE mappings
38 * @altmap: pre-allocated/reserved memory for vmemmap allocations 40 * @altmap: pre-allocated/reserved memory for vmemmap allocations
41 * @res: physical address range covered by @ref
42 * @ref: reference count that pins the devm_memremap_pages() mapping
39 * @dev: host device of the mapping for debug 43 * @dev: host device of the mapping for debug
40 */ 44 */
41struct dev_pagemap { 45struct dev_pagemap {
42 struct vmem_altmap *altmap; 46 struct vmem_altmap *altmap;
43 const struct resource *res; 47 const struct resource *res;
48 struct percpu_ref *ref;
44 struct device *dev; 49 struct device *dev;
45}; 50};
46 51
47#ifdef CONFIG_ZONE_DEVICE 52#ifdef CONFIG_ZONE_DEVICE
48void *devm_memremap_pages(struct device *dev, struct resource *res, 53void *devm_memremap_pages(struct device *dev, struct resource *res,
49 struct vmem_altmap *altmap); 54 struct percpu_ref *ref, struct vmem_altmap *altmap);
50struct dev_pagemap *find_dev_pagemap(resource_size_t phys); 55struct dev_pagemap *find_dev_pagemap(resource_size_t phys);
51#else 56#else
52static inline void *devm_memremap_pages(struct device *dev, 57static inline void *devm_memremap_pages(struct device *dev,
53 struct resource *res, struct vmem_altmap *altmap) 58 struct resource *res, struct percpu_ref *ref,
59 struct vmem_altmap *altmap)
54{ 60{
55 /* 61 /*
56 * Fail attempts to call devm_memremap_pages() without 62 * Fail attempts to call devm_memremap_pages() without
@@ -66,4 +72,43 @@ static inline struct dev_pagemap *find_dev_pagemap(resource_size_t phys)
66 return NULL; 72 return NULL;
67} 73}
68#endif 74#endif
75
76/**
77 * get_dev_pagemap() - take a new live reference on the dev_pagemap for @pfn
78 * @pfn: page frame number to lookup page_map
79 * @pgmap: optional known pgmap that already has a reference
80 *
81 * @pgmap allows the overhead of a lookup to be bypassed when @pfn lands in the
82 * same mapping.
83 */
84static inline struct dev_pagemap *get_dev_pagemap(unsigned long pfn,
85 struct dev_pagemap *pgmap)
86{
87 const struct resource *res = pgmap ? pgmap->res : NULL;
88 resource_size_t phys = PFN_PHYS(pfn);
89
90 /*
91 * In the cached case we're already holding a live reference so
92 * we can simply do a blind increment
93 */
94 if (res && phys >= res->start && phys <= res->end) {
95 percpu_ref_get(pgmap->ref);
96 return pgmap;
97 }
98
99 /* fall back to slow path lookup */
100 rcu_read_lock();
101 pgmap = find_dev_pagemap(phys);
102 if (pgmap && !percpu_ref_tryget_live(pgmap->ref))
103 pgmap = NULL;
104 rcu_read_unlock();
105
106 return pgmap;
107}
108
109static inline void put_dev_pagemap(struct dev_pagemap *pgmap)
110{
111 if (pgmap)
112 percpu_ref_put(pgmap->ref);
113}
69#endif /* _LINUX_MEMREMAP_H_ */ 114#endif /* _LINUX_MEMREMAP_H_ */