From nobody Sun May 10 14:12:05 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6F28BC433EF for ; Mon, 2 May 2022 10:25:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1384556AbiEBK3X (ORCPT ); Mon, 2 May 2022 06:29:23 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49664 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1384632AbiEBK3I (ORCPT ); Mon, 2 May 2022 06:29:08 -0400 Received: from mail-pf1-x42c.google.com (mail-pf1-x42c.google.com [IPv6:2607:f8b0:4864:20::42c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9F49FF68; Mon, 2 May 2022 03:25:39 -0700 (PDT) Received: by mail-pf1-x42c.google.com with SMTP id x23so6668707pff.9; Mon, 02 May 2022 03:25:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :organization; bh=gOhjSMrjCwKbxjE2hP/4KxUnfkTZuZCh55gMElj3XMU=; b=Ehamo+dQQmAOK1YoI0I3UK04H2DPrMfYMWCsyWSY+ge9FiF7XhCYWqSD2SjEWWP5NZ VzRBMqLW9urDqnoMZqVLJRpSUXA+00RecaQ1jQQLH0+Xfpz8e4s2szx+sL1QcgX/v8qr UfwuH0bqKDVNk0bl1UKLsFBfwGgl79i/2F5NN7Mk29UBtMlFv62v01VZCgZgFniflPxh jmAgYRnYrraKm2+mzVVtvFnjXQtOxvz05ZNx+as6IToW9uyJFkgeCN8z+TpZxZKG2Q2X 7vJSwWg7jv4U/eoSwrOEqLLZBokd+unuwjDXCXed5P8SCZNXPUuXP07EU5jkE9m4YWjY 3hLQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:organization; bh=gOhjSMrjCwKbxjE2hP/4KxUnfkTZuZCh55gMElj3XMU=; b=OflHN8bPoDhf70OEcceIdFB7oFC+cTo29cNnoorb4Z1N7MxkQJ0HnaAWdDSn5nsETl o6Vxr2sHsXt06zBnzO0O/StmFE6PoC+BoYicgFRdaJwLe5XsmPJncsBZVhzFlBClZveZ QjtsA668sk90Cb4oZn8SsVE+4ii8+CwLVa9SaO3kHxnJmr6bJO1f6QQ5UD/8lZHryxQI yDyKhzbfFB/HZRMpMESxWptUfPotBJ+N1aZOiHIJnhMkjQTdEo6UV6Y+ChF9dgKWX2ZQ BkAb0NVfVcPh8otMSlyqRgNjXhKacLKUX3DKevKAHacJOJGsCTEbm1tCSpCmanHcOT7M ZjBA== X-Gm-Message-State: AOAM531D1x81UngpA0Ub8hhqYEy9u2NhmU2OqZwfXN0+ExS+TucgA9rm YdiufDVnnv2J/ArBqJXBFmw= X-Google-Smtp-Source: ABdhPJyvUQaJ2I8Q9kqyrXZdIRi+rae9QP20YS5HJdELcfx/41lHwW1jsKJjsJq8r/YbPco/4OciKg== X-Received: by 2002:a05:6a00:1a4d:b0:50d:5921:1a8f with SMTP id h13-20020a056a001a4d00b0050d59211a8fmr10457377pfv.64.1651487138642; Mon, 02 May 2022 03:25:38 -0700 (PDT) Received: from localhost.localdomain ([123.201.245.164]) by smtp.googlemail.com with ESMTPSA id j14-20020aa7800e000000b0050dc762816bsm4347953pfi.69.2022.05.02.03.25.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 May 2022 03:25:38 -0700 (PDT) From: Dharmendra Singh To: miklos@szeredi.hu Cc: Dharmendra Singh , linux-fsdevel@vger.kernel.org, fuse-devel@lists.sourceforge.net, linux-kernel@vger.kernel.org, bschubert@ddn.com, Dharmendra Singh Subject: [PATCH v4 1/3] FUSE: Implement atomic lookup + create Date: Mon, 2 May 2022 15:55:19 +0530 Message-Id: <20220502102521.22875-2-dharamhans87@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20220502102521.22875-1-dharamhans87@gmail.com> References: <20220502102521.22875-1-dharamhans87@gmail.com> Organization: DDN STORAGE Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: Dharmendra Singh When we go for creating a file (O_CREAT), we trigger a lookup to FUSE USER SPACE. It is very much likely that file does not exist yet as O_CREAT is passed to open(). This lookup can be avoided and can be performed as part of create call into libfuse. This lookup + create in single call to libfuse and finally to USER SPACE has been named as atomic create. It is expected that USER SPACE create the file, open it and fills in the attributes which are then used to make inode stand/revalidate in the kernel cache. Also if file was newly created(does not exist yet by this time) in USER SPACE then it should be indicated in `struct fuse_file_info` by setting a bit which is again used by libfuse to send some flags back to fuse kernel to indicate that that file was newly created. These flags are used by kernel to indicate changes in parent directory. Fuse kernel automatically detects if atomic create is implemented by libfuse/USER SPACE or not. And depending upon the outcome of this check all further creates are decided to be atomic or non-atomic creates. If libfuse/USER SPACE has not implemented the atomic create operation then by default behaviour remains same i.e we do not optimize lookup calls which are triggered before create calls into libfuse. Signed-off-by: Dharmendra Singh --- fs/fuse/dir.c | 82 +++++++++++++++++++++++++++++++++++---- fs/fuse/fuse_i.h | 3 ++ include/uapi/linux/fuse.h | 3 ++ 3 files changed, 81 insertions(+), 7 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 656e921f3506..cad3322a007f 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -523,7 +523,7 @@ static int get_security_context(struct dentry *entry, u= mode_t mode, */ static int fuse_create_open(struct inode *dir, struct dentry *entry, struct file *file, unsigned int flags, - umode_t mode) + umode_t mode, uint32_t opcode) { int err; struct inode *inode; @@ -535,8 +535,10 @@ static int fuse_create_open(struct inode *dir, struct = dentry *entry, struct fuse_entry_out outentry; struct fuse_inode *fi; struct fuse_file *ff; + struct dentry *res =3D NULL; void *security_ctx =3D NULL; u32 security_ctxlen; + bool atomic_create =3D (opcode =3D=3D FUSE_ATOMIC_CREATE ? true : false); =20 /* Userspace expects S_IFREG in create mode */ BUG_ON((mode & S_IFMT) !=3D S_IFREG); @@ -566,7 +568,7 @@ static int fuse_create_open(struct inode *dir, struct d= entry *entry, inarg.open_flags |=3D FUSE_OPEN_KILL_SUIDGID; } =20 - args.opcode =3D FUSE_CREATE; + args.opcode =3D opcode; args.nodeid =3D get_node_id(dir); args.in_numargs =3D 2; args.in_args[0].size =3D sizeof(inarg); @@ -613,9 +615,44 @@ static int fuse_create_open(struct inode *dir, struct = dentry *entry, goto out_err; } kfree(forget); - d_instantiate(entry, inode); + /* + * In atomic create, we skipped lookup and it is very much likely that + * dentry has DCACHE_PAR_LOOKUP flag set on it so call d_splice_alias(). + * Note: Only REG file is allowed under create/atomic create. + */ + /* There is special case when at very first call where we check if + * atomic create is implemented by USER SPACE/libfuse or not, we + * skipped lookup. Now, in case where atomic create is not implemented + * underlying, we fall back to FUSE_CREATE. here we are required to handle + * DCACHE_PAR_LOOKUP flag. + */ + if (!atomic_create && !d_in_lookup(entry) && fm->fc->no_atomic_create) + d_instantiate(entry, inode); + else { + res =3D d_splice_alias(inode, entry); + if (res) { + /* Close the file in user space, but do not unlink it, + * if it was created - with network file systems other + * clients might have already accessed it. + */ + if (IS_ERR(res)) { + fi =3D get_fuse_inode(inode); + fuse_sync_release(fi, ff, flags); + fuse_queue_forget(fm->fc, forget, outentry.nodeid, 1); + err =3D PTR_ERR(res); + goto out_err; + } + /* res is expected to be NULL since its REG file */ + WARN_ON(res); + } + } fuse_change_entry_timeout(entry, &outentry); - fuse_dir_changed(dir); + /* + * In case of atomic create, we want to indicate directory change + * only if USER SPACE actually created the file. + */ + if (!atomic_create || (outopen.open_flags & FOPEN_FILE_CREATED)) + fuse_dir_changed(dir); err =3D finish_open(file, entry, generic_file_open); if (err) { fi =3D get_fuse_inode(inode); @@ -634,6 +671,29 @@ static int fuse_create_open(struct inode *dir, struct = dentry *entry, return err; } =20 +static int fuse_atomic_create(struct inode *dir, struct dentry *entry, + struct file *file, unsigned int flags, + umode_t mode) +{ + int err; + struct fuse_conn *fc =3D get_fuse_conn(dir); + + if (fc->no_atomic_create) + return -ENOSYS; + + err =3D fuse_create_open(dir, entry, file, flags, mode, + FUSE_ATOMIC_CREATE); + /* If atomic create is not implemented then indicate in fc so that next + * request falls back to normal create instead of going into libufse and + * returning with -ENOSYS. + */ + if (err =3D=3D -ENOSYS) { + if (!fc->no_atomic_create) + fc->no_atomic_create =3D 1; + } + return err; +} + static int fuse_mknod(struct user_namespace *, struct inode *, struct dent= ry *, umode_t, dev_t); static int fuse_atomic_open(struct inode *dir, struct dentry *entry, @@ -643,11 +703,12 @@ static int fuse_atomic_open(struct inode *dir, struct= dentry *entry, int err; struct fuse_conn *fc =3D get_fuse_conn(dir); struct dentry *res =3D NULL; + bool create =3D flags & O_CREAT ? true : false; =20 if (fuse_is_bad(dir)) return -EIO; =20 - if (d_in_lookup(entry)) { + if ((!create || fc->no_atomic_create) && d_in_lookup(entry)) { res =3D fuse_lookup(dir, entry, 0); if (IS_ERR(res)) return PTR_ERR(res); @@ -656,7 +717,7 @@ static int fuse_atomic_open(struct inode *dir, struct d= entry *entry, entry =3D res; } =20 - if (!(flags & O_CREAT) || d_really_is_positive(entry)) + if (!create || d_really_is_positive(entry)) goto no_open; =20 /* Only creates */ @@ -665,7 +726,13 @@ static int fuse_atomic_open(struct inode *dir, struct = dentry *entry, if (fc->no_create) goto mknod; =20 - err =3D fuse_create_open(dir, entry, file, flags, mode); + err =3D fuse_atomic_create(dir, entry, file, flags, mode); + /* Libfuse/user space has not implemented atomic create, therefore + * fall back to normal create. + */ + if (err =3D=3D -ENOSYS) + err =3D fuse_create_open(dir, entry, file, flags, mode, + FUSE_CREATE); if (err =3D=3D -ENOSYS) { fc->no_create =3D 1; goto mknod; @@ -683,6 +750,7 @@ static int fuse_atomic_open(struct inode *dir, struct d= entry *entry, } =20 /* + * Code shared between mknod, mkdir, symlink and link */ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args, diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index e8e59fbdefeb..d577a591ab16 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -669,6 +669,9 @@ struct fuse_conn { /** Is open/release not implemented by fs? */ unsigned no_open:1; =20 + /** Is atomic create not implemented by fs? */ + unsigned no_atomic_create:1; + /** Is opendir/releasedir not implemented by fs? */ unsigned no_opendir:1; =20 diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index d6ccee961891..e4b56004b148 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -301,6 +301,7 @@ struct fuse_file_lock { * FOPEN_CACHE_DIR: allow caching this directory * FOPEN_STREAM: the file is stream-like (no file position at all) * FOPEN_NOFLUSH: don't flush data cache on close (unless FUSE_WRITEBACK_C= ACHE) + * FOPEN_FILE_CREATED: the file was actually created */ #define FOPEN_DIRECT_IO (1 << 0) #define FOPEN_KEEP_CACHE (1 << 1) @@ -308,6 +309,7 @@ struct fuse_file_lock { #define FOPEN_CACHE_DIR (1 << 3) #define FOPEN_STREAM (1 << 4) #define FOPEN_NOFLUSH (1 << 5) +#define FOPEN_FILE_CREATED (1 << 6) =20 /** * INIT request/reply flags @@ -537,6 +539,7 @@ enum fuse_opcode { FUSE_SETUPMAPPING =3D 48, FUSE_REMOVEMAPPING =3D 49, FUSE_SYNCFS =3D 50, + FUSE_ATOMIC_CREATE =3D 51, =20 /* CUSE specific operations */ CUSE_INIT =3D 4096, --=20 2.17.1 From nobody Sun May 10 14:12:05 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9E8D0C433EF for ; Mon, 2 May 2022 10:26:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1384566AbiEBK30 (ORCPT ); Mon, 2 May 2022 06:29:26 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49620 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1384682AbiEBK3M (ORCPT ); Mon, 2 May 2022 06:29:12 -0400 Received: from mail-pj1-x1034.google.com (mail-pj1-x1034.google.com [IPv6:2607:f8b0:4864:20::1034]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A7A3DFD24; Mon, 2 May 2022 03:25:43 -0700 (PDT) Received: by mail-pj1-x1034.google.com with SMTP id cq17-20020a17090af99100b001dc0386cd8fso7005187pjb.5; Mon, 02 May 2022 03:25:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :organization; bh=l1dSrjQAuEBdUacWP6Ms303wdXzk6rFGelJiALQ8vx8=; b=OphJ2JRi6IalQLYDk5xioo+CJUSZo6HPlh9cPqp4cXNbYCbWpi/sd5e6hnuXArOfC+ /YAR7jZXZYb8wDdm/3Ker+mcVmnpxsDcsm2/vrTlalB5KIJ5rKs+Fnv7nf6CJ4xfwEom FQT1Al1QXwvckFr/ZSoNOabjL0yn5v/97IEHCijw4COjsnGhd6Xc3H6tiFma8RNB2iWe 8TKxe+gyzsVJXOw+4O+dYsfFTDKAiStetNRG+MHXmhatT0hCRMI38mTdm3j3MdFnETK0 UQ3b2DX8EGaJqecIjnWPMJ73NM0UOkugkttB6mjru4A99cKbF0KDS8L9r48SNMVk8IUz Kzmw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:organization; bh=l1dSrjQAuEBdUacWP6Ms303wdXzk6rFGelJiALQ8vx8=; b=x1KmTWzJOEDETMmiNbRTRxfJfUaJbquXJgjjem7rCiUzrI/YKOhyr2QkjPMn2TPoRQ xM7Jd/mhBmyCYV482L7VriryT6ySNWjsJa263KwsFpUx2f+CePtzab4OYRJXBEhMJITP XISh+EzhdAzyDYJn4rVMq9Q29Nb35sF0ZoMgHKJqms2Su3KWFUJyABFWTOs7UP708h8e x6JnVi338moURx+CE1kYBmAeeCpnTZUjQaiVQ7Te228lq32Qh224mxtVyrGCFsGmXjI/ Gfmdnz1r+eXScp1L8AcVNwAZVShPbCnTKEIGGLxgB4tnnKEDOBRio8ALWRNQC+KVoCHp hXqQ== X-Gm-Message-State: AOAM533G2FKsPHzu/wY5+cxJ19VuwEV8YQaLOAkoz37Jxh25vx9f923m P4fkIlYUa4kUWulqrbvpbqc= X-Google-Smtp-Source: ABdhPJzqSYbaNTH6joCYeUBSa7O5obu7n0hk3kK9x4ISQZ9KhBeOs6fVM6YHi3+2fWANS68A9k3CJA== X-Received: by 2002:a17:90a:e7cb:b0:1dc:6602:7178 with SMTP id kb11-20020a17090ae7cb00b001dc66027178mr2423427pjb.175.1651487143059; Mon, 02 May 2022 03:25:43 -0700 (PDT) Received: from localhost.localdomain ([123.201.245.164]) by smtp.googlemail.com with ESMTPSA id j14-20020aa7800e000000b0050dc762816bsm4347953pfi.69.2022.05.02.03.25.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 May 2022 03:25:42 -0700 (PDT) From: Dharmendra Singh To: miklos@szeredi.hu Cc: Dharmendra Singh , linux-fsdevel@vger.kernel.org, fuse-devel@lists.sourceforge.net, linux-kernel@vger.kernel.org, bschubert@ddn.com, Dharmendra Singh Subject: [PATCH v4 2/3] FUSE: Implement atomic lookup + open Date: Mon, 2 May 2022 15:55:20 +0530 Message-Id: <20220502102521.22875-3-dharamhans87@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20220502102521.22875-1-dharamhans87@gmail.com> References: <20220502102521.22875-1-dharamhans87@gmail.com> Organization: DDN STORAGE Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: Dharmendra Singh We can optimize aggressive lookups which are triggered when there is normal open for file/dir (dentry is new/negative). Here in this case since we are anyway going to open the file/dir with USER SPACE, avoid this separate lookup call into libfuse and combine it with open call into libfuse. This lookup + open in single call to libfuse has been named as atomic open. It is expected that USER SPACE opens the file and fills in the attributes, which are then used to make inode stand/revalidate in the kernel cache. Signed-off-by: Dharmendra Singh --- fs/fuse/dir.c | 78 ++++++++++++++++++++++++++++++--------- fs/fuse/fuse_i.h | 3 ++ fs/fuse/inode.c | 4 +- include/uapi/linux/fuse.h | 2 + 4 files changed, 69 insertions(+), 18 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index cad3322a007f..6879d3a86796 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -516,18 +516,18 @@ static int get_security_context(struct dentry *entry,= umode_t mode, } =20 /* - * Atomic create+open operation - * - * If the filesystem doesn't support this, then fall back to separate - * 'mknod' + 'open' requests. + * This is common function for initiating atomic operations into libfuse. + * Currently being used by Atomic create(atomic lookup + create) and + * Atomic open(atomic lookup + open). */ -static int fuse_create_open(struct inode *dir, struct dentry *entry, - struct file *file, unsigned int flags, - umode_t mode, uint32_t opcode) +static int fuse_atomic_do_common(struct inode *dir, struct dentry *entry, + struct dentry **alias, struct file *file, + unsigned int flags, umode_t mode, uint32_t opcode) { int err; struct inode *inode; struct fuse_mount *fm =3D get_fuse_mount(dir); + struct fuse_conn *fc =3D get_fuse_conn(dir); FUSE_ARGS(args); struct fuse_forget_link *forget; struct fuse_create_in inarg; @@ -539,9 +539,13 @@ static int fuse_create_open(struct inode *dir, struct = dentry *entry, void *security_ctx =3D NULL; u32 security_ctxlen; bool atomic_create =3D (opcode =3D=3D FUSE_ATOMIC_CREATE ? true : false); + bool create_op =3D (opcode =3D=3D FUSE_CREATE || + opcode =3D=3D FUSE_ATOMIC_CREATE) ? true : false; + if (alias) + *alias =3D NULL; =20 /* Userspace expects S_IFREG in create mode */ - BUG_ON((mode & S_IFMT) !=3D S_IFREG); + BUG_ON(create_op && (mode & S_IFMT) !=3D S_IFREG); =20 forget =3D fuse_alloc_forget(); err =3D -ENOMEM; @@ -553,7 +557,7 @@ static int fuse_create_open(struct inode *dir, struct d= entry *entry, if (!ff) goto out_put_forget_req; =20 - if (!fm->fc->dont_mask) + if (!fc->dont_mask) mode &=3D ~current_umask(); =20 flags &=3D ~O_NOCTTY; @@ -642,8 +646,9 @@ static int fuse_create_open(struct inode *dir, struct d= entry *entry, err =3D PTR_ERR(res); goto out_err; } - /* res is expected to be NULL since its REG file */ - WARN_ON(res); + entry =3D res; + if (alias) + *alias =3D res; } } fuse_change_entry_timeout(entry, &outentry); @@ -681,8 +686,8 @@ static int fuse_atomic_create(struct inode *dir, struct= dentry *entry, if (fc->no_atomic_create) return -ENOSYS; =20 - err =3D fuse_create_open(dir, entry, file, flags, mode, - FUSE_ATOMIC_CREATE); + err =3D fuse_atomic_do_common(dir, entry, NULL, file, flags, mode, + FUSE_ATOMIC_CREATE); /* If atomic create is not implemented then indicate in fc so that next * request falls back to normal create instead of going into libufse and * returning with -ENOSYS. @@ -694,6 +699,29 @@ static int fuse_atomic_create(struct inode *dir, struc= t dentry *entry, return err; } =20 +static int fuse_do_atomic_open(struct inode *dir, struct dentry *entry, + struct dentry **alias, struct file *file, + unsigned int flags, umode_t mode) +{ + int err; + struct fuse_conn *fc =3D get_fuse_conn(dir); + + if (!fc->do_atomic_open) + return -ENOSYS; + + err =3D fuse_atomic_do_common(dir, entry, alias, file, flags, mode, + FUSE_ATOMIC_OPEN); + /* Atomic open imply atomic trunc as well i.e truncate should be performed + * as part of atomic open call itself. + */ + if (!fc->atomic_o_trunc) { + if (fc->do_atomic_open) + fc->atomic_o_trunc =3D 1; + } + + return err; +} + static int fuse_mknod(struct user_namespace *, struct inode *, struct dent= ry *, umode_t, dev_t); static int fuse_atomic_open(struct inode *dir, struct dentry *entry, @@ -702,12 +730,23 @@ static int fuse_atomic_open(struct inode *dir, struct= dentry *entry, { int err; struct fuse_conn *fc =3D get_fuse_conn(dir); - struct dentry *res =3D NULL; + struct dentry *res =3D NULL, *alias =3D NULL; bool create =3D flags & O_CREAT ? true : false; =20 if (fuse_is_bad(dir)) return -EIO; =20 + if (!create) { + err =3D fuse_do_atomic_open(dir, entry, &alias, + file, flags, mode); + res =3D alias; + if (!err || err !=3D -ENOSYS) + goto out_dput; + } + /* + * ENOSYS fall back for open- user space does not have full + * atomic open. + */ if ((!create || fc->no_atomic_create) && d_in_lookup(entry)) { res =3D fuse_lookup(dir, entry, 0); if (IS_ERR(res)) @@ -730,9 +769,14 @@ static int fuse_atomic_open(struct inode *dir, struct = dentry *entry, /* Libfuse/user space has not implemented atomic create, therefore * fall back to normal create. */ - if (err =3D=3D -ENOSYS) - err =3D fuse_create_open(dir, entry, file, flags, mode, - FUSE_CREATE); + /* Atomic create+open operation + * If the filesystem doesn't support this, then fall back to separate + * 'mknod' + 'open' requests. + */ + if (err =3D=3D -ENOSYS) { + err =3D fuse_atomic_do_common(dir, entry, NULL, file, flags, + mode, FUSE_CREATE); + } if (err =3D=3D -ENOSYS) { fc->no_create =3D 1; goto mknod; diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index d577a591ab16..24793b82303f 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -669,6 +669,9 @@ struct fuse_conn { /** Is open/release not implemented by fs? */ unsigned no_open:1; =20 + /** Is atomic open implemented by fs ? */ + unsigned do_atomic_open : 1; + /** Is atomic create not implemented by fs? */ unsigned no_atomic_create:1; =20 diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index ee846ce371d8..5f667de69115 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -1190,6 +1190,8 @@ static void process_init_reply(struct fuse_mount *fm,= struct fuse_args *args, fc->setxattr_ext =3D 1; if (flags & FUSE_SECURITY_CTX) fc->init_security =3D 1; + if (flags & FUSE_DO_ATOMIC_OPEN) + fc->do_atomic_open =3D 1; } else { ra_pages =3D fc->max_read / PAGE_SIZE; fc->no_lock =3D 1; @@ -1235,7 +1237,7 @@ void fuse_send_init(struct fuse_mount *fm) FUSE_ABORT_ERROR | FUSE_MAX_PAGES | FUSE_CACHE_SYMLINKS | FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA | FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT | FUSE_INIT_EXT | - FUSE_SECURITY_CTX; + FUSE_SECURITY_CTX | FUSE_DO_ATOMIC_OPEN; #ifdef CONFIG_FUSE_DAX if (fm->fc->dax) flags |=3D FUSE_MAP_ALIGNMENT; diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index e4b56004b148..ab91e391241a 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -391,6 +391,7 @@ struct fuse_file_lock { /* bits 32..63 get shifted down 32 bits into the flags2 field */ #define FUSE_SECURITY_CTX (1ULL << 32) #define FUSE_HAS_INODE_DAX (1ULL << 33) +#define FUSE_DO_ATOMIC_OPEN (1ULL << 34) =20 /** * CUSE INIT request/reply flags @@ -540,6 +541,7 @@ enum fuse_opcode { FUSE_REMOVEMAPPING =3D 49, FUSE_SYNCFS =3D 50, FUSE_ATOMIC_CREATE =3D 51, + FUSE_ATOMIC_OPEN =3D 52, =20 /* CUSE specific operations */ CUSE_INIT =3D 4096, --=20 2.17.1 From nobody Sun May 10 14:12:05 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 67484C433EF for ; Mon, 2 May 2022 10:26:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1384615AbiEBKaN (ORCPT ); Mon, 2 May 2022 06:30:13 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49816 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1384529AbiEBK3g (ORCPT ); Mon, 2 May 2022 06:29:36 -0400 Received: from mail-pg1-x536.google.com (mail-pg1-x536.google.com [IPv6:2607:f8b0:4864:20::536]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 21CD51B7AD; Mon, 2 May 2022 03:25:48 -0700 (PDT) Received: by mail-pg1-x536.google.com with SMTP id j70so467644pge.1; Mon, 02 May 2022 03:25:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :organization; bh=WXFXGQi+iet5+lgGzqg5SszKRG5EecB0RAmpnm9jraE=; b=LgM2jeaQmqcHIf89473SvD++ixQlMsXNmu8NVUFzdYWl8614h0OaUr/cxF5wPOJ/qj +pL6KJpLnAHVVszczpfO0ZHlHXngRvrTxRmso+E4B0gZV0yaQeqOu3ADBVYo8EY3D5q5 iw1crHn6CvqLi4hDRS1Jcn6+L3RiV5nASY9WKSS5TuNn9X/ZclxHIl7HxgEfQ/KMC2WU +Se5cnMaHVH9DJe6Ts658ApiO6uU15Z3OhUt8AtWCpLTGoeEgQzBB821kVyKc8Fdn2ET wO/rbf4YAJVZvDneqfBD9UvSEXyhZAsCnxRJNhNTlk0Moc/ni8m9jjEQ78G4UZCUBn/W RKUg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:organization; bh=WXFXGQi+iet5+lgGzqg5SszKRG5EecB0RAmpnm9jraE=; b=8R+M458Nz+SCAnkNA7NOXoLfYzNr5Gl/XQqzMQ/eswUVOey/r16s95aRhA4dY34yWT hCj+BXY0xpExfd1snGsxBSGg6KtEXsGKG2x7dWMMRCFaopyHCl/Zim9MWpw/CUYkdHz/ 0l/K0EnL8mqVvs3tenELbc2YbtjoAiIzvuFNa5kvJEUb87a0POn1Kog1TdNnYAHgxLxn Gt0UL5jB0X811RWSL+kg2LtIwK3Bj0pcIOfGcx+zKGrCxTEmYdiD29lGRc6Ld7XW7Y5f Mk1edIn8iaN98KnN/wUNQ8tyqRxo/v37gQVKlIA/Kd7Upqn9PNOaLHureU/wGzeUCo2n OCqA== X-Gm-Message-State: AOAM5322oywD6hERdX7d38JxI/zSfBloCQNzHACp91QfBIvMJAsN1jbO pwgfH6f/JpLZPT8YELQr8b8= X-Google-Smtp-Source: ABdhPJytJoRJZaSY5s4Edwl4gX5asEoo9ZV//iD90x73pZ//oos1kbrIa8J5nWJ3U42fPNQWiz4jAg== X-Received: by 2002:a63:8c1:0:b0:3c1:7c1b:7084 with SMTP id 184-20020a6308c1000000b003c17c1b7084mr9033750pgi.438.1651487147386; Mon, 02 May 2022 03:25:47 -0700 (PDT) Received: from localhost.localdomain ([123.201.245.164]) by smtp.googlemail.com with ESMTPSA id j14-20020aa7800e000000b0050dc762816bsm4347953pfi.69.2022.05.02.03.25.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 May 2022 03:25:47 -0700 (PDT) From: Dharmendra Singh To: miklos@szeredi.hu Cc: Dharmendra Singh , linux-fsdevel@vger.kernel.org, fuse-devel@lists.sourceforge.net, linux-kernel@vger.kernel.org, bschubert@ddn.com, Dharmendra Singh Subject: [PATCH v4 3/3] FUSE: Avoid lookup in d_revalidate() Date: Mon, 2 May 2022 15:55:21 +0530 Message-Id: <20220502102521.22875-4-dharamhans87@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20220502102521.22875-1-dharamhans87@gmail.com> References: <20220502102521.22875-1-dharamhans87@gmail.com> Organization: DDN STORAGE Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: Dharmendra Singh With atomic open + lookup implemented, it is possible to avoid lookups in FUSE d_revalidate() for objects other than directories. If FUSE is mounted with default permissions then this optimization is not possible as we need to fetch fresh inode attributes for permission check. This lookup skipped in d_revalidate() can be performed as part of open call into libfuse which is made from fuse_file_open(). And when we return from USER SPACE with file opened and fresh attributes, we can revalidate the inode. Signed-off-by: Dharmendra Singh Reported-by: kernel test robot --- fs/fuse/dir.c | 89 ++++++++++++++++++++++++++++++++++++++++++------ fs/fuse/file.c | 30 ++++++++++++++-- fs/fuse/fuse_i.h | 10 +++++- fs/fuse/ioctl.c | 2 +- 4 files changed, 115 insertions(+), 16 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 6879d3a86796..1594fecc920f 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -196,6 +196,7 @@ static void fuse_lookup_init(struct fuse_conn *fc, stru= ct fuse_args *args, * the lookup once more. If the lookup results in the same inode, * then refresh the attributes, timeouts and mark the dentry valid. */ + static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags) { struct inode *inode; @@ -224,6 +225,17 @@ static int fuse_dentry_revalidate(struct dentry *entry= , unsigned int flags) =20 fm =3D get_fuse_mount(inode); =20 + /* If atomic open is supported by FUSE then use this opportunity + * (only for non-dir) to avoid this lookup and combine + * lookup + open into single call. + */ + if (!fm->fc->default_permissions && fm->fc->do_atomic_open && + !(flags & (LOOKUP_EXCL | LOOKUP_REVAL)) && + (flags & LOOKUP_OPEN) && !S_ISDIR(inode->i_mode)) { + ret =3D 1; + goto out; + } + forget =3D fuse_alloc_forget(); ret =3D -ENOMEM; if (!forget) @@ -515,14 +527,52 @@ static int get_security_context(struct dentry *entry,= umode_t mode, return err; } =20 +/* + * Revalidate the inode after we got fresh attributes from user space. + */ +static int fuse_atomic_open_revalidate_inode(struct inode *reval_inode, + struct dentry *entry, + struct fuse_forget_link *forget, + struct fuse_entry_out *outentry, + u64 attr_version) +{ + struct fuse_inode *fi; + struct fuse_conn *fc =3D get_fuse_conn(reval_inode); + + /* Mode should be other than directory */ + BUG_ON(S_ISDIR(reval_inode->i_mode)); + + if (outentry->nodeid !=3D get_node_id(reval_inode)) { + fuse_queue_forget(fc, forget, outentry->nodeid, 1); + return -ESTALE; + } + if (fuse_stale_inode(reval_inode, outentry->generation, + &outentry->attr)) { + fuse_make_bad(reval_inode); + return -ESTALE; + } + fi =3D get_fuse_inode(reval_inode); + spin_lock(&fi->lock); + fi->nlookup++; + spin_unlock(&fi->lock); + + forget_all_cached_acls(reval_inode); + fuse_change_attributes(reval_inode, &outentry->attr, + entry_attr_timeout(outentry), attr_version); + fuse_change_entry_timeout(entry, outentry); + return 0; +} + + /* * This is common function for initiating atomic operations into libfuse. * Currently being used by Atomic create(atomic lookup + create) and * Atomic open(atomic lookup + open). */ -static int fuse_atomic_do_common(struct inode *dir, struct dentry *entry, +static int fuse_do_atomic_common(struct inode *dir, struct dentry *entry, struct dentry **alias, struct file *file, - unsigned int flags, umode_t mode, uint32_t opcode) + struct inode *reval_inode, unsigned int flags, + umode_t mode, uint32_t opcode) { int err; struct inode *inode; @@ -541,6 +591,8 @@ static int fuse_atomic_do_common(struct inode *dir, str= uct dentry *entry, bool atomic_create =3D (opcode =3D=3D FUSE_ATOMIC_CREATE ? true : false); bool create_op =3D (opcode =3D=3D FUSE_CREATE || opcode =3D=3D FUSE_ATOMIC_CREATE) ? true : false; + u64 attr_version =3D fuse_get_attr_version(fm->fc); + if (alias) *alias =3D NULL; =20 @@ -609,6 +661,20 @@ static int fuse_atomic_do_common(struct inode *dir, st= ruct dentry *entry, ff->fh =3D outopen.fh; ff->nodeid =3D outentry.nodeid; ff->open_flags =3D outopen.open_flags; + + /* Inode revalidation was bypassed previously for type other than + * directories, revalidate now as we got fresh attributes. + */ + if (reval_inode) { + err =3D fuse_atomic_open_revalidate_inode(reval_inode, entry, + forget, &outentry, + attr_version); + if (err) + goto out_free_ff; + inode =3D reval_inode; + kfree(forget); + goto out_finish_open; + } inode =3D fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation, &outentry.attr, entry_attr_timeout(&outentry), 0); if (!inode) { @@ -659,6 +725,7 @@ static int fuse_atomic_do_common(struct inode *dir, str= uct dentry *entry, if (!atomic_create || (outopen.open_flags & FOPEN_FILE_CREATED)) fuse_dir_changed(dir); err =3D finish_open(file, entry, generic_file_open); +out_finish_open: if (err) { fi =3D get_fuse_inode(inode); fuse_sync_release(fi, ff, flags); @@ -686,7 +753,7 @@ static int fuse_atomic_create(struct inode *dir, struct= dentry *entry, if (fc->no_atomic_create) return -ENOSYS; =20 - err =3D fuse_atomic_do_common(dir, entry, NULL, file, flags, mode, + err =3D fuse_do_atomic_common(dir, entry, NULL, file, NULL, flags, mode, FUSE_ATOMIC_CREATE); /* If atomic create is not implemented then indicate in fc so that next * request falls back to normal create instead of going into libufse and @@ -699,9 +766,10 @@ static int fuse_atomic_create(struct inode *dir, struc= t dentry *entry, return err; } =20 -static int fuse_do_atomic_open(struct inode *dir, struct dentry *entry, - struct dentry **alias, struct file *file, - unsigned int flags, umode_t mode) +int fuse_do_atomic_open(struct inode *dir, struct dentry *entry, + struct dentry **alias, struct file *file, + struct inode *reval_inode, unsigned int flags, + umode_t mode) { int err; struct fuse_conn *fc =3D get_fuse_conn(dir); @@ -709,8 +777,8 @@ static int fuse_do_atomic_open(struct inode *dir, struc= t dentry *entry, if (!fc->do_atomic_open) return -ENOSYS; =20 - err =3D fuse_atomic_do_common(dir, entry, alias, file, flags, mode, - FUSE_ATOMIC_OPEN); + err =3D fuse_do_atomic_common(dir, entry, alias, file, reval_inode, flags, + mode, FUSE_ATOMIC_OPEN); /* Atomic open imply atomic trunc as well i.e truncate should be performed * as part of atomic open call itself. */ @@ -718,7 +786,6 @@ static int fuse_do_atomic_open(struct inode *dir, struc= t dentry *entry, if (fc->do_atomic_open) fc->atomic_o_trunc =3D 1; } - return err; } =20 @@ -738,7 +805,7 @@ static int fuse_atomic_open(struct inode *dir, struct d= entry *entry, =20 if (!create) { err =3D fuse_do_atomic_open(dir, entry, &alias, - file, flags, mode); + file, NULL, flags, mode); res =3D alias; if (!err || err !=3D -ENOSYS) goto out_dput; @@ -774,7 +841,7 @@ static int fuse_atomic_open(struct inode *dir, struct d= entry *entry, * 'mknod' + 'open' requests. */ if (err =3D=3D -ENOSYS) { - err =3D fuse_atomic_do_common(dir, entry, NULL, file, flags, + err =3D fuse_do_atomic_common(dir, entry, NULL, file, NULL, flags, mode, FUSE_CREATE); } if (err =3D=3D -ENOSYS) { diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 829094451774..2b0548163249 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -125,11 +125,15 @@ static void fuse_file_put(struct fuse_file *ff, bool = sync, bool isdir) } =20 struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid, - unsigned int open_flags, bool isdir) + struct file *file, unsigned int open_flags, + bool isdir) { struct fuse_conn *fc =3D fm->fc; struct fuse_file *ff; + struct dentry *dentry =3D NULL; + struct dentry *parent =3D NULL; int opcode =3D isdir ? FUSE_OPENDIR : FUSE_OPEN; + int ret; =20 ff =3D fuse_file_alloc(fm); if (!ff) @@ -138,6 +142,11 @@ struct fuse_file *fuse_file_open(struct fuse_mount *fm= , u64 nodeid, ff->fh =3D 0; /* Default for no-open */ ff->open_flags =3D FOPEN_KEEP_CACHE | (isdir ? FOPEN_CACHE_DIR : 0); + + /* For directories we already had lookup */ + if (!isdir && fc->do_atomic_open && file !=3D NULL) + goto revalidate_atomic_open; + if (isdir ? !fc->no_opendir : !fc->no_open) { struct fuse_open_out outarg; int err; @@ -164,12 +173,27 @@ struct fuse_file *fuse_file_open(struct fuse_mount *f= m, u64 nodeid, ff->nodeid =3D nodeid; =20 return ff; + +revalidate_atomic_open: + dentry =3D file->f_path.dentry; + /* Get ref on parent */ + parent =3D dget_parent(dentry); + open_flags &=3D ~(O_CREAT | O_EXCL | O_NOCTTY); + ret =3D fuse_do_atomic_open(d_inode_rcu(parent), dentry, NULL, file, + d_inode_rcu(dentry), open_flags, 0); + dput(parent); + if (ret) + goto err_out; + ff =3D file->private_data; + return ff; +err_out: + return ERR_PTR(ret); } =20 int fuse_do_open(struct fuse_mount *fm, u64 nodeid, struct file *file, bool isdir) { - struct fuse_file *ff =3D fuse_file_open(fm, nodeid, file->f_flags, isdir); + struct fuse_file *ff =3D fuse_file_open(fm, nodeid, file, file->f_flags, = isdir); =20 if (!IS_ERR(ff)) file->private_data =3D ff; @@ -252,7 +276,7 @@ int fuse_open_common(struct inode *inode, struct file *= file, bool isdir) } =20 err =3D fuse_do_open(fm, get_node_id(inode), file, isdir); - if (!err) + if (!err && (!fc->do_atomic_open || isdir)) fuse_finish_open(inode, file); =20 out: diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 24793b82303f..5c83e4249b7e 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1014,6 +1014,13 @@ void fuse_finish_open(struct inode *inode, struct fi= le *file); void fuse_sync_release(struct fuse_inode *fi, struct fuse_file *ff, unsigned int flags); =20 +/** + * Send atomic create + open or lookup + open + */ +int fuse_do_atomic_open(struct inode *dir, struct dentry *entry, + struct dentry **alias, struct file *file, + struct inode *reval_inode, unsigned int flags, + umode_t mode); /** * Send RELEASE or RELEASEDIR request */ @@ -1317,7 +1324,8 @@ int fuse_fileattr_set(struct user_namespace *mnt_user= ns, /* file.c */ =20 struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid, - unsigned int open_flags, bool isdir); + struct file *file, unsigned int open_flags, + bool isdir); void fuse_file_release(struct inode *inode, struct fuse_file *ff, unsigned int open_flags, fl_owner_t id, bool isdir); =20 diff --git a/fs/fuse/ioctl.c b/fs/fuse/ioctl.c index fbc09dab1f85..63106a54ba1a 100644 --- a/fs/fuse/ioctl.c +++ b/fs/fuse/ioctl.c @@ -408,7 +408,7 @@ static struct fuse_file *fuse_priv_ioctl_prepare(struct= inode *inode) if (!S_ISREG(inode->i_mode) && !isdir) return ERR_PTR(-ENOTTY); =20 - return fuse_file_open(fm, get_node_id(inode), O_RDONLY, isdir); + return fuse_file_open(fm, get_node_id(inode), NULL, O_RDONLY, isdir); } =20 static void fuse_priv_ioctl_cleanup(struct inode *inode, struct fuse_file = *ff) --=20 2.17.1