From ccfcc5162acc89fb89fb980075b32874eed95b04 Mon Sep 17 00:00:00 2001
From: chenrenhui <chenrenhui1@huawei.com>
Date: Tue, 6 May 2025 10:49:07 +0800
Subject: [PATCH] fuse3: support fastpath

---
 example/passthrough_hp.cc |  10 +-
 include/fuse_ipc.h        |   4 +
 include/fuse_kernel.h     |  15 +-
 include/fuse_lowlevel.h   |   2 +
 include/meson.build       |   3 +-
 lib/fuse_i.h              |  20 +++
 lib/fuse_ipc.c            | 359 ++++++++++++++++++++++++++++++++++++
 lib/fuse_loop.c           |  45 +++++
 lib/fuse_loop_mt.c        |   3 +
 lib/fuse_lowlevel.c       | 369 +++++++++++++++++++++++++++++++++++---
 lib/fuse_versionscript    |   1 +
 lib/meson.build           |   2 +-
 lib/mount.c               |   4 +
 13 files changed, 808 insertions(+), 29 deletions(-)
 create mode 100644 include/fuse_ipc.h
 create mode 100644 lib/fuse_ipc.c

diff --git a/example/passthrough_hp.cc b/example/passthrough_hp.cc
index 872fc73..1f96820 100644
--- a/example/passthrough_hp.cc
+++ b/example/passthrough_hp.cc
@@ -1146,7 +1146,10 @@ static cxxopts::ParseResult parse_options(int argc, char **argv) {
         ("help", "Print help")
         ("nocache", "Disable all caching")
         ("nosplice", "Do not use splice(2) to transfer data")
-        ("single", "Run single-threaded");
+        ("single", "Run single-threaded")
+        ("nointerrupt", "Do not process interrupt request")
+        ("noforget", "Do not process forget request")
+        ("usefastpath", "use fastpath");
 
     // FIXME: Find a better way to limit the try clause to just
     // opt_parser.parse() (cf. https://github.com/jarro2783/cxxopts/issues/146)
@@ -1225,7 +1228,10 @@ int main(int argc, char *argv[]) {
     if (fuse_opt_add_arg(&args, argv[0]) ||
         fuse_opt_add_arg(&args, "-o") ||
         fuse_opt_add_arg(&args, "default_permissions,fsname=hpps") ||
-        (options.count("debug-fuse") && fuse_opt_add_arg(&args, "-odebug")))
+        (options.count("debug-fuse") && fuse_opt_add_arg(&args, "-odebug")) ||
+        (options.count("nointerrupt") && fuse_opt_add_arg(&args, "-ono_interrupt")) ||
+        (options.count("noforget") && fuse_opt_add_arg(&args, "-ono_forget")) ||
+        (options.count("usefastpath") && fuse_opt_add_arg(&args, "-ouse_fastpath")))
         errx(3, "ERROR: Out of memory");
 
     fuse_lowlevel_ops sfs_oper {};
diff --git a/include/fuse_ipc.h b/include/fuse_ipc.h
new file mode 100644
index 0000000..9b1da9b
--- /dev/null
+++ b/include/fuse_ipc.h
@@ -0,0 +1,4 @@
+void fuse_ipc_create_server_mt(struct fuse_session *se);
+int fuse_ipc_wait_and_ret_call(int fd, unsigned long arg);
+int fuse_ipc_wait_call(int fd, unsigned long arg);
+int fuse_ipc_ret_call(int fd, unsigned long arg);
diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h
index 018a00a..331cbe0 100644
--- a/include/fuse_kernel.h
+++ b/include/fuse_kernel.h
@@ -821,8 +821,21 @@ struct fuse_notify_retrieve_in {
 	uint64_t	dummy4;
 };
 
+struct fuse_ipc_io {
+	char *buf;
+	uint64_t buf_len;
+	struct iovec *iov;
+	int count;
+};
+
 /* Device ioctls: */
-#define FUSE_DEV_IOC_CLONE	_IOR(229, 0, uint32_t)
+#define FUSE_DEV_IOC_MAGIC     229
+#define FUSE_DEV_IOC_CLONE     _IOR(FUSE_DEV_IOC_MAGIC, 0, uint32_t)
+#define FUSE_DEV_IOC_IPC_BIND   _IO(FUSE_DEV_IOC_MAGIC, 1)
+#define FUSE_DEV_IOC_WAIT_RET_CALL   _IOR(FUSE_DEV_IOC_MAGIC, 2, struct fuse_ipc_io)
+#define FUSE_DEV_IOC_IPC_UNBIND   _IO(FUSE_DEV_IOC_MAGIC, 4)
+#define FUSE_DEV_IOC_WAIT_CALL  _IOR(FUSE_DEV_IOC_MAGIC, 5, struct fuse_ipc_io)
+#define FUSE_DEV_IOC_RET_CALL   _IOR(FUSE_DEV_IOC_MAGIC, 6, struct fuse_ipc_io)
 
 struct fuse_lseek_in {
 	uint64_t	fh;
diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h
index d73e9fa..911a22e 100644
--- a/include/fuse_lowlevel.h
+++ b/include/fuse_lowlevel.h
@@ -30,6 +30,7 @@
 #include <sys/stat.h>
 #include <sys/statvfs.h>
 #include <sys/uio.h>
+#include <semaphore.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -1966,6 +1967,7 @@ int fuse_session_mount(struct fuse_session *se, const char *mountpoint);
  * @return 0, -errno, or a signal value
  */
 int fuse_session_loop(struct fuse_session *se);
+int fuse_session_loop_ipc(struct fuse_session *se);
 
 #if FUSE_USE_VERSION < 32
 int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd);
diff --git a/include/meson.build b/include/meson.build
index bf67197..3c757d3 100644
--- a/include/meson.build
+++ b/include/meson.build
@@ -1,4 +1,5 @@
 libfuse_headers = [ 'fuse.h', 'fuse_common.h', 'fuse_lowlevel.h',
-	            'fuse_opt.h', 'cuse_lowlevel.h', 'fuse_log.h' ]
+	            'fuse_opt.h', 'cuse_lowlevel.h', 'fuse_log.h',
+                'fuse_ipc.h' ]
 
 install_headers(libfuse_headers, subdir: 'fuse3')
diff --git a/lib/fuse_i.h b/lib/fuse_i.h
index d38b630..c7dd585 100644
--- a/lib/fuse_i.h
+++ b/lib/fuse_i.h
@@ -8,6 +8,7 @@
 
 #include "fuse.h"
 #include "fuse_lowlevel.h"
+#include "fuse_kernel.h"
 
 struct mount_opts;
 
@@ -41,6 +42,12 @@ struct fuse_notify_req {
 	struct fuse_notify_req *prev;
 };
 
+struct fuse_ipc_data {
+	struct fuse_ipc_io ipc_io;
+	int ipc_ret;
+	void *data_page;
+};
+
 struct fuse_session {
 	char *mountpoint;
 	volatile int exited;
@@ -64,6 +71,14 @@ struct fuse_session {
 	struct fuse_notify_req notify_list;
 	size_t bufsize;
 	int error;
+	int ipc_server_fd;
+	void *ipc_data;
+	int not_first_msg;
+	pthread_key_t ipc_data_key;
+	int no_interrupt;
+	int no_forget;
+	int use_fastpath;
+	sem_t finish;
 };
 
 struct fuse_chan {
@@ -125,12 +140,17 @@ int fuse_session_receive_buf_int(struct fuse_session *se, struct fuse_buf *buf,
 				 struct fuse_chan *ch);
 void fuse_session_process_buf_int(struct fuse_session *se,
 				  const struct fuse_buf *buf, struct fuse_chan *ch);
+void fuse_session_process_buf_int_fast(struct fuse_session *se,
+				  const struct fuse_buf *buf, struct fuse_chan *ch);
 
 struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op,
 		      size_t op_size, void *private_data);
 int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config *config);
 int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config *config);
 
+struct fuse_ipc_data *fuse_ll_get_ipc_data(struct fuse_session *se);
+void fuse_ll_clear_ipc_data(struct fuse_session *se);
+
 #define FUSE_MAX_MAX_PAGES 256
 #define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32
 
diff --git a/lib/fuse_ipc.c b/lib/fuse_ipc.c
new file mode 100644
index 0000000..6528098
--- /dev/null
+++ b/lib/fuse_ipc.c
@@ -0,0 +1,359 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <errno.h>
+#include <sched.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/sysinfo.h>
+#include "fuse_i.h"
+#include "fuse_ipc.h"
+#include "fuse_lowlevel.h"
+
+#define FUSE_DATA_PAGE_SIZE 4096
+
+struct fuse_ipc_thread_args {
+	struct fuse_session *se;
+
+	int wait_flag;
+	pthread_mutex_t mutex;
+	pthread_cond_t cond;
+
+	int create_result;
+
+	int cpu_id;
+};
+
+pthread_t *fuse_ipc_workers;
+
+static int fuse_ipc_cond_init(struct fuse_ipc_thread_args *args)
+{
+	int ret;
+
+	args->wait_flag = 0;
+
+	ret = pthread_mutex_init(&args->mutex, NULL);
+	if (ret != 0) {
+		fuse_log(FUSE_LOG_ERR, "mutex init failed, errno = %d\n", errno);
+		return -1;
+	}
+
+	ret = pthread_cond_init(&args->cond, NULL);
+	if (ret != 0) {
+		fuse_log(FUSE_LOG_ERR, "cond init failed, errno = %d\n", errno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static void fuse_ipc_cond_wait(struct fuse_ipc_thread_args *args)
+{
+	(void)pthread_mutex_lock(&args->mutex);
+	while (args->wait_flag == 0)
+		(void)pthread_cond_wait(&args->cond, &args->mutex);
+	(void)pthread_mutex_unlock(&args->mutex);
+}
+
+static void fuse_ipc_cond_signal(struct fuse_ipc_thread_args *args, int result)
+{
+	args->create_result = result;
+
+	(void)pthread_mutex_lock(&args->mutex);
+	if (args->wait_flag == 0) {
+		args->wait_flag = 1;
+		(void)pthread_cond_signal(&args->cond);
+	}
+	(void)pthread_mutex_unlock(&args->mutex);
+}
+
+static int fuse_ipc_server_thread_bind(int fd)
+{
+	int ret;
+
+	ret = ioctl(fd, FUSE_DEV_IOC_IPC_BIND);
+	if (ret < 0) {
+		fuse_log(FUSE_LOG_ERR, "fuse_ipc_server_thread bind failed, errno = %d\n", errno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+fuse_mmap_data_page(struct fuse_session* se, struct fuse_ipc_data* ipc_data)
+{
+	void* addr;
+
+	addr = mmap(NULL, FUSE_DATA_PAGE_SIZE,
+		PROT_READ | PROT_WRITE, MAP_SHARED, se->fd, 0);
+	if (addr == MAP_FAILED) {
+		fuse_log(FUSE_LOG_ERR, "mmap data page failed, errno = %d\n", errno);
+		return -1;
+	}
+
+	ipc_data->data_page = addr;
+
+	return 0;
+}
+
+static int fuse_munmap_data_page(struct fuse_ipc_data *ipc_data)
+{
+	int ret;
+
+	ret = munmap(ipc_data->data_page, FUSE_DATA_PAGE_SIZE);
+	if (ret) {
+		fuse_log(FUSE_LOG_ERR, "munmap data page failed, errno = %d\n", errno);
+		return ret;
+	}
+
+	ipc_data->data_page = NULL;
+
+	return ret;
+}
+
+int fuse_ipc_wait_and_ret_call(int fd, unsigned long arg)
+{
+	int ret;
+
+	ret = ioctl(fd, FUSE_DEV_IOC_WAIT_RET_CALL, arg);
+	if (ret < 0) {
+		if (errno != 4)
+			fuse_log(FUSE_LOG_ERR, "fuse_ipc_wait_and_ret call failed, errno = %d\n", errno);
+		return -1;
+	}
+
+	return ret;
+}
+
+int fuse_ipc_wait_call(int fd, unsigned long arg)
+{
+	int ret;
+
+	ret = ioctl(fd, FUSE_DEV_IOC_WAIT_CALL, arg);
+	if (ret < 0) {
+		if (errno != 4 && errno != 19)
+			fuse_log(FUSE_LOG_ERR, "fuse_ipc_wait_call failed, errno = %d\n", errno);
+		return -1;
+	}
+
+	return ret;
+}
+
+int fuse_ipc_ret_call(int fd, unsigned long arg)
+{
+	int ret;
+
+	ret = ioctl(fd, FUSE_DEV_IOC_RET_CALL, arg);
+	if (ret < 0) {
+		fuse_log(FUSE_LOG_ERR, "fuse_ipc_ret_call failed, errno = %d\n", errno);
+		return -1;
+	}
+	return ret;
+}
+
+static int fuse_ipc_server_thread_unbind(struct fuse_session *se)
+{
+	int ret;
+
+	ret = ioctl(se->fd, FUSE_DEV_IOC_IPC_UNBIND);
+	if (ret < 0) {
+		fuse_log(FUSE_LOG_ERR, "fuse_ipc_server_thread_unbind failed, errno = %d\n", errno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static void *fuse_ipc_server_thread(void *args)
+{
+	int ret = 0;
+	int volatile err = -1;
+	struct fuse_ipc_thread_args *thread_args = (struct fuse_ipc_thread_args *)args;
+	struct fuse_session *se = thread_args->se;
+	int cpu_id = thread_args->cpu_id;
+	struct fuse_ipc_io *ipc_io;
+	struct fuse_ipc_data *ipc_data;
+	struct fuse_buf fbuf = {
+		.mem = NULL,
+	};
+	cpu_set_t mask;
+
+	CPU_ZERO(&mask);
+	CPU_SET(cpu_id, &mask);
+
+	if (-1 == pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask)) {
+		fuse_log(FUSE_LOG_ERR, "error: pthread_setaffinity_np(%d) fiailed\n", cpu_id);
+		goto out;
+	}
+
+	ipc_data = fuse_ll_get_ipc_data(se);
+	if (ipc_data == NULL)
+		goto out;
+
+	ret = fuse_mmap_data_page(se, ipc_data);
+	if (ret)
+		goto clear;
+
+	ret = fuse_ipc_server_thread_bind(se->fd);
+	if (ret) {
+		goto unmap;
+	}
+
+	ipc_io = &(ipc_data->ipc_io);
+	ipc_io->buf = malloc(se->bufsize);
+	if (ipc_io->buf == NULL)
+		goto unbind;
+	ipc_io->buf_len = se->bufsize;
+
+	fuse_ipc_cond_signal(thread_args, 0);
+
+	err = 0;
+	fbuf.mem = ipc_io->buf;
+
+	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+	pthread_cleanup_push((void *)fuse_ll_clear_ipc_data, (void *)se);
+	pthread_cleanup_push((void *)fuse_munmap_data_page, (void *)ipc_data);
+	pthread_cleanup_push((void *)fuse_ipc_server_thread_unbind, (void *)se);
+	pthread_cleanup_push(free, ipc_io->buf);
+	for (;;) {
+		pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+		ret = fuse_ipc_wait_call(se->fd, (unsigned long)ipc_io);
+		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+		if (ret <= 0) {
+			fuse_session_exit(se);
+			break;
+		}
+		if (fuse_session_exited(se))
+			break;
+		fbuf.size = ret;
+		fuse_session_process_buf_int_fast(se, &fbuf, NULL);
+	}
+
+	pthread_cleanup_pop(0);
+	pthread_cleanup_pop(0);
+	pthread_cleanup_pop(0);
+	pthread_cleanup_pop(0);
+
+	free(ipc_io->buf);
+unbind:
+	fuse_ipc_server_thread_unbind(se);
+unmap:
+	fuse_munmap_data_page(ipc_data);
+clear:
+	fuse_ll_clear_ipc_data(se);
+out:
+	if (err)
+		fuse_ipc_cond_signal(thread_args, -1);
+
+	sem_post(&se->finish);
+
+	return NULL;
+}
+
+static int fuse_ipc_server_thread_create(struct fuse_session *se, int cpu_id)
+{
+	struct fuse_ipc_thread_args *args;
+	int ret;
+	pthread_t tid;
+	pthread_attr_t attr;
+	unsigned int stack_size = 8 * 1024 * 1024;
+	sigset_t oldset;
+	sigset_t newset;
+
+	args = (struct fuse_ipc_thread_args *)malloc(sizeof(struct fuse_ipc_thread_args));
+	if (!args) {
+		fuse_log(FUSE_LOG_ERR, "malloc fuse_ipc_thread_args failed\n");
+		return -1;
+	}
+
+	ret = fuse_ipc_cond_init(args);
+	if (ret != 0) {
+		goto failed;
+	}
+
+	ret = pthread_attr_init(&attr);
+	if (ret != 0) {
+		fuse_log(FUSE_LOG_ERR, "attr init failed, errno = %d\n", errno);
+		goto failed;
+	}
+
+	ret = pthread_attr_setstacksize(&attr, stack_size);
+	if (ret != 0) {
+		fuse_log(FUSE_LOG_ERR, "set stack size failed, errno = %d\n", errno);
+		goto failed;
+	}
+
+	args->se = se;
+	args->cpu_id = cpu_id;
+
+	sigemptyset(&newset);
+	sigaddset(&newset, SIGTERM);
+	sigaddset(&newset, SIGINT);
+	sigaddset(&newset, SIGHUP);
+	sigaddset(&newset, SIGQUIT);
+	pthread_sigmask(SIG_BLOCK, &newset, &oldset);
+	ret = pthread_create(&tid, &attr, fuse_ipc_server_thread, (void *)args);
+	pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+	pthread_attr_destroy(&attr);
+	if (ret != 0) {
+		fuse_log(FUSE_LOG_ERR, "pthread create failed, errno = %d\n", errno);
+		goto failed;
+	}
+
+	fuse_ipc_cond_wait(args);
+	if (args->create_result) {
+		fuse_log(FUSE_LOG_ERR, "server thread create failed\n");
+		ret = -1;
+	} else {
+		fuse_ipc_workers[cpu_id] = tid;
+	}
+
+failed:
+	free(args);
+	return ret;
+}
+
+void fuse_ipc_create_server_mt(struct fuse_session *se)
+{
+	int ret = 0;
+	int thread_num = get_nprocs_conf();
+	int created_num = 0;
+	int i = 0;
+
+	if (thread_num != 0) {
+		fuse_ipc_workers = (pthread_t *)malloc(sizeof(pthread_t) * thread_num);
+		if (fuse_ipc_workers == NULL) {
+			fuse_log(FUSE_LOG_ERR, "fuse_ipc_create_server_mt: out of memory\n");
+			goto out;
+		}
+	}
+
+	sem_init(&se->finish, 0, 0);
+	for (i = 0; i < thread_num; i++) {
+		ret = fuse_ipc_server_thread_create(se, i);
+		if (ret) {
+			fuse_session_exit(se);
+			break;
+		}
+	}
+
+	created_num = i;
+	while (!fuse_session_exited(se)) {
+		sem_wait(&se->finish);
+	}
+
+	for (i = 0; i < created_num; i++) {
+		pthread_cancel(fuse_ipc_workers[i]);
+	}
+	for (i = 0; i < created_num; i++) {
+		pthread_join(fuse_ipc_workers[i], NULL);
+	}
+
+out:
+	free(fuse_ipc_workers);
+}
diff --git a/lib/fuse_loop.c b/lib/fuse_loop.c
index e6560aa..4f86484 100644
--- a/lib/fuse_loop.c
+++ b/lib/fuse_loop.c
@@ -11,11 +11,50 @@
 #include "config.h"
 #include "fuse_lowlevel.h"
 #include "fuse_i.h"
+#include "fuse_ipc.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
 
+int fuse_session_loop_ipc(struct fuse_session *se)
+{
+	int res = 0;
+	struct fuse_buf fbuf = {
+		.mem = NULL,
+	};
+
+	while (!fuse_session_exited(se)) {
+		res = fuse_session_receive_buf_int(se, &fbuf, NULL);
+
+		if (res == -EINTR)
+			continue;
+		if (res <= 0)
+			break;
+
+		fuse_session_process_buf_int_fast(se, &fbuf, NULL);
+
+		if (se->got_init) {
+			if (se->debug)
+				fuse_log(FUSE_LOG_DEBUG, "FUSE_INIT request is got\n");
+			break;
+		}
+	}
+
+	if (res > 0 && se->got_init)
+		fuse_ipc_create_server_mt(se);
+
+	free(fbuf.mem);
+	if(res > 0)
+		/* No error, just the length of the most recently read
+		   request */
+		res = 0;
+	if(se->error != 0)
+		res = se->error;
+	fuse_session_reset(se);
+	return res;
+}
+
 int fuse_session_loop(struct fuse_session *se)
 {
 	int res = 0;
@@ -23,6 +62,12 @@ int fuse_session_loop(struct fuse_session *se)
 		.mem = NULL,
 	};
 
+	if (se->use_fastpath) {
+		fuse_log(FUSE_LOG_ERR,
+			"single thread is not supported in fuse fastpath mode\n");
+		return -1;
+	}
+
 	while (!fuse_session_exited(se)) {
 		res = fuse_session_receive_buf_int(se, &fbuf, NULL);
 
diff --git a/lib/fuse_loop_mt.c b/lib/fuse_loop_mt.c
index 8fcc46c..ba98212 100644
--- a/lib/fuse_loop_mt.c
+++ b/lib/fuse_loop_mt.c
@@ -310,6 +310,9 @@ int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config *co
 	struct fuse_mt mt;
 	struct fuse_worker *w;
 
+	if (se->use_fastpath)
+		return fuse_session_loop_ipc(se);
+
 	memset(&mt, 0, sizeof(struct fuse_mt));
 	mt.se = se;
 	mt.clone_fd = config->clone_fd;
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index 8e8fcb4..c6a5b6f 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -17,6 +17,7 @@
 #include "fuse_opt.h"
 #include "fuse_misc.h"
 #include "mount_util.h"
+#include "fuse_ipc.h"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -27,6 +28,7 @@
 #include <errno.h>
 #include <assert.h>
 #include <sys/file.h>
+#include <sys/mman.h>
 
 #ifndef F_LINUX_SPECIFIC_BASE
 #define F_LINUX_SPECIFIC_BASE       1024
@@ -133,14 +135,20 @@ void fuse_free_req(fuse_req_t req)
 	int ctr;
 	struct fuse_session *se = req->se;
 
-	pthread_mutex_lock(&se->lock);
-	req->u.ni.func = NULL;
-	req->u.ni.data = NULL;
-	list_del_req(req);
-	ctr = --req->ctr;
-	fuse_chan_put(req->ch);
-	req->ch = NULL;
-	pthread_mutex_unlock(&se->lock);
+	if (se->no_interrupt) {
+		ctr = --req->ctr;
+		fuse_chan_put(req->ch);
+		req->ch = NULL;
+	} else {
+		pthread_mutex_lock(&se->lock);
+		req->u.ni.func = NULL;
+		req->u.ni.data = NULL;
+		list_del_req(req);
+		ctr = --req->ctr;
+		fuse_chan_put(req->ch);
+		req->ch = NULL;
+		pthread_mutex_unlock(&se->lock);
+	}
 	if (!ctr)
 		destroy_req(req);
 }
@@ -162,12 +170,18 @@ static struct fuse_req *fuse_ll_alloc_req(struct fuse_session *se)
 	return req;
 }
 
+static int fuse_send_msg_fast(struct fuse_session *se, struct fuse_chan *ch,
+                         struct iovec *iov, int count);
+
 /* Send data. If *ch* is NULL, send via session master fd */
 static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch,
 			 struct iovec *iov, int count)
 {
 	struct fuse_out_header *out = iov[0].iov_base;
 
+	if (se->use_fastpath)
+		return fuse_send_msg_fast(se, ch, iov, count);
+
 	assert(se != NULL);
 	out->len = iov_length(iov, count);
 	if (se->debug) {
@@ -200,6 +214,60 @@ static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch,
 	return 0;
 }
 
+static int fuse_send_msg_fast(struct fuse_session *se, struct fuse_chan *ch,
+	struct iovec *iov, int count)
+{
+	struct fuse_out_header *out = iov[0].iov_base;
+	struct fuse_ipc_data *ipc_data;
+	struct fuse_ipc_io *ipc_io;
+	ssize_t res;
+
+	assert(se != NULL);
+	out->len = iov_length(iov, count);
+	if (se->debug) {
+		if (out->unique == 0) {
+			fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=%d length=%u\n",
+				out->error, out->len);
+		} else if (out->error) {
+			fuse_log(FUSE_LOG_DEBUG,
+				"   unique: %llu, error: %i (%s), outsize: %i\n",
+				(unsigned long long) out->unique, out->error,
+				strerror(-out->error), out->len);
+		} else {
+			fuse_log(FUSE_LOG_DEBUG,
+				"   unique: %llu, success, outsize: %i\n",
+				(unsigned long long) out->unique, out->len);
+		}
+
+	}
+
+	if (!se->not_first_msg) {
+		res = writev(ch ? ch->fd : se->fd,
+			iov, count);
+
+		int err = errno;
+
+		if (res == -1) {
+			/* ENOENT means the operation was interrupted */
+			if (!fuse_session_exited(se) && err != ENOENT)
+				perror("fuse: writing device");
+			return -err;
+		}
+		se->not_first_msg = 1;
+	} else {
+		ipc_data = fuse_ll_get_ipc_data(se);
+		memcpy(ipc_data->data_page, iov[0].iov_base, iov[0].iov_len);
+		ipc_io = &(ipc_data->ipc_io);
+		ipc_io->iov = &iov[1];
+		ipc_io->count = count - 1;
+		res = fuse_ipc_ret_call(se->fd, (unsigned long)ipc_io);
+		if (res == -1) {
+			return -errno;
+		}
+	}
+
+	return 0;
+}
 
 int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
 			       int count)
@@ -585,6 +653,29 @@ static struct fuse_ll_pipe *fuse_ll_get_pipe(struct fuse_session *se)
 }
 #endif
 
+struct fuse_ipc_data *fuse_ll_get_ipc_data(struct fuse_session *se)
+{
+	struct fuse_ipc_data *ipc_data = pthread_getspecific(se->ipc_data_key);
+	if (ipc_data == NULL) {
+		ipc_data = malloc(sizeof(struct fuse_ipc_data));
+		if (ipc_data == NULL)
+			return NULL;
+
+		pthread_setspecific(se->ipc_data_key, ipc_data);
+	}
+
+	return ipc_data;
+}
+
+void fuse_ll_clear_ipc_data(struct fuse_session *se)
+{
+	struct fuse_ipc_data *ipc_data = pthread_getspecific(se->ipc_data_key);
+	if (ipc_data) {
+		free(ipc_data);
+		pthread_setspecific(se->ipc_data_key, NULL);
+	}
+}
+
 static void fuse_ll_clear_pipe(struct fuse_session *se)
 {
 	struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key);
@@ -1374,6 +1465,31 @@ static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_err(req, ENOSYS);
 }
 
+static void do_write_fast(fuse_req_t req, fuse_ino_t nodeid, const void *inarg,
+	const struct fuse_buf *ibuf)
+{
+	struct fuse_write_in *arg = (struct fuse_write_in *) inarg;
+	struct fuse_file_info fi;
+	char *param = ibuf->mem;
+
+	memset(&fi, 0, sizeof(fi));
+	fi.fh = arg->fh;
+	fi.writepage = (arg->write_flags & FUSE_WRITE_CACHE) != 0;
+
+	if (req->se->conn.proto_minor < 9) {
+		param = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE;
+	} else {
+		fi.lock_owner = arg->lock_owner;
+		fi.flags = arg->flags;
+	}
+
+	if (req->se->op.write)
+		req->se->op.write(req, nodeid, param, arg->size,
+			arg->offset, &fi);
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
 static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg,
 			 const struct fuse_buf *ibuf)
 {
@@ -1397,8 +1513,10 @@ static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg,
 	} else {
 		fi.lock_owner = arg->lock_owner;
 		fi.flags = arg->flags;
-		if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
-			bufv.buf[0].mem = PARAM(arg);
+		if (!se->use_fastpath) {
+			if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
+				bufv.buf[0].mem = PARAM(arg);
+		}
 
 		bufv.buf[0].size -= sizeof(struct fuse_in_header) +
 			sizeof(struct fuse_write_in);
@@ -1785,6 +1903,36 @@ static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_err(req, ENOSYS);
 }
 
+static void do_ioctl_fast(fuse_req_t req, fuse_ino_t nodeid, const void *inarg,
+						const struct fuse_buf *ibuf)
+{
+	struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *) inarg;
+	unsigned int flags = arg->flags;
+	void *in_buf = arg->in_size ? ibuf->mem : NULL;
+	struct fuse_file_info fi;
+
+	if (flags & FUSE_IOCTL_DIR &&
+	    !(req->se->conn.want & FUSE_CAP_IOCTL_DIR)) {
+		fuse_reply_err(req, ENOTTY);
+		return;
+	}
+
+	memset(&fi, 0, sizeof(fi));
+	fi.fh = arg->fh;
+
+	if (sizeof(void *) == 4 && req->se->conn.proto_minor >= 16 &&
+	    !(flags & FUSE_IOCTL_32BIT)) {
+		req->ioctl_64bit = 1;
+	}
+
+	if (req->se->op.ioctl)
+		req->se->op.ioctl(req, nodeid, arg->cmd,
+				 (void *)(uintptr_t)arg->arg, &fi, flags,
+				 in_buf, arg->in_size, arg->out_size);
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
 static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 {
 	struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *) inarg;
@@ -1941,8 +2089,14 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 	if (arg->minor >= 6) {
 		if (arg->max_readahead < se->conn.max_readahead)
 			se->conn.max_readahead = arg->max_readahead;
-		if (arg->flags & FUSE_ASYNC_READ)
-			se->conn.capable |= FUSE_CAP_ASYNC_READ;
+		if (!se->use_fastpath) {
+			if (arg->flags & FUSE_ASYNC_READ)
+				se->conn.capable |= FUSE_CAP_ASYNC_READ;
+			if (arg->flags & FUSE_ASYNC_DIO)
+				se->conn.capable |= FUSE_CAP_ASYNC_DIO;
+			if (arg->flags & FUSE_WRITEBACK_CACHE)
+				se->conn.capable |= FUSE_CAP_WRITEBACK_CACHE;
+		}
 		if (arg->flags & FUSE_POSIX_LOCKS)
 			se->conn.capable |= FUSE_CAP_POSIX_LOCKS;
 		if (arg->flags & FUSE_ATOMIC_O_TRUNC)
@@ -1959,10 +2113,6 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 			se->conn.capable |= FUSE_CAP_READDIRPLUS;
 		if (arg->flags & FUSE_READDIRPLUS_AUTO)
 			se->conn.capable |= FUSE_CAP_READDIRPLUS_AUTO;
-		if (arg->flags & FUSE_ASYNC_DIO)
-			se->conn.capable |= FUSE_CAP_ASYNC_DIO;
-		if (arg->flags & FUSE_WRITEBACK_CACHE)
-			se->conn.capable |= FUSE_CAP_WRITEBACK_CACHE;
 		if (arg->flags & FUSE_NO_OPEN_SUPPORT)
 			se->conn.capable |= FUSE_CAP_NO_OPEN_SUPPORT;
 		if (arg->flags & FUSE_PARALLEL_DIROPS)
@@ -1990,12 +2140,14 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 	}
 
 	if (se->conn.proto_minor >= 14) {
+		if (!se->use_fastpath) {
 #ifdef HAVE_SPLICE
 #ifdef HAVE_VMSPLICE
-		se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE;
+			se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE;
 #endif
-		se->conn.capable |= FUSE_CAP_SPLICE_READ;
+			se->conn.capable |= FUSE_CAP_SPLICE_READ;
 #endif
+		}
 	}
 	if (se->conn.proto_minor >= 18)
 		se->conn.capable |= FUSE_CAP_IOCTL_DIR;
@@ -2073,8 +2225,15 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 	   by the max_write option */
 	outarg.flags |= FUSE_BIG_WRITES;
 
-	if (se->conn.want & FUSE_CAP_ASYNC_READ)
-		outarg.flags |= FUSE_ASYNC_READ;
+	if (!se->use_fastpath) {
+		if (se->conn.want & FUSE_CAP_ASYNC_READ)
+			outarg.flags |= FUSE_ASYNC_READ;
+		if (se->conn.want & FUSE_CAP_ASYNC_DIO)
+			outarg.flags |= FUSE_ASYNC_DIO;
+		if (se->conn.want & FUSE_CAP_WRITEBACK_CACHE)
+			outarg.flags |= FUSE_WRITEBACK_CACHE;
+	}
+
 	if (se->conn.want & FUSE_CAP_POSIX_LOCKS)
 		outarg.flags |= FUSE_POSIX_LOCKS;
 	if (se->conn.want & FUSE_CAP_ATOMIC_O_TRUNC)
@@ -2091,10 +2250,6 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		outarg.flags |= FUSE_DO_READDIRPLUS;
 	if (se->conn.want & FUSE_CAP_READDIRPLUS_AUTO)
 		outarg.flags |= FUSE_READDIRPLUS_AUTO;
-	if (se->conn.want & FUSE_CAP_ASYNC_DIO)
-		outarg.flags |= FUSE_ASYNC_DIO;
-	if (se->conn.want & FUSE_CAP_WRITEBACK_CACHE)
-		outarg.flags |= FUSE_WRITEBACK_CACHE;
 	if (se->conn.want & FUSE_CAP_POSIX_ACL)
 		outarg.flags |= FUSE_POSIX_ACL;
 	if (se->conn.want & FUSE_CAP_CACHE_SYMLINKS)
@@ -2207,6 +2362,9 @@ static int send_notify_iov(struct fuse_session *se, int notify_code,
 	if (!se->got_init)
 		return -ENOTCONN;
 
+	if (se->use_fastpath)
+		return -ENOSYS;
+
 	out.unique = 0;
 	out.error = notify_code;
 	iov[0].iov_base = &out;
@@ -2266,6 +2424,9 @@ int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
 	if (se->conn.proto_minor < 12)
 		return -ENOSYS;
 
+	if (se->use_fastpath)
+		return -ENOSYS;
+
 	outarg.parent = parent;
 	outarg.namelen = namelen;
 	outarg.padding = 0;
@@ -2291,6 +2452,9 @@ int fuse_lowlevel_notify_delete(struct fuse_session *se,
 	if (se->conn.proto_minor < 18)
 		return -ENOSYS;
 
+	if (se->use_fastpath)
+		return -ENOSYS;
+
 	outarg.parent = parent;
 	outarg.child = child;
 	outarg.namelen = namelen;
@@ -2320,6 +2484,9 @@ int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino,
 	if (se->conn.proto_minor < 15)
 		return -ENOSYS;
 
+	if (se->use_fastpath)
+		return -ENOSYS;
+
 	out.unique = 0;
 	out.error = FUSE_NOTIFY_STORE;
 
@@ -2398,6 +2565,9 @@ int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino,
 	if (se->conn.proto_minor < 15)
 		return -ENOSYS;
 
+	if (se->use_fastpath)
+		return -ENOSYS;
+
 	rreq = malloc(sizeof(*rreq));
 	if (rreq == NULL)
 		return -ENOMEM;
@@ -2685,6 +2855,116 @@ clear_pipe:
 	goto out_free;
 }
 
+void fuse_session_process_buf_int_fast(struct fuse_session *se,
+	const struct fuse_buf *buf, struct fuse_chan *ch)
+{
+	struct fuse_in_header *in;
+	const void *inarg;
+	struct fuse_req *req;
+	void *mbuf = NULL;
+	int err;
+	struct fuse_ipc_data *ipc_data;
+
+	if (se->got_init){
+		ipc_data = fuse_ll_get_ipc_data(se);
+		in = ipc_data->data_page;}
+	else {
+		in = buf->mem;
+	}
+
+	if (se->debug) {
+		fuse_log(FUSE_LOG_DEBUG,
+			"unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n",
+			(unsigned long long) in->unique,
+			opname((enum fuse_opcode) in->opcode), in->opcode,
+			(unsigned long long) in->nodeid, buf->size, in->pid);
+	}
+
+	req = fuse_ll_alloc_req(se);
+	if (req == NULL) {
+		struct fuse_out_header out = {
+			.unique = in->unique,
+			.error = -ENOMEM,
+		};
+		struct iovec iov = {
+			.iov_base = &out,
+			.iov_len = sizeof(struct fuse_out_header),
+		};
+
+		fuse_send_msg(se, ch, &iov, 1);
+		goto clear_pipe;
+	}
+
+	req->unique = in->unique;
+	req->ctx.uid = in->uid;
+	req->ctx.gid = in->gid;
+	req->ctx.pid = in->pid;
+	req->ch = ch ? fuse_chan_get(ch) : NULL;
+
+	err = EIO;
+	if (!se->got_init) {
+		enum fuse_opcode expected;
+
+		expected = se->cuse_data ? CUSE_INIT : FUSE_INIT;
+		if (in->opcode != expected)
+			goto reply_err;
+	} else if (in->opcode == FUSE_INIT || in->opcode == CUSE_INIT)
+		goto reply_err;
+
+	err = EACCES;
+	/* Implement -o allow_root */
+	if (se->deny_others && in->uid != se->owner && in->uid != 0 &&
+		in->opcode != FUSE_INIT && in->opcode != FUSE_READ &&
+		in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC &&
+		in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR &&
+		in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR &&
+		in->opcode != FUSE_NOTIFY_REPLY &&
+		in->opcode != FUSE_READDIRPLUS)
+		goto reply_err;
+
+	err = ENOSYS;
+	if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func)
+		goto reply_err;
+	/* Do not process interrupt request */
+	if (se->no_interrupt && in->opcode == FUSE_INTERRUPT) {
+		if (se->debug)
+			fuse_log(FUSE_LOG_DEBUG, "FUSE_INTERRUPT: reply to kernel to disable interrupt\n");
+		goto reply_err;
+	}
+	if (!se->no_interrupt && in->opcode != FUSE_INTERRUPT) {
+		struct fuse_req *intr;
+		pthread_mutex_lock(&se->lock);
+		intr = check_interrupt(se, req);
+		list_add_req(req, &se->list);
+		pthread_mutex_unlock(&se->lock);
+		if (intr)
+			fuse_reply_err(intr, EAGAIN);
+	}
+
+	inarg = (void *) &in[1];
+	if (in->opcode == FUSE_WRITE && se->op.write_buf)
+		do_write_buf(req, in->nodeid, inarg, buf);
+	else if (in->opcode == FUSE_NOTIFY_REPLY)
+		do_notify_reply(req, in->nodeid, inarg, buf);
+	else if (in->opcode == FUSE_IOCTL)
+		do_ioctl_fast(req, in->nodeid, inarg, buf);
+	else if (in->opcode == FUSE_WRITE && !se->op.write_buf)
+		do_write_fast(req, in->nodeid, inarg, buf);
+	else
+		fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg);
+
+out_free:
+	free(mbuf);
+	return;
+
+reply_err:
+	fuse_reply_err(req, err);
+clear_pipe:
+	if (buf->flags & FUSE_BUF_IS_FD)
+		fuse_ll_clear_pipe(se);
+	goto out_free;
+}
+
 #define LL_OPTION(n,o,v) \
 	{ n, offsetof(struct fuse_session, o), v }
 
@@ -2693,6 +2973,16 @@ static const struct fuse_opt fuse_ll_opts[] = {
 	LL_OPTION("-d", debug, 1),
 	LL_OPTION("--debug", debug, 1),
 	LL_OPTION("allow_root", deny_others, 1),
+#ifdef __aarch64__
+	LL_OPTION("no_interrupt", no_interrupt, 1),
+	LL_OPTION("no_forget", no_forget, 1),
+	/* The option use_fastpath is only supported on arm64,
+	 * because this feature is only enabled on arm64 kernel.
+	 * The main performance improvements come from increased parallelism,
+	 * which is more valuable for arm64 with more cores.
+	 */
+	LL_OPTION("use_fastpath", use_fastpath, 1),
+#endif
 	FUSE_OPT_END
 };
 
@@ -2710,7 +3000,13 @@ void fuse_lowlevel_help(void)
 	printf(
 "    -o allow_other         allow access by all users\n"
 "    -o allow_root          allow access by root\n"
+#ifdef __aarch64__
+"    -o auto_unmount        auto unmount on process termination\n"
+"    -o no_interrupt	    do not process interrupt request\n"
+"    -o no_forget	    do not process forget request\n");
+#else
 "    -o auto_unmount        auto unmount on process termination\n");
+#endif
 }
 
 void fuse_session_destroy(struct fuse_session *se)
@@ -2733,6 +3029,11 @@ void fuse_session_destroy(struct fuse_session *se)
 	free(se);
 }
 
+static void fuse_ll_ipc_data_destructor(void *data)
+{
+	struct fuse_ipc_data *ipc_data = data;
+	free(ipc_data);
+}
 
 static void fuse_ll_pipe_destructor(void *data)
 {
@@ -2939,6 +3240,17 @@ struct fuse_session *fuse_session_new(struct fuse_args *args,
 		if(fuse_opt_add_arg(args, "-oallow_other") == -1)
 			goto out2;
 	}
+
+	if (se->use_fastpath) {
+		if (fuse_opt_add_arg(args, "-ouse_fastpath") == -1)
+			goto out2;
+	}
+
+	if (se->no_forget) {
+		if (fuse_opt_add_arg(args, "-ono_forget") == -1)
+			goto out2;
+	}
+
 	mo = parse_mount_opts(args);
 	if (mo == NULL)
 		goto out3;
@@ -2968,6 +3280,15 @@ struct fuse_session *fuse_session_new(struct fuse_args *args,
 	se->notify_ctr = 1;
 	pthread_mutex_init(&se->lock, NULL);
 
+	if (se->use_fastpath) {
+		err = pthread_key_create(&se->ipc_data_key, fuse_ll_ipc_data_destructor);
+		if (err) {
+			fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: %s\n",
+				strerror(err));
+			goto out5;
+		}
+	}
+
 	err = pthread_key_create(&se->pipe_key, fuse_ll_pipe_destructor);
 	if (err) {
 		fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: %s\n",
diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
index a06f768..9deef71 100644
--- a/lib/fuse_versionscript
+++ b/lib/fuse_versionscript
@@ -17,6 +17,7 @@ FUSE_3.0 {
 		fuse_session_exit;
 		fuse_session_exited;
 		fuse_session_loop;
+		fuse_session_loop_ipc;
 		fuse_session_loop_mt;
 		fuse_session_reset;
 		fuse_session_fd;
diff --git a/lib/meson.build b/lib/meson.build
index 98461d8..89cebbf 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -2,7 +2,7 @@ libfuse_sources = ['fuse.c', 'fuse_i.h', 'fuse_loop.c', 'fuse_loop_mt.c',
                    'fuse_lowlevel.c', 'fuse_misc.h', 'fuse_opt.c',
                    'fuse_signals.c', 'buffer.c', 'cuse_lowlevel.c',
                    'helper.c', 'modules/subdir.c', 'mount_util.c',
-                   'fuse_log.c' ]
+                   'fuse_log.c', 'fuse_ipc.c']
 
 if host_machine.system().startswith('linux')
    libfuse_sources += [ 'mount.c' ]
diff --git a/lib/mount.c b/lib/mount.c
index 979f8d9..91b531e 100644
--- a/lib/mount.c
+++ b/lib/mount.c
@@ -111,6 +111,10 @@ static const struct fuse_opt fuse_mount_opts[] = {
 	FUSE_OPT_KEY("dirsync",			KEY_KERN_FLAG),
 	FUSE_OPT_KEY("atime",			KEY_KERN_FLAG),
 	FUSE_OPT_KEY("noatime",			KEY_KERN_FLAG),
+#ifdef __aarch64__
+	FUSE_OPT_KEY("no_forget",		KEY_KERN_OPT),
+	FUSE_OPT_KEY("use_fastpath",		KEY_KERN_OPT),
+#endif
 	FUSE_OPT_END
 };
 
-- 
2.45.1.windows.1

