Skip to content

fuse: throttle foreground io-uring requests with the writeback cache - #242

Closed
hbirth wants to merge 1 commit into
DDNStorage:redfs-ubuntu-hwe-6.17.0-16.16-24.04.1from
hbirth:redfs-ubuntu-hwe-6.17.0-16.16-24.04.1-uring
Closed

hbirth wants to merge 1 commit into
DDNStorage:redfs-ubuntu-hwe-6.17.0-16.16-24.04.1from
hbirth:redfs-ubuntu-hwe-6.17.0-16.16-24.04.1-uring

Conversation

@hbirth

@hbirth hbirth commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator

With the writeback cache, foreground requests can wait in userspace on DLM revokes. A revoke waits on page invalidation, which waits on writeback. If foreground requests held every entry of an io-uring queue, background writeback could not be sent and none of them would complete.

When the connection has the writeback cache, each queue now keeps a share of its entries for background requests. Background requests may use a quarter of the entries, but at least 2. Foreground requests may use the rest, but at least 8. A foreground request over the limit waits on the new fuse_req_fg_queue and only moves to fuse_req_queue when a slot frees up. Background requests keep their own path to free entries and are held to their cap, except that each queue may always have one active background request. If a queue has fewer than 10 entries both minimums cannot hold, and the foreground minimum wins. The foreground count is released on completion, on commit errors and on entry teardown.

Requests marked no_fg_limit skip the foreground limit. These are the page cache reads and writes (redfs_do_readfolio, redfs_send_readpages and redfs_send_write_pages) and the flush from write_inode. A page fault holds the inode range lock while its read is outstanding, and the revoke that is waiting for that range lock would never finish if the read were queued behind other foreground requests. The flush is sent by writeback itself. Without the writeback cache, dispatch is unchanged.

With the writeback cache, foreground requests can wait in userspace on
DLM revokes. A revoke waits on page invalidation, which waits on
writeback. If foreground requests held every entry of an io-uring
queue, background writeback could not be sent and none of them would
complete.

When the connection has the writeback cache, each queue now keeps a
share of its entries for background requests. Background requests may
use a quarter of the entries, but at least 2. Foreground requests may
use the rest, but at least 8. A foreground request over the limit waits
on the new fuse_req_fg_queue and only moves to fuse_req_queue when a
slot frees up. Background requests keep their own path to free entries
and are held to their cap, except that each queue may always have one
active background request. If a queue has fewer than 10 entries both
minimums cannot hold, and the foreground minimum wins. The foreground
count is released on completion, on commit errors and on entry
teardown.

Requests marked no_fg_limit skip the foreground limit. These are the
page cache reads and writes (redfs_do_readfolio, redfs_send_readpages
and redfs_send_write_pages) and the flush from write_inode. A page
fault holds the inode range lock while its read is outstanding, and the
revoke that is waiting for that range lock would never finish if the
read were queued behind other foreground requests. The flush is sent by
writeback itself. Without the writeback cache, dispatch is unchanged.

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

Copy link
Copy Markdown

Thanks, Horst, as I mentioned in my PR, I'll add subsequent commit to address the problem as there could have more commits coming in for new issues found... but thank you anyway.

@hbirth hbirth closed this Oct 9, 2026
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