From patchwork Tue Feb 6 14:24:45 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Amir Goldstein X-Patchwork-Id: 13547370 Received: from mail-wr1-f47.google.com (mail-wr1-f47.google.com [209.85.221.47]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2EA8273195 for ; Tue, 6 Feb 2024 14:25:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.47 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707229503; cv=none; b=XGJTnnsDoPnwWjAvbpPlZjPYdKvr90qd7DXjnayciXpFH/YpOJTXsFrmV6Sd5Yt7Ie6d70yXfoOzEuWJZn6FkPbez4Nms++Wb0tFGCgndY7nI7LjnId/I4/oj/Q5k5LQRcpCxEHqbIogdNSDFut+bA5qiBiKLsEEQ8AXoJkj6gw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707229503; c=relaxed/simple; bh=opejIIRYiFOTagPW0g2xr08qna2N3HX9goXTiiuoZWM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ff0KMCW1t880GksshgYcsicVhckD0Xm2LsR1/xI8In7RD3zoSXkko1RL+za4k5x0BR9o6fgM+wL8SoM+h+IAy5so07CSoWE632n+QMAdpMQVzjQI5X02GNwXhPd2ogL8ppp0d63bxgB8ARUBkmmaa63mQrEu6yrPhzYs5CWV2TM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=FDJbeJQ1; arc=none smtp.client-ip=209.85.221.47 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="FDJbeJQ1" Received: by mail-wr1-f47.google.com with SMTP id ffacd0b85a97d-3394b892691so411902f8f.1 for ; Tue, 06 Feb 2024 06:25:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1707229500; x=1707834300; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=x1N1OrLMa6t8qMkOD0BLoblqhbOaLOnVx09FMW3MOGg=; b=FDJbeJQ1J46Mtd+p22O5DkX0O8ggreSq4fQ+W9swwjbGHTJRdYPXrKStCni60OeMDL jO/mPpbG/XOIYWZem82OBKikWPGlBT+APeCJZ3DW9vo27zZkBC+9vgJsGEIrrpSru27o YaZUjozoJHrZmGS6h57AHq0qF4k5RBvhh9ez5sVUQ1LRO0OZomcu31E0/HqYej30j2ch GL7aTCJFpdLuQabIKhhemXa1NUmDUtSF8sIl4eqGCVNEqQ3OwEhuLz69UJsA8lY2Fbw3 YY6Nmoo+nd69/x21HKYHiofb/3bwT2kxZKFLWp/buPTCYvwnmxzgvHFswJAy+wz1FyTI hBIw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1707229500; x=1707834300; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=x1N1OrLMa6t8qMkOD0BLoblqhbOaLOnVx09FMW3MOGg=; b=nUKiKnzn3XRFPkf0x8WYiczXY3iH4fSBy70oWlLJYvt8XLfmuDbuZPUeYcmPcs5gIq XpCsgmDhQ4kaz3oLcIWf/PwkUnTU43x7kRCB3foCLHRHQwp0hUWwqHpc2ysEea9MDm4m Gk82UfCUzTCfPr8Tgbva1FdoL+sGscBnHtkm2HQFBcKGXbn+mpuNfps7GklDKucaG4QP 8+UvSmPnm6Ol4k7gCZfdRvDVbwRhwUmzEBLZaT++ybTtjIKkG55WgPtWMaw3Xx2tonGS AZfswtEKSmZspkjk3bATFAHibbnBrlNTNyNZEvUxYjsiGAD5qmb5WQJMDB3R/AK+ve9u AH+A== X-Gm-Message-State: AOJu0YyMp8IOMzqwPqZZgyrqNqR/GUVt6Mx3TRhcxGlYvGKKSkZpH0o1 yfqPxQk1Q/3FfV2YN61Tn3yHh6oFyABgDfxHnzbj1TfcQrzmMnjv X-Google-Smtp-Source: AGHT+IG5XbQiUG2x3bUg/AwmbC3B2SWAlnPh4G1yObQ2zvVMe1rNgPpV56tAgREbgzHfv3SU0h6jTA== X-Received: by 2002:adf:f585:0:b0:33b:4709:ebf with SMTP id f5-20020adff585000000b0033b47090ebfmr1285062wro.21.1707229500392; Tue, 06 Feb 2024 06:25:00 -0800 (PST) X-Forwarded-Encrypted: i=0; AJvYcCU6iHtHrNOLBoh2oVnEdzSfyrPYHgsudOHt1XKC8kOcErbyzCg5KzrJtGIMrSKeG23s8/dFwHiBAjwMnoH7H2106y/w3abfhTpkC9roYA== Received: from amir-ThinkPad-T480.lan (46-117-242-41.bb.netvision.net.il. [46.117.242.41]) by smtp.gmail.com with ESMTPSA id c28-20020adfa31c000000b0033b4a6f46d7sm629728wrb.87.2024.02.06.06.24.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Feb 2024 06:24:59 -0800 (PST) From: Amir Goldstein To: Miklos Szeredi Cc: Bernd Schubert , linux-fsdevel@vger.kernel.org Subject: [PATCH v15 1/9] fuse: factor out helper for FUSE_DEV_IOC_CLONE Date: Tue, 6 Feb 2024 16:24:45 +0200 Message-Id: <20240206142453.1906268-2-amir73il@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240206142453.1906268-1-amir73il@gmail.com> References: <20240206142453.1906268-1-amir73il@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 In preparation to adding more fuse dev ioctls. Signed-off-by: Amir Goldstein --- fs/fuse/dev.c | 59 ++++++++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 1a8f82f478cb..eba68b57bd7c 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -2251,43 +2251,50 @@ static int fuse_device_clone(struct fuse_conn *fc, struct file *new) return 0; } -static long fuse_dev_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) +static long fuse_dev_ioctl_clone(struct file *file, __u32 __user *argp) { int res; int oldfd; struct fuse_dev *fud = NULL; struct fd f; + if (get_user(oldfd, argp)) + return -EFAULT; + + f = fdget(oldfd); + if (!f.file) + return -EINVAL; + + /* + * Check against file->f_op because CUSE + * uses the same ioctl handler. + */ + if (f.file->f_op == file->f_op) + fud = fuse_get_dev(f.file); + + res = -EINVAL; + if (fud) { + mutex_lock(&fuse_mutex); + res = fuse_device_clone(fud->fc, file); + mutex_unlock(&fuse_mutex); + } + + fdput(f); + return res; +} + +static long fuse_dev_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + switch (cmd) { case FUSE_DEV_IOC_CLONE: - if (get_user(oldfd, (__u32 __user *)arg)) - return -EFAULT; + return fuse_dev_ioctl_clone(file, argp); - f = fdget(oldfd); - if (!f.file) - return -EINVAL; - - /* - * Check against file->f_op because CUSE - * uses the same ioctl handler. - */ - if (f.file->f_op == file->f_op) - fud = fuse_get_dev(f.file); - - res = -EINVAL; - if (fud) { - mutex_lock(&fuse_mutex); - res = fuse_device_clone(fud->fc, file); - mutex_unlock(&fuse_mutex); - } - fdput(f); - break; default: - res = -ENOTTY; - break; + return -ENOTTY; } - return res; } const struct file_operations fuse_dev_operations = { From patchwork Tue Feb 6 14:24:46 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Amir Goldstein X-Patchwork-Id: 13547371 Received: from mail-wm1-f51.google.com (mail-wm1-f51.google.com [209.85.128.51]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A4A16132466 for ; Tue, 6 Feb 2024 14:25:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707229505; cv=none; b=ClOg9TDRShLUODK+EOCDOHG/jcKAJadQA0HRcJPvcwsQW3fHDZZ6KxgRNyfnPALQ5McptycbCAjFQ9BhNMNXx76XI/7mKOWHQkRQ4D5E3Wc3n0wM8Eb/LQZ0xARXCJZYwLvfh3ztWa1Jzj0XOYn6+/Fnu69DXTLpd8KoJ27M8co= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707229505; c=relaxed/simple; bh=Kq+2LDxQXYPBmn7BmyHfD9Zo7QZ74NQHanx4ZCrmJuw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=s9n/1IYin3aF6e3caoddyW0fZCeDty6sPoxTLZd/IX7JOGEtVF5fP1rHCm/RMAz+VYVp2PTGmSpcNNAlWrF1XuZc8NSMimjyZccCnQX4PhTPv91TI3hy41nm+I/yLkJGyJPNNF4OSB0wJ7c/2N52Ghc7u7flwDUTvUDxEPpmnEk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Sfuwkirq; arc=none smtp.client-ip=209.85.128.51 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Sfuwkirq" Received: by mail-wm1-f51.google.com with SMTP id 5b1f17b1804b1-40fe00cb134so9828225e9.3 for ; Tue, 06 Feb 2024 06:25:03 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1707229502; x=1707834302; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=e+txD8wS3djRCj/7PZtcxqCRkr/lkZ8VWSwV0UYsjoA=; b=SfuwkirqSJu6ohBAqjtYZ43Q/Tec6J4keBEF5/NCdNKU9zfOMLBanUEqlQfaHPi51Y +y1QoCEM0bzNn96sJzbbkS8WUnckqTrmM+zCtlI9YXiK0Im9K0aOrFgCQ/PNiLySGlRA ExY0Pn1dRX9ZVZyrV0m8zys2W6Y9FVJliiwffKORydxaBpJcLGGniDerwzvDVLAzoIRG V4brepWE31qwecGN5ocgZcFj7sTDs74/YkW2UkX7i4m9sLzAYK+YzajemJyP3/01YBfb QWNHY5b2vxIVW1DfQPePFYTH6dAKMcSgjBWF3vCYkg3s6zXa7ZNOG2Jukpodiu8X8H+3 f+ig== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1707229502; x=1707834302; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=e+txD8wS3djRCj/7PZtcxqCRkr/lkZ8VWSwV0UYsjoA=; b=rY50szAYaKoMxfGje5Uyyq6BYHN42T35ZfIdiGCDNWKaMS6GN18JxT55J0JRvcIb3N L+I3hdcP0WSJBp8+KxU+//aQ4mILT5GxGXhtSEfybyRxg1E2Y3MCDuEeM+I+WiEFEt7z oAjKX32/o5IkB8QFvLI2jSm3RjvQDzywTDqiCqN6Af6AfiYqMjR7YW+S/PfbsB0ZLa4a VV8TDvQznzSlogc7W3LxmWJ7Few0E1D480QcOlzU3p0Cy3E1824fCtH4faEPg8wkq9Ao W5yLQrxqBVYtzZEU8ch1ClWdMwk5f9i+OBghn8xYhA8BltrekoL9b6TuKG3hjIVOqteu 2ixg== X-Gm-Message-State: AOJu0Yz72/hwneD51RGHAVmiv/31SzCYuHQYSHUPxx+V/h9rO6Mz0//9 9GPgTIdvu7E3mFJxggqGtNzVAPMiwNWXdJRl+ByMFsiB8hEdA9ir+6OnPH1/ X-Google-Smtp-Source: AGHT+IF6j8Xb3J+U29DA1otBcDZZnJdRMIOV7YhnaVB6FtU3g09yY0eXjc+tNDheAPaxrWTwFRIb7A== X-Received: by 2002:a5d:678a:0:b0:33b:28ee:645d with SMTP id v10-20020a5d678a000000b0033b28ee645dmr1430668wru.68.1707229501469; Tue, 06 Feb 2024 06:25:01 -0800 (PST) X-Forwarded-Encrypted: i=0; AJvYcCVl44A0SHBaodF3J3AnN5dMa0R1kAuoweB1RuoYtpW3eG+UcIHrYVbVrzlBJhQFU+T06SQDN+Kv0/zzm5N3V7y5+qcV+EObdasRHrIIVw== Received: from amir-ThinkPad-T480.lan (46-117-242-41.bb.netvision.net.il. [46.117.242.41]) by smtp.gmail.com with ESMTPSA id c28-20020adfa31c000000b0033b4a6f46d7sm629728wrb.87.2024.02.06.06.25.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Feb 2024 06:25:01 -0800 (PST) From: Amir Goldstein To: Miklos Szeredi Cc: Bernd Schubert , linux-fsdevel@vger.kernel.org Subject: [PATCH v15 2/9] fuse: introduce FUSE_PASSTHROUGH capability Date: Tue, 6 Feb 2024 16:24:46 +0200 Message-Id: <20240206142453.1906268-3-amir73il@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240206142453.1906268-1-amir73il@gmail.com> References: <20240206142453.1906268-1-amir73il@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 FUSE_PASSTHROUGH capability to passthrough FUSE operations to backing files will be made available with kernel config CONFIG_FUSE_PASSTHROUGH. When requesting FUSE_PASSTHROUGH, userspace needs to specify the max_stack_depth that is allowed for FUSE on top of backing files. Introduce the flag FOPEN_PASSTHROUGH and backing_id to fuse_open_out argument that can be used when replying to OPEN request, to setup passthrough of io operations on the fuse inode to a backing file. Introduce a refcounted fuse_backing object that will be used to associate an open backing file with a fuse inode. Signed-off-by: Amir Goldstein --- fs/fuse/Kconfig | 11 ++++++++++ fs/fuse/Makefile | 1 + fs/fuse/fuse_i.h | 42 +++++++++++++++++++++++++++++++++++++++ fs/fuse/inode.c | 26 ++++++++++++++++++++++++ fs/fuse/passthrough.c | 30 ++++++++++++++++++++++++++++ include/uapi/linux/fuse.h | 14 ++++++++++--- 6 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 fs/fuse/passthrough.c diff --git a/fs/fuse/Kconfig b/fs/fuse/Kconfig index 038ed0b9aaa5..8674dbfbe59d 100644 --- a/fs/fuse/Kconfig +++ b/fs/fuse/Kconfig @@ -52,3 +52,14 @@ config FUSE_DAX If you want to allow mounting a Virtio Filesystem with the "dax" option, answer Y. + +config FUSE_PASSTHROUGH + bool "FUSE passthrough operations support" + default y + depends on FUSE_FS + select FS_STACK + help + This allows bypassing FUSE server by mapping specific FUSE operations + to be performed directly on a backing file. + + If you want to allow passthrough operations, answer Y. diff --git a/fs/fuse/Makefile b/fs/fuse/Makefile index b734cc2a5e65..6e0228c6d0cb 100644 --- a/fs/fuse/Makefile +++ b/fs/fuse/Makefile @@ -10,5 +10,6 @@ obj-$(CONFIG_VIRTIO_FS) += virtiofs.o fuse-y := dev.o dir.o file.o inode.o control.o xattr.o acl.o readdir.o ioctl.o fuse-y += iomode.o fuse-$(CONFIG_FUSE_DAX) += dax.o +fuse-$(CONFIG_FUSE_PASSTHROUGH) += passthrough.o virtiofs-y := virtio_fs.o diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index dede4378c719..cae525147856 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -76,6 +76,15 @@ struct fuse_submount_lookup { struct fuse_forget_link *forget; }; +/** Container for data related to mapping to backing file */ +struct fuse_backing { + struct file *file; + + /** refcount */ + refcount_t count; + struct rcu_head rcu; +}; + /** FUSE inode */ struct fuse_inode { /** Inode data */ @@ -179,6 +188,10 @@ struct fuse_inode { #endif /** Submount specific lookup tracking */ struct fuse_submount_lookup *submount_lookup; +#ifdef CONFIG_FUSE_PASSTHROUGH + /** Reference to backing file in passthrough mode */ + struct fuse_backing *fb; +#endif }; /** FUSE inode state bits */ @@ -829,6 +842,12 @@ struct fuse_conn { /* Is statx not implemented by fs? */ unsigned int no_statx:1; + /** Passthrough support for read/write IO */ + unsigned int passthrough:1; + + /** Maximum stack depth for passthrough backing files */ + int max_stack_depth; + /** The number of requests waiting for completion */ atomic_t num_waiting; @@ -1373,4 +1392,27 @@ struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid, void fuse_file_release(struct inode *inode, struct fuse_file *ff, unsigned int open_flags, fl_owner_t id, bool isdir); +/* passthrough.c */ +static inline struct fuse_backing *fuse_inode_backing(struct fuse_inode *fi) +{ +#ifdef CONFIG_FUSE_PASSTHROUGH + return READ_ONCE(fi->fb); +#else + return NULL; +#endif +} + +static inline struct fuse_backing *fuse_inode_backing_set(struct fuse_inode *fi, + struct fuse_backing *fb) +{ +#ifdef CONFIG_FUSE_PASSTHROUGH + return xchg(&fi->fb, fb); +#else + return NULL; +#endif +} + +struct fuse_backing *fuse_backing_get(struct fuse_backing *fb); +void fuse_backing_put(struct fuse_backing *fb); + #endif /* _FS_FUSE_I_H */ diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 2a6d44f91729..c771bd3c1336 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -111,6 +111,9 @@ static struct inode *fuse_alloc_inode(struct super_block *sb) if (IS_ENABLED(CONFIG_FUSE_DAX) && !fuse_dax_inode_alloc(sb, fi)) goto out_free_forget; + if (IS_ENABLED(CONFIG_FUSE_PASSTHROUGH)) + fuse_inode_backing_set(fi, NULL); + return &fi->inode; out_free_forget: @@ -129,6 +132,9 @@ static void fuse_free_inode(struct inode *inode) #ifdef CONFIG_FUSE_DAX kfree(fi->dax); #endif + if (IS_ENABLED(CONFIG_FUSE_PASSTHROUGH)) + fuse_backing_put(fuse_inode_backing(fi)); + kmem_cache_free(fuse_inode_cachep, fi); } @@ -1284,6 +1290,24 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args, fc->create_supp_group = 1; if (flags & FUSE_DIRECT_IO_ALLOW_MMAP) fc->direct_io_allow_mmap = 1; + /* + * max_stack_depth is the max stack depth of FUSE fs, + * so it has to be at least 1 to support passthrough + * to backing files. + * + * with max_stack_depth > 1, the backing files can be + * on a stacked fs (e.g. overlayfs) themselves and with + * max_stack_depth == 1, FUSE fs can be stacked as the + * underlying fs of a stacked fs (e.g. overlayfs). + */ + if (IS_ENABLED(CONFIG_FUSE_PASSTHROUGH) && + (flags & FUSE_PASSTHROUGH) && + arg->max_stack_depth > 0 && + arg->max_stack_depth <= FILESYSTEM_MAX_STACK_DEPTH) { + fc->passthrough = 1; + fc->max_stack_depth = arg->max_stack_depth; + fm->sb->s_stack_depth = arg->max_stack_depth; + } } else { ra_pages = fc->max_read / PAGE_SIZE; fc->no_lock = 1; @@ -1339,6 +1363,8 @@ void fuse_send_init(struct fuse_mount *fm) #endif if (fm->fc->auto_submounts) flags |= FUSE_SUBMOUNTS; + if (IS_ENABLED(CONFIG_FUSE_PASSTHROUGH)) + flags |= FUSE_PASSTHROUGH; ia->in.flags = flags; ia->in.flags2 = flags >> 32; diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c new file mode 100644 index 000000000000..e8639c0a9ac6 --- /dev/null +++ b/fs/fuse/passthrough.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * FUSE passthrough to backing file. + * + * Copyright (c) 2023 CTERA Networks. + */ + +#include "fuse_i.h" + +#include + +struct fuse_backing *fuse_backing_get(struct fuse_backing *fb) +{ + if (fb && refcount_inc_not_zero(&fb->count)) + return fb; + return NULL; +} + +static void fuse_backing_free(struct fuse_backing *fb) +{ + if (fb->file) + fput(fb->file); + kfree_rcu(fb, rcu); +} + +void fuse_backing_put(struct fuse_backing *fb) +{ + if (fb && refcount_dec_and_test(&fb->count)) + fuse_backing_free(fb); +} diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index 1f452d9a5024..edfc458e5e8f 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -211,6 +211,10 @@ * 7.39 * - add FUSE_DIRECT_IO_ALLOW_MMAP * - add FUSE_STATX and related structures + * + * 7.40 + * - add max_stack_depth to fuse_init_out, add FUSE_PASSTHROUGH init flag + * - add backing_id to fuse_open_out, add FOPEN_PASSTHROUGH open flag */ #ifndef _LINUX_FUSE_H @@ -246,7 +250,7 @@ #define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ -#define FUSE_KERNEL_MINOR_VERSION 39 +#define FUSE_KERNEL_MINOR_VERSION 40 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 @@ -354,6 +358,7 @@ struct fuse_file_lock { * FOPEN_NOFLUSH: don't flush data cache on close (unless FUSE_WRITEBACK_CACHE) * FOPEN_PARALLEL_DIRECT_WRITES: Allow concurrent direct writes on the same inode * FOPEN_CACHE_IO: internal flag for mmap of direct_io (resereved for future use) + * FOPEN_PASSTHROUGH: passthrough read/write io for this open file */ #define FOPEN_DIRECT_IO (1 << 0) #define FOPEN_KEEP_CACHE (1 << 1) @@ -363,6 +368,7 @@ struct fuse_file_lock { #define FOPEN_NOFLUSH (1 << 5) #define FOPEN_PARALLEL_DIRECT_WRITES (1 << 6) #define FOPEN_CACHE_IO (1 << 7) +#define FOPEN_PASSTHROUGH (1 << 8) /** * INIT request/reply flags @@ -451,6 +457,7 @@ struct fuse_file_lock { #define FUSE_CREATE_SUPP_GROUP (1ULL << 34) #define FUSE_HAS_EXPIRE_ONLY (1ULL << 35) #define FUSE_DIRECT_IO_ALLOW_MMAP (1ULL << 36) +#define FUSE_PASSTHROUGH (1ULL << 37) /* Obsolete alias for FUSE_DIRECT_IO_ALLOW_MMAP */ #define FUSE_DIRECT_IO_RELAX FUSE_DIRECT_IO_ALLOW_MMAP @@ -763,7 +770,7 @@ struct fuse_create_in { struct fuse_open_out { uint64_t fh; uint32_t open_flags; - uint32_t padding; + int32_t backing_id; }; struct fuse_release_in { @@ -879,7 +886,8 @@ struct fuse_init_out { uint16_t max_pages; uint16_t map_alignment; uint32_t flags2; - uint32_t unused[7]; + uint32_t max_stack_depth; + uint32_t unused[6]; }; #define CUSE_INIT_INFO_MAX 4096 From patchwork Tue Feb 6 14:24:47 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Amir Goldstein X-Patchwork-Id: 13547372 Received: from mail-wr1-f45.google.com (mail-wr1-f45.google.com [209.85.221.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5BF4973195 for ; Tue, 6 Feb 2024 14:25:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707229507; cv=none; b=o6Icl/JgWhulqh5UV6VqiSMfZi+MadFniTE8cNSjtNmH1rixLAD6aoZaIYFJHf/+PkxDAwJfeZAu0Qrdc+QE/XpZXrRrnN6hvm7EzAFzJ6LJdxi5QE9pWj+WD1KidJEx6I2VgAyPh4PVRaiSdjeTz2BBYPi3Dlq20x97iQAwe2g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707229507; c=relaxed/simple; bh=/M/pX7CFZkwNXJJ7l9gZf0aR4nyGhj52ycL2nycezDk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=kM2hPeMiSMUBT/8eTrQR1yPNst6hlslFqdojraesFEfHUyYrwK1abk1ogHtb08szkh2qzSVMqUlkg1b+kKtkxlicql2HzErrTRyV34L9rPHbZEK95O33NH+83J3nLvDBR+2LawqffntqoADxOrfuyFadUU5oZZrr3WS3aNvxXOo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=YyOwe3/P; arc=none smtp.client-ip=209.85.221.45 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="YyOwe3/P" Received: by mail-wr1-f45.google.com with SMTP id ffacd0b85a97d-33b409fc4aeso1311340f8f.1 for ; Tue, 06 Feb 2024 06:25:05 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1707229503; x=1707834303; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Ka/d4U+hcRjXP9iCEYKjVzOWDDwWPs33Is0BPjiquDE=; b=YyOwe3/PmHGXhPO1dtfWAb475x9k7Aq5ZcDM3RMk84aWOEwfAyW7gkjS4lU0yMxpVM DomrKEq80VdXs1fzSUEUjCsmCwa8q2scDfuh0Uev1bH/J+IN8q6KGEnsCBwdStPpfgQ6 9q2iy6GSflbSAxEEN4U0so/TCa+JF6DXKm9O/VNFtuofvPpFT1s2ZJCTT6MZ9k37NWZ4 qIAviJaybUGeaNbb49yYdTSLCA7AVTv7uy3mFz0TJbp+upt1HdTok4kVhY+fu5/jSa0i hB4cBywVYwootn3VBastBAJV2wPy3u5hBgxbsuLfTTinQPCqZE4vt2SIf9sOV3p7i4sN ecpA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1707229503; x=1707834303; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Ka/d4U+hcRjXP9iCEYKjVzOWDDwWPs33Is0BPjiquDE=; b=uJZfbG1aOs0Sgdv/tbKTkCkWWfLRweBeFoUu1bKVM844+45y4Q6Qwrec5ZrJfoa+We TO8r0DwtjQ3UHcm5ePDkLYz+G9Fz9T2L35ctabj9Jv1jaWK116Ad/ESJhPB0NNd8Bm8M Fz8+8cFVCXZ/jxet4QYm9aaG1tVViPv61qwD/8I+l8VW5lg0zUrHRrptbLHp1ojYYAIa 6OB08O6QjklFskdHt4ax9ctcVQODGd2p63wNTpCTGW1NKF6nNB9A1u4yQYWMtXzhWO6F a/u+vguufHsRcT7FPUFEdCuaNClWPXQz+eiqzmex4t0lsFvoRs9XbBKffQbqWZVwYwk8 XGJA== X-Gm-Message-State: AOJu0YxpKgMza9HqwjC1WgU8H3Dw7lWFuETNGCZ3PTOmqveW5RRahhPJ YP74Vjv2qR9rLKAjI566M+zbGayr/nlYcJfFYZniPb1VPczEDy1Wm/adM0Vc X-Google-Smtp-Source: AGHT+IGFJxNsYzNfATqdCMuY5p0AWlmuCi2qdSoPhX1X2jU9ZGXkyc6mU88sv1SyPl/U+/qCM3Aymg== X-Received: by 2002:adf:f410:0:b0:33b:3fe8:6413 with SMTP id g16-20020adff410000000b0033b3fe86413mr1601538wro.27.1707229503145; Tue, 06 Feb 2024 06:25:03 -0800 (PST) X-Forwarded-Encrypted: i=0; AJvYcCVnzAD1eK/+eCXs18Pr5SQqhA01qYvSjsCocqA/AVw1rFAjMt1BsQjbvksnLQr16z349GxrYSfSCeaJen2mqlyTeSFHrzQZoPFZoTBp3ZkC+P+sL0VoE7JqqtUMDjIbtRQ+2w== Received: from amir-ThinkPad-T480.lan (46-117-242-41.bb.netvision.net.il. [46.117.242.41]) by smtp.gmail.com with ESMTPSA id c28-20020adfa31c000000b0033b4a6f46d7sm629728wrb.87.2024.02.06.06.25.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Feb 2024 06:25:02 -0800 (PST) From: Amir Goldstein To: Miklos Szeredi Cc: Bernd Schubert , linux-fsdevel@vger.kernel.org, Alessio Balsini Subject: [PATCH v15 3/9] fuse: implement ioctls to manage backing files Date: Tue, 6 Feb 2024 16:24:47 +0200 Message-Id: <20240206142453.1906268-4-amir73il@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240206142453.1906268-1-amir73il@gmail.com> References: <20240206142453.1906268-1-amir73il@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 FUSE server calls the FUSE_DEV_IOC_BACKING_OPEN ioctl with a backing file descriptor. If the call succeeds, a backing file identifier is returned. A later change will be using this backing file id in a reply to OPEN request with the flag FOPEN_PASSTHROUGH to setup passthrough of file operations on the open FUSE file to the backing file. The FUSE server should call FUSE_DEV_IOC_BACKING_CLOSE ioctl to close the backing file by its id. This can be done at any time, but if an open reply with FOPEN_PASSTHROUGH flag is still in progress, the open may fail if the backing file is closed before the fuse file was opened. Setting up backing files requires a server with CAP_SYS_ADMIN privileges. For the backing file to be successfully setup, the backing file must implement both read_iter and write_iter file operations. The limitation on the level of filesystem stacking allowed for the backing file is enforced before setting up the backing file. Signed-off-by: Alessio Balsini Signed-off-by: Amir Goldstein --- fs/fuse/dev.c | 41 ++++++++++++ fs/fuse/fuse_i.h | 10 +++ fs/fuse/inode.c | 5 ++ fs/fuse/passthrough.c | 135 ++++++++++++++++++++++++++++++++++++++ include/uapi/linux/fuse.h | 9 +++ 5 files changed, 200 insertions(+) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index eba68b57bd7c..b680787bd66d 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -2283,6 +2283,41 @@ static long fuse_dev_ioctl_clone(struct file *file, __u32 __user *argp) return res; } +static long fuse_dev_ioctl_backing_open(struct file *file, + struct fuse_backing_map __user *argp) +{ + struct fuse_dev *fud = fuse_get_dev(file); + struct fuse_backing_map map; + + if (!fud) + return -EINVAL; + + if (!IS_ENABLED(CONFIG_FUSE_PASSTHROUGH)) + return -EOPNOTSUPP; + + if (copy_from_user(&map, argp, sizeof(map))) + return -EFAULT; + + return fuse_backing_open(fud->fc, &map); +} + +static long fuse_dev_ioctl_backing_close(struct file *file, __u32 __user *argp) +{ + struct fuse_dev *fud = fuse_get_dev(file); + int backing_id; + + if (!fud) + return -EINVAL; + + if (!IS_ENABLED(CONFIG_FUSE_PASSTHROUGH)) + return -EOPNOTSUPP; + + if (get_user(backing_id, argp)) + return -EFAULT; + + return fuse_backing_close(fud->fc, backing_id); +} + static long fuse_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -2292,6 +2327,12 @@ static long fuse_dev_ioctl(struct file *file, unsigned int cmd, case FUSE_DEV_IOC_CLONE: return fuse_dev_ioctl_clone(file, argp); + case FUSE_DEV_IOC_BACKING_OPEN: + return fuse_dev_ioctl_backing_open(file, argp); + + case FUSE_DEV_IOC_BACKING_CLOSE: + return fuse_dev_ioctl_backing_close(file, argp); + default: return -ENOTTY; } diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index cae525147856..fb9ef02cbf45 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -79,6 +79,7 @@ struct fuse_submount_lookup { /** Container for data related to mapping to backing file */ struct fuse_backing { struct file *file; + struct cred *cred; /** refcount */ refcount_t count; @@ -897,6 +898,11 @@ struct fuse_conn { /* New writepages go into this bucket */ struct fuse_sync_bucket __rcu *curr_bucket; + +#ifdef CONFIG_FUSE_PASSTHROUGH + /** IDR for backing files ids */ + struct idr backing_files_map; +#endif }; /* @@ -1414,5 +1420,9 @@ static inline struct fuse_backing *fuse_inode_backing_set(struct fuse_inode *fi, struct fuse_backing *fuse_backing_get(struct fuse_backing *fb); void fuse_backing_put(struct fuse_backing *fb); +void fuse_backing_files_init(struct fuse_conn *fc); +void fuse_backing_files_free(struct fuse_conn *fc); +int fuse_backing_open(struct fuse_conn *fc, struct fuse_backing_map *map); +int fuse_backing_close(struct fuse_conn *fc, int backing_id); #endif /* _FS_FUSE_I_H */ diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index c771bd3c1336..c26a84439934 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -930,6 +930,9 @@ void fuse_conn_init(struct fuse_conn *fc, struct fuse_mount *fm, fc->max_pages = FUSE_DEFAULT_MAX_PAGES_PER_REQ; fc->max_pages_limit = FUSE_MAX_MAX_PAGES; + if (IS_ENABLED(CONFIG_FUSE_PASSTHROUGH)) + fuse_backing_files_init(fc); + INIT_LIST_HEAD(&fc->mounts); list_add(&fm->fc_entry, &fc->mounts); fm->fc = fc; @@ -1392,6 +1395,8 @@ EXPORT_SYMBOL_GPL(fuse_send_init); void fuse_free_conn(struct fuse_conn *fc) { WARN_ON(!list_empty(&fc->devices)); + if (IS_ENABLED(CONFIG_FUSE_PASSTHROUGH)) + fuse_backing_files_free(fc); kfree_rcu(fc, rcu); } EXPORT_SYMBOL_GPL(fuse_free_conn); diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c index e8639c0a9ac6..6604d414adb5 100644 --- a/fs/fuse/passthrough.c +++ b/fs/fuse/passthrough.c @@ -18,8 +18,11 @@ struct fuse_backing *fuse_backing_get(struct fuse_backing *fb) static void fuse_backing_free(struct fuse_backing *fb) { + pr_debug("%s: fb=0x%p\n", __func__, fb); + if (fb->file) fput(fb->file); + put_cred(fb->cred); kfree_rcu(fb, rcu); } @@ -28,3 +31,135 @@ void fuse_backing_put(struct fuse_backing *fb) if (fb && refcount_dec_and_test(&fb->count)) fuse_backing_free(fb); } + +void fuse_backing_files_init(struct fuse_conn *fc) +{ + idr_init(&fc->backing_files_map); +} + +static int fuse_backing_id_alloc(struct fuse_conn *fc, struct fuse_backing *fb) +{ + int id; + + idr_preload(GFP_KERNEL); + spin_lock(&fc->lock); + id = idr_alloc_cyclic(&fc->backing_files_map, fb, 1, 0, GFP_ATOMIC); + spin_unlock(&fc->lock); + idr_preload_end(); + + WARN_ON_ONCE(id == 0); + return id; +} + +static struct fuse_backing *fuse_backing_id_remove(struct fuse_conn *fc, + int id) +{ + struct fuse_backing *fb; + + spin_lock(&fc->lock); + fb = idr_remove(&fc->backing_files_map, id); + spin_unlock(&fc->lock); + + return fb; +} + +static int fuse_backing_id_free(int id, void *p, void *data) +{ + struct fuse_backing *fb = p; + + WARN_ON_ONCE(refcount_read(&fb->count) != 1); + fuse_backing_free(fb); + return 0; +} + +void fuse_backing_files_free(struct fuse_conn *fc) +{ + idr_for_each(&fc->backing_files_map, fuse_backing_id_free, NULL); + idr_destroy(&fc->backing_files_map); +} + +int fuse_backing_open(struct fuse_conn *fc, struct fuse_backing_map *map) +{ + struct file *file; + struct super_block *backing_sb; + struct fuse_backing *fb = NULL; + int res; + + pr_debug("%s: fd=%d flags=0x%x\n", __func__, map->fd, map->flags); + + /* TODO: relax CAP_SYS_ADMIN once backing files are visible to lsof */ + res = -EPERM; + if (!fc->passthrough || !capable(CAP_SYS_ADMIN)) + goto out; + + res = -EINVAL; + if (map->flags) + goto out; + + file = fget(map->fd); + res = -EBADF; + if (!file) + goto out; + + res = -EOPNOTSUPP; + if (!file->f_op->read_iter || !file->f_op->write_iter) + goto out_fput; + + backing_sb = file_inode(file)->i_sb; + res = -ELOOP; + if (backing_sb->s_stack_depth >= fc->max_stack_depth) + goto out_fput; + + fb = kmalloc(sizeof(struct fuse_backing), GFP_KERNEL); + res = -ENOMEM; + if (!fb) + goto out_fput; + + fb->file = file; + fb->cred = prepare_creds(); + refcount_set(&fb->count, 1); + + res = fuse_backing_id_alloc(fc, fb); + if (res < 0) { + fuse_backing_free(fb); + fb = NULL; + } + +out: + pr_debug("%s: fb=0x%p, ret=%i\n", __func__, fb, res); + + return res; + +out_fput: + fput(file); + goto out; +} + +int fuse_backing_close(struct fuse_conn *fc, int backing_id) +{ + struct fuse_backing *fb = NULL; + int err; + + pr_debug("%s: backing_id=%d\n", __func__, backing_id); + + /* TODO: relax CAP_SYS_ADMIN once backing files are visible to lsof */ + err = -EPERM; + if (!fc->passthrough || !capable(CAP_SYS_ADMIN)) + goto out; + + err = -EINVAL; + if (backing_id <= 0) + goto out; + + err = -ENOENT; + fb = fuse_backing_id_remove(fc, backing_id); + if (!fb) + goto out; + + fuse_backing_put(fb); + err = 0; +out: + pr_debug("%s: fb=0x%p, err=%i\n", __func__, fb, err); + + return err; +} diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index edfc458e5e8f..af0fe3aec329 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -1059,9 +1059,18 @@ struct fuse_notify_retrieve_in { uint64_t dummy4; }; +struct fuse_backing_map { + int32_t fd; + uint32_t flags; + uint64_t padding; +}; + /* Device ioctls: */ #define FUSE_DEV_IOC_MAGIC 229 #define FUSE_DEV_IOC_CLONE _IOR(FUSE_DEV_IOC_MAGIC, 0, uint32_t) +#define FUSE_DEV_IOC_BACKING_OPEN _IOW(FUSE_DEV_IOC_MAGIC, 1, \ + struct fuse_backing_map) +#define FUSE_DEV_IOC_BACKING_CLOSE _IOW(FUSE_DEV_IOC_MAGIC, 2, uint32_t) struct fuse_lseek_in { uint64_t fh; From patchwork Tue Feb 6 14:24:48 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Amir Goldstein X-Patchwork-Id: 13547373 Received: from mail-wm1-f54.google.com (mail-wm1-f54.google.com [209.85.128.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8BE73132C01 for ; Tue, 6 Feb 2024 14:25:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707229508; cv=none; b=g/f/186FCxqfsRXbF+NH/tbaMjMxE95aoM12bd07q07Dff+eLDZK2BjXzop2R1wExcdxPlcKL+b8wgP5zDy/orBmcEYdHcbXnmZmBJZRy1gIR3TIqHtC+MIuEt/UiCeDGF0y9zFT/xpKd4ZnzeanDmrR4RAquI9alujryMgWdxY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707229508; c=relaxed/simple; bh=qk3g+eB8rHG+CqGuXWL6Kq+fKCGENQ/QF4MzcDMzfm4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=jLJLfXe4UMB0oiP9zh1vL30TvboPp8e5mwDFDjzY/I77F+p2K4nyiiuAhxOov5G8hjqSJkbOouYmdMAg1ttofxdjIunJaHlRaUZTAxNN0nKB3IHyJp3LL4FOlotSvIrIsPMGigZe+aM28Ud+FRjFgl4cGXqYpZXutt1xwqiZx0k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=W/gfPgtY; arc=none smtp.client-ip=209.85.128.54 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="W/gfPgtY" Received: by mail-wm1-f54.google.com with SMTP id 5b1f17b1804b1-40fe2ed8746so5805805e9.3 for ; Tue, 06 Feb 2024 06:25:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1707229505; x=1707834305; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=6w+6IpIzqyBB0prz82XVFsSS1wwVL5Tjm5Oc10QusGY=; b=W/gfPgtYlLgEE4DbCsOwLtibE7D0z6gboQ9swPWziYPyz/zdzV8w7gGgWIv3JmVf7X /5BdCTYovKyfNAR2rEYv8w2UapTrc0D5aXW9LLEqyOPCr8cBkHLY9JnUEb1PWzvJ5lhM jHHdvjjAv0vo8AsxPKuMCw/BKeMroyUEOcSpVD9HyUOpYOsWbx71pomoGBVkHJvAr29H 3NHwoOtE3FHwwSHNBHs5S8DTY0ROa10KN56H0o4/QpxCdq2nCSzbCCFcjAv59apIVu9F Euz92XvecLOUCbIxKOU5brLUbEJ3jktPgmLIPFcu0ishd2bjrrCHd80Qu3bM1HZ6IvpN RoZA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1707229505; x=1707834305; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=6w+6IpIzqyBB0prz82XVFsSS1wwVL5Tjm5Oc10QusGY=; b=Hyh1fT025jZI1PuwByUcNzbl1Fr7EwiWRhxZBBjLyHfqg1noGGAq3gNZXav88udhpN XXMHEiWyMSvarzySFtaFzc+kzfFNmAh3Nc5T34OyUpJaA9vT9DHNbxi+/WxjFXAHBECB /SSNCjQ7U95j1BbNyD2m4/oPHxxExhmvw6wwLIzg5mqWgKTkZuyUoK2xOje7HC8XFwtg OySDu+/f5ppATHcyopJahy0hHPIF42KRlPLdaEdCDtWexH7mQKZPMvCIYDgn00MjEl9n Dxb6+w3GzjfRJn3ZDhyCBDdECjjOfMHuSmmMeGYHCQANva5NfVtEvdU2Tev40iPj/5xK qf2g== X-Gm-Message-State: AOJu0YwDEO2OYCyF0l11V6PuIUWcuS/FyRcXLzooHL9AIa4nZciIPM4u 9TtD6ABdgB+oR21+zGl8CTsBKNUu74BbbMcOMRNED9oYLjmZ80JsRvswlF/Q X-Google-Smtp-Source: AGHT+IE6m8gdN64i+z/OQg7rZinLsYVp9H6PdbSutx8bUnYDNFXCUx8goaeIs8a0NWg+o+YzCUY7Zw== X-Received: by 2002:a05:600c:4f90:b0:40f:df20:3bc3 with SMTP id n16-20020a05600c4f9000b0040fdf203bc3mr1568279wmq.3.1707229504772; Tue, 06 Feb 2024 06:25:04 -0800 (PST) X-Forwarded-Encrypted: i=0; AJvYcCUjoQ9r3JC8cMZufVoxxKQtBnKqx6NZtxKO+SZElmE/gsTqtAqCMZl9CdIlox/fNKhsHbDMDodgHSeXH2hTfqmYApyIupnnhuS2GUvM7g== Received: from amir-ThinkPad-T480.lan (46-117-242-41.bb.netvision.net.il. [46.117.242.41]) by smtp.gmail.com with ESMTPSA id c28-20020adfa31c000000b0033b4a6f46d7sm629728wrb.87.2024.02.06.06.25.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Feb 2024 06:25:03 -0800 (PST) From: Amir Goldstein To: Miklos Szeredi Cc: Bernd Schubert , linux-fsdevel@vger.kernel.org Subject: [PATCH v15 4/9] fuse: prepare for opening file in passthrough mode Date: Tue, 6 Feb 2024 16:24:48 +0200 Message-Id: <20240206142453.1906268-5-amir73il@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240206142453.1906268-1-amir73il@gmail.com> References: <20240206142453.1906268-1-amir73il@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 In preparation for opening file in passthrough mode, store the fuse_open_out argument in ff->args to be passed into fuse_file_io_open() with the optional backing_id member. This will be used for setting up passthrough to backing file on open reply with FOPEN_PASSTHROUGH flag and a valid backing_id. Opening a file in passthrough mode may fail for several reasons, such as missing capability, conflicting open flags or inode in caching mode. Return EIO from fuse_file_io_open() in those cases. The combination of FOPEN_PASSTHROUGH and FOPEN_DIRECT_IO is allowed - it mean that read/write operations will go directly to the server, but mmap will be done to the backing file. Signed-off-by: Amir Goldstein --- fs/fuse/dir.c | 12 +++++++----- fs/fuse/file.c | 34 +++++++++++++++------------------- fs/fuse/fuse_i.h | 19 ++++++++++++++++--- fs/fuse/iomode.c | 48 +++++++++++++++++++++++++++++++++++++++++------- 4 files changed, 79 insertions(+), 34 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index ea635c17572a..95330c2ca3d8 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -615,7 +615,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, FUSE_ARGS(args); struct fuse_forget_link *forget; struct fuse_create_in inarg; - struct fuse_open_out outopen; + struct fuse_open_out *outopenp; struct fuse_entry_out outentry; struct fuse_inode *fi; struct fuse_file *ff; @@ -659,8 +659,10 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, args.out_numargs = 2; args.out_args[0].size = sizeof(outentry); args.out_args[0].value = &outentry; - args.out_args[1].size = sizeof(outopen); - args.out_args[1].value = &outopen; + /* Store outarg for fuse_finish_open() */ + outopenp = &ff->args->open_outarg; + args.out_args[1].size = sizeof(*outopenp); + args.out_args[1].value = outopenp; err = get_create_ext(&args, dir, entry, mode); if (err) @@ -676,9 +678,9 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, fuse_invalid_attr(&outentry.attr)) goto out_free_ff; - ff->fh = outopen.fh; + ff->fh = outopenp->fh; ff->nodeid = outentry.nodeid; - ff->open_flags = outopen.open_flags; + ff->open_flags = outopenp->open_flags; inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation, &outentry.attr, ATTR_TIMEOUT(&outentry), 0); if (!inode) { diff --git a/fs/fuse/file.c b/fs/fuse/file.c index eb226457c4bd..04be04b6b2af 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -50,12 +50,6 @@ static int fuse_send_open(struct fuse_mount *fm, u64 nodeid, return fuse_simple_request(fm, &args); } -struct fuse_release_args { - struct fuse_args args; - struct fuse_release_in inarg; - struct inode *inode; -}; - struct fuse_file *fuse_file_alloc(struct fuse_mount *fm, bool release) { struct fuse_file *ff; @@ -66,9 +60,8 @@ struct fuse_file *fuse_file_alloc(struct fuse_mount *fm, bool release) ff->fm = fm; if (release) { - ff->release_args = kzalloc(sizeof(*ff->release_args), - GFP_KERNEL_ACCOUNT); - if (!ff->release_args) { + ff->args = kzalloc(sizeof(*ff->args), GFP_KERNEL_ACCOUNT); + if (!ff->args) { kfree(ff); return NULL; } @@ -87,7 +80,7 @@ struct fuse_file *fuse_file_alloc(struct fuse_mount *fm, bool release) void fuse_file_free(struct fuse_file *ff) { - kfree(ff->release_args); + kfree(ff->args); mutex_destroy(&ff->readdir.lock); kfree(ff); } @@ -110,7 +103,7 @@ static void fuse_release_end(struct fuse_mount *fm, struct fuse_args *args, static void fuse_file_put(struct fuse_file *ff, bool sync) { if (refcount_dec_and_test(&ff->count)) { - struct fuse_release_args *ra = ff->release_args; + struct fuse_release_args *ra = &ff->args->release_args; struct fuse_args *args = (ra ? &ra->args : NULL); if (ra && ra->inode) @@ -147,20 +140,21 @@ struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid, /* Default for no-open */ ff->open_flags = FOPEN_KEEP_CACHE | (isdir ? FOPEN_CACHE_DIR : 0); if (!noopen) { - struct fuse_open_out outarg; + /* Store outarg for fuse_finish_open() */ + struct fuse_open_out *outargp = &ff->args->open_outarg; int err; - err = fuse_send_open(fm, nodeid, open_flags, opcode, &outarg); + err = fuse_send_open(fm, nodeid, open_flags, opcode, outargp); if (!err) { - ff->fh = outarg.fh; - ff->open_flags = outarg.open_flags; + ff->fh = outargp->fh; + ff->open_flags = outargp->open_flags; } else if (err != -ENOSYS) { fuse_file_free(ff); return ERR_PTR(err); } else { /* No release needed */ - kfree(ff->release_args); - ff->release_args = NULL; + kfree(ff->args); + ff->args = NULL; if (isdir) fc->no_opendir = 1; else @@ -299,7 +293,7 @@ static void fuse_prepare_release(struct fuse_inode *fi, struct fuse_file *ff, unsigned int flags, int opcode, bool sync) { struct fuse_conn *fc = ff->fm->fc; - struct fuse_release_args *ra = ff->release_args; + struct fuse_release_args *ra = &ff->args->release_args; /* Inode is NULL on error path of fuse_create_open() */ if (likely(fi)) { @@ -317,6 +311,8 @@ static void fuse_prepare_release(struct fuse_inode *fi, struct fuse_file *ff, if (!ra) return; + /* ff->args was used for open outarg */ + memset(ff->args, 0, sizeof(*ff->args)); ra->inarg.fh = ff->fh; ra->inarg.flags = flags; ra->args.in_numargs = 1; @@ -339,7 +335,7 @@ void fuse_file_release(struct inode *inode, struct fuse_file *ff, unsigned int open_flags, fl_owner_t id, bool isdir) { struct fuse_inode *fi = get_fuse_inode(inode); - struct fuse_release_args *ra = ff->release_args; + struct fuse_release_args *ra = &ff->args->release_args; int opcode = isdir ? FUSE_RELEASEDIR : FUSE_RELEASE; fuse_prepare_release(fi, ff, open_flags, opcode, false); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index fb9ef02cbf45..eea8f1ffc766 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -213,15 +213,15 @@ enum { struct fuse_conn; struct fuse_mount; -struct fuse_release_args; +union fuse_file_args; /** FUSE specific file data */ struct fuse_file { /** Fuse connection for this file */ struct fuse_mount *fm; - /* Argument space reserved for release */ - struct fuse_release_args *release_args; + /* Argument space reserved for open/release */ + union fuse_file_args *args; /** Kernel file handle guaranteed to be unique */ u64 kh; @@ -320,6 +320,19 @@ struct fuse_args_pages { unsigned int num_pages; }; +struct fuse_release_args { + struct fuse_args args; + struct fuse_release_in inarg; + struct inode *inode; +}; + +union fuse_file_args { + /* Used during open() */ + struct fuse_open_out open_outarg; + /* Used during release() */ + struct fuse_release_args release_args; +}; + #define FUSE_ARGS(args) struct fuse_args args = {} /** The request IO state (for asynchronous processing) */ diff --git a/fs/fuse/iomode.c b/fs/fuse/iomode.c index acd0833ae873..48105f3c00f6 100644 --- a/fs/fuse/iomode.c +++ b/fs/fuse/iomode.c @@ -138,9 +138,40 @@ void fuse_file_uncached_io_end(struct inode *inode) wake_up(&fi->direct_io_waitq); } +/* + * Open flags that are allowed in combination with FOPEN_PASSTHROUGH. + * A combination of FOPEN_PASSTHROUGH and FOPEN_DIRECT_IO means that read/write + * operations go directly to the server, but mmap is done on the backing file. + * FOPEN_PASSTHROUGH mode should not co-exist with any users of the fuse inode + * page cache, so FOPEN_KEEP_CACHE is a strange and undesired combination. + */ +#define FOPEN_PASSTHROUGH_MASK \ + (FOPEN_PASSTHROUGH | FOPEN_DIRECT_IO | FOPEN_PARALLEL_DIRECT_WRITES | \ + FOPEN_NOFLUSH) + +static int fuse_file_passthrough_open(struct file *file, struct inode *inode) +{ + struct fuse_file *ff = file->private_data; + struct fuse_conn *fc = get_fuse_conn(inode); + int err; + + /* Check allowed conditions for file open in passthrough mode */ + if (!IS_ENABLED(CONFIG_FUSE_PASSTHROUGH) || !fc->passthrough || + (ff->open_flags & ~FOPEN_PASSTHROUGH_MASK)) + return -EINVAL; + + /* TODO: implement backing file open */ + return -EOPNOTSUPP; + + /* First passthrough file open denies caching inode io mode */ + err = fuse_file_uncached_io_start(inode); + + return err; +} + /* Open flags to determine regular file io mode */ #define FOPEN_IO_MODE_MASK \ - (FOPEN_DIRECT_IO | FOPEN_CACHE_IO) + (FOPEN_DIRECT_IO | FOPEN_CACHE_IO | FOPEN_PASSTHROUGH) /* Request access to submit new io to inode via open file */ int fuse_file_io_open(struct file *file, struct inode *inode) @@ -162,7 +193,7 @@ int fuse_file_io_open(struct file *file, struct inode *inode) * implement open. */ err = -EINVAL; - if (FUSE_IS_DAX(inode) || !ff->release_args) { + if (FUSE_IS_DAX(inode) || !ff->args) { if (iomode_flags) goto fail; return 0; @@ -170,7 +201,7 @@ int fuse_file_io_open(struct file *file, struct inode *inode) /* * FOPEN_CACHE_IO is an internal flag that is set on file not open in - * direct io mode and it cannot be set explicitly by the server. + * direct io or passthrough mode and it cannot be set by the server. * This includes a file open with O_DIRECT, but server did not specify * FOPEN_DIRECT_IO. In this case, a later fcntl() could remove O_DIRECT, * so we put the inode in caching mode to prevent parallel dio. @@ -178,7 +209,7 @@ int fuse_file_io_open(struct file *file, struct inode *inode) */ if (ff->open_flags & FOPEN_CACHE_IO) { goto fail; - } else if (!(ff->open_flags & FOPEN_DIRECT_IO)) { + } else if (!(ff->open_flags & FOPEN_IO_MODE_MASK)) { ff->open_flags |= FOPEN_CACHE_IO; ff->open_flags &= ~FOPEN_PARALLEL_DIRECT_WRITES; } @@ -189,6 +220,8 @@ int fuse_file_io_open(struct file *file, struct inode *inode) err = 0; if (ff->open_flags & FOPEN_CACHE_IO) err = fuse_file_cached_io_start(inode); + else if (ff->open_flags & FOPEN_PASSTHROUGH) + err = fuse_file_passthrough_open(file, inode); if (err) goto fail; @@ -206,17 +239,18 @@ int fuse_file_io_open(struct file *file, struct inode *inode) return -EIO; } -/* Request access to submit new io to inode via mmap */ +/* Request access to submit cached io to inode via mmap */ int fuse_file_io_mmap(struct fuse_file *ff, struct inode *inode) { struct fuse_inode *fi = get_fuse_inode(inode); int err = 0; /* There are no io modes if server does not implement open */ - if (!ff->release_args) + if (!ff->args) return 0; - if (WARN_ON(!ff->io_opened)) + if (WARN_ON(ff->open_flags & FOPEN_PASSTHROUGH) || + WARN_ON(!ff->io_opened)) return -ENODEV; spin_lock(&fi->lock); From patchwork Tue Feb 6 14:24:49 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Amir Goldstein X-Patchwork-Id: 13547374 Received: from mail-wm1-f52.google.com (mail-wm1-f52.google.com [209.85.128.52]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 50076132C0A for ; Tue, 6 Feb 2024 14:25:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.52 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707229510; cv=none; b=FD27uzFJJdDfDDEo2nwq7o8w2/vU95QXNtmN1FgDsMAkV+wrItAMfIOoaJrhGrJSmpFB2/S+nNdzl9+zVz3nOhJ5IN/Nq54QW4JwiN4kyS2OY6Y/Wy0YLRePFzIUwFq5t4ApKAs8pxEt4044hkbMOprDfIhp+mASvVv3N0sS9WU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707229510; c=relaxed/simple; bh=P+btxCY7RSVXwfrXfDOZGKZUmiasY3ovEnSE8AP6xmA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Smq8Jzt8vZnROQkAwKRAuAaG3VZKoAeCKzIP6exjdH2Yo7X0//2N5ydTl2zeM/uHPlOv0G7D9FYkU8epHSOB+B9hytbDTDoKWspgtDJyjgx7Ah+3ZCztioCbYqIK0+N3eB4Mb7qo6yXhhVbuHzAojq6fkIIvj6gkMCaKuSxsS4w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=YWorjpzX; arc=none smtp.client-ip=209.85.128.52 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="YWorjpzX" Received: by mail-wm1-f52.google.com with SMTP id 5b1f17b1804b1-40fc654a718so37166715e9.2 for ; Tue, 06 Feb 2024 06:25:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1707229506; x=1707834306; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=DCKsYMVjhCqPV/y73w6CgCE26tRKOupMcaRLuTEMIAU=; b=YWorjpzXzEnh/75gWq5Yez7+5rKJv+nUHCDhKoMP6n/h3QUDuOIz3DW8ihBzUSFLXU DaznF02SOfesG40J8uC/T/Gk8lBMP8JQWsl4ao03XiJ/1nG6FxHNXc9+eXo8YaHZehBB c/pnDxg9uEA/yWzC0VXgc4L5h/UktK1jvWsj0bLU/KgNndB63f0wkqv6qy9m9pEEAAI6 4RFU8i+ndS+lmqa3Vpt6kF3tu+KrJsVA5T/p79w2P6FMShHvu7lG7n7Jnpd3iPVxOz2y pYX1sXRAL9QMm3D3wlPoCYQiIbMdW3MFYwrFX5ZS+2xqOfGhZekWp8OPclPRe1Ef4AB9 pIVg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1707229506; x=1707834306; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=DCKsYMVjhCqPV/y73w6CgCE26tRKOupMcaRLuTEMIAU=; b=C0RcP/9YAXsMNB2UAnSK7yeu1Bk+VEuf4/cSgwXdhhvNimygMJtWZvNLhtxz4viZOJ Y3Jt6fUtgEhAZounH4wKU5AQWQm5+OEzDekqKjDvtMmcy3HfK8vCLv01cfTDT0BkmtUs QvOypUtRlxENQ+wjYbtL5zeexWf0sKGysSWmSR+j8Usf0XGnuf15jP7clSr/zeuKjtJc ffNEqeShXaI9PQe7C544O+4ueos6rTWQHZbVe6mT8rkE3DO4mJ+Zgb6H5hfNPMdSAQHK 4C0CtH3Ep83qt2WneJEWrLh6HFh/cK3uPOCXfVQuKhhx/N1VFLuhmJTOsUO9fdavxuNI M47g== X-Gm-Message-State: AOJu0Yy8rLV/KAHxJCT+N2W238TfMsAfW3eYZr6ERWalzvMosx92kvv+ k4AY2RpbB+CZ9ZCSIiKHJk31rt0qI5Kg6o+vtPQPoaeEWNR+R3IX5LvHXRRA X-Google-Smtp-Source: AGHT+IEMcxil83lH0Bvl1Ymn4wI9PF6EyIF1KuMAzUh60TtxLBF0ClOiYfMK/jHL0aAnj0w/IHU0nQ== X-Received: by 2002:a5d:47c2:0:b0:33b:481e:8ddd with SMTP id o2-20020a5d47c2000000b0033b481e8dddmr1805367wrc.62.1707229506234; Tue, 06 Feb 2024 06:25:06 -0800 (PST) X-Forwarded-Encrypted: i=0; AJvYcCVgg1gZtAyewyLmbTv7miB8+Ktdo/QP0JhL9xDF1syzUlSG2zlPTB7Ddq8U0rKfph4FfkzG5B8g/Oh3wFEWl/SL/cTD1pC+g34uhtFMFA== Received: from amir-ThinkPad-T480.lan (46-117-242-41.bb.netvision.net.il. [46.117.242.41]) by smtp.gmail.com with ESMTPSA id c28-20020adfa31c000000b0033b4a6f46d7sm629728wrb.87.2024.02.06.06.25.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Feb 2024 06:25:05 -0800 (PST) From: Amir Goldstein To: Miklos Szeredi Cc: Bernd Schubert , linux-fsdevel@vger.kernel.org Subject: [PATCH v15 5/9] fuse: implement open in passthrough mode Date: Tue, 6 Feb 2024 16:24:49 +0200 Message-Id: <20240206142453.1906268-6-amir73il@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240206142453.1906268-1-amir73il@gmail.com> References: <20240206142453.1906268-1-amir73il@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 After getting a backing file id with FUSE_DEV_IOC_BACKING_OPEN ioctl, a FUSE server can reply to an OPEN request with flag FOPEN_PASSTHROUGH and the backing file id. The FUSE server should reuse the same backing file id for all the open replies of the same FUSE inode and open will fail (with -EIO) if a the server attempts to open the same inode with conflicting io modes or to setup passthrough to two different backing files for the same FUSE inode. Using the same backing file id for several different inodes is allowed. Opening a new file with FOPEN_DIRECT_IO for an inode that is already open for passthrough is allowed, but only if the FOPEN_PASSTHROUGH flag and correct backing file id are specified as well. The read/write IO of such files will not use passthrough operations to the backing file, but mmap, which does not support direct_io, will use the backing file insead of using the page cache as it always did. Even though all FUSE passthrough files of the same inode use the same backing file as a backing inode reference, each FUSE file opens a unique instance of a backing_file object to store the FUSE path that was used to open the inode and the open flags of the specific open file. The per-file, backing_file object is released along with the FUSE file. The inode associated fuse_backing object is released when the last FUSE passthrough file of that inode is released AND when the backing file id is closed by the server using the FUSE_DEV_IOC_BACKING_CLOSE ioctl. Signed-off-by: Amir Goldstein --- fs/fuse/file.c | 9 ++++++- fs/fuse/fuse_i.h | 35 ++++++++++++++++++++++++- fs/fuse/iomode.c | 51 ++++++++++++++++++++++++++++++++----- fs/fuse/passthrough.c | 59 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 145 insertions(+), 9 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 04be04b6b2af..bdcee82fef9a 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -295,6 +295,9 @@ static void fuse_prepare_release(struct fuse_inode *fi, struct fuse_file *ff, struct fuse_conn *fc = ff->fm->fc; struct fuse_release_args *ra = &ff->args->release_args; + if (fuse_file_passthrough(ff)) + fuse_passthrough_release(ff, fuse_inode_backing(fi)); + /* Inode is NULL on error path of fuse_create_open() */ if (likely(fi)) { spin_lock(&fi->lock); @@ -1372,7 +1375,7 @@ static void fuse_dio_lock(struct kiocb *iocb, struct iov_iter *from, * have raced, so check it again. */ if (fuse_io_past_eof(iocb, from) || - fuse_file_uncached_io_start(inode) != 0) { + fuse_file_uncached_io_start(inode, NULL) != 0) { inode_unlock_shared(inode); inode_lock(inode); *exclusive = true; @@ -2522,6 +2525,10 @@ static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma) if (FUSE_IS_DAX(file_inode(file))) return fuse_dax_mmap(file, vma); + /* TODO: implement mmap to backing file */ + if (fuse_file_passthrough(ff)) + return -ENODEV; + /* * FOPEN_DIRECT_IO handling is special compared to O_DIRECT, * as does not allow MAP_SHARED mmap without FUSE_DIRECT_IO_ALLOW_MMAP. diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index eea8f1ffc766..407b24c79ebb 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -266,6 +266,12 @@ struct fuse_file { /** Wait queue head for poll */ wait_queue_head_t poll_wait; +#ifdef CONFIG_FUSE_PASSTHROUGH + /** Reference to backing file in passthrough mode */ + struct file *passthrough; + const struct cred *cred; +#endif + /** Has flock been performed on this file? */ bool flock:1; @@ -1398,7 +1404,7 @@ int fuse_fileattr_set(struct mnt_idmap *idmap, /* iomode.c */ int fuse_file_cached_io_start(struct inode *inode); void fuse_file_cached_io_end(struct inode *inode); -int fuse_file_uncached_io_start(struct inode *inode); +int fuse_file_uncached_io_start(struct inode *inode, struct fuse_backing *fb); void fuse_file_uncached_io_end(struct inode *inode); int fuse_file_io_open(struct file *file, struct inode *inode); @@ -1431,11 +1437,38 @@ static inline struct fuse_backing *fuse_inode_backing_set(struct fuse_inode *fi, #endif } +#ifdef CONFIG_FUSE_PASSTHROUGH struct fuse_backing *fuse_backing_get(struct fuse_backing *fb); void fuse_backing_put(struct fuse_backing *fb); +#else + +static inline struct fuse_backing *fuse_backing_get(struct fuse_backing *fb) +{ + return NULL; +} + +static inline void fuse_backing_put(struct fuse_backing *fb) +{ +} +#endif + void fuse_backing_files_init(struct fuse_conn *fc); void fuse_backing_files_free(struct fuse_conn *fc); int fuse_backing_open(struct fuse_conn *fc, struct fuse_backing_map *map); int fuse_backing_close(struct fuse_conn *fc, int backing_id); +struct fuse_backing *fuse_passthrough_open(struct file *file, + struct inode *inode, + int backing_id); +void fuse_passthrough_release(struct fuse_file *ff, struct fuse_backing *fb); + +static inline struct file *fuse_file_passthrough(struct fuse_file *ff) +{ +#ifdef CONFIG_FUSE_PASSTHROUGH + return ff->passthrough; +#else + return NULL; +#endif +} + #endif /* _FS_FUSE_I_H */ diff --git a/fs/fuse/iomode.c b/fs/fuse/iomode.c index 48105f3c00f6..dce369f3b201 100644 --- a/fs/fuse/iomode.c +++ b/fs/fuse/iomode.c @@ -115,13 +115,24 @@ void fuse_file_cached_io_end(struct inode *inode) } /* Start strictly uncached io mode where cache access is not allowed */ -int fuse_file_uncached_io_start(struct inode *inode) +int fuse_file_uncached_io_start(struct inode *inode, struct fuse_backing *fb) { struct fuse_inode *fi = get_fuse_inode(inode); - int err; + struct fuse_backing *oldfb; + int err = -EBUSY; spin_lock(&fi->lock); - err = fuse_inode_deny_io_cache(fi); + /* deny conflicting backing files on same fuse inode */ + oldfb = fuse_inode_backing(fi); + if (!oldfb || oldfb == fb) + err = fuse_inode_deny_io_cache(fi); + /* fuse inode holds a single refcount of backing file */ + if (!oldfb && !err) { + oldfb = fuse_inode_backing_set(fi, fb); + WARN_ON_ONCE(oldfb != NULL); + } else if (!err) { + fuse_backing_put(fb); + } spin_unlock(&fi->lock); return err; } @@ -129,13 +140,18 @@ int fuse_file_uncached_io_start(struct inode *inode) void fuse_file_uncached_io_end(struct inode *inode) { struct fuse_inode *fi = get_fuse_inode(inode); + struct fuse_backing *oldfb = NULL; int uncached_io; spin_lock(&fi->lock); uncached_io = fuse_inode_allow_io_cache(fi); + if (!uncached_io) + oldfb = fuse_inode_backing_set(fi, NULL); spin_unlock(&fi->lock); if (!uncached_io) wake_up(&fi->direct_io_waitq); + if (oldfb) + fuse_backing_put(oldfb); } /* @@ -153,6 +169,7 @@ static int fuse_file_passthrough_open(struct file *file, struct inode *inode) { struct fuse_file *ff = file->private_data; struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_backing *fb; int err; /* Check allowed conditions for file open in passthrough mode */ @@ -160,11 +177,18 @@ static int fuse_file_passthrough_open(struct file *file, struct inode *inode) (ff->open_flags & ~FOPEN_PASSTHROUGH_MASK)) return -EINVAL; - /* TODO: implement backing file open */ - return -EOPNOTSUPP; + fb = fuse_passthrough_open(file, inode, + ff->args->open_outarg.backing_id); + if (IS_ERR(fb)) + return PTR_ERR(fb); /* First passthrough file open denies caching inode io mode */ - err = fuse_file_uncached_io_start(inode); + err = fuse_file_uncached_io_start(inode, fb); + if (!err) + return 0; + + fuse_passthrough_release(ff, fb); + fuse_backing_put(fb); return err; } @@ -177,6 +201,7 @@ static int fuse_file_passthrough_open(struct file *file, struct inode *inode) int fuse_file_io_open(struct file *file, struct inode *inode) { struct fuse_file *ff = file->private_data; + struct fuse_inode *fi = get_fuse_inode(inode); int iomode_flags = ff->open_flags & FOPEN_IO_MODE_MASK; int err; @@ -199,6 +224,13 @@ int fuse_file_io_open(struct file *file, struct inode *inode) return 0; } + /* + * Server is expected to use FOPEN_PASSTHROUGH for all opens of an inode + * which is already open for passthrough. + */ + if (fuse_inode_backing(fi) && !(ff->open_flags & FOPEN_PASSTHROUGH)) + goto fail; + /* * FOPEN_CACHE_IO is an internal flag that is set on file not open in * direct io or passthrough mode and it cannot be set by the server. @@ -271,9 +303,14 @@ void fuse_file_io_release(struct fuse_file *ff, struct inode *inode) if (!ff->io_opened) return; - /* Last caching file close exits caching inode io mode */ + /* + * Last caching file close allows passthrough open of inode and + * Last passthrough file close allows caching open of inode. + */ if (ff->open_flags & FOPEN_CACHE_IO) fuse_file_cached_io_end(inode); + else if (ff->open_flags & FOPEN_PASSTHROUGH) + fuse_file_uncached_io_end(inode); ff->io_opened = false; } diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c index 6604d414adb5..098a1f765e99 100644 --- a/fs/fuse/passthrough.c +++ b/fs/fuse/passthrough.c @@ -8,6 +8,7 @@ #include "fuse_i.h" #include +#include struct fuse_backing *fuse_backing_get(struct fuse_backing *fb) { @@ -163,3 +164,61 @@ int fuse_backing_close(struct fuse_conn *fc, int backing_id) return err; } + +/* + * Setup passthrough to a backing file. + * + * Returns an fb object with elevated refcount to be stored in fuse inode. + */ +struct fuse_backing *fuse_passthrough_open(struct file *file, + struct inode *inode, + int backing_id) +{ + struct fuse_file *ff = file->private_data; + struct fuse_conn *fc = ff->fm->fc; + struct fuse_backing *fb = NULL; + struct file *backing_file; + int err; + + err = -EINVAL; + if (backing_id <= 0) + goto out; + + rcu_read_lock(); + fb = idr_find(&fc->backing_files_map, backing_id); + fb = fuse_backing_get(fb); + rcu_read_unlock(); + + err = -ENOENT; + if (!fb) + goto out; + + /* Allocate backing file per fuse file to store fuse path */ + backing_file = backing_file_open(&file->f_path, file->f_flags, + &fb->file->f_path, fb->cred); + err = PTR_ERR(backing_file); + if (IS_ERR(backing_file)) { + fuse_backing_put(fb); + goto out; + } + + err = 0; + ff->passthrough = backing_file; + ff->cred = get_cred(fb->cred); +out: + pr_debug("%s: backing_id=%d, fb=0x%p, backing_file=0x%p, err=%i\n", __func__, + backing_id, fb, ff->passthrough, err); + + return err ? ERR_PTR(err) : fb; +} + +void fuse_passthrough_release(struct fuse_file *ff, struct fuse_backing *fb) +{ + pr_debug("%s: fb=0x%p, backing_file=0x%p\n", __func__, + fb, ff->passthrough); + + fput(ff->passthrough); + ff->passthrough = NULL; + put_cred(ff->cred); + ff->cred = NULL; +} From patchwork Tue Feb 6 14:24:50 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Amir Goldstein X-Patchwork-Id: 13547375 Received: from mail-wr1-f53.google.com (mail-wr1-f53.google.com [209.85.221.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5F69573195 for ; Tue, 6 Feb 2024 14:25:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707229512; cv=none; b=fgvKn5kwZkvx9i8GdXOA9CVVZeGCpxmj4xr7540dB0RhtKaEknp2hyElkM3Lo+rkoE8SCmh0HKKuq+wqeHM3PUJ5wTgwlUOAe4k7ZowkesZb1oZEiw/AoDTuXfhwM5uJBsK3wR/aQuNB9qf796YxnVGneUOUFv137h64VQaRmYw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707229512; c=relaxed/simple; bh=X/gXm1BOk2cs7PUctRvR123VKBpJaivRsDRFEllkC/w=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=lzTKtbnYS9+rgKPW/XT6ejCacENtyUb4Co811RsWMoAoc27RgYxRsLe0GUKzDva0STaSLmxWCVJXsAaZcAEJaVQtMMRS4qbm1v2NmFocT1bnZ0R/4z318xxb8xAtrh33CnpBIvS5ROC/ywBjMbcn7WtLOafljte4Oe9XmwSjCLk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=btxJeXJ9; arc=none smtp.client-ip=209.85.221.53 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="btxJeXJ9" Received: by mail-wr1-f53.google.com with SMTP id ffacd0b85a97d-33b4b121de1so73260f8f.0 for ; Tue, 06 Feb 2024 06:25:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1707229508; x=1707834308; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=G0owb3I7frEqNojIjsVx2vGk6v+M8FrlQ1+POn5DDlc=; b=btxJeXJ9pJGbMU+f84KZCD4AuaaDhwiWzEV+rKYb2xUNe/JyM3XZ1vI6qY8p1/PKfR PLamGXAz5cCn+eLMxTw5Rdx4CPMCVTVcVDNkrJTBEQXG58Q4nmG1UzZZbxb73PeE7SZf x4vuAZWdGSSf+OG6XvDqJpufD+U5t4OwPDH8YeIRQPFp7RhzqnEXz04Q7yHM2LZfv3kj JcIalDM7HW0I78OWF1YtBDCYcpNZAL5EMRNKuUadzaVr+bwPAgdDoMMiYN56qIUbVpSl oEqlbDAdAmowlFCYfHRBwdWu+EN7vojDFao9MzIaV+htPmD215uNZrVNwJMeQ0f7oZ8C wp5A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1707229508; x=1707834308; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=G0owb3I7frEqNojIjsVx2vGk6v+M8FrlQ1+POn5DDlc=; b=oTLXd0NC3Gl2gT2t71O1v+vOglWj1SPFBDu0s78sCvHOqxTGVDV1w5fS/GWjouVC6e QhEKeokEnVyJ7TL0MbAYStAT64ZjeLpKQUc8Y3bQn9twllIF5iExB5/MswjTZfyhGQ8n xialwvDTpNuOwjzpaWLNBQ2GC58YelnQGHrmoLOr0vQwVz++jGb4Mzh/Y10A/r8ceN6N m2uYENqWZYgZyB8I0xDqK4CtIVChi9PJ+7WLQfR5wJ68rqOEZXUxq0VLiUDpp+qYPxub OyOwPNxlBO6dnHuTAoXoORMdmlH/CmcymiB0iVMqS9te/EhFpZURufdh1WEwAzybAcxi vP+Q== X-Gm-Message-State: AOJu0YyY7x1X1JSOZlAUSTuB+ABsXNW5by61Zz9aKgj3NdWF+mkqnSeh DPEzmSoDhYFXVlrlOOXxdAAqa22+/ZYk/v4mTrxVk06g5XDcv2jg X-Google-Smtp-Source: AGHT+IHEKTkswE4uzXtp4iCXsGM0VzSJPR+muG1++gqx9DecBkWYkAFri3bNt/csRGpng69hrAEj+w== X-Received: by 2002:a5d:64eb:0:b0:33b:48f5:b5c7 with SMTP id g11-20020a5d64eb000000b0033b48f5b5c7mr1523129wri.11.1707229507763; Tue, 06 Feb 2024 06:25:07 -0800 (PST) X-Forwarded-Encrypted: i=0; AJvYcCXyNNOU5GD9cYn9T5wwlswYcnzA0W3AXxtm53apyL45bg4LG3BqsNjn13FUn628zs8feXgaUmx5EOTwYkeceh5OqALiVqfJmFue9gQ8YQ== Received: from amir-ThinkPad-T480.lan (46-117-242-41.bb.netvision.net.il. [46.117.242.41]) by smtp.gmail.com with ESMTPSA id c28-20020adfa31c000000b0033b4a6f46d7sm629728wrb.87.2024.02.06.06.25.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Feb 2024 06:25:06 -0800 (PST) From: Amir Goldstein To: Miklos Szeredi Cc: Bernd Schubert , linux-fsdevel@vger.kernel.org Subject: [PATCH v15 6/9] fuse: implement read/write passthrough Date: Tue, 6 Feb 2024 16:24:50 +0200 Message-Id: <20240206142453.1906268-7-amir73il@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240206142453.1906268-1-amir73il@gmail.com> References: <20240206142453.1906268-1-amir73il@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Use the backing file read/write helpers to implement read/write passthrough to a backing file. After read/write, we invalidate a/c/mtime/size attributes if the backing inode attributes differ from FUSE inode attributes. Signed-off-by: Amir Goldstein --- fs/fuse/file.c | 18 ++++++--- fs/fuse/fuse_i.h | 3 ++ fs/fuse/passthrough.c | 86 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 6 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index bdcee82fef9a..0f7c53ea2f13 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1688,10 +1688,13 @@ static ssize_t fuse_file_read_iter(struct kiocb *iocb, struct iov_iter *to) if (FUSE_IS_DAX(inode)) return fuse_dax_read_iter(iocb, to); - if (!(ff->open_flags & FOPEN_DIRECT_IO)) - return fuse_cache_read_iter(iocb, to); - else + /* FOPEN_DIRECT_IO overrides FOPEN_PASSTHROUGH */ + if (ff->open_flags & FOPEN_DIRECT_IO) return fuse_direct_read_iter(iocb, to); + else if (fuse_file_passthrough(ff)) + return fuse_passthrough_read_iter(iocb, to); + else + return fuse_cache_read_iter(iocb, to); } static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from) @@ -1706,10 +1709,13 @@ static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from) if (FUSE_IS_DAX(inode)) return fuse_dax_write_iter(iocb, from); - if (!(ff->open_flags & FOPEN_DIRECT_IO)) - return fuse_cache_write_iter(iocb, from); - else + /* FOPEN_DIRECT_IO overrides FOPEN_PASSTHROUGH */ + if (ff->open_flags & FOPEN_DIRECT_IO) return fuse_direct_write_iter(iocb, from); + else if (fuse_file_passthrough(ff)) + return fuse_passthrough_write_iter(iocb, from); + else + return fuse_cache_write_iter(iocb, from); } static void fuse_writepage_free(struct fuse_writepage_args *wpa) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 407b24c79ebb..e47d7e8e4285 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1471,4 +1471,7 @@ static inline struct file *fuse_file_passthrough(struct fuse_file *ff) #endif } +ssize_t fuse_passthrough_read_iter(struct kiocb *iocb, struct iov_iter *iter); +ssize_t fuse_passthrough_write_iter(struct kiocb *iocb, struct iov_iter *iter); + #endif /* _FS_FUSE_I_H */ diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c index 098a1f765e99..206eedbf6bf8 100644 --- a/fs/fuse/passthrough.c +++ b/fs/fuse/passthrough.c @@ -10,6 +10,92 @@ #include #include +static void fuse_file_accessed(struct file *file) +{ + struct inode *inode = file_inode(file); + struct fuse_inode *fi = get_fuse_inode(inode); + struct fuse_backing *fb = fuse_inode_backing(fi); + struct inode *backing_inode = file_inode(fb->file); + struct timespec64 atime = inode_get_atime(inode); + struct timespec64 batime = inode_get_atime(backing_inode); + + /* Mimic atime update policy of backing inode, not the actual value */ + if (!timespec64_equal(&batime, &atime)) + fuse_invalidate_atime(inode); +} + +static void fuse_file_modified(struct file *file) +{ + struct inode *inode = file_inode(file); + struct fuse_inode *fi = get_fuse_inode(inode); + struct fuse_backing *fb = fuse_inode_backing(fi); + struct inode *backing_inode = file_inode(fb->file); + struct timespec64 ctime = inode_get_ctime(inode); + struct timespec64 mtime = inode_get_mtime(inode); + struct timespec64 bctime = inode_get_ctime(backing_inode); + struct timespec64 bmtime = inode_get_mtime(backing_inode); + + if (!timespec64_equal(&bctime, &ctime) || + !timespec64_equal(&bmtime, &mtime) || + i_size_read(backing_inode) != i_size_read(inode)) + fuse_invalidate_attr_mask(inode, FUSE_STATX_MODSIZE); +} + +ssize_t fuse_passthrough_read_iter(struct kiocb *iocb, struct iov_iter *iter) +{ + struct file *file = iocb->ki_filp; + struct fuse_file *ff = file->private_data; + struct file *backing_file = fuse_file_passthrough(ff); + size_t count = iov_iter_count(iter); + ssize_t ret; + struct backing_file_ctx ctx = { + .cred = ff->cred, + .user_file = file, + .accessed = fuse_file_accessed, + }; + + + pr_debug("%s: backing_file=0x%p, pos=%lld, len=%zu\n", __func__, + backing_file, iocb->ki_pos, count); + + if (!count) + return 0; + + ret = backing_file_read_iter(backing_file, iter, iocb, iocb->ki_flags, + &ctx); + + return ret; +} + +ssize_t fuse_passthrough_write_iter(struct kiocb *iocb, + struct iov_iter *iter) +{ + struct file *file = iocb->ki_filp; + struct inode *inode = file_inode(file); + struct fuse_file *ff = file->private_data; + struct file *backing_file = fuse_file_passthrough(ff); + size_t count = iov_iter_count(iter); + ssize_t ret; + struct backing_file_ctx ctx = { + .cred = ff->cred, + .user_file = file, + .end_write = fuse_file_modified, + }; + + pr_debug("%s: backing_file=0x%p, pos=%lld, len=%zu\n", __func__, + backing_file, iocb->ki_pos, count); + + if (!count) + return 0; + + inode_lock(inode); + ret = backing_file_write_iter(backing_file, iter, iocb, iocb->ki_flags, + &ctx); + inode_unlock(inode); + + return ret; +} + struct fuse_backing *fuse_backing_get(struct fuse_backing *fb) { if (fb && refcount_inc_not_zero(&fb->count)) From patchwork Tue Feb 6 14:24:51 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Amir Goldstein X-Patchwork-Id: 13547376 Received: from mail-wr1-f48.google.com (mail-wr1-f48.google.com [209.85.221.48]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A8049132C13 for ; Tue, 6 Feb 2024 14:25:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.48 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707229512; cv=none; b=fV4AnkOJCUQbQEYpNPhjVOAJqbw9yBothMGQvw3Qvj1rM+uv3wi1JWCSjYlqzv48nEBx8hKQ6hdAjdC8d380/Phk/NT7OsKyjJm0oiE1FQXNqtAF/Bt2hl0+Khf+0WRANZRf7eBDfl88kmeh9cSEyLUYZiAGKJNxfLtMZWpLcfs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707229512; c=relaxed/simple; bh=FIKWwn8qUIa/349NQRgrEQCCO6Ls3szt4TRLuwbkTAs=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=LoG6kYH80VUUNpvalq4V2s3aP/Vs9RJlLHMPPG8iVh9BdkJ2p9E4FI8ZoAjtRP+0EKRzLtjcjkjIo74alsPx1Ze+x6ZoEPSx9/2iI5dHcc/+bq03zZDB82gwTHqORKnrAZ8t+xl6vXoqskx1fU1jOgpoJGKZe/mxRXBvnbFHCqc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=gXmFee9E; arc=none smtp.client-ip=209.85.221.48 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="gXmFee9E" Received: by mail-wr1-f48.google.com with SMTP id ffacd0b85a97d-33b436dbdcfso935809f8f.0 for ; Tue, 06 Feb 2024 06:25:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1707229509; x=1707834309; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=z2WiMh2LRrBIs7GLc9i6LGZkDarGIitoi6Al8x+Sqf4=; b=gXmFee9EpTETlZOxMB9cVXqrgA3uVjx4q8p6VBUNjJmSlD818B1YTQ/FKDTB3Kx0Es RVXA/eg2gDrYFdxI12NiXqQDuR1H8YwYukilmS4uAQYkPVmC9BkajchpwsYsLiC/aamm aRiRxodS7t5gRIHBr5QRhTAtYj9dqaREy0Vfgq+uhxC/ooioH1agB1d98QFVY6ks211z gFOSqhvOfxtUuRltXM4ejWhezH4ApvSdyjGmAfIFM0jFdPhcyo/Zd0tevyshqan5a7mC 5w/zkJ7PaJEnhGWcvqu1FDaTSZEm54/1KsmS9jB7vMCBDB0j5947G3xg8C5KADkf4DpY T7gQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1707229509; x=1707834309; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=z2WiMh2LRrBIs7GLc9i6LGZkDarGIitoi6Al8x+Sqf4=; b=uI2Y6wIwZWpdZ2jP9h2xUZbfaJFsHmQKae8+r+ZeER5yRXjW25XGH8cEKXfFI/hLNl SmlGjdAOkFvazHOh/woRAk1nuK+naxeu5vtCawAB1vaO+oi7d9kiFJQcsnOBZ5Jq4Yd2 Jz+y7xiOAkdRB4MkKNMujbweTxqTi/3wazp0FtwlSIjF+tjd2ZDLWcfZ8WzukkJ0L6gX SO90TRtGMOMqfuT3sE27ocQhx3K7PiHztNJT8XHfwIwANi7GU7806AucUsU8fLl+OvS/ RnztY9/sR6MgWR2v18vnLb2bWPxWTQx59QWWVqcYeeiO2g6Dh+y7dPLAXh/zbY3tFnh0 VMLg== X-Gm-Message-State: AOJu0Yx4aAgjNayOrerJoXSaXwZEnMXecfMaftiBY8bXtmSlKu180V5b a9baBjCh+0YOyQ2d2aU4BHStj3uQEmKcs6P91fxlTGDrVkT6F1i5ZL+gpHLj X-Google-Smtp-Source: AGHT+IH7R3pzKj38YtmNUKBvPobt5oPA3o5vFuJv0It7k2avJ3JKdjncAh9pguJAcEZGBHZI280zsg== X-Received: by 2002:adf:f346:0:b0:33b:3275:2721 with SMTP id e6-20020adff346000000b0033b32752721mr1324252wrp.40.1707229508882; Tue, 06 Feb 2024 06:25:08 -0800 (PST) X-Forwarded-Encrypted: i=0; AJvYcCW8iq8X3xA1WHosD+yXpcsNtTKeLLpRCoRoawsu7NfFMp1YkcDUVqQCg9TS9y6INJjdJ292HgrLaH1pmGs9Lxsczqa902X12BhWW98qJA== Received: from amir-ThinkPad-T480.lan (46-117-242-41.bb.netvision.net.il. [46.117.242.41]) by smtp.gmail.com with ESMTPSA id c28-20020adfa31c000000b0033b4a6f46d7sm629728wrb.87.2024.02.06.06.25.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Feb 2024 06:25:08 -0800 (PST) From: Amir Goldstein To: Miklos Szeredi Cc: Bernd Schubert , linux-fsdevel@vger.kernel.org Subject: [PATCH v15 7/9] fuse: implement splice read/write passthrough Date: Tue, 6 Feb 2024 16:24:51 +0200 Message-Id: <20240206142453.1906268-8-amir73il@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240206142453.1906268-1-amir73il@gmail.com> References: <20240206142453.1906268-1-amir73il@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 This allows passing fstests generic/249 and generic/591. Signed-off-by: Amir Goldstein --- fs/fuse/file.c | 29 ++++++++++++++++++++++++++-- fs/fuse/fuse_i.h | 6 ++++++ fs/fuse/passthrough.c | 45 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 2 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 0f7c53ea2f13..80f98affbe7d 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1718,6 +1718,31 @@ static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from) return fuse_cache_write_iter(iocb, from); } +static ssize_t fuse_splice_read(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags) +{ + struct fuse_file *ff = in->private_data; + + /* FOPEN_DIRECT_IO overrides FOPEN_PASSTHROUGH */ + if (fuse_file_passthrough(ff) && !(ff->open_flags & FOPEN_DIRECT_IO)) + return fuse_passthrough_splice_read(in, ppos, pipe, len, flags); + else + return filemap_splice_read(in, ppos, pipe, len, flags); +} + +static ssize_t fuse_splice_write(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags) +{ + struct fuse_file *ff = out->private_data; + + /* FOPEN_DIRECT_IO overrides FOPEN_PASSTHROUGH */ + if (fuse_file_passthrough(ff) && !(ff->open_flags & FOPEN_DIRECT_IO)) + return fuse_passthrough_splice_write(pipe, out, ppos, len, flags); + else + return iter_file_splice_write(pipe, out, ppos, len, flags); +} + static void fuse_writepage_free(struct fuse_writepage_args *wpa) { struct fuse_args_pages *ap = &wpa->ia.ap; @@ -3298,8 +3323,8 @@ static const struct file_operations fuse_file_operations = { .lock = fuse_file_lock, .get_unmapped_area = thp_get_unmapped_area, .flock = fuse_file_flock, - .splice_read = filemap_splice_read, - .splice_write = iter_file_splice_write, + .splice_read = fuse_splice_read, + .splice_write = fuse_splice_write, .unlocked_ioctl = fuse_file_ioctl, .compat_ioctl = fuse_file_compat_ioctl, .poll = fuse_file_poll, diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index e47d7e8e4285..c495eaa11b49 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1473,5 +1473,11 @@ static inline struct file *fuse_file_passthrough(struct fuse_file *ff) ssize_t fuse_passthrough_read_iter(struct kiocb *iocb, struct iov_iter *iter); ssize_t fuse_passthrough_write_iter(struct kiocb *iocb, struct iov_iter *iter); +ssize_t fuse_passthrough_splice_read(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, + size_t len, unsigned int flags); +ssize_t fuse_passthrough_splice_write(struct pipe_inode_info *pipe, + struct file *out, loff_t *ppos, + size_t len, unsigned int flags); #endif /* _FS_FUSE_I_H */ diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c index 206eedbf6bf8..df8343cebd0a 100644 --- a/fs/fuse/passthrough.c +++ b/fs/fuse/passthrough.c @@ -9,6 +9,7 @@ #include #include +#include static void fuse_file_accessed(struct file *file) { @@ -96,6 +97,50 @@ ssize_t fuse_passthrough_write_iter(struct kiocb *iocb, return ret; } +ssize_t fuse_passthrough_splice_read(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, + size_t len, unsigned int flags) +{ + struct fuse_file *ff = in->private_data; + struct file *backing_file = fuse_file_passthrough(ff); + struct backing_file_ctx ctx = { + .cred = ff->cred, + .user_file = in, + .accessed = fuse_file_accessed, + }; + + pr_debug("%s: backing_file=0x%p, pos=%lld, len=%zu, flags=0x%x\n", __func__, + backing_file, ppos ? *ppos : 0, len, flags); + + return backing_file_splice_read(backing_file, ppos, pipe, len, flags, + &ctx); +} + +ssize_t fuse_passthrough_splice_write(struct pipe_inode_info *pipe, + struct file *out, loff_t *ppos, + size_t len, unsigned int flags) +{ + struct fuse_file *ff = out->private_data; + struct file *backing_file = fuse_file_passthrough(ff); + struct inode *inode = file_inode(out); + ssize_t ret; + struct backing_file_ctx ctx = { + .cred = ff->cred, + .user_file = out, + .end_write = fuse_file_modified, + }; + + pr_debug("%s: backing_file=0x%p, pos=%lld, len=%zu, flags=0x%x\n", __func__, + backing_file, ppos ? *ppos : 0, len, flags); + + inode_lock(inode); + ret = backing_file_splice_write(pipe, backing_file, ppos, len, flags, + &ctx); + inode_unlock(inode); + + return ret; +} + struct fuse_backing *fuse_backing_get(struct fuse_backing *fb) { if (fb && refcount_inc_not_zero(&fb->count)) From patchwork Tue Feb 6 14:24:52 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Amir Goldstein X-Patchwork-Id: 13547377 Received: from mail-wm1-f52.google.com (mail-wm1-f52.google.com [209.85.128.52]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A0A51132C19 for ; Tue, 6 Feb 2024 14:25:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.52 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707229513; cv=none; b=eaRkf2cI/UbhXhpRMQWEEtzoxSGAnaicrfcn7J+px+nPs5beVeLkJY7ZUFjcgxF6Iw9vDkwJVx0z+AuL+9BU9Z58tdnuxqYyPVjXmhOnk6zqhdpf6BY2LDIFifYmsZpaWcOI/uybbaNFamaNFgRl3331zOu8rFgq1VM6+Kr2Xj8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707229513; c=relaxed/simple; bh=Ee+4G9M+hVE3/0FrDQNy7QjZiZttVJRp2tWdKTf5vkk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=BPpfqvFrrqGP75OH6GUcVwwpP0Fe3641tf+wsWoWOfE9HQwo+IYZb3R49ILOvTwb+YJ2XyFDe6obTGIhEdrLIxR9G/VQYGdEW42WBGSu2c4aP7QOxV/DYbesDJogue8T5Npjbc49SVHfBgQfPkELeUohCZ1I+RCvL01FLdB+izY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=T3Wahcxc; arc=none smtp.client-ip=209.85.128.52 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="T3Wahcxc" Received: by mail-wm1-f52.google.com with SMTP id 5b1f17b1804b1-40fc549ab9bso38268455e9.0 for ; Tue, 06 Feb 2024 06:25:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1707229510; x=1707834310; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=HTWT98yeBNfdBQwDTGSlxmuWSq3AOov4hX8TNCj0bRM=; b=T3WahcxcxTc26D1Aq/Brm/tUs2wuBB4Bj7EXaPjp+GDoDcHeWLnvi97O2vnAgQlV8i PH/Jb+2FtcC+veQ/wTB2ksSfWXYkWnFVGPzYdcev4kc/Jm293VhpalrwNF1d/FHy547t KzTN971S8home2rMFE+2w9NersPIEIlab5dpkXgRhVFoZXKEZj4tyShrXrhp5DnNExs9 XbLBR7PcZp+b/xaG/ktGOwMnFFvUOiOexQOW+B5a0m8oHHo6ue2TpGrFuJuc84Coawcd I+VkHQXCSE96NLC3xg6zGqywARyVTVpd5seyeCDPRU2BUrun/RvnmO/wEwomgdKW2yfh eu+w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1707229510; x=1707834310; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=HTWT98yeBNfdBQwDTGSlxmuWSq3AOov4hX8TNCj0bRM=; b=Y7ICDdlwIBhpyuq/Lh+3DsxjZPrsAx2l+K5h2a8GqziPJnSNMa3m3WcJQI2WoCWAHJ zS2nMzBwnnIp4QaA0/IVdkPm8/Ed0WxogkkW7T4/33sVmv+RL680KDBaJ6gQCp06Qpeo XL4sR2tbLDGjNrPWIwN760ZvWtIT3hpnjftvvzQUg7xJCTTf9GP3KzYkvlGUzc7fGi1t b8Dj0RBaX+JMM/WwAdf+Vh4Z37c+V2fuZBqfKMbrhUlKkGi8QuZ3qC8r6Z7hdWcyaVCq 3q3q/T6xQ+ZN3Knh0ICxv0jTwI1byQbgwD6KnoQS7g4sR0BBZfshB/dzCnkRjN/6Ej4i XoOw== X-Gm-Message-State: AOJu0Yxoy2djITak1PItszbtKc8JjlxwXhq/GvQrqr5qfbRyxSw4MP9v ad3OGLkuRI1hKcex5RBEr1vpaE6NC/pEZeObtkdFkIW1sd0g6+W4eJV1e3Iv X-Google-Smtp-Source: AGHT+IGxTlv3u/LASUXKij6lp5y34IkrHXTYqgltYTi1n4eCIWCA8D8wu70JSJXsSIXr3va/4hosKw== X-Received: by 2002:a05:600c:524a:b0:40e:f9df:3531 with SMTP id fc10-20020a05600c524a00b0040ef9df3531mr2304573wmb.8.1707229509890; Tue, 06 Feb 2024 06:25:09 -0800 (PST) X-Forwarded-Encrypted: i=0; AJvYcCUjO5sBflogMDoZUsWYKkq0P9evmPMFH0u6pOsWFvG9ouTnyyTiK/vcXzfh050U2aoasDXYJ6mYewAVV+i3kJ2PLqfRYSkhzCYbyzu0kQ== Received: from amir-ThinkPad-T480.lan (46-117-242-41.bb.netvision.net.il. [46.117.242.41]) by smtp.gmail.com with ESMTPSA id c28-20020adfa31c000000b0033b4a6f46d7sm629728wrb.87.2024.02.06.06.25.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Feb 2024 06:25:09 -0800 (PST) From: Amir Goldstein To: Miklos Szeredi Cc: Bernd Schubert , linux-fsdevel@vger.kernel.org Subject: [PATCH v15 8/9] fuse: implement passthrough for mmap Date: Tue, 6 Feb 2024 16:24:52 +0200 Message-Id: <20240206142453.1906268-9-amir73il@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240206142453.1906268-1-amir73il@gmail.com> References: <20240206142453.1906268-1-amir73il@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 An mmap request for a file open in passthrough mode, maps the memory directly to the backing file. An mmap of a file in direct io mode, usually uses cached mmap and puts the inode in caching io mode, which denies new passthrough opens of that inode, because caching io mode is conflicting with passthrough io mode. For the same reason, trying to mmap a direct io file, while there is a passthrough file open on the same inode will fail with -ENODEV. An mmap of a file in direct io mode, also needs to wait for parallel dio writes in-progress to complete. If a passthrough file is opened, while an mmap of another direct io file is waiting for parallel dio writes to complete, the wait is aborted and mmap fails with -ENODEV. A FUSE server that uses passthrough and direct io opens on the same inode that may also be mmaped, is advised to provide a backing fd also for the files that are open in direct io mode (i.e. use the flags combination FOPEN_DIRECT_IO | FOPEN_PASSTHROUGH), so that mmap will always use the backing file, even if read/write do not passthrough. Signed-off-by: Amir Goldstein --- fs/fuse/file.c | 13 ++++++++++--- fs/fuse/fuse_i.h | 1 + fs/fuse/iomode.c | 9 ++++++++- fs/fuse/passthrough.c | 16 ++++++++++++++++ 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 80f98affbe7d..edbcb9ceb7e7 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -2550,14 +2550,21 @@ static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma) { struct fuse_file *ff = file->private_data; struct fuse_conn *fc = ff->fm->fc; + struct inode *inode = file_inode(file); int rc; /* DAX mmap is superior to direct_io mmap */ - if (FUSE_IS_DAX(file_inode(file))) + if (FUSE_IS_DAX(inode)) return fuse_dax_mmap(file, vma); - /* TODO: implement mmap to backing file */ + /* + * If inode is in passthrough io mode, because it has some file open + * in passthrough mode, either mmap to backing file or fail mmap, + * because mixing cached mmap and passthrough io mode is not allowed. + */ if (fuse_file_passthrough(ff)) + return fuse_passthrough_mmap(file, vma); + else if (fuse_inode_backing(get_fuse_inode(inode))) return -ENODEV; /* @@ -2579,7 +2586,7 @@ static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma) * Also waits for parallel dio writers to go into serial mode * (exclusive instead of shared lock). */ - rc = fuse_file_io_mmap(ff, file_inode(file)); + rc = fuse_file_io_mmap(ff, inode); if (rc) return rc; diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index c495eaa11b49..98f878a52af1 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1479,5 +1479,6 @@ ssize_t fuse_passthrough_splice_read(struct file *in, loff_t *ppos, ssize_t fuse_passthrough_splice_write(struct pipe_inode_info *pipe, struct file *out, loff_t *ppos, size_t len, unsigned int flags); +ssize_t fuse_passthrough_mmap(struct file *file, struct vm_area_struct *vma); #endif /* _FS_FUSE_I_H */ diff --git a/fs/fuse/iomode.c b/fs/fuse/iomode.c index dce369f3b201..c545058a01e1 100644 --- a/fs/fuse/iomode.c +++ b/fs/fuse/iomode.c @@ -17,7 +17,7 @@ */ static inline bool fuse_is_io_cache_wait(struct fuse_inode *fi) { - return READ_ONCE(fi->iocachectr) < 0; + return READ_ONCE(fi->iocachectr) < 0 && !fuse_inode_backing(fi); } /* @@ -42,6 +42,13 @@ static int fuse_inode_get_io_cache(struct fuse_inode *fi) !fuse_is_io_cache_wait(fi)); spin_lock(&fi->lock); } + /* + * Check if inode entered passthrough io mode while waiting for parallel + * dio write completion. + */ + if (fuse_inode_backing(fi)) + err = -ETXTBSY; + /* * Enter caching mode or clear the FUSE_I_CACHE_IO_MODE bit if we * failed to enter caching mode and no other caching open exists. diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c index df8343cebd0a..260e76fc72d5 100644 --- a/fs/fuse/passthrough.c +++ b/fs/fuse/passthrough.c @@ -141,6 +141,22 @@ ssize_t fuse_passthrough_splice_write(struct pipe_inode_info *pipe, return ret; } +ssize_t fuse_passthrough_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct fuse_file *ff = file->private_data; + struct file *backing_file = fuse_file_passthrough(ff); + struct backing_file_ctx ctx = { + .cred = ff->cred, + .user_file = file, + .accessed = fuse_file_accessed, + }; + + pr_debug("%s: backing_file=0x%p, start=%lu, end=%lu\n", __func__, + backing_file, vma->vm_start, vma->vm_end); + + return backing_file_mmap(backing_file, vma, &ctx); +} + struct fuse_backing *fuse_backing_get(struct fuse_backing *fb) { if (fb && refcount_inc_not_zero(&fb->count)) From patchwork Tue Feb 6 14:24:53 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Amir Goldstein X-Patchwork-Id: 13547378 Received: from mail-wr1-f45.google.com (mail-wr1-f45.google.com [209.85.221.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E9215132C37 for ; Tue, 6 Feb 2024 14:25:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707229514; cv=none; b=kAPXw1A/OExaJR0+3MxlPDtv+NQu6F1iNuatJSiqOBrxnvs5xNM2CIQVg5YHpdsYic8EyQnYYbyVoTMoDDFHSmLwD3IBKX3nelIJ8vHpGkb/ofAbwyASeP5dIZkPwwYBH0q1fzUMVGfyMP8frkr45hnJXzK9HTthY5iu1fSkDXQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707229514; c=relaxed/simple; bh=uplzIYz81OjPNsqsXBnf8den2rH5SzRCaVDLZ7EAXMI=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Yyp2USwMJRGBZdLoZqE5qJCMV+ZVCeiMVXT+Ukjhlvt8krFDdNmHXH0FvBgH/wFVSjAM8anTP2W9KcbKk5ji2Hur7WntSsDJwqwDNNcPW/zr0XNiNK2FuPtgwY8vfmlEXFApTyDirRKpeXrPavBmeC0mg1m5tK7mb1BTY15OD9A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=dFPXNm/x; arc=none smtp.client-ip=209.85.221.45 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="dFPXNm/x" Received: by mail-wr1-f45.google.com with SMTP id ffacd0b85a97d-33b466bc363so479781f8f.3 for ; Tue, 06 Feb 2024 06:25:12 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1707229511; x=1707834311; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=/hyy8pftoIAuFr3lIeUur4NLP4UN4IOZ33cfl3dry9Q=; b=dFPXNm/xQ555noyuVkMg3YFrubTtFh7tBppyBpxAV5LEVwy0Rtr0CKK2Eo0LDVhk1x QXVC8vvmdPufdmTrKKZVqD7weZsz6yyL+k5sM6jVw72Gw7hDcf9l9/dpmfSYX8Q2SvJ5 W7AYdp2Z4vTBYExS7drM/FV8Aq2sw2U2en0omPndJPNISnbQBaDtXpyVQosdvz5spu+V kXFBmroRV4pZanm4hmsECrOFekdVUlL1YYHEFtQaCbY3nK2XD+dA8jXhoqMHGIdAF4cA hg6V65QW3Kuxpl4Bn/hYazTdUSLh2jFQNLd5idRqdhKBh6I4SMALpaNEiS+yi4Wv0Vb3 Hm/Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1707229511; x=1707834311; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=/hyy8pftoIAuFr3lIeUur4NLP4UN4IOZ33cfl3dry9Q=; b=JeGecjsOmxeWkIh1AfOK8e41I0R0StvpSMDHDKX6L/ED1lUlSFodYbCRyIBB54JLIZ YqRKgLAT7dQqUTPGV56n8NROs/KsTWsLlvpFqR1XC4CCpSDzLcaHZnSS0fSaxPKRQYfi tA/udzhhPpxFph21Z1WV3P+AM1g2hF8cn6lJAV8ZVl7YPN0rtffaMImzdcKNXZWfPv1R eaYnPSwAVkKD4QJPelaIbEsn9wGyhzsoYgpOgsDMNgVdNTm8rPj8enHECt2CSMMVVB2o vEW9M6OxphaHjyDh1f0Wz4PEFf6ANMMQeiNuH37O6vOjeiHMfGyv9snvS5wvvqjigdjU Yh9Q== X-Gm-Message-State: AOJu0YxDreF3GBX6AdzoU8JTxOeb8aN6zsd5M5DKTrT7XsE/0z84zmIg zjoublVUQR1bI1p4FM6/M6VazZrUZ1Y3YimHwUuZhDRU2xsnzGuAkhoaU7/N X-Google-Smtp-Source: AGHT+IHYDOjW7uBDH+jJFqfhLAJyqDPF5kxeADXz6p7j08oUG95N+I5IDC8dgGd18hvkV3o3P2Ekgg== X-Received: by 2002:a5d:66cd:0:b0:33b:2a02:61a8 with SMTP id k13-20020a5d66cd000000b0033b2a0261a8mr1366308wrw.45.1707229511188; Tue, 06 Feb 2024 06:25:11 -0800 (PST) X-Forwarded-Encrypted: i=0; AJvYcCXt6y+UbNHp+RzN/iQRp0j/FbrWMM8B02A2d5QWkSrTSuKi+y5z3wc68EamLUwSflGcqDJdcw7hrDgUandXcNF8H4VvJLB7OiuUHsLD2w== Received: from amir-ThinkPad-T480.lan (46-117-242-41.bb.netvision.net.il. [46.117.242.41]) by smtp.gmail.com with ESMTPSA id c28-20020adfa31c000000b0033b4a6f46d7sm629728wrb.87.2024.02.06.06.25.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Feb 2024 06:25:10 -0800 (PST) From: Amir Goldstein To: Miklos Szeredi Cc: Bernd Schubert , linux-fsdevel@vger.kernel.org Subject: [PATCH v15 9/9] fuse: auto-invalidate inode attributes in passthrough mode Date: Tue, 6 Feb 2024 16:24:53 +0200 Message-Id: <20240206142453.1906268-10-amir73il@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240206142453.1906268-1-amir73il@gmail.com> References: <20240206142453.1906268-1-amir73il@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 After passthrough read/write, we invalidate a/c/mtime/size attributes if the backing inode attributes differ from FUSE inode attributes. Do the same in fuse_getattr() and after detach of backing inode, so that passthrough mmap read/write changes to a/c/mtime/size attribute of the backing inode will be propagated to the FUSE inode. The rules of invalidating a/c/mtime/size attributes with writeback cache are more complicated, so for now, writeback cache and passthrough cannot be enabled on the same filesystem. Signed-off-by: Amir Goldstein --- fs/fuse/dir.c | 4 ++++ fs/fuse/fuse_i.h | 2 ++ fs/fuse/inode.c | 4 ++++ fs/fuse/iomode.c | 5 +++- fs/fuse/passthrough.c | 55 ++++++++++++++++++++++++++++++++++++------- 5 files changed, 61 insertions(+), 9 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 95330c2ca3d8..7f9d002b8f23 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -2118,6 +2118,10 @@ static int fuse_getattr(struct mnt_idmap *idmap, return -EACCES; } + /* Maybe update/invalidate attributes from backing inode */ + if (fuse_inode_backing(get_fuse_inode(inode))) + fuse_backing_update_attr_mask(inode, request_mask); + return fuse_update_get_attr(inode, NULL, stat, request_mask, flags); } diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 98f878a52af1..4b011d31012f 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1456,6 +1456,8 @@ void fuse_backing_files_init(struct fuse_conn *fc); void fuse_backing_files_free(struct fuse_conn *fc); int fuse_backing_open(struct fuse_conn *fc, struct fuse_backing_map *map); int fuse_backing_close(struct fuse_conn *fc, int backing_id); +void fuse_backing_update_attr(struct inode *inode, struct fuse_backing *fb); +void fuse_backing_update_attr_mask(struct inode *inode, u32 request_mask); struct fuse_backing *fuse_passthrough_open(struct file *file, struct inode *inode, diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index c26a84439934..c68f005b6e86 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -1302,9 +1302,13 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args, * on a stacked fs (e.g. overlayfs) themselves and with * max_stack_depth == 1, FUSE fs can be stacked as the * underlying fs of a stacked fs (e.g. overlayfs). + * + * For now, writeback cache and passthrough cannot be + * enabled on the same filesystem. */ if (IS_ENABLED(CONFIG_FUSE_PASSTHROUGH) && (flags & FUSE_PASSTHROUGH) && + !fc->writeback_cache && arg->max_stack_depth > 0 && arg->max_stack_depth <= FILESYSTEM_MAX_STACK_DEPTH) { fc->passthrough = 1; diff --git a/fs/fuse/iomode.c b/fs/fuse/iomode.c index c545058a01e1..96eb311fe7bd 100644 --- a/fs/fuse/iomode.c +++ b/fs/fuse/iomode.c @@ -157,8 +157,11 @@ void fuse_file_uncached_io_end(struct inode *inode) spin_unlock(&fi->lock); if (!uncached_io) wake_up(&fi->direct_io_waitq); - if (oldfb) + if (oldfb) { + /* Maybe update attributes after detaching backing inode */ + fuse_backing_update_attr(inode, oldfb); fuse_backing_put(oldfb); + } } /* diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c index 260e76fc72d5..c1bb80a6e536 100644 --- a/fs/fuse/passthrough.c +++ b/fs/fuse/passthrough.c @@ -11,11 +11,8 @@ #include #include -static void fuse_file_accessed(struct file *file) +static void fuse_backing_accessed(struct inode *inode, struct fuse_backing *fb) { - struct inode *inode = file_inode(file); - struct fuse_inode *fi = get_fuse_inode(inode); - struct fuse_backing *fb = fuse_inode_backing(fi); struct inode *backing_inode = file_inode(fb->file); struct timespec64 atime = inode_get_atime(inode); struct timespec64 batime = inode_get_atime(backing_inode); @@ -25,11 +22,8 @@ static void fuse_file_accessed(struct file *file) fuse_invalidate_atime(inode); } -static void fuse_file_modified(struct file *file) +static void fuse_backing_modified(struct inode *inode, struct fuse_backing *fb) { - struct inode *inode = file_inode(file); - struct fuse_inode *fi = get_fuse_inode(inode); - struct fuse_backing *fb = fuse_inode_backing(fi); struct inode *backing_inode = file_inode(fb->file); struct timespec64 ctime = inode_get_ctime(inode); struct timespec64 mtime = inode_get_mtime(inode); @@ -42,6 +36,51 @@ static void fuse_file_modified(struct file *file) fuse_invalidate_attr_mask(inode, FUSE_STATX_MODSIZE); } +/* Called from fuse_file_uncached_io_end() after detach of backing inode */ +void fuse_backing_update_attr(struct inode *inode, struct fuse_backing *fb) +{ + fuse_backing_modified(inode, fb); + fuse_backing_accessed(inode, fb); +} + +/* Called from fuse_getattr() - may race with detach of backing inode */ +void fuse_backing_update_attr_mask(struct inode *inode, u32 request_mask) +{ + struct fuse_inode *fi = get_fuse_inode(inode); + struct fuse_backing *fb; + + rcu_read_lock(); + fb = fuse_backing_get(fuse_inode_backing(fi)); + rcu_read_unlock(); + if (!fb) + return; + + if (request_mask & FUSE_STATX_MODSIZE) + fuse_backing_modified(inode, fb); + if (request_mask & STATX_ATIME) + fuse_backing_accessed(inode, fb); + + fuse_backing_put(fb); +} + +static void fuse_file_accessed(struct file *file) +{ + struct inode *inode = file_inode(file); + struct fuse_inode *fi = get_fuse_inode(inode); + struct fuse_backing *fb = fuse_inode_backing(fi); + + fuse_backing_accessed(inode, fb); +} + +static void fuse_file_modified(struct file *file) +{ + struct inode *inode = file_inode(file); + struct fuse_inode *fi = get_fuse_inode(inode); + struct fuse_backing *fb = fuse_inode_backing(fi); + + fuse_backing_modified(inode, fb); +} + ssize_t fuse_passthrough_read_iter(struct kiocb *iocb, struct iov_iter *iter) { struct file *file = iocb->ki_filp;