From: Konstantin Khorenko <khorenko@virtuozzo.com>
To: Alexey Kuznetsov <kuznet@virtuozzo.com>,
OpenVZ devel list <devel@openvz.org>
Cc: Liu Kui <kui.liu@virtuozzo.com>
Subject: [Devel] [PATCH hci-8.1 v2] fs/fuse: strictly segregate vstorage features of generic fuse
Date: Wed, 23 Sep 2026 18:13:35 +0200 [thread overview]
Message-ID: <20260923161335.3110033-1-khorenko@virtuozzo.com> (raw)
In-Reply-To: <aqlrneHiHEUuNiwK@mops2>
From: Alexey Kuznetsov <kuznet@virtuozzo.com>
1. Makes explicit use of option "close_wait" as specific to vstorage.
The option requires some time to be fully engaged, the support
should appear in kernel first then we use it in userspace,
then we can get rid of original ugly way relying on special
string name [pv]storage://
2. Option "close_wait" is privileged. It cannot be used inside
containers and ordinary users of fusermount are not allowed.
3. Our enhancements like multiple queues, zero-copy transfers,
lease invalidate FUSE_NOTIFY_INVAL_FILES etc. are explicitly
prohibited w/o close_wait.
4. When close_wait is enabled, we explicitly disallow to use
creepy bug prone fuse features like fuse interrupts, resends,
we reject all obscure notifications except for supported ones,
which is now only FUSE_NOTIFY_INVAL_FILES and value 4, which
will be dropped after old user space is washed out
https://virtuozzo.atlassian.net/browse/VSTOR-144529
Feature: vStorage
Signed-off-by: Alexey Kuznetsov <kuznet@virtuozzo.com>
Reviewed-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
v1 -> v2:
- is_close_wait_allowed() returns bool instead of int (khorenko@)
fs/fuse/dev.c | 32 ++++++++++++++++++--------
fs/fuse/fuse_i.h | 5 -----
fs/fuse/inode.c | 58 ++++++++++++++++++++++++++++++++++++++----------
3 files changed, 69 insertions(+), 26 deletions(-)
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index a9de0f815774a..6a22fa73330b7 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -2019,6 +2019,8 @@ static int fuse_notify_inval_files(struct fuse_dev *fud, unsigned int size,
struct fuse_notify_inval_files_out outarg;
int err = -EINVAL;
+ if (!fc->close_wait)
+ goto err;
if (size != sizeof(outarg))
goto err;
@@ -2048,8 +2050,19 @@ static int fuse_notify(struct fuse_dev *fud, enum fuse_notify_code code,
cs->move_pages = 0;
/* ASSUMPTION: pstorage fused doesn't use FUSE_NOTIFY_STORE */
- if (fc->compat_inval_files && code == 4)
- code = FUSE_NOTIFY_INVAL_FILES;
+ if (fc->close_wait) {
+ if (code == 4)
+ code = FUSE_NOTIFY_INVAL_FILES;
+ if (code != FUSE_NOTIFY_INVAL_FILES) {
+ fuse_copy_finish(cs);
+ return -EINVAL;
+ }
+ } else {
+ if (code == FUSE_NOTIFY_INVAL_FILES) {
+ fuse_copy_finish(cs);
+ return -EINVAL;
+ }
+ }
switch (code) {
case FUSE_NOTIFY_POLL:
@@ -2412,10 +2425,11 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
}
err = -EINVAL;
- if (oh.error != FUSE_OUT_SPLICES &&
- oh.error != FUSE_OUT_KRPCZC &&
- (oh.error <= -512 || oh.error > 0))
- goto copy_finish;
+ if (oh.error <= -512 || oh.error > 0) {
+ if (!fc->close_wait ||
+ (oh.error != FUSE_OUT_SPLICES && oh.error != FUSE_OUT_KRPCZC))
+ goto copy_finish;
+ }
spin_lock(&fpq->lock);
req = NULL;
@@ -2438,7 +2452,7 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
err = -EINVAL;
else if (oh.error == -ENOSYS)
fc->no_interrupt = 1;
- else if (oh.error == -EAGAIN)
+ else if (oh.error == -EAGAIN && !fc->no_interrupt)
err = queue_interrupt(req->args->fiq, req);
fuse_put_request(req);
@@ -2455,10 +2469,10 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
if (!req->args->page_replace)
cs->move_pages = 0;
- if (oh.error == FUSE_OUT_SPLICES) {
+ if (oh.error == FUSE_OUT_SPLICES && fc->close_wait) {
req->out.h.error = 0;
err = copy_out_splices(cs, req->args, nbytes);
- } else if (oh.error == FUSE_OUT_KRPCZC) {
+ } else if (oh.error == FUSE_OUT_KRPCZC && fc->close_wait) {
req->out.h.error = 0;
err = copy_out_krpczc(cs, req->args, nbytes);
} else if (oh.error)
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 8d90f43f01c92..21f8733376270 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -669,8 +669,6 @@ struct fuse_fs_context {
bool umount_wait:1;
bool close_wait:1;
bool disable_close_wait:1;
- bool compat_inval_files:1;
- bool no_fiemap:1;
bool kdirect_io:1;
bool destroy:1;
bool no_control:1;
@@ -1056,9 +1054,6 @@ struct fuse_conn {
/** Does the disable synchronous close? */
unsigned disable_close_wait:1;
- /** Handle wrong FUSE_NOTIFY_INVAL_FILES from old fused */
- unsigned compat_inval_files:1;
-
/** No ioctl(FIEMAP) */
unsigned no_fiemap:1;
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index ea158367a1efd..eb2e9a4f87564 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -912,6 +912,7 @@ enum {
OPT_ODIRECT,
OPT_UMOUNT_WAIT,
OPT_DISABLE_CLOSE_WAIT,
+ OPT_CLOSE_WAIT,
OPT_ERR,
OPT_KIO_NAME
};
@@ -930,11 +931,18 @@ static const struct fs_parameter_spec fuse_fs_parameters[] = {
fsparam_flag ("direct_enable", OPT_ODIRECT),
fsparam_flag ("umount_wait", OPT_UMOUNT_WAIT),
fsparam_flag ("disable_close_wait", OPT_DISABLE_CLOSE_WAIT),
+ fsparam_flag ("close_wait", OPT_CLOSE_WAIT),
fsparam_string ("kdirect", OPT_KIO_NAME),
fsparam_string ("subtype", OPT_SUBTYPE),
{}
};
+static bool is_close_wait_allowed(void)
+{
+ return ve_is_super(get_exec_env()) && capable(CAP_SYS_ADMIN) &&
+ uid_eq(current_uid(), GLOBAL_ROOT_UID);
+}
+
static int fuse_parse_param(struct fs_context *fsc, struct fs_parameter *param)
{
struct fs_parse_result result;
@@ -966,19 +974,22 @@ static int fuse_parse_param(struct fs_context *fsc, struct fs_parameter *param)
param->string = NULL;
/* Hack to distinguish pcs fuse service and to force
- * synchronous close for it.
- *
- * libfuse is not ready for fiemap autodetection, so disable
- * fiemap support check in advance for all FUSE fs except for
- * pStorage.
+ * synchronous close for it. It is on way out, we are going
+ * to switch to mount option "close_wait", but this requires
+ * full cycle: support mount option in kernel, switch
+ * user space, wait till old user space is gone.
*/
if (fsc->source &&
(strncmp(fsc->source, "pstorage://", 11) == 0 ||
strncmp(fsc->source, "vstorage://", 11) == 0)) {
- ctx->close_wait = 1;
- ctx->compat_inval_files = 1;
- } else
- ctx->no_fiemap = 1;
+ if (is_close_wait_allowed())
+ ctx->close_wait = 1;
+ /* If not allowed, just ignore. We can use vstorage mount
+ * in restricted environment, but with disabled
+ * features like multiple queues, precise metadata
+ * tracking and kernel acceleration.
+ */
+ }
break;
@@ -1066,6 +1077,13 @@ static int fuse_parse_param(struct fs_context *fsc, struct fs_parameter *param)
ctx->close_wait = 0;
break;
+ case OPT_CLOSE_WAIT:
+ if (is_close_wait_allowed())
+ ctx->close_wait = 1;
+ else
+ return -EPERM;
+ break;
+
case OPT_KIO_NAME: {
if (!ve_is_super(get_exec_env()))
return -EPERM;
@@ -1118,6 +1136,8 @@ static int fuse_show_options(struct seq_file *m, struct dentry *root)
seq_puts(m, ",umount_wait");
if (fc->disable_close_wait)
seq_puts(m, ",disable_close_wait");
+ if (fc->close_wait)
+ seq_puts(m, ",close_wait");
if (fc->max_read != ~0)
seq_printf(m, ",max_read=%u", fc->max_read);
if (sb->s_bdev && sb->s_blocksize != FUSE_DEFAULT_BLKSIZE)
@@ -1241,6 +1261,9 @@ int fuse_install_iq_route(struct fuse_dev *fud, struct fuse_iq_routing *req)
&fud->fc->rrt;
struct fuse_rtable rtl;
+ if (!fud->fc->close_wait)
+ return -EINVAL;
+
if (rt->type != FUSE_ROUTING_NONE && rt->type != req->type)
return -EINVAL;
@@ -2182,17 +2205,28 @@ int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx)
fc->umount_wait = ctx->umount_wait;
fc->close_wait = ctx->close_wait;
fc->disable_close_wait = ctx->disable_close_wait;
- fc->compat_inval_files = ctx->compat_inval_files;
- fc->no_fiemap = ctx->no_fiemap;
+ fc->no_fiemap = 0;
fc->writeback_cache = ctx->writeback_cache;
fc->destroy = ctx->destroy || ctx->umount_wait;
fc->no_control = ctx->no_control;
fc->no_force_umount = ctx->no_force_umount;
fc->kdirect_io = ctx->kdirect_io;
+ if (fc->close_wait) {
+ fc->no_interrupt = 1;
+ } else {
+ /* libfuse is not ready for fiemap autodetection, so disable
+ * fiemap support check in advance for all FUSE fs except for
+ * pStorage.
+ * NOTE as of 260912. This might be not true now, yet this
+ * still makes sense.
+ */
+ fc->no_fiemap = 1;
+ }
+
if (fc->kdirect_io) {
err = -EINVAL;
- if (!fc->writeback_cache)
+ if (!fc->writeback_cache || !fc->close_wait)
goto err_dev_free;
fc->kio.cached_op = fuse_kio_get(fc, ctx->kio_name);
--
2.43.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
next prev parent reply other threads:[~2026-09-23 16:15 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-15 16:00 [Devel] [PATCH VZ10] " Alexey Kuznetsov
2026-09-23 16:13 ` Konstantin Khorenko [this message]
2026-09-23 16:18 ` [Devel] [PATCH hci-8.1 v2] " Konstantin Khorenko
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=20260923161335.3110033-1-khorenko@virtuozzo.com \
--to=khorenko@virtuozzo.com \
--cc=devel@openvz.org \
--cc=kui.liu@virtuozzo.com \
--cc=kuznet@virtuozzo.com \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.