From 8bb62a632caa4269bb6436cae67307404882b936 Mon Sep 17 00:00:00 2001
From: Miklos Szeredi <mszeredi@suse.cz>
Date: Wed, 29 Jan 2014 14:13:36 +0100
Subject: [PATCH] libfuse: Add "async_dio" and "writeback_cache" options

Asynchronous direct I/O is supported by linux kernels 3.13 and
later, writeback caching is supported by 3.14 and later.
---
 ChangeLog             |  6 ++++++
 include/fuse_common.h |  7 +++++++
 include/fuse_kernel.h | 12 +++++++++++-
 lib/fuse_i.h          |  4 ++++
 lib/fuse_lowlevel.c   | 33 ++++++++++++++++++++++++++++-----
 5 files changed, 56 insertions(+), 6 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 753aaab..f327084 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2014-01-29  Miklos Szeredi <miklos@szeredi.hu>
+
+	* libfuse: Add "async_dio" and "writeback_cache" options.
+	Asynchronous direct I/O is supported by linux kernels 3.13 and
+	later, writeback caching is supported by 3.14 and later.
+
 2013-08-26  Miklos Szeredi <miklos@szeredi.hu>
 
 	* libfuse: Add missing includes.  This allows compiling fuse with
diff --git a/include/fuse_common.h b/include/fuse_common.h
index 765e0a3..22d9591 100644
--- a/include/fuse_common.h
+++ b/include/fuse_common.h
@@ -96,6 +96,11 @@ struct fuse_file_info {
  * FUSE_CAP_SPLICE_MOVE: ability to move data to the fuse device with splice()
  * FUSE_CAP_SPLICE_READ: ability to use splice() to read from the fuse device
  * FUSE_CAP_IOCTL_DIR: ioctl support on directories
+ * FUSE_CAP_AUTO_INVAL_DATA: automatically invalidate cached pages
+ * FUSE_CAP_DO_READDIRPLUS: do READDIRPLUS (READDIR+LOOKUP in one)
+ * FUSE_CAP_READDIRPLUS_AUTO: adaptive readdirplus
+ * FUSE_CAP_ASYNC_DIO: asynchronous direct I/O submission
+ * FUSE_CAP_WRITEBACK_CACHE: use writeback cache for buffered writes
  */
 #define FUSE_CAP_ASYNC_READ		(1 << 0)
 #define FUSE_CAP_POSIX_LOCKS		(1 << 1)
@@ -111,6 +116,8 @@ struct fuse_file_info {
 #define FUSE_CAP_AUTO_INVAL_DATA 	(1 << 12)
 #define FUSE_CAP_READDIRPLUS		(1 << 13)
 #define FUSE_CAP_READDIRPLUS_AUTO	(1 << 14)
+#define FUSE_CAP_ASYNC_DIO		(1 << 15)
+#define FUSE_CAP_WRITEBACK_CACHE	(1 << 16)
 
 /**
  * Ioctl flags
diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h
index 706d035..7974721 100644
--- a/include/fuse_kernel.h
+++ b/include/fuse_kernel.h
@@ -90,6 +90,12 @@
  * 7.21
  *  - add FUSE_READDIRPLUS
  *  - send the requested events in POLL request
+ *
+ * 7.22
+ *  - add FUSE_ASYNC_DIO
+ *
+ * 7.23
+ *  - add FUSE_WRITEBACK_CACHE
  */
 
 #ifndef _LINUX_FUSE_H
@@ -125,7 +131,7 @@
 #define FUSE_KERNEL_VERSION 7
 
 /** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 21
+#define FUSE_KERNEL_MINOR_VERSION 22
 
 /** The node ID of the root inode */
 #define FUSE_ROOT_ID 1
@@ -215,6 +221,8 @@ struct fuse_file_lock {
  * FUSE_AUTO_INVAL_DATA: automatically invalidate cached pages
  * FUSE_DO_READDIRPLUS: do READDIRPLUS (READDIR+LOOKUP in one)
  * FUSE_READDIRPLUS_AUTO: adaptive readdirplus
+ * FUSE_ASYNC_DIO: asynchronous direct I/O submission
+ * FUSE_WRITEBACK_CACHE: use writeback cache for buffered writes
  */
 #define FUSE_ASYNC_READ		(1 << 0)
 #define FUSE_POSIX_LOCKS	(1 << 1)
@@ -231,6 +239,8 @@ struct fuse_file_lock {
 #define FUSE_AUTO_INVAL_DATA	(1 << 12)
 #define FUSE_DO_READDIRPLUS	(1 << 13)
 #define FUSE_READDIRPLUS_AUTO	(1 << 14)
+#define FUSE_ASYNC_DIO		(1 << 15)
+#define FUSE_WRITEBACK_CACHE	(1 << 16)
 
 /**
  * CUSE INIT request/reply flags
diff --git a/lib/fuse_i.h b/lib/fuse_i.h
index 5823743..30fe415 100644
--- a/lib/fuse_i.h
+++ b/lib/fuse_i.h
@@ -74,6 +74,10 @@ struct fuse_ll {
 	int no_auto_inval_data;
 	int no_readdirplus;
 	int no_readdirplus_auto;
+	int async_dio;
+	int no_async_dio;
+	int writeback_cache;
+	int no_writeback_cache;
 	struct fuse_lowlevel_ops op;
 	int got_init;
 	struct cuse_data *cuse_data;
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index 1e9b92c..19feb14 100755
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -1901,6 +1901,10 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 			f->conn.capable |= FUSE_CAP_READDIRPLUS;
 		if (arg->flags & FUSE_READDIRPLUS_AUTO)
 			f->conn.capable |= FUSE_CAP_READDIRPLUS_AUTO;
+		if (arg->flags & FUSE_ASYNC_DIO)
+			f->conn.capable |= FUSE_CAP_ASYNC_DIO;
+		if (arg->flags & FUSE_WRITEBACK_CACHE)
+			f->conn.capable |= FUSE_CAP_WRITEBACK_CACHE;
 	} else {
 		f->conn.async_read = 0;
 		f->conn.max_readahead = 0;
@@ -1938,6 +1942,10 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		if (!f->no_readdirplus_auto)
 			f->conn.want |= FUSE_CAP_READDIRPLUS_AUTO;
 	}
+	if (f->async_dio)
+		f->conn.want |= FUSE_CAP_ASYNC_DIO;
+	if (f->writeback_cache)
+		f->conn.want |= FUSE_CAP_WRITEBACK_CACHE;
 
 	if (bufsize < FUSE_MIN_READ_BUFFER) {
 		fprintf(stderr, "fuse: warning: buffer size too small: %zu\n",
@@ -1965,6 +1973,11 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		f->conn.want &= ~FUSE_CAP_READDIRPLUS;
 	if (f->no_readdirplus_auto)
 		f->conn.want &= ~FUSE_CAP_READDIRPLUS_AUTO;
+	if (f->no_async_dio)
+		f->conn.want &= ~FUSE_CAP_ASYNC_DIO;
+	if (f->no_writeback_cache)
+		f->conn.want &= ~FUSE_CAP_WRITEBACK_CACHE;
+
 	if (f->conn.async_read || (f->conn.want & FUSE_CAP_ASYNC_READ))
 		outarg.flags |= FUSE_ASYNC_READ;
 	if (f->conn.want & FUSE_CAP_POSIX_LOCKS)
@@ -1985,6 +1998,10 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		outarg.flags |= FUSE_DO_READDIRPLUS;
 	if (f->conn.want & FUSE_CAP_READDIRPLUS_AUTO)
 		outarg.flags |= FUSE_READDIRPLUS_AUTO;
+	if (f->conn.want & FUSE_CAP_ASYNC_DIO)
+		outarg.flags |= FUSE_ASYNC_DIO;
+	if (f->conn.want & FUSE_CAP_WRITEBACK_CACHE)
+		outarg.flags |= FUSE_WRITEBACK_CACHE;
 	outarg.max_readahead = f->conn.max_readahead;
 	outarg.max_write = f->conn.max_write;
 	if (f->conn.proto_minor >= 13) {
@@ -2604,6 +2621,10 @@ static const struct fuse_opt fuse_ll_opts[] = {
 	{ "readdirplus=yes", offsetof(struct fuse_ll, no_readdirplus_auto), 1},
 	{ "readdirplus=auto", offsetof(struct fuse_ll, no_readdirplus), 0},
 	{ "readdirplus=auto", offsetof(struct fuse_ll, no_readdirplus_auto), 0},
+	{ "async_dio", offsetof(struct fuse_ll, async_dio), 1},
+	{ "no_async_dio", offsetof(struct fuse_ll, no_async_dio), 1},
+	{ "writeback_cache", offsetof(struct fuse_ll, writeback_cache), 1},
+	{ "no_writeback_cache", offsetof(struct fuse_ll, no_writeback_cache), 1},
 	FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_DISCARD),
 	FUSE_OPT_KEY("-h", KEY_HELP),
 	FUSE_OPT_KEY("--help", KEY_HELP),
@@ -2631,12 +2652,14 @@ static void fuse_ll_help(void)
 "    -o big_writes          enable larger than 4kB writes\n"
 "    -o no_remote_lock      disable remote file locking\n"
 "    -o no_remote_flock     disable remote file locking (BSD)\n"
-"    -o no_remote_posix_lock disable remove file locking (POSIX)\n"
-"    -o [no_]splice_write   use splice to write to the fuse device\n"
-"    -o [no_]splice_move    move data while splicing to the fuse device\n"
-"    -o [no_]splice_read    use splice to read from the fuse device\n"
+"    -o no_remote_posix_lock  disable remove file locking (POSIX)\n"
+"    -o [no_]splice_write     use splice to write to the fuse device\n"
+"    -o [no_]splice_move      move data while splicing to the fuse device\n"
+"    -o [no_]splice_read      use splice to read from the fuse device\n"
 "    -o [no_]auto_inval_data  use automatic kernel cache invalidation logic\n"
-"    -o readdirplus=S       control readdirplus use (yes|no|auto)\n"
+"    -o readdirplus=S         control readdirplus use (yes|no|auto)\n"
+"    -o [no_]async_dio        asynchronous direct I/O\n"
+"    -o [no_]writeback_cache  asynchronous, buffered writes\n"
 );
 }
 
-- 
2.30.2