Skip to content

Commit e763848

Browse files
committed
mm: introduce MEMORY_DEVICE_FS_DAX and CONFIG_DEV_PAGEMAP_OPS
In preparation for fixing dax-dma-vs-unmap issues, filesystems need to be able to rely on the fact that they will get wakeups on dev_pagemap page-idle events. Introduce MEMORY_DEVICE_FS_DAX and generic_dax_page_free() as common indicator / infrastructure for dax filesytems to require. With this change there are no users of the MEMORY_DEVICE_HOST designation, so remove it. The HMM sub-system extended dev_pagemap to arrange a callback when a dev_pagemap managed page is freed. Since a dev_pagemap page is free / idle when its reference count is 1 it requires an additional branch to check the page-type at put_page() time. Given put_page() is a hot-path we do not want to incur that check if HMM is not in use, so a static branch is used to avoid that overhead when not necessary. Now, the FS_DAX implementation wants to reuse this mechanism for receiving dev_pagemap ->page_free() callbacks. Rework the HMM-specific static-key into a generic mechanism that either HMM or FS_DAX code paths can enable. For ARCH=um builds, and any other arch that lacks ZONE_DEVICE support, care must be taken to compile out the DEV_PAGEMAP_OPS infrastructure. However, we still need to support FS_DAX in the FS_DAX_LIMITED case implemented by the s390/dcssblk driver. Cc: Martin Schwidefsky <[email protected]> Cc: Heiko Carstens <[email protected]> Cc: Michal Hocko <[email protected]> Reported-by: kbuild test robot <[email protected]> Reported-by: Thomas Meyer <[email protected]> Reported-by: Dave Jiang <[email protected]> Cc: "Jérôme Glisse" <[email protected]> Reviewed-by: Jan Kara <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]> Signed-off-by: Dan Williams <[email protected]>
1 parent 5981690 commit e763848

File tree

10 files changed

+137
-65
lines changed

10 files changed

+137
-65
lines changed

drivers/dax/super.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ int __bdev_dax_supported(struct super_block *sb, int blocksize)
8686
{
8787
struct block_device *bdev = sb->s_bdev;
8888
struct dax_device *dax_dev;
89+
bool dax_enabled = false;
8990
pgoff_t pgoff;
9091
int err, id;
9192
void *kaddr;
@@ -134,14 +135,21 @@ int __bdev_dax_supported(struct super_block *sb, int blocksize)
134135
* on being able to do (page_address(pfn_to_page())).
135136
*/
136137
WARN_ON(IS_ENABLED(CONFIG_ARCH_HAS_PMEM_API));
138+
dax_enabled = true;
137139
} else if (pfn_t_devmap(pfn)) {
138-
/* pass */;
139-
} else {
140+
struct dev_pagemap *pgmap;
141+
142+
pgmap = get_dev_pagemap(pfn_t_to_pfn(pfn), NULL);
143+
if (pgmap && pgmap->type == MEMORY_DEVICE_FS_DAX)
144+
dax_enabled = true;
145+
put_dev_pagemap(pgmap);
146+
}
147+
148+
if (!dax_enabled) {
140149
pr_debug("VFS (%s): error: dax support not enabled\n",
141150
sb->s_id);
142151
return -EOPNOTSUPP;
143152
}
144-
145153
return 0;
146154
}
147155
EXPORT_SYMBOL_GPL(__bdev_dax_supported);

drivers/nvdimm/pfn_devs.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -561,8 +561,6 @@ static int __nvdimm_setup_pfn(struct nd_pfn *nd_pfn, struct dev_pagemap *pgmap)
561561
res->start += start_pad;
562562
res->end -= end_trunc;
563563

564-
pgmap->type = MEMORY_DEVICE_HOST;
565-
566564
if (nd_pfn->mode == PFN_MODE_RAM) {
567565
if (offset < SZ_8K)
568566
return -EINVAL;

drivers/nvdimm/pmem.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,27 @@ static void pmem_release_disk(void *__pmem)
294294
put_disk(pmem->disk);
295295
}
296296

297+
static void pmem_release_pgmap_ops(void *__pgmap)
298+
{
299+
dev_pagemap_put_ops();
300+
}
301+
302+
static void fsdax_pagefree(struct page *page, void *data)
303+
{
304+
wake_up_var(&page->_refcount);
305+
}
306+
307+
static int setup_pagemap_fsdax(struct device *dev, struct dev_pagemap *pgmap)
308+
{
309+
dev_pagemap_get_ops();
310+
if (devm_add_action_or_reset(dev, pmem_release_pgmap_ops, pgmap))
311+
return -ENOMEM;
312+
pgmap->type = MEMORY_DEVICE_FS_DAX;
313+
pgmap->page_free = fsdax_pagefree;
314+
315+
return 0;
316+
}
317+
297318
static int pmem_attach_disk(struct device *dev,
298319
struct nd_namespace_common *ndns)
299320
{
@@ -353,6 +374,8 @@ static int pmem_attach_disk(struct device *dev,
353374
pmem->pfn_flags = PFN_DEV;
354375
pmem->pgmap.ref = &q->q_usage_counter;
355376
if (is_nd_pfn(dev)) {
377+
if (setup_pagemap_fsdax(dev, &pmem->pgmap))
378+
return -ENOMEM;
356379
addr = devm_memremap_pages(dev, &pmem->pgmap);
357380
pfn_sb = nd_pfn->pfn_sb;
358381
pmem->data_offset = le64_to_cpu(pfn_sb->dataoff);
@@ -364,6 +387,8 @@ static int pmem_attach_disk(struct device *dev,
364387
} else if (pmem_should_map_pages(dev)) {
365388
memcpy(&pmem->pgmap.res, &nsio->res, sizeof(pmem->pgmap.res));
366389
pmem->pgmap.altmap_valid = false;
390+
if (setup_pagemap_fsdax(dev, &pmem->pgmap))
391+
return -ENOMEM;
367392
addr = devm_memremap_pages(dev, &pmem->pgmap);
368393
pmem->pfn_flags |= PFN_MAP;
369394
memcpy(&bb_res, &pmem->pgmap.res, sizeof(bb_res));

fs/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ config FS_DAX
3838
bool "Direct Access (DAX) support"
3939
depends on MMU
4040
depends on !(ARM || MIPS || SPARC)
41+
select DEV_PAGEMAP_OPS if (ZONE_DEVICE && !FS_DAX_LIMITED)
4142
select FS_IOMAP
4243
select DAX
4344
help

include/linux/memremap.h

Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
/* SPDX-License-Identifier: GPL-2.0 */
22
#ifndef _LINUX_MEMREMAP_H_
33
#define _LINUX_MEMREMAP_H_
4-
#include <linux/mm.h>
54
#include <linux/ioport.h>
65
#include <linux/percpu-refcount.h>
76

@@ -30,13 +29,6 @@ struct vmem_altmap {
3029
* Specialize ZONE_DEVICE memory into multiple types each having differents
3130
* usage.
3231
*
33-
* MEMORY_DEVICE_HOST:
34-
* Persistent device memory (pmem): struct page might be allocated in different
35-
* memory and architecture might want to perform special actions. It is similar
36-
* to regular memory, in that the CPU can access it transparently. However,
37-
* it is likely to have different bandwidth and latency than regular memory.
38-
* See Documentation/nvdimm/nvdimm.txt for more information.
39-
*
4032
* MEMORY_DEVICE_PRIVATE:
4133
* Device memory that is not directly addressable by the CPU: CPU can neither
4234
* read nor write private memory. In this case, we do still have struct pages
@@ -53,11 +45,19 @@ struct vmem_altmap {
5345
* driver can hotplug the device memory using ZONE_DEVICE and with that memory
5446
* type. Any page of a process can be migrated to such memory. However no one
5547
* should be allow to pin such memory so that it can always be evicted.
48+
*
49+
* MEMORY_DEVICE_FS_DAX:
50+
* Host memory that has similar access semantics as System RAM i.e. DMA
51+
* coherent and supports page pinning. In support of coordinating page
52+
* pinning vs other operations MEMORY_DEVICE_FS_DAX arranges for a
53+
* wakeup event whenever a page is unpinned and becomes idle. This
54+
* wakeup is used to coordinate physical address space management (ex:
55+
* fs truncate/hole punch) vs pinned pages (ex: device dma).
5656
*/
5757
enum memory_type {
58-
MEMORY_DEVICE_HOST = 0,
59-
MEMORY_DEVICE_PRIVATE,
58+
MEMORY_DEVICE_PRIVATE = 1,
6059
MEMORY_DEVICE_PUBLIC,
60+
MEMORY_DEVICE_FS_DAX,
6161
};
6262

6363
/*
@@ -129,8 +129,6 @@ struct dev_pagemap *get_dev_pagemap(unsigned long pfn,
129129

130130
unsigned long vmem_altmap_offset(struct vmem_altmap *altmap);
131131
void vmem_altmap_free(struct vmem_altmap *altmap, unsigned long nr_pfns);
132-
133-
static inline bool is_zone_device_page(const struct page *page);
134132
#else
135133
static inline void *devm_memremap_pages(struct device *dev,
136134
struct dev_pagemap *pgmap)
@@ -161,20 +159,6 @@ static inline void vmem_altmap_free(struct vmem_altmap *altmap,
161159
}
162160
#endif /* CONFIG_ZONE_DEVICE */
163161

164-
#if defined(CONFIG_DEVICE_PRIVATE) || defined(CONFIG_DEVICE_PUBLIC)
165-
static inline bool is_device_private_page(const struct page *page)
166-
{
167-
return is_zone_device_page(page) &&
168-
page->pgmap->type == MEMORY_DEVICE_PRIVATE;
169-
}
170-
171-
static inline bool is_device_public_page(const struct page *page)
172-
{
173-
return is_zone_device_page(page) &&
174-
page->pgmap->type == MEMORY_DEVICE_PUBLIC;
175-
}
176-
#endif /* CONFIG_DEVICE_PRIVATE || CONFIG_DEVICE_PUBLIC */
177-
178162
static inline void put_dev_pagemap(struct dev_pagemap *pgmap)
179163
{
180164
if (pgmap)

include/linux/mm.h

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -821,27 +821,65 @@ static inline bool is_zone_device_page(const struct page *page)
821821
}
822822
#endif
823823

824-
#if defined(CONFIG_DEVICE_PRIVATE) || defined(CONFIG_DEVICE_PUBLIC)
825-
void put_zone_device_private_or_public_page(struct page *page);
826-
DECLARE_STATIC_KEY_FALSE(device_private_key);
827-
#define IS_HMM_ENABLED static_branch_unlikely(&device_private_key)
828-
static inline bool is_device_private_page(const struct page *page);
829-
static inline bool is_device_public_page(const struct page *page);
830-
#else /* CONFIG_DEVICE_PRIVATE || CONFIG_DEVICE_PUBLIC */
831-
static inline void put_zone_device_private_or_public_page(struct page *page)
824+
#ifdef CONFIG_DEV_PAGEMAP_OPS
825+
void dev_pagemap_get_ops(void);
826+
void dev_pagemap_put_ops(void);
827+
void __put_devmap_managed_page(struct page *page);
828+
DECLARE_STATIC_KEY_FALSE(devmap_managed_key);
829+
static inline bool put_devmap_managed_page(struct page *page)
830+
{
831+
if (!static_branch_unlikely(&devmap_managed_key))
832+
return false;
833+
if (!is_zone_device_page(page))
834+
return false;
835+
switch (page->pgmap->type) {
836+
case MEMORY_DEVICE_PRIVATE:
837+
case MEMORY_DEVICE_PUBLIC:
838+
case MEMORY_DEVICE_FS_DAX:
839+
__put_devmap_managed_page(page);
840+
return true;
841+
default:
842+
break;
843+
}
844+
return false;
845+
}
846+
847+
static inline bool is_device_private_page(const struct page *page)
832848
{
849+
return is_zone_device_page(page) &&
850+
page->pgmap->type == MEMORY_DEVICE_PRIVATE;
833851
}
834-
#define IS_HMM_ENABLED 0
852+
853+
static inline bool is_device_public_page(const struct page *page)
854+
{
855+
return is_zone_device_page(page) &&
856+
page->pgmap->type == MEMORY_DEVICE_PUBLIC;
857+
}
858+
859+
#else /* CONFIG_DEV_PAGEMAP_OPS */
860+
static inline void dev_pagemap_get_ops(void)
861+
{
862+
}
863+
864+
static inline void dev_pagemap_put_ops(void)
865+
{
866+
}
867+
868+
static inline bool put_devmap_managed_page(struct page *page)
869+
{
870+
return false;
871+
}
872+
835873
static inline bool is_device_private_page(const struct page *page)
836874
{
837875
return false;
838876
}
877+
839878
static inline bool is_device_public_page(const struct page *page)
840879
{
841880
return false;
842881
}
843-
#endif /* CONFIG_DEVICE_PRIVATE || CONFIG_DEVICE_PUBLIC */
844-
882+
#endif /* CONFIG_DEV_PAGEMAP_OPS */
845883

846884
static inline void get_page(struct page *page)
847885
{
@@ -859,16 +897,13 @@ static inline void put_page(struct page *page)
859897
page = compound_head(page);
860898

861899
/*
862-
* For private device pages we need to catch refcount transition from
863-
* 2 to 1, when refcount reach one it means the private device page is
864-
* free and we need to inform the device driver through callback. See
900+
* For devmap managed pages we need to catch refcount transition from
901+
* 2 to 1, when refcount reach one it means the page is free and we
902+
* need to inform the device driver through callback. See
865903
* include/linux/memremap.h and HMM for details.
866904
*/
867-
if (IS_HMM_ENABLED && unlikely(is_device_private_page(page) ||
868-
unlikely(is_device_public_page(page)))) {
869-
put_zone_device_private_or_public_page(page);
905+
if (put_devmap_managed_page(page))
870906
return;
871-
}
872907

873908
if (put_page_testzero(page))
874909
__put_page(page);

kernel/memremap.c

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <linux/memory_hotplug.h>
1010
#include <linux/swap.h>
1111
#include <linux/swapops.h>
12+
#include <linux/wait_bit.h>
1213

1314
static DEFINE_MUTEX(pgmap_lock);
1415
static RADIX_TREE(pgmap_radix, GFP_KERNEL);
@@ -300,9 +301,32 @@ struct dev_pagemap *get_dev_pagemap(unsigned long pfn,
300301

301302
return pgmap;
302303
}
304+
EXPORT_SYMBOL_GPL(get_dev_pagemap);
303305

304-
#if IS_ENABLED(CONFIG_DEVICE_PRIVATE) || IS_ENABLED(CONFIG_DEVICE_PUBLIC)
305-
void put_zone_device_private_or_public_page(struct page *page)
306+
#ifdef CONFIG_DEV_PAGEMAP_OPS
307+
DEFINE_STATIC_KEY_FALSE(devmap_managed_key);
308+
EXPORT_SYMBOL_GPL(devmap_managed_key);
309+
static atomic_t devmap_enable;
310+
311+
/*
312+
* Toggle the static key for ->page_free() callbacks when dev_pagemap
313+
* pages go idle.
314+
*/
315+
void dev_pagemap_get_ops(void)
316+
{
317+
if (atomic_inc_return(&devmap_enable) == 1)
318+
static_branch_enable(&devmap_managed_key);
319+
}
320+
EXPORT_SYMBOL_GPL(dev_pagemap_get_ops);
321+
322+
void dev_pagemap_put_ops(void)
323+
{
324+
if (atomic_dec_and_test(&devmap_enable))
325+
static_branch_disable(&devmap_managed_key);
326+
}
327+
EXPORT_SYMBOL_GPL(dev_pagemap_put_ops);
328+
329+
void __put_devmap_managed_page(struct page *page)
306330
{
307331
int count = page_ref_dec_return(page);
308332

@@ -322,5 +346,5 @@ void put_zone_device_private_or_public_page(struct page *page)
322346
} else if (!count)
323347
__put_page(page);
324348
}
325-
EXPORT_SYMBOL(put_zone_device_private_or_public_page);
326-
#endif /* CONFIG_DEVICE_PRIVATE || CONFIG_DEVICE_PUBLIC */
349+
EXPORT_SYMBOL_GPL(__put_devmap_managed_page);
350+
#endif /* CONFIG_DEV_PAGEMAP_OPS */

mm/Kconfig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,9 @@ config ARCH_HAS_HMM
692692
config MIGRATE_VMA_HELPER
693693
bool
694694

695+
config DEV_PAGEMAP_OPS
696+
bool
697+
695698
config HMM
696699
bool
697700
select MIGRATE_VMA_HELPER
@@ -712,6 +715,7 @@ config DEVICE_PRIVATE
712715
bool "Unaddressable device memory (GPU memory, ...)"
713716
depends on ARCH_HAS_HMM
714717
select HMM
718+
select DEV_PAGEMAP_OPS
715719

716720
help
717721
Allows creation of struct pages to represent unaddressable device
@@ -722,6 +726,7 @@ config DEVICE_PUBLIC
722726
bool "Addressable device memory (like GPU memory)"
723727
depends on ARCH_HAS_HMM
724728
select HMM
729+
select DEV_PAGEMAP_OPS
725730

726731
help
727732
Allows creation of struct pages to represent addressable device

mm/hmm.c

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,6 @@
3535

3636
#define PA_SECTION_SIZE (1UL << PA_SECTION_SHIFT)
3737

38-
#if defined(CONFIG_DEVICE_PRIVATE) || defined(CONFIG_DEVICE_PUBLIC)
39-
/*
40-
* Device private memory see HMM (Documentation/vm/hmm.txt) or hmm.h
41-
*/
42-
DEFINE_STATIC_KEY_FALSE(device_private_key);
43-
EXPORT_SYMBOL(device_private_key);
44-
#endif /* CONFIG_DEVICE_PRIVATE || CONFIG_DEVICE_PUBLIC */
45-
46-
4738
#if IS_ENABLED(CONFIG_HMM_MIRROR)
4839
static const struct mmu_notifier_ops hmm_mmu_notifier_ops;
4940

@@ -1167,7 +1158,7 @@ struct hmm_devmem *hmm_devmem_add(const struct hmm_devmem_ops *ops,
11671158
resource_size_t addr;
11681159
int ret;
11691160

1170-
static_branch_enable(&device_private_key);
1161+
dev_pagemap_get_ops();
11711162

11721163
devmem = devres_alloc_node(&hmm_devmem_release, sizeof(*devmem),
11731164
GFP_KERNEL, dev_to_node(device));
@@ -1261,7 +1252,7 @@ struct hmm_devmem *hmm_devmem_add_resource(const struct hmm_devmem_ops *ops,
12611252
if (res->desc != IORES_DESC_DEVICE_PUBLIC_MEMORY)
12621253
return ERR_PTR(-EINVAL);
12631254

1264-
static_branch_enable(&device_private_key);
1255+
dev_pagemap_get_ops();
12651256

12661257
devmem = devres_alloc_node(&hmm_devmem_release, sizeof(*devmem),
12671258
GFP_KERNEL, dev_to_node(device));

mm/swap.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <linux/cpu.h>
3030
#include <linux/notifier.h>
3131
#include <linux/backing-dev.h>
32+
#include <linux/memremap.h>
3233
#include <linux/memcontrol.h>
3334
#include <linux/gfp.h>
3435
#include <linux/uio.h>
@@ -743,7 +744,7 @@ void release_pages(struct page **pages, int nr)
743744
flags);
744745
locked_pgdat = NULL;
745746
}
746-
put_zone_device_private_or_public_page(page);
747+
put_devmap_managed_page(page);
747748
continue;
748749
}
749750

0 commit comments

Comments
 (0)