[Devel] [PATCH RHEL7 COMMIT] fuse: add kdirect io engine v0.1

Konstantin Khorenko khorenko at virtuozzo.com
Mon Feb 19 14:16:28 MSK 2018


The commit is pushed to "branch-rh7-3.10.0-693.17.1.vz7.45.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-693.17.1.vz7.43.7
------>
commit 1d7f6e26f02fd89b1e82cf8fab77461db11c9d33
Author: Dmitry Monakhov <dmonakhov at openvz.org>
Date:   Mon Feb 19 14:16:28 2018 +0300

    fuse: add kdirect io engine v0.1
    
    Add fast path API for fuse.
    Idea is simple: Divide IO-path from (signaling/metadata)-path.
    
    metadata operations always require strong consistency and it is hard to make it
    fast, but sustaned IO may( and should) be superfast and scalable.
    In fact it can be done quite easily by adding hook to request_send which may
    interpect requests right before submission to userspace thread, and forward
    it to fast-path is possible.
    
    patch status:
    - [X] Draft skeleton
    - [ ] More intitive patch split
        - move fuse functions to helpers first
        - pure API patch
    - [ ] io_inode seems is redundant. I need to initialize req->inode for each request
    
    https://jira.sw.ru/browse/PSBM-80680
    Signed-off-by: Dmitry Monakhov <dmonakhov at openvz.org>
---
 fs/fuse/dev.c    | 51 +++++++++++++++++++++++++----
 fs/fuse/dir.c    | 17 +++++++---
 fs/fuse/file.c   | 22 +++++++++----
 fs/fuse/fuse_i.h | 62 ++++++++++++++++++++++++++++++++++-
 fs/fuse/inode.c  | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++----
 5 files changed, 226 insertions(+), 24 deletions(-)

diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index a4ea60370586..a958783ee113 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -55,10 +55,12 @@ static void fuse_request_init(struct fuse_conn *fc,
 	__set_bit(FR_PENDING, &req->flags);
 }
 
-static struct fuse_req *__fuse_request_alloc(struct fuse_conn *fc,
+struct fuse_req *fuse_generic_request_alloc(struct fuse_conn *fc,
+					    struct kmem_cache *cachep,
 					     unsigned npages, gfp_t flags)
 {
-	struct fuse_req *req = kmem_cache_alloc(fuse_req_cachep, flags);
+	struct fuse_req *req = kmem_cache_alloc(cachep, flags);
+
 	if (req) {
 		struct page **pages;
 		struct fuse_page_desc *page_descs;
@@ -80,9 +82,21 @@ static struct fuse_req *__fuse_request_alloc(struct fuse_conn *fc,
 		}
 
 		fuse_request_init(fc, req, pages, page_descs, npages);
+		req->cache = cachep;
 	}
 	return req;
 }
+EXPORT_SYMBOL_GPL(fuse_generic_request_alloc);
+
+static struct fuse_req *__fuse_request_alloc(struct fuse_conn *fc,
+					     unsigned npages, gfp_t flags)
+{
+
+	if (fc->kio.op && fc->kio.op->req_alloc)
+		return fc->kio.op->req_alloc(fc, npages, flags);
+
+	return fuse_generic_request_alloc(fc, fuse_req_cachep, npages, flags);
+}
 
 struct fuse_req *fuse_request_alloc(struct fuse_conn *fc, unsigned npages)
 {
@@ -95,13 +109,21 @@ struct fuse_req *fuse_request_alloc_nofs(struct fuse_conn *fc, unsigned npages)
 	return __fuse_request_alloc(fc, npages, GFP_NOFS);
 }
 
-void fuse_request_free(struct fuse_req *req)
+void fuse_generic_request_free(struct fuse_req *req)
 {
 	if (req->pages != req->inline_pages) {
 		kfree(req->pages);
 		kfree(req->page_descs);
 	}
-	kmem_cache_free(fuse_req_cachep, req);
+	kmem_cache_free(req->cache, req);
+}
+
+void fuse_request_free(struct fuse_conn *fc, struct fuse_req *req)
+{
+	if (fc->kio.op && fc->kio.op->req_free)
+		return fc->kio.op->req_free(fc, req);
+
+	return fuse_generic_request_free(req);
 }
 
 void __fuse_get_request(struct fuse_req *req)
@@ -129,6 +151,7 @@ void fuse_set_initialized(struct fuse_conn *fc)
 	smp_wmb();
 	fc->initialized = 1;
 }
+EXPORT_SYMBOL_GPL(fuse_set_initialized);
 
 static bool fuse_block_alloc(struct fuse_conn *fc, bool for_background)
 {
@@ -288,7 +311,7 @@ void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
 		if (req->stolen_file)
 			put_reserved_req(fc, req);
 		else
-			fuse_request_free(req);
+			fuse_request_free(fc, req);
 	}
 }
 EXPORT_SYMBOL_GPL(fuse_put_request);
@@ -362,7 +385,7 @@ static void flush_bg_queue(struct fuse_conn *fc, struct fuse_iqueue *fiq)
  * the 'end' callback is called if given, else the reference to the
  * request is released
  */
-static void request_end(struct fuse_conn *fc, struct fuse_req *req)
+void request_end(struct fuse_conn *fc, struct fuse_req *req)
 {
 	struct fuse_iqueue *fiq = req->fiq;
 
@@ -399,6 +422,7 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req)
 	wake_up(&req->waitq);
 	fuse_put_request(fc, req);
 }
+EXPORT_SYMBOL_GPL(request_end);
 
 static void queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req)
 {
@@ -467,6 +491,10 @@ static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req,
 	struct fuse_iqueue *fiq = req->fiq;
 
 	BUG_ON(test_bit(FR_BACKGROUND, &req->flags));
+
+	if (fc->kio.op && !fc->kio.op->req_send(fc, req, false, false))
+		return;
+
 	spin_lock(&fiq->waitq.lock);
 	if (!fiq->connected) {
 		spin_unlock(&fiq->waitq.lock);
@@ -516,6 +544,10 @@ void fuse_request_send_background_locked(struct fuse_conn *fc,
 	struct fuse_iqueue *fiq = req->fiq;
 
 	BUG_ON(!test_bit(FR_BACKGROUND, &req->flags));
+
+	if (fc->kio.op && !fc->kio.op->req_send(fc, req, true, true))
+		return;
+
 	if (!test_bit(FR_WAITING, &req->flags)) {
 		__set_bit(FR_WAITING, &req->flags);
 		atomic_inc(&fc->num_waiting);
@@ -536,6 +568,10 @@ void fuse_request_send_background_locked(struct fuse_conn *fc,
 void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req)
 {
 	BUG_ON(!req->end);
+
+	if (fc->kio.op && !fc->kio.op->req_send(fc, req, true, false))
+		return;
+
 	spin_lock(&fc->lock);
 	if (req->page_cache && req->ff &&
 	    test_bit(FUSE_S_FAIL_IMMEDIATELY, &req->ff->ff_state)) {
@@ -2174,6 +2210,9 @@ void fuse_abort_conn(struct fuse_conn *fc)
 			list_splice_init(&fpq->processing, &to_end2);
 			spin_unlock(&fpq->lock);
 		}
+		if (fc->kio.op)
+			fc->kio.op->conn_abort(fc);
+
 		fc->max_background = UINT_MAX;
 		for_each_online_cpu(cpu)
 			flush_bg_queue(fc, per_cpu_ptr(fc->iqs, cpu));
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index effe085c00d0..d4427c56012b 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -495,11 +495,19 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
 			atomic_dec(&fi->num_openers);
 		}
 		fuse_sync_release(ff, flags);
-	} else {
-		file->private_data = fuse_file_get(ff);
-		fuse_finish_open(inode, file);
+		return err;
 	}
-	return err;
+
+	file->private_data = fuse_file_get(ff);
+	fuse_finish_open(inode, file);
+	if (fc->kio.op && fc->kio.op->file_open &&
+	    fc->kio.op->file_open(fc, file, inode)) {
+		if (err) {
+			fput(file);
+			return  err;
+		}
+	}
+	return 0;
 
 out_free_ff:
 	fuse_file_free(ff);
@@ -1663,6 +1671,7 @@ static void fuse_setattr_fill(struct fuse_conn *fc, struct fuse_req *req,
 			      struct fuse_setattr_in *inarg_p,
 			      struct fuse_attr_out *outarg_p)
 {
+	req->io_inode = inode;
 	req->in.h.opcode = FUSE_SETATTR;
 	req->in.h.nodeid = get_node_id(inode);
 	req->in.numargs = 1;
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index fe7e9d00ae8e..7e22154e398c 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -111,7 +111,7 @@ static void fuse_file_list_del(struct fuse_file *ff)
 void fuse_file_free(struct fuse_file *ff)
 {
 	fuse_file_list_del(ff);
-	fuse_request_free(ff->reserved_req);
+	fuse_request_free(ff->fc, ff->reserved_req);
 	kfree(ff);
 }
 
@@ -290,6 +290,11 @@ int fuse_open_common(struct inode *inode, struct file *file, bool isdir)
 	err = fuse_do_open(fc, get_node_id(inode), file, isdir);
 	if (err)
 		return err;
+	if (fc->kio.op && fc->kio.op->file_open &&
+	    fc->kio.op->file_open(fc, file, inode)) {
+		fuse_release_common(file, FUSE_RELEASE);
+		return -EINVAL;
+	}
 
 	if (fc->writeback_cache && !isdir) {
 		struct fuse_inode *fi = get_fuse_inode(inode);
@@ -743,6 +748,7 @@ int fuse_fsync_common(struct file *file, loff_t start, loff_t end,
 	inarg.fh = ff->fh;
 	inarg.fsync_flags = datasync ? 1 : 0;
 	req->in.h.opcode = isdir ? FUSE_FSYNCDIR : FUSE_FSYNC;
+	req->io_inode = inode;
 	req->in.h.nodeid = get_node_id(inode);
 	req->in.numargs = 1;
 	req->in.args[0].size = sizeof(inarg);
@@ -787,6 +793,7 @@ void fuse_read_fill(struct fuse_req *req, struct file *file, loff_t pos,
 	req->out.argvar = 1;
 	req->out.numargs = 1;
 	req->out.args[0].size = count;
+	req->io_inode = file_inode(file);
 
 	if (opcode == FUSE_READ) {
 		struct fuse_iqueue *fiq = __this_cpu_ptr(ff->fc->iqs);
@@ -1308,7 +1315,7 @@ static ssize_t fuse_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
 }
 
 static void fuse_write_fill(struct fuse_req *req, struct fuse_file *ff,
-			    loff_t pos, size_t count)
+			    struct inode *inode, loff_t pos, size_t count)
 {
 	struct fuse_write_in *inarg = &req->misc.write.in;
 	struct fuse_write_out *outarg = &req->misc.write.out;
@@ -1328,6 +1335,7 @@ static void fuse_write_fill(struct fuse_req *req, struct fuse_file *ff,
 	req->out.numargs = 1;
 	req->out.args[0].size = sizeof(struct fuse_write_out);
 	req->out.args[0].value = outarg;
+	req->io_inode = inode;
 }
 
 static size_t fuse_send_unmap(struct fuse_req *req, struct fuse_io_priv *io,
@@ -1367,7 +1375,7 @@ static size_t fuse_send_write(struct fuse_req *req, struct fuse_io_priv *io,
 	struct fuse_conn *fc = ff->fc;
 	struct fuse_write_in *inarg = &req->misc.write.in;
 
-	fuse_write_fill(req, ff, pos, count);
+	fuse_write_fill(req, ff, file_inode(file), pos, count);
 	fuse_account_request(fc, count);
 	inarg->flags = file->f_flags;
 	if (owner != NULL) {
@@ -2092,7 +2100,7 @@ static int fuse_writepage_locked(struct page *page,
 		goto err_nofile;
 	if (ff_pp)
 		*ff_pp = fuse_file_get(req->ff);
-	fuse_write_fill(req, req->ff, page_offset(page), 0);
+	fuse_write_fill(req, req->ff, inode, page_offset(page), 0);
 	fuse_account_request(fc, PAGE_CACHE_SIZE);
 
 	copy_highpage(tmp_page, page);
@@ -2122,7 +2130,7 @@ static int fuse_writepage_locked(struct page *page,
 	printk("FUSE: page dirtied on dead file\n");
 	__free_page(tmp_page);
 err_free:
-	fuse_request_free(req);
+	fuse_request_free(fc, req);
 err:
 	end_page_writeback(page);
 	return -ENOMEM;
@@ -2229,7 +2237,7 @@ static int fuse_send_writepages(struct fuse_fill_data *data)
 	}
 
 	req->ff = fuse_file_get(ff);
-	fuse_write_fill(req, ff, off, 0);
+	fuse_write_fill(req, ff, inode, off, 0);
 	fuse_account_request(fc, req->num_pages << PAGE_CACHE_SHIFT);
 
 	req->misc.write.in.write_flags |= FUSE_WRITE_CACHE;
@@ -2801,6 +2809,7 @@ static sector_t fuse_bmap(struct address_space *mapping, sector_t block)
 	memset(&inarg, 0, sizeof(inarg));
 	inarg.block = block;
 	inarg.blocksize = inode->i_sb->s_blocksize;
+	req->io_inode = inode;
 	req->in.h.opcode = FUSE_BMAP;
 	req->in.h.nodeid = get_node_id(inode);
 	req->in.numargs = 1;
@@ -3677,6 +3686,7 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
 		goto out;
 	}
 
+	req->io_inode = inode;
 	req->in.h.opcode = FUSE_FALLOCATE;
 	req->in.h.nodeid = ff->nodeid;
 	req->in.numargs = 1;
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index b2edcf9a1f8e..a4cff705689c 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -35,6 +35,9 @@
 /** Number of dentries for each connection in the control filesystem */
 #define FUSE_CTL_NUM_DENTRIES 10
 
+/** FUSE kdirect engine name max */
+#define FUSE_KIO_NAME 32
+
 /** If the FUSE_DEFAULT_PERMISSIONS flag is given, the filesystem
     module will check permissions based on the file mode.  Otherwise no
     permission checking is done in the kernel */
@@ -53,6 +56,9 @@
 /* Disable synchronous close */
 #define FUSE_DISABLE_CLOSE_WAIT	(1 << 4)
 
+/* Enable custom kernel io engine */
+#define FUSE_KDIRECT_IO         (1 << 10)
+
 /** Number of page pointers embedded in fuse_req */
 #define FUSE_REQ_INLINE_PAGES 1
 
@@ -127,6 +133,9 @@ struct fuse_inode {
 
 	/** Even though num_openers>0, trust server i_size */
 	int i_size_unstable;
+
+	/** Private kdirect io context */
+	void *private;
 };
 
 /** FUSE inode state bits */
@@ -369,6 +378,11 @@ struct fuse_req {
 		} fallocate;
 		struct fuse_notify_retrieve_in retrieve_in;
 		struct fuse_lk_in lk_in;
+		struct {
+			struct fuse_ioctl_in  in;
+			struct fuse_ioctl_out out;
+			void *ctx;
+		} ioctl;
 	} misc;
 
 	/** page vector */
@@ -396,6 +410,8 @@ struct fuse_req {
 	/** File used in the request (or NULL) */
 	struct fuse_file *ff;
 
+	struct inode *io_inode;
+
 	/** Inode used in the request or NULL */
 	struct inode *inode;
 
@@ -413,6 +429,9 @@ struct fuse_req {
 
 	/** Request will be handled by fud pointing to this fiq */
 	struct fuse_iqueue *fiq;
+
+	/** kmem cache which this request was allocated from */
+	struct kmem_cache *cache;
 };
 
 struct fuse_iqueue {
@@ -476,6 +495,37 @@ struct fuse_dev {
 	struct list_head entry;
 };
 
+
+/**
+ * Fuse kdirect io operations
+ */
+struct fuse_kio_ops {
+	struct list_head	list;
+	char			*name;
+	struct module		*owner;
+
+	int (*probe)(struct fuse_conn *fc, char *name);
+
+	/* Connection scope hooks */
+	int (*conn_init)(struct fuse_conn *fc);
+	void (*conn_fini)(struct fuse_conn *fc);
+	void (*conn_abort)(struct fuse_conn *fc);
+
+	/* Request handling hooks */
+	struct fuse_req *(*req_alloc)(struct fuse_conn *fc, unsigned nrpages,
+				      gfp_t flags);
+	void (*req_free)(struct fuse_conn *fc, struct fuse_req *req);
+	int (*req_send)(struct fuse_conn *fc, struct fuse_req *req, bool bg,
+			bool locked);
+
+	/* Inode scope hooks */
+	int  (*file_open)(struct fuse_conn *fc, struct file *file,
+			  struct inode *inode);
+	void (*inode_release)(struct fuse_inode *fi);
+
+};
+int fuse_register_kio(struct fuse_kio_ops *ops);
+void fuse_unregister_kio(struct fuse_kio_ops *ops);
 /**
  * A Fuse connection.
  *
@@ -708,6 +758,12 @@ struct fuse_conn {
 
 	/** List of device instances belonging to this connection */
 	struct list_head devices;
+
+	/** Kdirect io operations */
+	struct {
+		struct fuse_kio_ops *op;
+		void *ctx;
+	} kio;
 };
 
 static inline struct fuse_conn *get_fuse_conn_super(struct super_block *sb)
@@ -848,7 +904,7 @@ struct fuse_req *fuse_request_alloc_nofs(struct fuse_conn *fc, unsigned npages);
 /**
  * Free a request
  */
-void fuse_request_free(struct fuse_req *req);
+void fuse_request_free(struct fuse_conn *fc, struct fuse_req *req);
 
 /**
  * Get a request, may fail with -ENOMEM,
@@ -1021,6 +1077,10 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
 		    struct file *file);
 
 void fuse_set_initialized(struct fuse_conn *fc);
+void request_end(struct fuse_conn *fc, struct fuse_req *req);
+struct fuse_req *fuse_generic_request_alloc(struct fuse_conn *fc,
+					    struct kmem_cache *cachep,
+					    unsigned npages, gfp_t flags);
 
 int fuse_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
 		__u64 start, __u64 len);
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 8abb68bb4684..f89bda719c71 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -27,6 +27,7 @@ MODULE_DESCRIPTION("Filesystem in Userspace");
 MODULE_LICENSE("GPL");
 
 static struct kmem_cache *fuse_inode_cachep;
+static LIST_HEAD(fuse_kios_list);
 struct list_head fuse_conn_list;
 DEFINE_MUTEX(fuse_mutex);
 
@@ -73,6 +74,7 @@ struct fuse_mount_data {
 	unsigned flags;
 	unsigned max_read;
 	unsigned blksize;
+	char kio_name[FUSE_KIO_NAME];
 };
 
 struct fuse_forget_link *fuse_alloc_forget(void)
@@ -98,6 +100,8 @@ static struct inode *fuse_alloc_inode(struct super_block *sb)
 	fi->orig_ino = 0;
 	fi->state = 0;
 	fi->i_size_unstable = 0;
+	fi->private = NULL;
+
 	INIT_LIST_HEAD(&fi->write_files);
 	INIT_LIST_HEAD(&fi->rw_files);
 	INIT_LIST_HEAD(&fi->queued_writes);
@@ -121,10 +125,16 @@ static void fuse_i_callback(struct rcu_head *head)
 static void fuse_destroy_inode(struct inode *inode)
 {
 	struct fuse_inode *fi = get_fuse_inode(inode);
+	struct fuse_conn *fc = get_fuse_conn(inode);
 	BUG_ON(!list_empty(&fi->write_files));
 	BUG_ON(!list_empty(&fi->rw_files));
 	BUG_ON(!list_empty(&fi->queued_writes));
 	kfree(fi->forget);
+
+	/* TODO: Probably kio context should be released inside fuse forget */
+	if (fc->kio.op && fc->kio.op->inode_release)
+		fc->kio.op->inode_release(fi);
+
 	call_rcu(&inode->i_rcu, fuse_i_callback);
 }
 
@@ -470,6 +480,52 @@ static void fuse_bdi_destroy(struct fuse_conn *fc)
 		bdi_destroy(&fc->bdi);
 }
 
+int fuse_register_kio(struct fuse_kio_ops *ops)
+{
+	mutex_lock(&fuse_mutex);
+	list_add(&ops->list, &fuse_kios_list);
+	mutex_unlock(&fuse_mutex);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fuse_register_kio);
+
+void fuse_unregister_kio(struct fuse_kio_ops *ops)
+{
+	mutex_lock(&fuse_mutex);
+	list_del(&ops->list);
+	mutex_unlock(&fuse_mutex);
+}
+EXPORT_SYMBOL_GPL(fuse_unregister_kio);
+
+static struct fuse_kio_ops *fuse_kio_get(struct fuse_conn *fc, char *name)
+{
+	struct fuse_kio_ops *ops;
+
+	mutex_lock(&fuse_mutex);
+	list_for_each_entry(ops, &fuse_kios_list, list) {
+		if (!strncmp(name, ops->name, FUSE_KIO_NAME) &&
+		    ops->probe(fc, name) && try_module_get(ops->owner)) {
+			__module_get(THIS_MODULE);
+			mutex_unlock(&fuse_mutex);
+			return ops;
+		}
+	}
+	mutex_unlock(&fuse_mutex);
+	return NULL;
+}
+
+static void fuse_kio_put(struct fuse_kio_ops *ops)
+{
+	module_put(ops->owner);
+	module_put(THIS_MODULE);
+}
+
+static void fuse_kdirect_put(struct fuse_conn *fc)
+{
+	if (fc->kio.op)
+		fuse_kio_put(fc->kio.op);
+}
+
 static void fuse_put_super(struct super_block *sb)
 {
 	struct fuse_conn *fc = get_fuse_conn_super(sb);
@@ -482,7 +538,7 @@ static void fuse_put_super(struct super_block *sb)
 	fuse_ctl_remove_conn(fc);
 	mutex_unlock(&fuse_mutex);
 	fuse_bdi_destroy(fc);
-
+	fuse_kdirect_put(fc);
 	fuse_conn_put(fc);
 }
 
@@ -546,7 +602,8 @@ enum {
 	OPT_ODIRECT,
 	OPT_UMOUNT_WAIT,
 	OPT_DISABLE_CLOSE_WAIT,
-	OPT_ERR
+	OPT_ERR,
+	OPT_KIO_NAME
 };
 
 static const match_table_t tokens = {
@@ -562,6 +619,7 @@ static const match_table_t tokens = {
 	{OPT_ODIRECT,			"direct_enable"},
 	{OPT_UMOUNT_WAIT,		"umount_wait"},
 	{OPT_DISABLE_CLOSE_WAIT,	"disable_close_wait"},
+	{OPT_KIO_NAME,			"kdirect=%s"},
 	{OPT_ERR,			NULL}
 };
 
@@ -656,7 +714,17 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev)
 		case OPT_DISABLE_CLOSE_WAIT:
 			d->flags |= FUSE_DISABLE_CLOSE_WAIT;
 			break;
-
+		case OPT_KIO_NAME: {
+			char *name;
+			name = match_strdup(&args[0]);
+			if (!name)
+				return 1;
+
+			strncpy(d->kio_name, name, FUSE_KIO_NAME);
+			d->flags |= FUSE_KDIRECT_IO;
+			kfree(name);
+			break;
+		}
 		default:
 			return 0;
 		}
@@ -692,6 +760,8 @@ static int fuse_show_options(struct seq_file *m, struct dentry *root)
 		seq_printf(m, ",blksize=%lu", sb->s_blocksize);
 	if (fc->writeback_cache)
 		seq_puts(m, ",writeback_enable");
+	if (fc->flags & FUSE_KDIRECT_IO)
+		seq_printf(m, ",kdirect=%s", fc->kio.op->name);
 	return 0;
 }
 
@@ -753,7 +823,7 @@ void fuse_conn_put(struct fuse_conn *fc)
 {
 	if (atomic_dec_and_test(&fc->count)) {
 		if (fc->destroy_req)
-			fuse_request_free(fc->destroy_req);
+			fuse_request_free(fc, fc->destroy_req);
 		mutex_destroy(&fc->inst_mutex);
 		fc->release(fc);
 	}
@@ -1044,6 +1114,12 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
 		fc->max_write = arg->minor < 5 ? 4096 : arg->max_write;
 		fc->max_write = max_t(unsigned, 4096, fc->max_write);
 		fc->conn_init = 1;
+
+		if (fc->kio.op) {
+			if (!fc->kio.op->conn_init(fc))
+				return;
+			fc->conn_error = 1;
+		}
 	}
 	fuse_set_initialized(fc);
 	wake_up_all(&fc->blocked_waitq);
@@ -1249,7 +1325,13 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
 	fc->group_id = d.group_id;
 	fc->max_read = max_t(unsigned, 4096, d.max_read);
 	fc->writeback_cache = d.writeback_cache;
-
+	if (fc->flags & FUSE_KDIRECT_IO) {
+		fc->kio.op = fuse_kio_get(fc, d.kio_name);
+		if (!fc->kio.op) {
+			err = -EINVAL;
+			goto err_dev_free;
+		}
+	}
 	/* Used by get_root_inode() */
 	sb->s_fs_info = fc;
 
@@ -1257,7 +1339,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
 	root = fuse_get_root_inode(sb, d.rootmode);
 	root_dentry = d_make_root(root);
 	if (!root_dentry)
-		goto err_dev_free;
+		goto err_put_io;
 	/* only now - we want root dentry with NULL ->d_op */
 	sb->s_d_op = &fuse_dentry_operations;
 
@@ -1299,9 +1381,11 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
  err_unlock:
 	mutex_unlock(&fuse_mutex);
  err_free_init_req:
-	fuse_request_free(init_req);
+	fuse_request_free(fc, init_req);
  err_put_root:
 	dput(root_dentry);
+ err_put_io:
+	fuse_kdirect_put(fc);
  err_dev_free:
 	fuse_dev_free(fud);
  err_put_conn:


More information about the Devel mailing list