Skip to content

Commit 3db3e26

Browse files
committed
base-patches-30: Add support fuse_passthrough
1 parent 43e8316 commit 3db3e26

8 files changed

Lines changed: 1314 additions & 0 deletions
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
From 37b164bedccd9c5abb0ba8ffbceac405ac331569 Mon Sep 17 00:00:00 2001
2+
From: Alessio Balsini <balsini@google.com>
3+
Date: Wed, 1 Jul 2020 14:14:28 +0100
4+
Subject: [PATCH 1/5] DO NOT MERGE Enable passthrough mode for read/write
5+
operations
6+
7+
Add support for filesystem passthrough read/write of files.
8+
9+
When the FUSE_PASSTHROUGH capability is enabled, the FUSE daemon may
10+
decide while handling the "open" or "create" operation, if the given
11+
file can be accessed by that process in "passthrough" mode, meaning that
12+
all the further read and write operations would be forwarded by the
13+
kernel directly to the lower filesystem rather than to the FUSE daemon.
14+
All requests that aren't read or write are still handled by the
15+
userspace code.
16+
17+
This allows for an improved performance on reads and writes, especially
18+
in the case of reads at random offsets, for which no (readahead)
19+
caching mechanism would help, reducing the performance gap between FUSE
20+
and native filesystem access.
21+
22+
Extend also the passthrough_hp example with the new passthrough feature.
23+
24+
Bug: 174548608
25+
Test: atest ScopedStorageTest
26+
Signed-off-by: Alessio Balsini <balsini@android.com>
27+
Change-Id: I38aff0cf7198b7cd92eccc97547d47f4e1132b00
28+
---
29+
include/fuse_common.h | 17 +++++++++++++++++
30+
include/fuse_kernel.h | 13 +++++++++++--
31+
include/fuse_lowlevel.h | 2 ++
32+
lib/fuse_lowlevel.c | 19 +++++++++++++++++++
33+
lib/fuse_versionscript | 1 +
34+
5 files changed, 50 insertions(+), 2 deletions(-)
35+
36+
diff --git a/include/fuse_common.h b/include/fuse_common.h
37+
index 2d686b2..7ac28d7 100644
38+
--- a/include/fuse_common.h
39+
+++ b/include/fuse_common.h
40+
@@ -92,6 +92,11 @@ struct fuse_file_info {
41+
* same file handle. */
42+
uint64_t fh;
43+
44+
+ /** Passthrough file handle id. May be filled in by filesystem in
45+
+ * create and open. It is used to create a passthrough connection
46+
+ * between FUSE file and lower file system file. */
47+
+ uint32_t passthrough_fh;
48+
+
49+
/** Lock owner id. Available in locking operations and flush */
50+
uint64_t lock_owner;
51+
52+
@@ -358,6 +363,18 @@ struct fuse_loop_config {
53+
*/
54+
#define FUSE_CAP_NO_OPENDIR_SUPPORT (1 << 24)
55+
56+
+/**
57+
+ * Indicates support for passthrough mode access for read/write operations.
58+
+ *
59+
+ * If this flag is set in the `capable` field of the `fuse_conn_info`
60+
+ * structure, then the FUSE kernel module supports redirecting read/write
61+
+ * operations to the lower file system instead of letting them to be handled
62+
+ * by the FUSE daemon.
63+
+ *
64+
+ * This feature is disabled by default.
65+
+ */
66+
+#define FUSE_CAP_PASSTHROUGH (1 << 31)
67+
+
68+
/**
69+
* Ioctl flags
70+
*
71+
diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h
72+
index 8a45f42..8bd7f0d 100644
73+
--- a/include/fuse_kernel.h
74+
+++ b/include/fuse_kernel.h
75+
@@ -301,6 +301,7 @@ struct fuse_file_lock {
76+
#define FUSE_CACHE_SYMLINKS (1 << 23)
77+
#define FUSE_NO_OPENDIR_SUPPORT (1 << 24)
78+
#define FUSE_EXPLICIT_INVAL_DATA (1 << 25)
79+
+#define FUSE_PASSTHROUGH (1 << 31)
80+
81+
/**
82+
* CUSE INIT request/reply flags
83+
@@ -547,7 +548,7 @@ struct fuse_create_in {
84+
struct fuse_open_out {
85+
uint64_t fh;
86+
uint32_t open_flags;
87+
- uint32_t padding;
88+
+ uint32_t passthrough_fh;
89+
};
90+
91+
struct fuse_release_in {
92+
@@ -574,6 +575,13 @@ struct fuse_read_in {
93+
uint32_t padding;
94+
};
95+
96+
+struct fuse_passthrough_out {
97+
+ uint32_t fd;
98+
+ /* For future implementation */
99+
+ uint32_t len;
100+
+ void * vec;
101+
+};
102+
+
103+
#define FUSE_COMPAT_WRITE_IN_SIZE 24
104+
105+
struct fuse_write_in {
106+
@@ -825,7 +833,8 @@ struct fuse_notify_retrieve_in {
107+
};
108+
109+
/* Device ioctls: */
110+
-#define FUSE_DEV_IOC_CLONE _IOR(229, 0, uint32_t)
111+
+#define FUSE_DEV_IOC_CLONE _IOR(229, 0, uint32_t)
112+
+#define FUSE_DEV_IOC_PASSTHROUGH_OPEN _IOW(229, 1, struct fuse_passthrough_out)
113+
114+
struct fuse_lseek_in {
115+
uint64_t fh;
116+
diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h
117+
index e81c282..e916112 100644
118+
--- a/include/fuse_lowlevel.h
119+
+++ b/include/fuse_lowlevel.h
120+
@@ -1349,6 +1349,8 @@ int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
121+
*/
122+
int fuse_reply_readlink(fuse_req_t req, const char *link);
123+
124+
+int fuse_passthrough_enable(fuse_req_t req, unsigned int fd);
125+
+
126+
/**
127+
* Reply with the canonical path for inotify
128+
*
129+
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
130+
index fc76b7c..c7efc3d 100644
131+
--- a/lib/fuse_lowlevel.c
132+
+++ b/lib/fuse_lowlevel.c
133+
@@ -27,6 +27,7 @@
134+
#include <errno.h>
135+
#include <assert.h>
136+
#include <sys/file.h>
137+
+#include <sys/ioctl.h>
138+
139+
#ifndef F_LINUX_SPECIFIC_BASE
140+
#define F_LINUX_SPECIFIC_BASE 1024
141+
@@ -388,6 +389,7 @@ static void fill_open(struct fuse_open_out *arg,
142+
const struct fuse_file_info *f)
143+
{
144+
arg->fh = f->fh;
145+
+ arg->passthrough_fh = f->passthrough_fh;
146+
if (f->direct_io)
147+
arg->open_flags |= FOPEN_DIRECT_IO;
148+
if (f->keep_cache)
149+
@@ -457,6 +459,19 @@ int fuse_reply_canonical_path(fuse_req_t req, const char *path)
150+
return send_reply_ok(req, path, strlen(path) + 1);
151+
}
152+
153+
+int fuse_passthrough_enable(fuse_req_t req, unsigned int fd) {
154+
+ struct fuse_passthrough_out out = {};
155+
+ int ret;
156+
+
157+
+ out.fd = fd;
158+
+
159+
+ ret = ioctl(req->se->fd, FUSE_DEV_IOC_PASSTHROUGH_OPEN, &out);
160+
+ if (ret <= 0)
161+
+ fuse_log(FUSE_LOG_ERR, "fuse: passthrough_enable: %s\n", strerror(errno));
162+
+
163+
+ return ret;
164+
+}
165+
+
166+
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f)
167+
{
168+
struct fuse_open_out arg;
169+
@@ -1990,6 +2005,8 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
170+
bufsize = max_bufsize;
171+
}
172+
}
173+
+ if (arg->flags & FUSE_PASSTHROUGH)
174+
+ se->conn.capable |= FUSE_PASSTHROUGH;
175+
} else {
176+
se->conn.max_readahead = 0;
177+
}
178+
@@ -2102,6 +2119,8 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
179+
outarg.flags |= FUSE_WRITEBACK_CACHE;
180+
if (se->conn.want & FUSE_CAP_POSIX_ACL)
181+
outarg.flags |= FUSE_POSIX_ACL;
182+
+ if (se->conn.want & FUSE_CAP_PASSTHROUGH)
183+
+ outarg.flags |= FUSE_PASSTHROUGH;
184+
outarg.max_readahead = se->conn.max_readahead;
185+
outarg.max_write = se->conn.max_write;
186+
if (se->conn.proto_minor >= 13) {
187+
diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
188+
index 4c075a3..b01699a 100644
189+
--- a/lib/fuse_versionscript
190+
+++ b/lib/fuse_versionscript
191+
@@ -163,6 +163,7 @@ FUSE_3.7 {
192+
global:
193+
fuse_set_log_func;
194+
fuse_log;
195+
+ fuse_passthrough_enable;
196+
fuse_reply_canonical_path;
197+
} FUSE_3.3;
198+
199+
--
200+
2.48.1
201+
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
From 677cb55133a55c47bfa1dc1c5bde1f5ef994d09a Mon Sep 17 00:00:00 2001
2+
From: Alessio Balsini <balsini@google.com>
3+
Date: Thu, 28 Jan 2021 19:30:27 +0000
4+
Subject: [PATCH 2/5] DO NOT MERGE FUSE passthrough: handle unstable kernel
5+
interface
6+
7+
The interface for FUSE passthrough is still under discussion upstream,
8+
thus this change fixes the issue of being able to support multiple
9+
interfaces for passthrough, depending on what the kernel provides.
10+
11+
This implementation tries to explore the different FUSE passthrough
12+
interface implementation and updates an index to the one that has
13+
been found working.
14+
15+
Bug: 174548608
16+
Test: Manual read/write of passthrough files with extra printks
17+
Signed-off-by: Alessio Balsini <balsini@google.com>
18+
Change-Id: I74e0e6c0691b37160a00af77fb18eda5342630f5
19+
---
20+
include/fuse_kernel.h | 5 ++--
21+
lib/fuse_lowlevel.c | 64 ++++++++++++++++++++++++++++++++++++++-----
22+
2 files changed, 60 insertions(+), 9 deletions(-)
23+
24+
diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h
25+
index 8bd7f0d..55d8e2c 100644
26+
--- a/include/fuse_kernel.h
27+
+++ b/include/fuse_kernel.h
28+
@@ -575,7 +575,7 @@ struct fuse_read_in {
29+
uint32_t padding;
30+
};
31+
32+
-struct fuse_passthrough_out {
33+
+struct fuse_passthrough_out_v0 {
34+
uint32_t fd;
35+
/* For future implementation */
36+
uint32_t len;
37+
@@ -834,7 +834,8 @@ struct fuse_notify_retrieve_in {
38+
39+
/* Device ioctls: */
40+
#define FUSE_DEV_IOC_CLONE _IOR(229, 0, uint32_t)
41+
-#define FUSE_DEV_IOC_PASSTHROUGH_OPEN _IOW(229, 1, struct fuse_passthrough_out)
42+
+#define FUSE_DEV_IOC_PASSTHROUGH_OPEN_V0 _IOW(229, 1, struct fuse_passthrough_out_v0)
43+
+#define FUSE_DEV_IOC_PASSTHROUGH_OPEN_V1 _IOW(229, 127, struct fuse_passthrough_out_v0)
44+
45+
struct fuse_lseek_in {
46+
uint64_t fh;
47+
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
48+
index c7efc3d..baa6d2b 100644
49+
--- a/lib/fuse_lowlevel.c
50+
+++ b/lib/fuse_lowlevel.c
51+
@@ -459,17 +459,67 @@ int fuse_reply_canonical_path(fuse_req_t req, const char *path)
52+
return send_reply_ok(req, path, strlen(path) + 1);
53+
}
54+
55+
+enum {
56+
+ FUSE_PASSTHROUGH_API_UNAVAILABLE,
57+
+ FUSE_PASSTHROUGH_API_V0,
58+
+ FUSE_PASSTHROUGH_API_V1,
59+
+ FUSE_PASSTHROUGH_API_STABLE,
60+
+};
61+
+
62+
+/*
63+
+ * Requests the FUSE passthrough feature to be enabled on a specific file
64+
+ * through the passed fd.
65+
+ * This function returns an identifier that must be used as passthrough_fh
66+
+ * when the open/create_open request reply is sent back to /dev/fuse.
67+
+ * As for the current FUSE passthrough implementation, passthrough_fh values
68+
+ * are only valid if > 0, so in case the FUSE passthrough open ioctl returns
69+
+ * a value <= 0, this must be considered an error and is returned as-is by
70+
+ * this function.
71+
+ */
72+
int fuse_passthrough_enable(fuse_req_t req, unsigned int fd) {
73+
- struct fuse_passthrough_out out = {};
74+
- int ret;
75+
+ static sig_atomic_t passthrough_version = FUSE_PASSTHROUGH_API_STABLE;
76+
+ int ret = 0; /* values <= 0 represent errors in FUSE passthrough */
77+
78+
- out.fd = fd;
79+
+ /*
80+
+ * The interface of FUSE passthrough is still unstable in the kernel,
81+
+ * so the following solution is to search for the most updated API
82+
+ * version and, if not found, fall back to an older one.
83+
+ * This happens when ioctl() returns -1 and errno is set to ENOTTY,
84+
+ * an error code that corresponds to the lack of a specific ioctl.
85+
+ */
86+
+ switch (passthrough_version) {
87+
+ case FUSE_PASSTHROUGH_API_STABLE:
88+
+ /* There is not a stable API yet */
89+
+ passthrough_version = FUSE_PASSTHROUGH_API_V1;
90+
+ case FUSE_PASSTHROUGH_API_V1: {
91+
+ struct fuse_passthrough_out_v0 out = {};
92+
+ out.fd = fd;
93+
+
94+
+ ret = ioctl(req->se->fd, FUSE_DEV_IOC_PASSTHROUGH_OPEN_V1, &out);
95+
+ if (ret == -1 && errno == ENOTTY)
96+
+ passthrough_version = FUSE_PASSTHROUGH_API_V0;
97+
+ else
98+
+ break;
99+
+ }
100+
+ case FUSE_PASSTHROUGH_API_V0: {
101+
+ struct fuse_passthrough_out_v0 out = {};
102+
+ out.fd = fd;
103+
104+
- ret = ioctl(req->se->fd, FUSE_DEV_IOC_PASSTHROUGH_OPEN, &out);
105+
- if (ret <= 0)
106+
- fuse_log(FUSE_LOG_ERR, "fuse: passthrough_enable: %s\n", strerror(errno));
107+
+ ret = ioctl(req->se->fd, FUSE_DEV_IOC_PASSTHROUGH_OPEN_V0, &out);
108+
+ if (ret == -1 && errno == ENOTTY)
109+
+ passthrough_version = FUSE_PASSTHROUGH_API_UNAVAILABLE;
110+
+ else
111+
+ break;
112+
+ }
113+
+ default:
114+
+ fuse_log(FUSE_LOG_ERR, "fuse: passthrough_enable no valid API\n");
115+
+ return -ENOTTY;
116+
+ }
117+
+
118+
+ if (ret <= 0)
119+
+ fuse_log(FUSE_LOG_ERR, "fuse: passthrough_enable: %s\n", strerror(errno));
120+
121+
- return ret;
122+
+ return ret;
123+
}
124+
125+
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f)
126+
--
127+
2.48.1
128+

0 commit comments

Comments
 (0)