Skip to content

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

Merged
hbirth merged 1 commit into
DDNStorage:redfs-ubuntu-hwe-6.17.0-16.16-24.04.1from
hazhou-ddn:redfs-ubuntu-hwe-6.17.0-16.16-24.04.1-uring
Oct 9, 2026
Merged

hbirth merged 1 commit into
DDNStorage:redfs-ubuntu-hwe-6.17.0-16.16-24.04.1from
hazhou-ddn:redfs-ubuntu-hwe-6.17.0-16.16-24.04.1-uring

Conversation

@hazhou-ddn

Copy link
Copy Markdown

FUSE IO-URING: add throttling mechanism for the fuse foreground worker threads to preserve the io-uring entries to the write-back background tasks, otherwise, no chance to complete the background tasks but the foreground worker threads are waiting on the background tasks, then form deadlock issues.

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
Author

@hbirth Here I'm back to create PR based on my "forked DDN Linux" repo, just in case I have more subsequent commits coming... Please take a review here instead. Thanks.

@hazhou-ddn
hazhou-ddn requested a review from cding-ddn October 9, 2026 00:48
@hbirth

hbirth commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator

As I said in the other PR. I don't think this is a real problem in the issues we're seeing, but I'm in no position to decline, so ... here we go.

In the tests that showed the hang we never had many waiting fuse requests (highest number around 3)

@hazhou-ddn

Copy link
Copy Markdown
Author

As I said in the other PR. I don't think this is a real problem in the issues we're seeing, but I'm in no position to deecline, so ... here we go.

In the tests that showed the hang we never had many waiting fuse requests (highest number around 3)

Horst, not sure which case you did review.... For the cases I investigated, my VM has two io-uring queues and each queue depth is 8, and total 16 entries were consumed by FUSE requests, then waiting on the invalidation completion.

So this is a real problem, not "imagine" problem:-)

@hbirth
hbirth merged commit 611e47d into DDNStorage:redfs-ubuntu-hwe-6.17.0-16.16-24.04.1 Oct 9, 2026
2 checks passed
Comment thread fs/fuse/dev_uring.c
* invalidation, which waits on writeback - if they held all entries,
* background writeback could not be sent and none of them would complete.
*/
#define FUSE_URING_MIN_FOREGROUND 8U

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is used any where, so this can probably come out unless there was some use case that was forgotten

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, Allison. Here I need to refine it for the calculation... but team is rushing to provide the testing, thus quickly merging the PR before I complete my another working commit... so I have to create another PR for my next commit for the io-uring change...

Comment thread fs/fuse/dev_uring.c
return queue->nr_ents;
}

return queue->nr_ents - bg;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was FUSE_URING_MIN_FOREGROUND maybe supposed to go here? This works out to nr_ents - max(nr_ents/4, 2). I think (max(nr_ents - bg, min(FUSE_URING_MIN_FOREGROUND, nr_ents))) makes more sense?

That would match the commit message "Foreground requests may use the rest, but at least 8" and "if a queue has fewer than 10 entries ... the foreground minimum wins."

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. Here I need to revise it too... Anyway, I will have another commit to contains this. Thanks.

Comment thread fs/fuse/dev_uring.c
spin_lock(&queue->lock);
ent->cmd = cmd;
queue->nr_ents++;
fuse_uring_flush_queue_fg(queue);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: since the queue is still empty here, we're going to get a warning every time the module loads. I wonder if we need the flush at this point since we're still registering?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point! Let me think about this, and address in the working commit if needed. Thanks, and please help to continue review the codes, then I can try to address all these in one commit:-)

Comment thread fs/fuse/dev_uring.c
/* remove entry from queue->fpq->processing */
list_del_init(&req->list);
if (test_and_clear_bit(FR_URING_FG, &req->flags))
queue->active_foreground--;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need a fuse_uring_flush_queue_fg for decrementing the foreground?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure for now, will check it later.

@achhenderson

Copy link
Copy Markdown

nits aside this looks good. istr you mentioned a server side companion fix during the meeting? maybe link it here when you get a chance. Thanks!

@hazhou-ddn

Copy link
Copy Markdown
Author

nits aside this looks good. istr you mentioned a server side companion fix during the meeting? maybe link it here when you get a chance. Thanks!

Yes, I do not yet create the userspace PR yet, which is used to set or configure the io-uring depth to have "reserved" slots other than the default 8 entries per queue. Will put it here once I have it. Thank you for your review comments!

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.

3 participants