The obseleted dmap entries can be put back to global free list
immediately and we don't have to rely on reclaim code to free them.

Signed-off-by: Peng Tao <[email protected]>
---
 fs/fuse/dir.c    |  5 ++++
 fs/fuse/file.c   | 69 +++++++++++++++++++++++++++++++++++++++---------
 fs/fuse/fuse_i.h |  2 ++
 3 files changed, 63 insertions(+), 13 deletions(-)

diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 3f923fe7841a..233c3ed391f1 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1751,6 +1751,11 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr 
*attr,
                down_write(&fi->i_mmap_sem);
                truncate_pagecache(inode, outarg.attr.size);
                invalidate_inode_pages2(inode->i_mapping);
+               // Free dmap beyond i_size
+               if (IS_DAX(inode) && oldsize > outarg.attr.size)
+                       fuse_dax_free_mappings_range(fc, inode,
+                                                    outarg.attr.size, -1);
+
                up_write(&fi->i_mmap_sem);
        }

diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 51faed351c7c..6d130f2f3a23 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -30,6 +30,8 @@ static long __fuse_file_fallocate(struct file *file, int mode,
                                        loff_t offset, loff_t length);
 static struct fuse_dax_mapping *alloc_dax_mapping_reclaim(struct fuse_conn *fc,
                                        struct inode *inode);
+static void fuse_dax_do_remove_mapping_locked(struct fuse_inode *fi,
+                                       struct fuse_dax_mapping *dmap);

 static int fuse_send_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
                          int opcode, struct fuse_open_out *outargp)
@@ -3752,9 +3754,7 @@ int fuse_dax_reclaim_dmap_locked(struct fuse_conn *fc, 
struct inode *inode,
                return ret;
        }

-       /* Remove dax mapping from inode interval tree now */
-       fuse_dax_interval_tree_remove(dmap, &fi->dmap_tree);
-       fi->nr_dmaps--;
+       fuse_dax_do_remove_mapping_locked(fi, dmap);
        return 0;
 }

@@ -3831,6 +3831,58 @@ static struct fuse_dax_mapping 
*alloc_dax_mapping_reclaim(struct fuse_conn *fc,
        }
 }

+/* Cleanup dmap entry and add back to free list */
+static void fuse_dax_do_free_mapping_locked(struct fuse_conn *fc, struct 
fuse_dax_mapping *dmap)
+{
+       pr_debug("fuse: freed memory range start=0x%llx end=0x%llx "
+               "window_offset=0x%llx length=0x%llx\n", dmap->start,
+               dmap->end, dmap->window_offset, dmap->length);
+       spin_lock(&fc->lock);
+       __dmap_remove_busy_list(fc, dmap);
+       dmap->inode = NULL;
+       dmap->start = dmap->end = 0;
+       __free_dax_mapping(fc, dmap);
+       spin_unlock(&fc->lock);
+}
+
+/* Remove dax mapping from inode interval tree */
+static void fuse_dax_do_remove_mapping_locked(struct fuse_inode *fi, struct 
fuse_dax_mapping *dmap)
+{
+       fuse_dax_interval_tree_remove(dmap, &fi->dmap_tree);
+       fi->nr_dmaps--;
+}
+
+/*
+ * Free inode dmap entries whose range falls entirely inside [start, end].
+ * Called with inode->i_rwsem and fuse_inode->i_mmap_sem held.
+ *
+ * No need to send FUSE_REMOVEMAPPING to userspace because the userspace 
mappings
+ * will be overridden next time dmap is used -- same logic is applied in
+ * fuse_dax_reclaim_first_mapping().
+ */
+void fuse_dax_free_mappings_range(struct fuse_conn *fc, struct inode *inode, 
loff_t start, loff_t end)
+{
+       struct fuse_inode *fi = get_fuse_inode(inode);
+       struct fuse_dax_mapping *dmap;
+
+       WARN_ON(!inode_is_locked(inode));
+       WARN_ON(!rwsem_is_locked(&fi->i_mmap_sem));
+
+       /* interval tree search matches intersecting entries.
+        * Adjust the range to avoid dropping partial valid entries. */
+       start = ALIGN(start, FUSE_DAX_MEM_RANGE_SZ);
+       end = ALIGN_DOWN(end, FUSE_DAX_MEM_RANGE_SZ);
+
+       pr_debug("fuse: fuse_dax_free_mappings_range start=0x%llx, 
end=0x%llx\n", start, end);
+       /* Lock ordering follows fuse_dax_free_one_mapping() */
+       down_write(&fi->i_dmap_sem);
+       while ((dmap = fuse_dax_interval_tree_iter_first(&fi->dmap_tree, start, 
end))) {
+               fuse_dax_do_remove_mapping_locked(fi, dmap);
+               fuse_dax_do_free_mapping_locked(fc, dmap);
+       }
+       up_write(&fi->i_dmap_sem);
+}
+
 int fuse_dax_free_one_mapping_locked(struct fuse_conn *fc, struct inode *inode,
                                u64 dmap_start)
 {
@@ -3852,17 +3904,8 @@ int fuse_dax_free_one_mapping_locked(struct fuse_conn 
*fc, struct inode *inode,
        if (ret < 0)
                return ret;

-       /* Cleanup dmap entry and add back to free list */
-       spin_lock(&fc->lock);
-       __dmap_remove_busy_list(fc, dmap);
-       dmap->inode = NULL;
-       dmap->start = dmap->end = 0;
-       __free_dax_mapping(fc, dmap);
-       spin_unlock(&fc->lock);
+       fuse_dax_do_free_mapping_locked(fc, dmap);

-       pr_debug("fuse: freed memory range window_offset=0x%llx,"
-                               " length=0x%llx\n", dmap->window_offset,
-                               dmap->length);
        return ret;
 }

diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 1149281ab1e8..e273f1209f5b 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -1189,5 +1189,7 @@ unsigned fuse_len_args(unsigned numargs, struct fuse_arg 
*args);
 u64 fuse_get_unique(struct fuse_iqueue *fiq);
 void fuse_dax_free_mem_worker(struct work_struct *work);
 void fuse_removemapping(struct inode *inode);
+void fuse_dax_free_mappings_range(struct fuse_conn *fc, struct inode *inode,
+                       loff_t start, loff_t end);

 #endif /* _FS_FUSE_I_H */
-- 
2.17.1

Reply via email to