FILESYSTEM IN USERSPACE (FUSE) development
 help / color / mirror / Atom feed
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


  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