Skip to content

fuse: serialize cached IO and invalidation with per-inode range locks - #201

Open
hazhou-ddn wants to merge 1 commit into
redfs-ubuntu-hwe-6.17.0-16.16-24.04.1from
redfs-ubuntu-hwe-6.17.0-16.16-24.04.1-local-brl
Open

fuse: serialize cached IO and invalidation with per-inode range locks#201
hazhou-ddn wants to merge 1 commit into
redfs-ubuntu-hwe-6.17.0-16.16-24.04.1from
redfs-ubuntu-hwe-6.17.0-16.16-24.04.1-local-brl

Conversation

@hazhou-ddn

Copy link
Copy Markdown

Add fuse_range_lock, an interval-tree based byte-range lock embedded in struct fuse_inode. It serializes concurrent cached reads and writes that touch overlapping byte ranges of the same file while letting non-overlapping IO proceed concurrently, and lets attribute and BRL invalidation (fuse_reverse_inval_inode(), truncate in fuse_do_setattr()) block only on in-progress IO that overlaps the range being invalidated rather than serializing against all IO on the inode.

Each held range carries a two-state lifecycle. A range is first acquired in INIT state, which reserves it against other local readers/writers but stays invisible to invalidation; this covers the window where a fuse_get_dlm_lock() request to the DLM server may be in flight, so a NOTIFY invalidate never blocks on that unbounded round trip. Once IO is about to touch the page cache the range is promoted to READY, at which point it becomes fully exclusive against overlapping ranges, including other READY holders and invalidation.

Callers are responsible for gating on fc->writeback_cache && fc->dlm before acquiring or releasing a range lock, since the range lock only needs to run when the DLM coherency path is active; this keeps the non-DLM read/write paths free of any added locking overhead.

dev.c grows a 'complete' callback invoked from fuse_request_end() before the requester is woken, so range locks can be promoted to READY (or released) synchronously with the reply to the DLM lock request rather than requiring the waiter to do it after waking up.

Wire the new range lock into the read, write, and writeback paths in file.c, the setattr/truncate path in dir.c, and the invalidate path in inode.c.

Signed-off-by Hai Zhong Zhou hazhou@ddn.com

Add fuse_range_lock, an interval-tree based byte-range lock embedded
in struct fuse_inode. It serializes concurrent cached reads and
writes that touch overlapping byte ranges of the same file while
letting non-overlapping IO proceed concurrently, and lets attribute
and BRL invalidation (fuse_reverse_inval_inode(), truncate in
fuse_do_setattr()) block only on in-progress IO that overlaps the
range being invalidated rather than serializing against all IO on
the inode.

Each held range carries a two-state lifecycle. A range is first
acquired in INIT state, which reserves it against other local
readers/writers but stays invisible to invalidation; this covers the
window where a fuse_get_dlm_lock() request to the DLM server may be
in flight, so a NOTIFY invalidate never blocks on that unbounded
round trip. Once IO is about to touch the page cache the range is
promoted to READY, at which point it becomes fully exclusive against
overlapping ranges, including other READY holders and invalidation.

Callers are responsible for gating on fc->writeback_cache && fc->dlm
before acquiring or releasing a range lock, since the range lock only
needs to run when the DLM coherency path is active; this keeps the
non-DLM read/write paths free of any added locking overhead.

dev.c grows a 'complete' callback invoked from fuse_request_end()
before the requester is woken, so range locks can be promoted to
READY (or released) synchronously with the reply to the DLM lock
request rather than requiring the waiter to do it after waking up.

Wire the new range lock into the read, write, and writeback paths in
file.c, the setattr/truncate path in dir.c, and the invalidate path
in inode.c.

Signed-off-by Hai Zhong Zhou <hazhou@ddn.com>
@hbirth

hbirth commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

since I had done this before and we decided against it ... I don't think we need the range lock

@hazhou-ddn

Copy link
Copy Markdown
Author

since I had done this before and we decided against it ... I don't think we need the range lock

Horst, I think the background is changed now... At that time, byte range lock should be used for read/write IO protection and no "gatekeeper" requirement, now this range lock in my patch is also used as fine-grained "gatekeeper" between invalidation and read/write IO. In addition, I believe at that time we discussed about to introduce the DLM byte range into the kernel to hold daemon side DLM BRL, that is to tight with the DLM... While this range lock is just fuse kernel local range lock, not directly hold the daemon side DLM BRL.

Let me explain how this works and why we need this range lock.

@hazhou-ddn

Copy link
Copy Markdown
Author

Just had a call with Horst, and we will merge the range lock interval tree into the DLM cache interval tree. Let me refresh the patch.

@hazhou-ddn

Copy link
Copy Markdown
Author

Just had a call with Horst, and we will merge the range lock interval tree into the DLM cache interval tree. Let me refresh the patch.

Horst just create a new PR (#202) which shares the DLM cache interval tree with range lock. So I'll suspend my "merging" interval tree work for this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants