Repository navigation
fuse: reserve io-uring entries for critical requests with writeback c… - #249
Conversation
achhenderson
left a comment
There was a problem hiding this comment.
Agree with Yong Ze's comments, especially the dispatch one — after fuse_uring_end_active() admits both a critical-bg and a non-critical request, only one gets paired with the completing entry, and the other can strand on fuse_req_queue while entries sit idle. Looping entry/request pairs there, as he suggests, closes it. Lock ordering and the accounting paths check out otherwise, and the head builds clean. Happy to approve once the dispatch loop is in.
…ache With the writeback cache, a request can wait in userspace on a DLM revoke. The revoke waits on page invalidation on this node, which waits on writeback and on locked pages being read. Those page reads and writes are themselves fuse requests. If requests that may block in userspace held every entry of an io-uring queue, the page I/O could never be sent and none of the requests would complete. Add a uring_critical flag to fuse_args for requests on the path that invalidation waits for. These are the page reads in fuse_do_readfolio() and fuse_send_readpages() (both sync and async readahead), the page writes in fuse_send_write_pages() and fuse_send_writepage(), and fuse_flush_times(), which writeback calls. The daemon must never block a critical request on a DLM revoke that waits on page invalidation on this node. When the connection has the writeback cache, a quarter of the entries of each queue, but at least 2, are critical entries. Non-critical requests, foreground and background together, may hold only the other entries, counted in active_noncritical. A queue always keeps at least one entry for non-critical requests, and at least one critical entry when it has 2 or more entries. Critical requests are not counted in that limit and may use any entry. A non-critical foreground request over the limit waits on the new fuse_req_fg_queue. A non-critical background request waits on fuse_req_bg_queue, now limited by both the background limits and the non-critical limit. Critical background requests wait on the new fuse_req_bg_crit_queue, so they never sit behind non-critical ones, and are admitted first. They are still held to max_background, except that each queue may always have one critical background request active. Critical foreground requests go straight to fuse_req_queue. When a non-critical slot is freed, it is offered first to the same kind of request, foreground or background, that freed it, so neither kind can starve the other. Registering a new entry raises the limit and admits waiting requests of both kinds. The counts are released through one helper, fuse_uring_end_active(), on completion, on commit errors and on request removal. Entry teardown and queue abort also drop the non-critical count. Without the writeback cache, dispatch is unchanged. Signed-off-by: Hai Zhong Zhou <hazhou@ddn.com>
1c52017 to
b63a9a8
Compare
Agreed, and done with a new push. Thanks! |
bf1bd05
into
DDNStorage:redfs-ubuntu-hwe-6.17.0-16.16-24.04.1
This is a follow-up improvement for the previous io-uring change (PR: #244)
With the writeback cache, a request can wait in userspace on a DLM revoke. The revoke waits on page invalidation on this node, which waits on writeback and on locked pages being read. Those page reads and writes are themselves fuse requests. If requests that may block in userspace held every entry of an io-uring queue, the page I/O could never be sent and none of the requests would complete.
Add a uring_critical flag to fuse_args for requests on the path that invalidation waits for. These are the page reads in fuse_do_readfolio() and fuse_send_readpages() (both sync and async readahead), the page writes in fuse_send_write_pages() and fuse_send_writepage(), and fuse_flush_times(), which writeback calls. The daemon must never block a critical request on a DLM revoke that waits on page invalidation on this node.
When the connection has the writeback cache, a quarter of the entries of each queue, but at least 2, are critical entries. Non-critical requests, foreground and background together, may hold only the other entries, counted in active_noncritical. A queue always keeps at least one entry for non-critical requests, and at least one critical entry when it has 2 or more entries. Critical requests are not counted in that limit and may use any entry.
A non-critical foreground request over the limit waits on the new fuse_req_fg_queue. A non-critical background request waits on fuse_req_bg_queue, now limited by both the background limits and the non-critical limit. Critical background requests wait on the new fuse_req_bg_crit_queue, so they never sit behind non-critical ones, and are admitted first. They are still held to max_background, except that each queue may always have one critical background request active. Critical foreground requests go straight to fuse_req_queue.
When a non-critical slot is freed, it is offered first to the same kind of request, foreground or background, that freed it, so neither kind can starve the other. Registering a new entry raises the limit and admits waiting requests of both kinds. The counts are released through one helper, fuse_uring_end_active(), on completion, on commit errors and on request removal. Entry teardown and queue abort also drop the non-critical count. Without the writeback cache, dispatch is unchanged.