From 9a3719ad9ef0b7fd183817fa44ccb738895ce43d Mon Sep 17 00:00:00 2001 From: Hai Zhong Zhou Date: Thu, 16 Jul 2026 09:55:59 +0000 Subject: [PATCH] fuse: acquire dlm lock for the normal buffer read Acquire the dlm lock from fuse server for the normal buffer read path to ensure the distributed page cache across different nodes can be co-existing and consistency. More importantly, this change will correct the DLM lock and folio locks ordering for the buffer read path, thus can avoid the potential deadlock between the buffer read and page cache invalidation processes. Signed-off-by Hai Zhong Zhou --- fs/fuse/file.c | 14 +++++++++++--- fs/fuse/fuse_dlm_cache.c | 22 ++++++++++++---------- fs/fuse/fuse_dlm_cache.h | 6 +++--- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 467093acadbb89..a0ac055ba246d5 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1199,7 +1199,8 @@ static void fuse_readahead(struct readahead_control *rac) static ssize_t fuse_cache_read_iter(struct kiocb *iocb, struct iov_iter *to) { - struct inode *inode = iocb->ki_filp->f_mapping->host; + struct file *file = iocb->ki_filp; + struct inode *inode = file->f_mapping->host; struct fuse_conn *fc = get_fuse_conn(inode); /* @@ -1215,6 +1216,12 @@ static ssize_t fuse_cache_read_iter(struct kiocb *iocb, struct iov_iter *to) return err; } + /* if we have dlm support acquire a read lock for the area + * we are reading from. */ + if (fc->writeback_cache && fc->dlm) + fuse_get_dlm_lock(file, iocb->ki_pos, + iov_iter_count(to), FUSE_PAGE_LOCK_READ); + return generic_file_read_iter(iocb, to); } @@ -1708,7 +1715,8 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from) iocb->ki_pos; size_t length = iov_iter_count(from); - fuse_get_dlm_write_lock(file, pos, length); + fuse_get_dlm_lock(file, pos, length, + FUSE_PAGE_LOCK_WRITE); } } } @@ -2570,7 +2578,7 @@ static void fuse_vma_close(struct vm_area_struct *vma) /** * Request a DLM lock from the FUSE server. * - * This routine is similar to fuse_get_dlm_write_lock(), but it + * This routine is similar to fuse_get_dlm_lock(), but it * does not cache the DLM lock in the kernel. */ static int fuse_get_page_mkwrite_lock(struct file *file, loff_t offset, size_t length) diff --git a/fs/fuse/fuse_dlm_cache.c b/fs/fuse/fuse_dlm_cache.c index d765dd8018cc6a..ea296a2e9ec89c 100644 --- a/fs/fuse/fuse_dlm_cache.c +++ b/fs/fuse/fuse_dlm_cache.c @@ -487,10 +487,14 @@ bool fuse_dlm_range_is_locked(struct fuse_inode *inode, uint64_t start, } /** - * request a dlm lock from the fuse server + * fuse_get_dlm_lock - request a dlm lock from the fuse server + * @file: the file being accessed + * @offset: byte offset into the file (need not be page-aligned) + * @length: length of the region in bytes (need not be page-aligned) + * @mode: FUSE_PAGE_LOCK_READ or FUSE_PAGE_LOCK_WRITE */ -void fuse_get_dlm_write_lock(struct file *file, loff_t offset, - size_t length) +void fuse_get_dlm_lock(struct file *file, loff_t offset, + size_t length, enum fuse_page_lock_mode mode) { struct fuse_file *ff = file->private_data; struct inode *inode = file_inode(file); @@ -500,7 +504,7 @@ void fuse_get_dlm_write_lock(struct file *file, loff_t offset, uint64_t end = (offset + length - 1) | (PAGE_SIZE - 1); /* note that the offset and length don't have to be page aligned here - * but since we only get here on writeback caching we will send out + * but since we only get here on writeback caching we will send out * page aligned requests */ offset &= PAGE_MASK; @@ -513,8 +517,7 @@ void fuse_get_dlm_write_lock(struct file *file, loff_t offset, * at the same time. It is intentionally not protected * since a DLM implementation in the FUSE server should take care * of any races in lock requests */ - if (fuse_dlm_range_is_locked(fi, offset, - end, FUSE_PAGE_LOCK_WRITE)) + if (fuse_dlm_range_is_locked(fi, offset, end, mode)) return; /* we already have this area locked */ memset(&inarg, 0, sizeof(inarg)); @@ -522,7 +525,8 @@ void fuse_get_dlm_write_lock(struct file *file, loff_t offset, inarg.start = offset; inarg.end = end; - inarg.type = FUSE_DLM_LOCK_WRITE; + inarg.type = (mode == FUSE_PAGE_LOCK_WRITE) ? + FUSE_DLM_LOCK_WRITE : FUSE_DLM_LOCK_READ; args.opcode = FUSE_DLM_WB_LOCK; args.nodeid = get_node_id(inode); @@ -551,8 +555,6 @@ void fuse_get_dlm_write_lock(struct file *file, loff_t offset, return; } else { /* ignore any errors here, there is no way we can react appropriately */ - fuse_dlm_lock_range(fi, outarg.start, - outarg.end, - FUSE_PAGE_LOCK_WRITE); + fuse_dlm_lock_range(fi, outarg.start, outarg.end, mode); } } diff --git a/fs/fuse/fuse_dlm_cache.h b/fs/fuse/fuse_dlm_cache.h index 438d31d28b666e..5c3deaa3536866 100644 --- a/fs/fuse/fuse_dlm_cache.h +++ b/fs/fuse/fuse_dlm_cache.h @@ -43,8 +43,8 @@ int fuse_dlm_unlock_range(struct fuse_inode *inode, uint64_t start, bool fuse_dlm_range_is_locked(struct fuse_inode *inode, uint64_t start, uint64_t end, enum fuse_page_lock_mode mode); -/* this is the interface to the filesystem */ -void fuse_get_dlm_write_lock(struct file *file, loff_t offset, - size_t length); +/* This is the interface to the filesystem */ +void fuse_get_dlm_lock(struct file *file, loff_t offset, + size_t length, enum fuse_page_lock_mode mode); #endif /* _FS_FUSE_DLM_CACHE_H */