From: Baokun Li <libaokun@linux.alibaba.com>
To: fuse-devel@lists.linux.dev
Cc: bernd@bsbernd.com, miklos@szeredi.hu
Subject: [PATCH 1/2] fuse_lowlevel: add support for FUSE_NOTIFY_RESEND
Date: Fri, 9 Oct 2026 15:54:15 +0800 [thread overview]
Message-ID: <20261009075417.2521879-2-libaokun@linux.alibaba.com> (raw)
In-Reply-To: <20261009075417.2521879-1-libaokun@linux.alibaba.com>
The kernel supports resending pending requests since protocol 7.40
(FUSE_NOTIFY_RESEND / FUSE_HAS_RESEND), added in Linux 6.9 by commits
760eac73f9f6 ("fuse: Introduce a new notification type for resend
pending requests") and 9e7f5296f475 ("fuse: Use the high bit of request
ID for indicating resend requests"). A daemon can ask the kernel to
requeue requests that were already sent to the daemon but never
answered, e.g. to recover in-flight requests after a daemon takeover.
Add fuse_lowlevel_notify_resend() to send the notification, expose
kernel support through the new FUSE_CAP_HAS_RESEND capability, and add
fuse_req_is_resend() so file systems can detect resent requests via the
FUSE_UNIQUE_RESEND bit in the request ID and handle them idempotently.
Signed-off-by: Baokun Li <libaokun@linux.alibaba.com>
---
ChangeLog.rst | 7 +++++++
doc/README.notifications | 2 +-
include/fuse_common.h | 13 +++++++++++++
include/fuse_lowlevel.h | 28 ++++++++++++++++++++++++++++
lib/fuse_cap_names_i.h | 1 +
lib/fuse_lowlevel.c | 23 +++++++++++++++++++++++
lib/fuse_versionscript | 2 ++
7 files changed, 75 insertions(+), 1 deletion(-)
diff --git a/ChangeLog.rst b/ChangeLog.rst
index d5392b251929..ec8cbc3dd1d5 100644
--- a/ChangeLog.rst
+++ b/ChangeLog.rst
@@ -1,6 +1,13 @@
Unreleased Changes
==================
+* ``fuse_lowlevel_notify_resend()`` lets a filesystem ask the kernel to
+ re-queue requests that were already sent to the daemon but never
+ answered, for example to recover in-flight requests after a daemon
+ takeover. Support is reported through the new ``FUSE_CAP_HAS_RESEND``
+ capability, and ``fuse_req_is_resend()`` tells whether a request was
+ redelivered this way. Requires protocol 7.40 (Linux 6.9) or newer.
+
* ``fuse_session_custom_io()`` is disabled unless libfuse is built with
``-Denable-custom-io=true``, and returns ``-ENOTSUP`` otherwise. The
``hello_ll_uds`` example is built only with that option, and enabling it
diff --git a/doc/README.notifications b/doc/README.notifications
index 149e47e19c9f..30051ed87b51 100644
--- a/doc/README.notifications
+++ b/doc/README.notifications
@@ -26,7 +26,7 @@ notifications:
| STORE | fuse_lowlevel_notify_store |
| RETRIEVE | fuse_lowlevel_notify_retrieve |
| DELETE | fuse_lowlevel_notify_delete |
-| RESEND | - |
+| RESEND | fuse_lowlevel_notify_resend |
| PRUNE | fuse_lowlevel_notify_prune |
|-------------+----------------------------------|
diff --git a/include/fuse_common.h b/include/fuse_common.h
index 7533626175db..c2a7211e2f53 100644
--- a/include/fuse_common.h
+++ b/include/fuse_common.h
@@ -551,6 +551,19 @@ struct fuse_loop_config_v1 {
*/
#define FUSE_CAP_IO_URING_BUFPOOL (1ULL << 34)
+/**
+ * Indicates that the kernel supports resending pending requests
+ *
+ * The file system can ask the kernel to re-queue requests that were
+ * already sent to the daemon but never answered, by sending the
+ * FUSE_NOTIFY_RESEND notification (see fuse_lowlevel_notify_resend()).
+ * Resent requests carry the FUSE_UNIQUE_RESEND bit in the request ID,
+ * which can be queried with fuse_req_is_resend().
+ *
+ * This feature is enabled by default when supported by the kernel.
+ */
+#define FUSE_CAP_HAS_RESEND (1ULL << 35)
+
/**
* Ioctl flags
*
diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h
index f228bdfd951f..a6313e24a5a8 100644
--- a/include/fuse_lowlevel.h
+++ b/include/fuse_lowlevel.h
@@ -1977,6 +1977,23 @@ int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino,
int fuse_lowlevel_notify_prune(struct fuse_session *se,
fuse_ino_t *nodeids, uint32_t count);
+/**
+ * Notify the kernel to resend pending requests
+ *
+ * Ask the kernel to re-queue requests that were already sent to the file
+ * system but not yet answered, e.g. to recover in-flight requests after
+ * a daemon takeover.
+ *
+ * Resent requests carry the FUSE_UNIQUE_RESEND bit in the request ID; use
+ * fuse_req_is_resend() to detect them. Since they may have been partially
+ * processed before, they should be handled idempotently or duplicates
+ * should be detected.
+ *
+ * @param se the session object
+ * @return zero for success, -errno for failure
+ */
+int fuse_lowlevel_notify_resend(struct fuse_session *se);
+
/* ----------------------------------------------------------- *
* Utility functions *
* ----------------------------------------------------------- */
@@ -2100,6 +2117,17 @@ void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func,
*/
int fuse_req_interrupted(fuse_req_t req);
+/**
+ * Check if a request is a resent request
+ *
+ * A resent request was already sent to the file system, but was never
+ * answered (see fuse_lowlevel_notify_resend()).
+ *
+ * @param req request handle
+ * @return 1 if the request is a resend request, 0 otherwise
+ */
+int fuse_req_is_resend(fuse_req_t req);
+
/* ----------------------------------------------------------- *
* Inquiry functions *
diff --git a/lib/fuse_cap_names_i.h b/lib/fuse_cap_names_i.h
index aa85b910200d..36e99147dd89 100644
--- a/lib/fuse_cap_names_i.h
+++ b/lib/fuse_cap_names_i.h
@@ -63,6 +63,7 @@ static const struct fuse_cap_name fuse_cap_names[] = {
{ FUSE_CAP_ALLOW_IDMAP, "FUSE_CAP_ALLOW_IDMAP"},
{ FUSE_CAP_SECURITY_CTX, "FUSE_CAP_SECURITY_CTX"},
{ FUSE_CAP_IO_URING_BUFPOOL, "FUSE_CAP_IO_URING_BUFPOOL"},
+ { FUSE_CAP_HAS_RESEND, "FUSE_CAP_HAS_RESEND"},
// Add any new capabilities here
{ 0, NULL} // Sentinel to mark the end of the array
};
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index 3951bbb6dd09..bbc58523b033 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -2960,6 +2960,8 @@ _do_init(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in,
se->conn.capable_ext |= FUSE_CAP_ALLOW_IDMAP;
if (inargflags & FUSE_SECURITY_CTX)
se->conn.capable_ext |= FUSE_CAP_SECURITY_CTX;
+ if (inargflags & FUSE_HAS_RESEND)
+ se->conn.capable_ext |= FUSE_CAP_HAS_RESEND;
} else {
se->conn.max_readahead = 0;
}
@@ -3005,6 +3007,7 @@ _do_init(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in,
FUSE_CAP_READDIRPLUS_AUTO);
LL_SET_DEFAULT(1, FUSE_CAP_OVER_IO_URING);
LL_SET_DEFAULT(se->uring.bufpool, FUSE_CAP_IO_URING_BUFPOOL);
+ LL_SET_DEFAULT(1, FUSE_CAP_HAS_RESEND);
/* This could safely become default, but libfuse needs an API extension
* to support it
@@ -3133,6 +3136,8 @@ _do_init(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in,
outargflags |= FUSE_SECURITY_CTX;
if (se->conn.want_ext & FUSE_CAP_ALLOW_IDMAP)
outargflags |= FUSE_ALLOW_IDMAP;
+ if (se->conn.want_ext & FUSE_CAP_HAS_RESEND)
+ outargflags |= FUSE_HAS_RESEND;
if ((inargflags & FUSE_REQUEST_TIMEOUT) && se->conn.request_timeout) {
outargflags |= FUSE_REQUEST_TIMEOUT;
@@ -3644,6 +3649,19 @@ int fuse_lowlevel_notify_prune(struct fuse_session *se,
return send_notify_iov(se, FUSE_NOTIFY_PRUNE, iov, 3);
}
+int fuse_lowlevel_notify_resend(struct fuse_session *se)
+{
+ struct iovec iov[1];
+
+ if (!se)
+ return -EINVAL;
+
+ if (!(se->conn.capable_ext & FUSE_CAP_HAS_RESEND))
+ return -ENOSYS;
+
+ return send_notify_iov(se, FUSE_NOTIFY_RESEND, iov, 1);
+}
+
void *fuse_req_userdata(fuse_req_t req)
{
return req->se->userdata;
@@ -3768,6 +3786,11 @@ int fuse_req_interrupted(fuse_req_t req)
return interrupted;
}
+int fuse_req_is_resend(fuse_req_t req)
+{
+ return !!(req->unique & FUSE_UNIQUE_RESEND);
+}
+
bool fuse_req_is_uring(fuse_req_t req)
{
return req->flags.is_uring;
diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
index b40f141f7eb9..e074cd4a0831 100644
--- a/lib/fuse_versionscript
+++ b/lib/fuse_versionscript
@@ -228,6 +228,8 @@ FUSE_3.19 {
fuse_session_start_teardown_watchdog;
fuse_session_stop_teardown_watchdog;
fuse_lowlevel_notify_prune;
+ fuse_lowlevel_notify_resend;
+ fuse_req_is_resend;
fuse_daemonize_early_start;
fuse_daemonize_early_success;
fuse_daemonize_early_fail;
--
2.43.7
next prev parent reply other threads:[~2026-10-09 8:00 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-09 7:54 [PATCH 0/2] libfuse: add support for FUSE_NOTIFY_RESEND Baokun Li
2026-10-09 7:54 ` Baokun Li [this message]
2026-10-09 7:54 ` [PATCH 2/2] test: exercise FUSE_NOTIFY_RESEND Baokun Li
2026-10-09 8:19 ` Bernd Schubert
2026-10-09 9:31 ` Baokun Li
2026-10-09 13:51 ` Luis Henriques
2026-10-10 2:33 ` Baokun Li
2026-10-13 8:32 ` Luis Henriques
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261009075417.2521879-2-libaokun@linux.alibaba.com \
--to=libaokun@linux.alibaba.com \
--cc=bernd@bsbernd.com \
--cc=fuse-devel@lists.linux.dev \
--cc=miklos@szeredi.hu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox