]> git.karo-electronics.de Git - linux-beck.git/blobdiff - include/linux/memremap.h
mm, dax, pmem: introduce {get|put}_dev_pagemap() for dax-gup
[linux-beck.git] / include / linux / memremap.h
index aa3e82a80d7b56e7a6c3251314ad7d77e634f628..bcaa634139a996160247738d94107ce08485e185 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef _LINUX_MEMREMAP_H_
 #define _LINUX_MEMREMAP_H_
 #include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/percpu-refcount.h>
 
 struct resource;
 struct device;
@@ -36,21 +38,25 @@ static inline struct vmem_altmap *to_vmem_altmap(unsigned long memmap_start)
 /**
  * struct dev_pagemap - metadata for ZONE_DEVICE mappings
  * @altmap: pre-allocated/reserved memory for vmemmap allocations
+ * @res: physical address range covered by @ref
+ * @ref: reference count that pins the devm_memremap_pages() mapping
  * @dev: host device of the mapping for debug
  */
 struct dev_pagemap {
        struct vmem_altmap *altmap;
        const struct resource *res;
+       struct percpu_ref *ref;
        struct device *dev;
 };
 
 #ifdef CONFIG_ZONE_DEVICE
 void *devm_memremap_pages(struct device *dev, struct resource *res,
-               struct vmem_altmap *altmap);
+               struct percpu_ref *ref, struct vmem_altmap *altmap);
 struct dev_pagemap *find_dev_pagemap(resource_size_t phys);
 #else
 static inline void *devm_memremap_pages(struct device *dev,
-               struct resource *res, struct vmem_altmap *altmap)
+               struct resource *res, struct percpu_ref *ref,
+               struct vmem_altmap *altmap)
 {
        /*
         * Fail attempts to call devm_memremap_pages() without
@@ -66,4 +72,43 @@ static inline struct dev_pagemap *find_dev_pagemap(resource_size_t phys)
        return NULL;
 }
 #endif
+
+/**
+ * get_dev_pagemap() - take a new live reference on the dev_pagemap for @pfn
+ * @pfn: page frame number to lookup page_map
+ * @pgmap: optional known pgmap that already has a reference
+ *
+ * @pgmap allows the overhead of a lookup to be bypassed when @pfn lands in the
+ * same mapping.
+ */
+static inline struct dev_pagemap *get_dev_pagemap(unsigned long pfn,
+               struct dev_pagemap *pgmap)
+{
+       const struct resource *res = pgmap ? pgmap->res : NULL;
+       resource_size_t phys = PFN_PHYS(pfn);
+
+       /*
+        * In the cached case we're already holding a live reference so
+        * we can simply do a blind increment
+        */
+       if (res && phys >= res->start && phys <= res->end) {
+               percpu_ref_get(pgmap->ref);
+               return pgmap;
+       }
+
+       /* fall back to slow path lookup */
+       rcu_read_lock();
+       pgmap = find_dev_pagemap(phys);
+       if (pgmap && !percpu_ref_tryget_live(pgmap->ref))
+               pgmap = NULL;
+       rcu_read_unlock();
+
+       return pgmap;
+}
+
+static inline void put_dev_pagemap(struct dev_pagemap *pgmap)
+{
+       if (pgmap)
+               percpu_ref_put(pgmap->ref);
+}
 #endif /* _LINUX_MEMREMAP_H_ */