Skip to content

Commit d5b3afe

Browse files
apopple-nvidiaakpm00
authored andcommitted
fs/dax: create a common implementation to break DAX layouts
Prior to freeing a block file systems supporting FS DAX must check that the associated pages are both unmapped from user-space and not undergoing DMA or other access from eg. get_user_pages(). This is achieved by unmapping the file range and scanning the FS DAX page-cache to see if any pages within the mapping have an elevated refcount. This is done using two functions - dax_layout_busy_page_range() which returns a page to wait for the refcount to become idle on. Rather than open-code this introduce a common implementation to both unmap and wait for the page to become idle. Link: https://lkml.kernel.org/r/c4d381e41fc618296cee2820403c166d80599d5c.1740713401.git-series.apopple@nvidia.com Signed-off-by: Alistair Popple <[email protected]> Reviewed-by: Dan Williams <[email protected]> Tested-by: Alison Schofield <[email protected]> Cc: Alexander Gordeev <[email protected]> Cc: Asahi Lina <[email protected]> Cc: Balbir Singh <[email protected]> Cc: Bjorn Helgaas <[email protected]> Cc: Catalin Marinas <[email protected]> Cc: Christian Borntraeger <[email protected]> Cc: Christoph Hellwig <[email protected]> Cc: Chunyan Zhang <[email protected]> Cc: "Darrick J. Wong" <[email protected]> Cc: Dave Chinner <[email protected]> Cc: Dave Hansen <[email protected]> Cc: Dave Jiang <[email protected]> Cc: David Hildenbrand <[email protected]> Cc: Gerald Schaefer <[email protected]> Cc: Heiko Carstens <[email protected]> Cc: Huacai Chen <[email protected]> Cc: Ira Weiny <[email protected]> Cc: Jan Kara <[email protected]> Cc: Jason Gunthorpe <[email protected]> Cc: Jason Gunthorpe <[email protected]> Cc: John Hubbard <[email protected]> Cc: linmiaohe <[email protected]> Cc: Logan Gunthorpe <[email protected]> Cc: Matthew Wilcow (Oracle) <[email protected]> Cc: Michael "Camp Drill Sergeant" Ellerman <[email protected]> Cc: Nicholas Piggin <[email protected]> Cc: Peter Xu <[email protected]> Cc: Sven Schnelle <[email protected]> Cc: Ted Ts'o <[email protected]> Cc: Vasily Gorbik <[email protected]> Cc: Vishal Verma <[email protected]> Cc: Vivek Goyal <[email protected]> Cc: WANG Xuerui <[email protected]> Cc: Will Deacon <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent e6fa396 commit d5b3afe

File tree

6 files changed

+63
-61
lines changed

6 files changed

+63
-61
lines changed

fs/dax.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,39 @@ int dax_delete_mapping_entry(struct address_space *mapping, pgoff_t index)
846846
return ret;
847847
}
848848

849+
static int wait_page_idle(struct page *page,
850+
void (cb)(struct inode *),
851+
struct inode *inode)
852+
{
853+
return ___wait_var_event(page, dax_page_is_idle(page),
854+
TASK_INTERRUPTIBLE, 0, 0, cb(inode));
855+
}
856+
857+
/*
858+
* Unmaps the inode and waits for any DMA to complete prior to deleting the
859+
* DAX mapping entries for the range.
860+
*/
861+
int dax_break_layout(struct inode *inode, loff_t start, loff_t end,
862+
void (cb)(struct inode *))
863+
{
864+
struct page *page;
865+
int error = 0;
866+
867+
if (!dax_mapping(inode->i_mapping))
868+
return 0;
869+
870+
do {
871+
page = dax_layout_busy_page_range(inode->i_mapping, start, end);
872+
if (!page)
873+
break;
874+
875+
error = wait_page_idle(page, cb, inode);
876+
} while (error == 0);
877+
878+
return error;
879+
}
880+
EXPORT_SYMBOL_GPL(dax_break_layout);
881+
849882
/*
850883
* Invalidate DAX entry if it is clean.
851884
*/

fs/ext4/inode.c

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3911,21 +3911,10 @@ static void ext4_wait_dax_page(struct inode *inode)
39113911

39123912
int ext4_break_layouts(struct inode *inode)
39133913
{
3914-
struct page *page;
3915-
int error;
3916-
39173914
if (WARN_ON_ONCE(!rwsem_is_locked(&inode->i_mapping->invalidate_lock)))
39183915
return -EINVAL;
39193916

3920-
do {
3921-
page = dax_layout_busy_page(inode->i_mapping);
3922-
if (!page)
3923-
return 0;
3924-
3925-
error = dax_wait_page_idle(page, ext4_wait_dax_page, inode);
3926-
} while (error == 0);
3927-
3928-
return error;
3917+
return dax_break_layout_inode(inode, ext4_wait_dax_page);
39293918
}
39303919

39313920
/*

fs/fuse/dax.c

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -666,33 +666,12 @@ static void fuse_wait_dax_page(struct inode *inode)
666666
filemap_invalidate_lock(inode->i_mapping);
667667
}
668668

669-
/* Should be called with mapping->invalidate_lock held exclusively */
670-
static int __fuse_dax_break_layouts(struct inode *inode, bool *retry,
671-
loff_t start, loff_t end)
672-
{
673-
struct page *page;
674-
675-
page = dax_layout_busy_page_range(inode->i_mapping, start, end);
676-
if (!page)
677-
return 0;
678-
679-
*retry = true;
680-
return dax_wait_page_idle(page, fuse_wait_dax_page, inode);
681-
}
682-
669+
/* Should be called with mapping->invalidate_lock held exclusively. */
683670
int fuse_dax_break_layouts(struct inode *inode, u64 dmap_start,
684671
u64 dmap_end)
685672
{
686-
bool retry;
687-
int ret;
688-
689-
do {
690-
retry = false;
691-
ret = __fuse_dax_break_layouts(inode, &retry, dmap_start,
692-
dmap_end);
693-
} while (ret == 0 && retry);
694-
695-
return ret;
673+
return dax_break_layout(inode, dmap_start, dmap_end,
674+
fuse_wait_dax_page);
696675
}
697676

698677
ssize_t fuse_dax_read_iter(struct kiocb *iocb, struct iov_iter *to)

fs/xfs/xfs_inode.c

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2735,21 +2735,17 @@ xfs_mmaplock_two_inodes_and_break_dax_layout(
27352735
struct xfs_inode *ip2)
27362736
{
27372737
int error;
2738-
bool retry;
27392738
struct page *page;
27402739

27412740
if (ip1->i_ino > ip2->i_ino)
27422741
swap(ip1, ip2);
27432742

27442743
again:
2745-
retry = false;
27462744
/* Lock the first inode */
27472745
xfs_ilock(ip1, XFS_MMAPLOCK_EXCL);
2748-
error = xfs_break_dax_layouts(VFS_I(ip1), &retry);
2749-
if (error || retry) {
2746+
error = xfs_break_dax_layouts(VFS_I(ip1));
2747+
if (error) {
27502748
xfs_iunlock(ip1, XFS_MMAPLOCK_EXCL);
2751-
if (error == 0 && retry)
2752-
goto again;
27532749
return error;
27542750
}
27552751

@@ -2764,7 +2760,7 @@ xfs_mmaplock_two_inodes_and_break_dax_layout(
27642760
* for this nested lock case.
27652761
*/
27662762
page = dax_layout_busy_page(VFS_I(ip2)->i_mapping);
2767-
if (page && page_ref_count(page) != 1) {
2763+
if (!dax_page_is_idle(page)) {
27682764
xfs_iunlock(ip2, XFS_MMAPLOCK_EXCL);
27692765
xfs_iunlock(ip1, XFS_MMAPLOCK_EXCL);
27702766
goto again;
@@ -3008,19 +3004,11 @@ xfs_wait_dax_page(
30083004

30093005
int
30103006
xfs_break_dax_layouts(
3011-
struct inode *inode,
3012-
bool *retry)
3007+
struct inode *inode)
30133008
{
3014-
struct page *page;
3015-
30163009
xfs_assert_ilocked(XFS_I(inode), XFS_MMAPLOCK_EXCL);
30173010

3018-
page = dax_layout_busy_page(inode->i_mapping);
3019-
if (!page)
3020-
return 0;
3021-
3022-
*retry = true;
3023-
return dax_wait_page_idle(page, xfs_wait_dax_page, inode);
3011+
return dax_break_layout_inode(inode, xfs_wait_dax_page);
30243012
}
30253013

30263014
int
@@ -3038,8 +3026,8 @@ xfs_break_layouts(
30383026
retry = false;
30393027
switch (reason) {
30403028
case BREAK_UNMAP:
3041-
error = xfs_break_dax_layouts(inode, &retry);
3042-
if (error || retry)
3029+
error = xfs_break_dax_layouts(inode);
3030+
if (error)
30433031
break;
30443032
fallthrough;
30453033
case BREAK_WRITE:

fs/xfs/xfs_inode.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -603,7 +603,7 @@ xfs_itruncate_extents(
603603
return xfs_itruncate_extents_flags(tpp, ip, whichfork, new_size, 0);
604604
}
605605

606-
int xfs_break_dax_layouts(struct inode *inode, bool *retry);
606+
int xfs_break_dax_layouts(struct inode *inode);
607607
int xfs_break_layouts(struct inode *inode, uint *iolock,
608608
enum layout_break_reason reason);
609609

include/linux/dax.h

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -207,12 +207,9 @@ int dax_zero_range(struct inode *inode, loff_t pos, loff_t len, bool *did_zero,
207207
int dax_truncate_page(struct inode *inode, loff_t pos, bool *did_zero,
208208
const struct iomap_ops *ops);
209209

210-
static inline int dax_wait_page_idle(struct page *page,
211-
void (cb)(struct inode *),
212-
struct inode *inode)
210+
static inline bool dax_page_is_idle(struct page *page)
213211
{
214-
return ___wait_var_event(page, page_ref_count(page) == 1,
215-
TASK_INTERRUPTIBLE, 0, 0, cb(inode));
212+
return page && page_ref_count(page) == 1;
216213
}
217214

218215
#if IS_ENABLED(CONFIG_DAX)
@@ -228,6 +225,15 @@ static inline void dax_read_unlock(int id)
228225
{
229226
}
230227
#endif /* CONFIG_DAX */
228+
229+
#if !IS_ENABLED(CONFIG_FS_DAX)
230+
static inline int __must_check dax_break_layout(struct inode *inode,
231+
loff_t start, loff_t end, void (cb)(struct inode *))
232+
{
233+
return 0;
234+
}
235+
#endif
236+
231237
bool dax_alive(struct dax_device *dax_dev);
232238
void *dax_get_private(struct dax_device *dax_dev);
233239
long dax_direct_access(struct dax_device *dax_dev, pgoff_t pgoff, long nr_pages,
@@ -251,6 +257,13 @@ vm_fault_t dax_finish_sync_fault(struct vm_fault *vmf,
251257
int dax_delete_mapping_entry(struct address_space *mapping, pgoff_t index);
252258
int dax_invalidate_mapping_entry_sync(struct address_space *mapping,
253259
pgoff_t index);
260+
int __must_check dax_break_layout(struct inode *inode, loff_t start,
261+
loff_t end, void (cb)(struct inode *));
262+
static inline int __must_check dax_break_layout_inode(struct inode *inode,
263+
void (cb)(struct inode *))
264+
{
265+
return dax_break_layout(inode, 0, LLONG_MAX, cb);
266+
}
254267
int dax_dedupe_file_range_compare(struct inode *src, loff_t srcoff,
255268
struct inode *dest, loff_t destoff,
256269
loff_t len, bool *is_same,

0 commit comments

Comments
 (0)