fsdax: set a CoW flag when associate reflink mappings
Introduce a PAGE_MAPPING_DAX_COW flag to support association with CoW file mappings. In this case, since the dax-rmap has already took the responsibility to look up for shared files by given dax page, the page->mapping is no longer to used for rmap but for marking that this dax page is shared. And to make sure disassociation works fine, we use page->index as refcount, and clear page->mapping to the initial state when page->index is decreased to 0. With the help of this new flag, it is able to distinguish normal case and CoW case, and keep the warning in normal case. Link: https://lkml.kernel.org/r/20220603053738.1218681-8-ruansy.fnst@fujitsu.com Signed-off-by: Shiyang Ruan <ruansy.fnst@fujitsu.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Darrick J. Wong <djwong@kernel.org> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: Dan Williams <dan.j.wiliams@intel.com> Cc: Dan Williams <dan.j.williams@intel.com> Cc: Dave Chinner <david@fromorbit.com> Cc: Goldwyn Rodrigues <rgoldwyn@suse.com> Cc: Goldwyn Rodrigues <rgoldwyn@suse.de> Cc: Jane Chu <jane.chu@oracle.com> Cc: Matthew Wilcox <willy@infradead.org> Cc: Miaohe Lin <linmiaohe@huawei.com> Cc: Naoya Horiguchi <naoya.horiguchi@nec.com> Cc: Ritesh Harjani <riteshh@linux.ibm.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
parent
6f643c57d5
commit
6061b69b9a
50
fs/dax.c
50
fs/dax.c
@ -334,13 +334,35 @@ static unsigned long dax_end_pfn(void *entry)
|
||||
for (pfn = dax_to_pfn(entry); \
|
||||
pfn < dax_end_pfn(entry); pfn++)
|
||||
|
||||
static inline bool dax_mapping_is_cow(struct address_space *mapping)
|
||||
{
|
||||
return (unsigned long)mapping == PAGE_MAPPING_DAX_COW;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: for reflink+dax we need a way to associate a single page with
|
||||
* multiple address_space instances at different linear_page_index()
|
||||
* offsets.
|
||||
* Set the page->mapping with FS_DAX_MAPPING_COW flag, increase the refcount.
|
||||
*/
|
||||
static inline void dax_mapping_set_cow(struct page *page)
|
||||
{
|
||||
if ((uintptr_t)page->mapping != PAGE_MAPPING_DAX_COW) {
|
||||
/*
|
||||
* Reset the index if the page was already mapped
|
||||
* regularly before.
|
||||
*/
|
||||
if (page->mapping)
|
||||
page->index = 1;
|
||||
page->mapping = (void *)PAGE_MAPPING_DAX_COW;
|
||||
}
|
||||
page->index++;
|
||||
}
|
||||
|
||||
/*
|
||||
* When it is called in dax_insert_entry(), the cow flag will indicate that
|
||||
* whether this entry is shared by multiple files. If so, set the page->mapping
|
||||
* FS_DAX_MAPPING_COW, and use page->index as refcount.
|
||||
*/
|
||||
static void dax_associate_entry(void *entry, struct address_space *mapping,
|
||||
struct vm_area_struct *vma, unsigned long address)
|
||||
struct vm_area_struct *vma, unsigned long address, bool cow)
|
||||
{
|
||||
unsigned long size = dax_entry_size(entry), pfn, index;
|
||||
int i = 0;
|
||||
@ -352,9 +374,13 @@ static void dax_associate_entry(void *entry, struct address_space *mapping,
|
||||
for_each_mapped_pfn(entry, pfn) {
|
||||
struct page *page = pfn_to_page(pfn);
|
||||
|
||||
WARN_ON_ONCE(page->mapping);
|
||||
page->mapping = mapping;
|
||||
page->index = index + i++;
|
||||
if (cow) {
|
||||
dax_mapping_set_cow(page);
|
||||
} else {
|
||||
WARN_ON_ONCE(page->mapping);
|
||||
page->mapping = mapping;
|
||||
page->index = index + i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -370,7 +396,12 @@ static void dax_disassociate_entry(void *entry, struct address_space *mapping,
|
||||
struct page *page = pfn_to_page(pfn);
|
||||
|
||||
WARN_ON_ONCE(trunc && page_ref_count(page) > 1);
|
||||
WARN_ON_ONCE(page->mapping && page->mapping != mapping);
|
||||
if (dax_mapping_is_cow(page->mapping)) {
|
||||
/* keep the CoW flag if this page is still shared */
|
||||
if (page->index-- > 0)
|
||||
continue;
|
||||
} else
|
||||
WARN_ON_ONCE(page->mapping && page->mapping != mapping);
|
||||
page->mapping = NULL;
|
||||
page->index = 0;
|
||||
}
|
||||
@ -830,7 +861,8 @@ static void *dax_insert_entry(struct xa_state *xas,
|
||||
void *old;
|
||||
|
||||
dax_disassociate_entry(entry, mapping, false);
|
||||
dax_associate_entry(new_entry, mapping, vmf->vma, vmf->address);
|
||||
dax_associate_entry(new_entry, mapping, vmf->vma, vmf->address,
|
||||
false);
|
||||
/*
|
||||
* Only swap our new entry into the page cache if the current
|
||||
* entry is a zero page or an empty entry. If a normal PTE or
|
||||
|
@ -661,6 +661,12 @@ PAGEFLAG_FALSE(VmemmapSelfHosted, vmemmap_self_hosted)
|
||||
#define PAGE_MAPPING_KSM (PAGE_MAPPING_ANON | PAGE_MAPPING_MOVABLE)
|
||||
#define PAGE_MAPPING_FLAGS (PAGE_MAPPING_ANON | PAGE_MAPPING_MOVABLE)
|
||||
|
||||
/*
|
||||
* Different with flags above, this flag is used only for fsdax mode. It
|
||||
* indicates that this page->mapping is now under reflink case.
|
||||
*/
|
||||
#define PAGE_MAPPING_DAX_COW 0x1
|
||||
|
||||
static __always_inline bool folio_mapping_flags(struct folio *folio)
|
||||
{
|
||||
return ((unsigned long)folio->mapping & PAGE_MAPPING_FLAGS) != 0;
|
||||
|
Loading…
Reference in New Issue
Block a user