All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCHSET] CUSE: implement CUSE, take #3
@ 2009-04-14  1:54 Tejun Heo
  2009-04-14  1:54 ` [PATCH 1/6] FUSE: misc cleanups Tejun Heo
                   ` (7 more replies)
  0 siblings, 8 replies; 17+ messages in thread
From: Tejun Heo @ 2009-04-14  1:54 UTC (permalink / raw)
  To: linux-kernel, fuse-devel, miklos

Hello, Miklos.

This is the third take of implement-CUSE patchset.  Sorry about the
huge delay.  :-)

Changes from the last take[L] are...

* Rather large FUSE update to make inode, superblock and mnt optional.

* CUSE no longer creates its superblock or mount.  It simply sets cdev
  file->private_data to fuse_file and operates on it.

* cdev lifetime handling fixed.  CUSE cdev now atomically looks up
  matching device (connection) like other cdevs do.

* CUSE init protocal updated.  An additional request CUSE_INIT_DONE is
  sent after successful creation of devices.  This is to allow
  userland server to synchronize init completion.

This patchset contains the following six patches.

  0001-FUSE-misc-cleanups.patch
  0002-FUSE-use-fuse-objects-as-the-main-objects-in-file-h.patch
  0003-FUSE-make-ff-fi-optional.patch
  0004-FUSE-update-fuse_conn_init-and-separate-out-fuse_.patch
  0005-FUSE-export-symbols-to-be-used-by-CUSE.patch
  0006-CUSE-implement-CUSE-Character-device-in-Userspace.patch

0001 is misc preparation.  0002 updates fuse file handling operations
such that it primarily operations on fuse_file instead of vfs objects.
This patch is rather huge but I couldn't think of a good meaningful
way to chop it down further.  As scary as it looks, it should have
zero functional difference and most changes are mechanical.

0003 makes fuse_file->fi (added by 0002) optional so that fuse_file
can be used separately.

0004-0005 prepares for CUSE implementation and 0006 does it.

I didn't replace the kthread init helper with req->end() callback for
two reasons.  Firstly, init involves issuing two commands - FUSE_INIT
and CUSE_INIT.  Chaning init sequence via req->end() would require
another split in the init sequence.  Secondly, the second command
would have to be issued from ->end(), which is just hairy.  I think
doing the latter part of initialization in kthread is better here.

This patchset is on top of the current linus#master[1] and available in the following git tree.

  git://git.kernel.org/pub/scm/linux/kernel/git/tj/misc.git cuse

diffstat follows.

 fs/Kconfig            |   10 
 fs/fuse/Makefile      |    1 
 fs/fuse/cuse.c        |  675 ++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/fuse/dev.c         |    9 
 fs/fuse/dir.c         |   97 ++++---
 fs/fuse/file.c        |  613 +++++++++++++++++++++++----------------------
 fs/fuse/fuse_i.h      |  100 ++++++-
 fs/fuse/inode.c       |   54 ++--
 include/linux/cuse.h  |   49 +++
 include/linux/fuse.h  |    2 
 include/linux/magic.h |    5 
 11 files changed, 1237 insertions(+), 378 deletions(-)

Thanks.

--
tejun

[L] http://thread.gmane.org/gmane.linux.kernel/759831
[1] 80a04d3f2f94fb68b5df05e3ac6697130bc3467a

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 1/6] FUSE: misc cleanups
  2009-04-14  1:54 [PATCHSET] CUSE: implement CUSE, take #3 Tejun Heo
@ 2009-04-14  1:54 ` Tejun Heo
  2009-04-14  1:54 ` [PATCH 2/6] FUSE: use fuse objects as the main objects in file handling functions Tejun Heo
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 17+ messages in thread
From: Tejun Heo @ 2009-04-14  1:54 UTC (permalink / raw)
  To: linux-kernel, fuse-devel, miklos; +Cc: Tejun Heo

* fuse_file_alloc() was structured in weird way.  The success path was
  split between else block and code following the block.  Restructure
  the code such that it's easier to read and modify.

* Unindent success path of fuse_release_common() to ease future
  changes.

* Rename fuse_file_do_ioctl() to fuse_file_ioctl_common() for
  consistency.

Signed-off-by: Tejun Heo <tj@kernel.org>
---
 fs/fuse/file.c |   89 ++++++++++++++++++++++++++++++--------------------------
 1 files changed, 48 insertions(+), 41 deletions(-)

diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 2b25133..01c7245 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -49,22 +49,26 @@ static int fuse_send_open(struct inode *inode, struct file *file, int isdir,
 struct fuse_file *fuse_file_alloc(struct fuse_conn *fc)
 {
 	struct fuse_file *ff;
+
 	ff = kmalloc(sizeof(struct fuse_file), GFP_KERNEL);
-	if (ff) {
-		ff->reserved_req = fuse_request_alloc();
-		if (!ff->reserved_req) {
-			kfree(ff);
-			return NULL;
-		} else {
-			INIT_LIST_HEAD(&ff->write_entry);
-			atomic_set(&ff->count, 0);
-			spin_lock(&fc->lock);
-			ff->kh = ++fc->khctr;
-			spin_unlock(&fc->lock);
-		}
-		RB_CLEAR_NODE(&ff->polled_node);
-		init_waitqueue_head(&ff->poll_wait);
+	if (unlikely(!ff))
+		return NULL;
+
+	ff->reserved_req = fuse_request_alloc();
+	if (unlikely(!ff->reserved_req)) {
+		kfree(ff);
+		return NULL;
 	}
+
+	INIT_LIST_HEAD(&ff->write_entry);
+	atomic_set(&ff->count, 0);
+	RB_CLEAR_NODE(&ff->polled_node);
+	init_waitqueue_head(&ff->poll_wait);
+
+	spin_lock(&fc->lock);
+	ff->kh = ++fc->khctr;
+	spin_unlock(&fc->lock);
+
 	return ff;
 }
 
@@ -158,34 +162,37 @@ void fuse_release_fill(struct fuse_file *ff, u64 nodeid, int flags, int opcode)
 
 int fuse_release_common(struct inode *inode, struct file *file, int isdir)
 {
-	struct fuse_file *ff = file->private_data;
-	if (ff) {
-		struct fuse_conn *fc = get_fuse_conn(inode);
-		struct fuse_req *req = ff->reserved_req;
+	struct fuse_conn *fc;
+	struct fuse_file *ff;
+	struct fuse_req *req;
 
-		fuse_release_fill(ff, get_node_id(inode), file->f_flags,
-				  isdir ? FUSE_RELEASEDIR : FUSE_RELEASE);
+	ff = file->private_data;
+	if (unlikely(!ff))
+		return 0;	/* return value is ignored by VFS */
 
-		/* Hold vfsmount and dentry until release is finished */
-		req->misc.release.vfsmount = mntget(file->f_path.mnt);
-		req->misc.release.dentry = dget(file->f_path.dentry);
+	fc = get_fuse_conn(inode);
+	req = ff->reserved_req;
 
-		spin_lock(&fc->lock);
-		list_del(&ff->write_entry);
-		if (!RB_EMPTY_NODE(&ff->polled_node))
-			rb_erase(&ff->polled_node, &fc->polled_files);
-		spin_unlock(&fc->lock);
+	fuse_release_fill(ff, get_node_id(inode), file->f_flags,
+			  isdir ? FUSE_RELEASEDIR : FUSE_RELEASE);
 
-		wake_up_interruptible_sync(&ff->poll_wait);
-		/*
-		 * Normally this will send the RELEASE request,
-		 * however if some asynchronous READ or WRITE requests
-		 * are outstanding, the sending will be delayed
-		 */
-		fuse_file_put(ff);
-	}
+	/* Hold vfsmount and dentry until release is finished */
+	req->misc.release.vfsmount = mntget(file->f_path.mnt);
+	req->misc.release.dentry = dget(file->f_path.dentry);
+
+	spin_lock(&fc->lock);
+	list_del(&ff->write_entry);
+	if (!RB_EMPTY_NODE(&ff->polled_node))
+		rb_erase(&ff->polled_node, &fc->polled_files);
+	spin_unlock(&fc->lock);
 
-	/* Return value is ignored by VFS */
+	wake_up_interruptible_sync(&ff->poll_wait);
+	/*
+	 * Normally this will send the RELEASE request, however if
+	 * some asynchronous READ or WRITE requests are outstanding,
+	 * the sending will be delayed.
+	 */
+	fuse_file_put(ff);
 	return 0;
 }
 
@@ -1601,8 +1608,8 @@ static int fuse_ioctl_copy_user(struct page **pages, struct iovec *iov,
  * limits ioctl data transfers to well-formed ioctls and is the forced
  * behavior for all FUSE servers.
  */
-static long fuse_file_do_ioctl(struct file *file, unsigned int cmd,
-			       unsigned long arg, unsigned int flags)
+static long fuse_file_ioctl_common(struct file *file, unsigned int cmd,
+				   unsigned long arg, unsigned int flags)
 {
 	struct inode *inode = file->f_dentry->d_inode;
 	struct fuse_file *ff = file->private_data;
@@ -1779,13 +1786,13 @@ static long fuse_file_do_ioctl(struct file *file, unsigned int cmd,
 static long fuse_file_ioctl(struct file *file, unsigned int cmd,
 			    unsigned long arg)
 {
-	return fuse_file_do_ioctl(file, cmd, arg, 0);
+	return fuse_file_ioctl_common(file, cmd, arg, 0);
 }
 
 static long fuse_file_compat_ioctl(struct file *file, unsigned int cmd,
 				   unsigned long arg)
 {
-	return fuse_file_do_ioctl(file, cmd, arg, FUSE_IOCTL_COMPAT);
+	return fuse_file_ioctl_common(file, cmd, arg, FUSE_IOCTL_COMPAT);
 }
 
 /*
-- 
1.6.0.2


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 2/6] FUSE: use fuse objects as the main objects in file handling functions
  2009-04-14  1:54 [PATCHSET] CUSE: implement CUSE, take #3 Tejun Heo
  2009-04-14  1:54 ` [PATCH 1/6] FUSE: misc cleanups Tejun Heo
@ 2009-04-14  1:54 ` Tejun Heo
  2009-04-14  1:54 ` [PATCH 3/6] FUSE: make ff->fi optional Tejun Heo
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 17+ messages in thread
From: Tejun Heo @ 2009-04-14  1:54 UTC (permalink / raw)
  To: linux-kernel, fuse-devel, miklos; +Cc: Tejun Heo

Generic struct file and/or inode have been used as handle in file
related functions and fuse specific data structures - fuse_file and
fuse_inode - didn't have enough linkage to other objects and thus
couldn't be used as object handles (other objects couldn't be reached
via ff).  This works fine but at times which object is being needed by
a function isn't clear and requires all fuse files to have full
generic filesystem inode/superblock which won't be true for cuse
files.

This patch adds ->fc, ->fi and ->file pointers to struct fuse_file and
replace req->inode with req->fi and use fuse_file and fuse_inode as
the main object handle in file related functions.

Functions which deal with a file now only take a pointer to struct
fuse_file as object handle and deferences all other objects via it.
The following three helpers are added to help non-trivial
dereferencing and ease future changes.

  fuse_file_nodeid(ff)		: get nodeid of @ff
  fuse_file_vfs_inode(ff)	: return generic vfs inode backing @ff
  fuse_file_bad(ff)		: test whether ff's vfs inode is bad

Whenever generic vfs inode is used, it's explicitly named vfs_inode to
distinguish its usage.  vfs_inode is necessary and used only for open,
writeback and locking.

* For functions which take both @file and @inode parameters, it's
  assumed that @file->f_dentry->d_inode == @inode.  For functions
  which get @mapping someway, it's assumed that @file->f_mapping ==
  @mapping.

* fuse_open_common() now takes @fc, @fi, @vfs_inode and initializes
  @ff accordingly.

* fuse_release_common() now takes @ff.  The file->private_data == NULL
  test is moved to its callers and fuse_release_common() is made void.

* fuse_fsync() assumes that @file->f_dentry == @de.

* fuse_readpage() always sets ff->req and fuse_readpages_end() tests
  fc->async_read directly.

* fuse_readpages_fill() now gets struct fuse_req ** instead of struct
  fuse_fill_data *.  fuse_file is dereferenced using req->ff and all
  the rest from the fuse_file.  struct fuse_fill_data is dropped.

* fuse_write_fill() now takes @f_flags instead of @file.

Signed-off-by: Tejun Heo <tj@kernel.org>
---
 fs/fuse/dir.c    |   97 ++++++-----
 fs/fuse/file.c   |  497 ++++++++++++++++++++++++++----------------------------
 fs/fuse/fuse_i.h |   54 +++++--
 3 files changed, 333 insertions(+), 315 deletions(-)

diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 8b8eebc..63b9546 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -84,9 +84,9 @@ static u64 entry_attr_timeout(struct fuse_entry_out *o)
  * Mark the attributes as stale, so that at the next call to
  * ->getattr() they will be fetched from userspace
  */
-void fuse_invalidate_attr(struct inode *inode)
+void fuse_invalidate_attr(struct fuse_inode *fi)
 {
-	get_fuse_inode(inode)->i_time = 0;
+	fi->i_time = 0;
 }
 
 /*
@@ -408,7 +408,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
 		goto out_put_forget_req;
 
 	err = -ENOMEM;
-	ff = fuse_file_alloc(fc);
+	ff = fuse_file_alloc(fc, NULL, NULL);	/* will set fi and file later */
 	if (!ff)
 		goto out_put_request;
 
@@ -454,17 +454,19 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
 		fuse_send_forget(fc, forget_req, outentry.nodeid, 1);
 		return -ENOMEM;
 	}
+	ff->fi = get_fuse_inode(inode);
 	fuse_put_request(fc, forget_req);
 	d_instantiate(entry, inode);
 	fuse_change_entry_timeout(entry, &outentry);
-	fuse_invalidate_attr(dir);
+	fuse_invalidate_attr(ff->fi);
 	file = lookup_instantiate_filp(nd, entry, generic_file_open);
 	if (IS_ERR(file)) {
 		ff->fh = outopen.fh;
 		fuse_sync_release(fc, ff, outentry.nodeid, flags);
 		return PTR_ERR(file);
 	}
-	fuse_finish_open(inode, file, ff, &outopen);
+	ff->file = file;
+	fuse_finish_open(ff, &outopen);
 	return 0;
 
  out_free_ff:
@@ -540,7 +542,7 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req,
 		d_instantiate(entry, inode);
 
 	fuse_change_entry_timeout(entry, &outarg);
-	fuse_invalidate_attr(dir);
+	fuse_invalidate_attr(get_fuse_inode(dir));
 	return 0;
 
  out_put_forget_req:
@@ -643,8 +645,8 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
 		 * lookup/getattr.
 		 */
 		clear_nlink(inode);
-		fuse_invalidate_attr(inode);
-		fuse_invalidate_attr(dir);
+		fuse_invalidate_attr(get_fuse_inode(inode));
+		fuse_invalidate_attr(get_fuse_inode(dir));
 		fuse_invalidate_entry_cache(entry);
 	} else if (err == -EINTR)
 		fuse_invalidate_entry(entry);
@@ -669,7 +671,7 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry)
 	fuse_put_request(fc, req);
 	if (!err) {
 		clear_nlink(entry->d_inode);
-		fuse_invalidate_attr(dir);
+		fuse_invalidate_attr(get_fuse_inode(dir));
 		fuse_invalidate_entry_cache(entry);
 	} else if (err == -EINTR)
 		fuse_invalidate_entry(entry);
@@ -702,11 +704,11 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent,
 	fuse_put_request(fc, req);
 	if (!err) {
 		/* ctime changes */
-		fuse_invalidate_attr(oldent->d_inode);
+		fuse_invalidate_attr(get_fuse_inode(oldent->d_inode));
 
-		fuse_invalidate_attr(olddir);
+		fuse_invalidate_attr(get_fuse_inode(olddir));
 		if (olddir != newdir)
-			fuse_invalidate_attr(newdir);
+			fuse_invalidate_attr(get_fuse_inode(newdir));
 
 		/* newent will end up negative */
 		if (newent->d_inode)
@@ -752,7 +754,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
 	   etc.)
 	*/
 	if (!err || err == -EINTR)
-		fuse_invalidate_attr(inode);
+		fuse_invalidate_attr(get_fuse_inode(inode));
 	return err;
 }
 
@@ -831,21 +833,20 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat,
 	return err;
 }
 
-int fuse_update_attributes(struct inode *inode, struct kstat *stat,
+int fuse_update_attributes(struct fuse_inode *fi, struct kstat *stat,
 			   struct file *file, bool *refreshed)
 {
-	struct fuse_inode *fi = get_fuse_inode(inode);
 	int err;
 	bool r;
 
 	if (fi->i_time < get_jiffies_64()) {
 		r = true;
-		err = fuse_do_getattr(inode, stat, file);
+		err = fuse_do_getattr(&fi->inode, stat, file);
 	} else {
 		r = false;
 		err = 0;
 		if (stat) {
-			generic_fillattr(inode, stat);
+			generic_fillattr(&fi->inode, stat);
 			stat->mode = fi->orig_i_mode;
 		}
 	}
@@ -950,7 +951,8 @@ static int fuse_permission(struct inode *inode, int mask)
 	 */
 	if ((fc->flags & FUSE_DEFAULT_PERMISSIONS) ||
 	    ((mask & MAY_EXEC) && S_ISREG(inode->i_mode))) {
-		err = fuse_update_attributes(inode, NULL, NULL, &refreshed);
+		err = fuse_update_attributes(get_fuse_inode(inode), NULL, NULL,
+					     &refreshed);
 		if (err)
 			return err;
 	}
@@ -1016,11 +1018,11 @@ static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
 	int err;
 	size_t nbytes;
 	struct page *page;
-	struct inode *inode = file->f_path.dentry->d_inode;
-	struct fuse_conn *fc = get_fuse_conn(inode);
+	struct fuse_file *ff = file->private_data;
+	struct fuse_conn *fc = ff->fc;
 	struct fuse_req *req;
 
-	if (is_bad_inode(inode))
+	if (fuse_file_bad(ff))
 		return -EIO;
 
 	req = fuse_get_req(fc);
@@ -1035,7 +1037,7 @@ static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
 	req->out.argpages = 1;
 	req->num_pages = 1;
 	req->pages[0] = page;
-	fuse_read_fill(req, file, inode, file->f_pos, PAGE_SIZE, FUSE_READDIR);
+	fuse_read_fill(req, ff, file->f_pos, PAGE_SIZE, FUSE_READDIR);
 	fuse_request_send(fc, req);
 	nbytes = req->out.args[0].size;
 	err = req->out.h.error;
@@ -1045,7 +1047,7 @@ static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
 				    filldir);
 
 	__free_page(page);
-	fuse_invalidate_attr(inode); /* atime changed */
+	fuse_invalidate_attr(ff->fi); /* atime changed */
 	return err;
 }
 
@@ -1078,7 +1080,7 @@ static char *read_link(struct dentry *dentry)
 		link[req->out.args[0].size] = '\0';
  out:
 	fuse_put_request(fc, req);
-	fuse_invalidate_attr(inode); /* atime changed */
+	fuse_invalidate_attr(get_fuse_inode(inode)); /* atime changed */
 	return link;
 }
 
@@ -1101,18 +1103,29 @@ static void fuse_put_link(struct dentry *dentry, struct nameidata *nd, void *c)
 
 static int fuse_dir_open(struct inode *inode, struct file *file)
 {
-	return fuse_open_common(inode, file, 1);
+	return fuse_open_common(get_fuse_conn(inode), get_fuse_inode(inode),
+				inode, file, 1);
 }
 
 static int fuse_dir_release(struct inode *inode, struct file *file)
 {
-	return fuse_release_common(inode, file, 1);
+	struct fuse_file *ff = file->private_data;
+
+	if (ff)
+		fuse_release_common(ff, 1);
+	return 0;	/* return value is ignored by VFS */
 }
 
 static int fuse_dir_fsync(struct file *file, struct dentry *de, int datasync)
 {
+	struct fuse_file *ff;
+
 	/* nfsd can call this with no file */
-	return file ? fuse_fsync_common(file, de, datasync, 1) : 0;
+	if (!file)
+		return 0;
+
+	ff = file->private_data;
+	return fuse_fsync_common(ff, datasync, 1);
 }
 
 static bool update_mtime(unsigned ivalid)
@@ -1163,12 +1176,11 @@ static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg)
  * This is done by adding a negative bias to the inode write counter
  * and waiting for all pending writes to finish.
  */
-void fuse_set_nowrite(struct inode *inode)
+void fuse_set_nowrite(struct fuse_inode *fi)
 {
-	struct fuse_conn *fc = get_fuse_conn(inode);
-	struct fuse_inode *fi = get_fuse_inode(inode);
+	struct fuse_conn *fc = get_fuse_conn(&fi->inode);
 
-	BUG_ON(!mutex_is_locked(&inode->i_mutex));
+	BUG_ON(!mutex_is_locked(&fi->inode.i_mutex));
 
 	spin_lock(&fc->lock);
 	BUG_ON(fi->writectr < 0);
@@ -1183,21 +1195,19 @@ void fuse_set_nowrite(struct inode *inode)
  * Remove the bias from the writecounter and send any queued
  * writepages.
  */
-static void __fuse_release_nowrite(struct inode *inode)
+static void __fuse_release_nowrite(struct fuse_inode *fi)
 {
-	struct fuse_inode *fi = get_fuse_inode(inode);
-
 	BUG_ON(fi->writectr != FUSE_NOWRITE);
 	fi->writectr = 0;
-	fuse_flush_writepages(inode);
+	fuse_flush_writepages(fi);
 }
 
-void fuse_release_nowrite(struct inode *inode)
+void fuse_release_nowrite(struct fuse_inode *fi)
 {
-	struct fuse_conn *fc = get_fuse_conn(inode);
+	struct fuse_conn *fc = get_fuse_conn(&fi->inode);
 
 	spin_lock(&fc->lock);
-	__fuse_release_nowrite(inode);
+	__fuse_release_nowrite(fi);
 	spin_unlock(&fc->lock);
 }
 
@@ -1214,6 +1224,7 @@ static int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
 {
 	struct inode *inode = entry->d_inode;
 	struct fuse_conn *fc = get_fuse_conn(inode);
+	struct fuse_inode *fi = get_fuse_inode(inode);
 	struct fuse_req *req;
 	struct fuse_setattr_in inarg;
 	struct fuse_attr_out outarg;
@@ -1250,7 +1261,7 @@ static int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
 		return PTR_ERR(req);
 
 	if (is_truncate)
-		fuse_set_nowrite(inode);
+		fuse_set_nowrite(fi);
 
 	memset(&inarg, 0, sizeof(inarg));
 	memset(&outarg, 0, sizeof(outarg));
@@ -1281,7 +1292,7 @@ static int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
 	fuse_put_request(fc, req);
 	if (err) {
 		if (err == -EINTR)
-			fuse_invalidate_attr(inode);
+			fuse_invalidate_attr(fi);
 		goto error;
 	}
 
@@ -1299,7 +1310,7 @@ static int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
 
 	if (is_truncate) {
 		/* NOTE: this may release/reacquire fc->lock */
-		__fuse_release_nowrite(inode);
+		__fuse_release_nowrite(fi);
 	}
 	spin_unlock(&fc->lock);
 
@@ -1317,7 +1328,7 @@ static int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
 
 error:
 	if (is_truncate)
-		fuse_release_nowrite(inode);
+		fuse_release_nowrite(fi);
 
 	return err;
 }
@@ -1339,7 +1350,7 @@ static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
 	if (!fuse_allow_task(fc, current))
 		return -EACCES;
 
-	return fuse_update_attributes(inode, stat, NULL, NULL);
+	return fuse_update_attributes(get_fuse_inode(inode), stat, NULL, NULL);
 }
 
 static int fuse_setxattr(struct dentry *entry, const char *name,
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 01c7245..f32d2c8 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -15,10 +15,10 @@
 
 static const struct file_operations fuse_direct_io_file_operations;
 
-static int fuse_send_open(struct inode *inode, struct file *file, int isdir,
+static int fuse_send_open(struct fuse_file *ff, int isdir,
 			  struct fuse_open_out *outargp)
 {
-	struct fuse_conn *fc = get_fuse_conn(inode);
+	struct fuse_conn *fc = ff->fc;
 	struct fuse_open_in inarg;
 	struct fuse_req *req;
 	int err;
@@ -28,11 +28,11 @@ static int fuse_send_open(struct inode *inode, struct file *file, int isdir,
 		return PTR_ERR(req);
 
 	memset(&inarg, 0, sizeof(inarg));
-	inarg.flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY);
+	inarg.flags = ff->file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY);
 	if (!fc->atomic_o_trunc)
 		inarg.flags &= ~O_TRUNC;
 	req->in.h.opcode = isdir ? FUSE_OPENDIR : FUSE_OPEN;
-	req->in.h.nodeid = get_node_id(inode);
+	req->in.h.nodeid = fuse_file_nodeid(ff);
 	req->in.numargs = 1;
 	req->in.args[0].size = sizeof(inarg);
 	req->in.args[0].value = &inarg;
@@ -46,7 +46,8 @@ static int fuse_send_open(struct inode *inode, struct file *file, int isdir,
 	return err;
 }
 
-struct fuse_file *fuse_file_alloc(struct fuse_conn *fc)
+struct fuse_file *fuse_file_alloc(struct fuse_conn *fc, struct fuse_inode *fi,
+				  struct file *file)
 {
 	struct fuse_file *ff;
 
@@ -64,6 +65,9 @@ struct fuse_file *fuse_file_alloc(struct fuse_conn *fc)
 	atomic_set(&ff->count, 0);
 	RB_CLEAR_NODE(&ff->polled_node);
 	init_waitqueue_head(&ff->poll_wait);
+	ff->fc = fc;
+	ff->fi = fi;
+	ff->file = file;
 
 	spin_lock(&fc->lock);
 	ff->kh = ++fc->khctr;
@@ -94,30 +98,30 @@ static void fuse_file_put(struct fuse_file *ff)
 {
 	if (atomic_dec_and_test(&ff->count)) {
 		struct fuse_req *req = ff->reserved_req;
-		struct inode *inode = req->misc.release.dentry->d_inode;
-		struct fuse_conn *fc = get_fuse_conn(inode);
+
 		req->end = fuse_release_end;
-		fuse_request_send_background(fc, req);
+		fuse_request_send_background(ff->fc, req);
 		kfree(ff);
 	}
 }
 
-void fuse_finish_open(struct inode *inode, struct file *file,
-		      struct fuse_file *ff, struct fuse_open_out *outarg)
+void fuse_finish_open(struct fuse_file *ff, struct fuse_open_out *outarg)
 {
+	struct inode *vfs_inode = fuse_file_vfs_inode(ff);
+
 	if (outarg->open_flags & FOPEN_DIRECT_IO)
-		file->f_op = &fuse_direct_io_file_operations;
+		ff->file->f_op = &fuse_direct_io_file_operations;
 	if (!(outarg->open_flags & FOPEN_KEEP_CACHE))
-		invalidate_inode_pages2(inode->i_mapping);
+		invalidate_inode_pages2(vfs_inode->i_mapping);
 	if (outarg->open_flags & FOPEN_NONSEEKABLE)
-		nonseekable_open(inode, file);
+		nonseekable_open(vfs_inode, ff->file);
 	ff->fh = outarg->fh;
-	file->private_data = fuse_file_get(ff);
+	ff->file->private_data = fuse_file_get(ff);
 }
 
-int fuse_open_common(struct inode *inode, struct file *file, int isdir)
+int fuse_open_common(struct fuse_conn *fc, struct fuse_inode *fi,
+		     struct inode *vfs_inode, struct file *file, int isdir)
 {
-	struct fuse_conn *fc = get_fuse_conn(inode);
 	struct fuse_open_out outarg;
 	struct fuse_file *ff;
 	int err;
@@ -126,21 +130,21 @@ int fuse_open_common(struct inode *inode, struct file *file, int isdir)
 	if (file->f_flags & O_DIRECT)
 		return -EINVAL;
 
-	err = generic_file_open(inode, file);
+	err = generic_file_open(vfs_inode, file);
 	if (err)
 		return err;
 
-	ff = fuse_file_alloc(fc);
+	ff = fuse_file_alloc(fc, fi, file);
 	if (!ff)
 		return -ENOMEM;
 
-	err = fuse_send_open(inode, file, isdir, &outarg);
+	err = fuse_send_open(ff, isdir, &outarg);
 	if (err)
 		fuse_file_free(ff);
 	else {
 		if (isdir)
 			outarg.open_flags &= ~FOPEN_DIRECT_IO;
-		fuse_finish_open(inode, file, ff, &outarg);
+		fuse_finish_open(ff, &outarg);
 	}
 
 	return err;
@@ -160,25 +164,17 @@ void fuse_release_fill(struct fuse_file *ff, u64 nodeid, int flags, int opcode)
 	req->in.args[0].value = inarg;
 }
 
-int fuse_release_common(struct inode *inode, struct file *file, int isdir)
+void fuse_release_common(struct fuse_file *ff, int isdir)
 {
-	struct fuse_conn *fc;
-	struct fuse_file *ff;
-	struct fuse_req *req;
-
-	ff = file->private_data;
-	if (unlikely(!ff))
-		return 0;	/* return value is ignored by VFS */
-
-	fc = get_fuse_conn(inode);
-	req = ff->reserved_req;
+	struct fuse_conn *fc = ff->fc;
+	struct fuse_req *req = ff->reserved_req;
 
-	fuse_release_fill(ff, get_node_id(inode), file->f_flags,
+	fuse_release_fill(ff, fuse_file_nodeid(ff), ff->file->f_flags,
 			  isdir ? FUSE_RELEASEDIR : FUSE_RELEASE);
 
 	/* Hold vfsmount and dentry until release is finished */
-	req->misc.release.vfsmount = mntget(file->f_path.mnt);
-	req->misc.release.dentry = dget(file->f_path.dentry);
+	req->misc.release.vfsmount = mntget(ff->file->f_path.mnt);
+	req->misc.release.dentry = dget(ff->file->f_path.dentry);
 
 	spin_lock(&fc->lock);
 	list_del(&ff->write_entry);
@@ -193,17 +189,21 @@ int fuse_release_common(struct inode *inode, struct file *file, int isdir)
 	 * the sending will be delayed.
 	 */
 	fuse_file_put(ff);
-	return 0;
 }
 
 static int fuse_open(struct inode *inode, struct file *file)
 {
-	return fuse_open_common(inode, file, 0);
+	return fuse_open_common(get_fuse_conn(inode), get_fuse_inode(inode),
+				inode, file, 0);
 }
 
 static int fuse_release(struct inode *inode, struct file *file)
 {
-	return fuse_release_common(inode, file, 0);
+	struct fuse_file *ff = file->private_data;
+
+	if (ff)
+		fuse_release_common(ff, 0);
+	return 0;	/* return value is ignored by VFS */
 }
 
 /*
@@ -234,10 +234,9 @@ u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id)
  * This is currently done by walking the list of writepage requests
  * for the inode, which can be pretty inefficient.
  */
-static bool fuse_page_is_writeback(struct inode *inode, pgoff_t index)
+static bool fuse_page_is_writeback(struct fuse_inode *fi, pgoff_t index)
 {
-	struct fuse_conn *fc = get_fuse_conn(inode);
-	struct fuse_inode *fi = get_fuse_inode(inode);
+	struct fuse_conn *fc = get_fuse_conn(&fi->inode);
 	struct fuse_req *req;
 	bool found = false;
 
@@ -245,7 +244,7 @@ static bool fuse_page_is_writeback(struct inode *inode, pgoff_t index)
 	list_for_each_entry(req, &fi->writepages, writepages_entry) {
 		pgoff_t curr_index;
 
-		BUG_ON(req->inode != inode);
+		BUG_ON(req->fi != fi);
 		curr_index = req->misc.write.in.offset >> PAGE_CACHE_SHIFT;
 		if (curr_index == index) {
 			found = true;
@@ -263,24 +262,21 @@ static bool fuse_page_is_writeback(struct inode *inode, pgoff_t index)
  * Since fuse doesn't rely on the VM writeback tracking, this has to
  * use some other means.
  */
-static int fuse_wait_on_page_writeback(struct inode *inode, pgoff_t index)
+static int fuse_wait_on_page_writeback(struct fuse_inode *fi, pgoff_t index)
 {
-	struct fuse_inode *fi = get_fuse_inode(inode);
-
-	wait_event(fi->page_waitq, !fuse_page_is_writeback(inode, index));
+	wait_event(fi->page_waitq, !fuse_page_is_writeback(fi, index));
 	return 0;
 }
 
 static int fuse_flush(struct file *file, fl_owner_t id)
 {
-	struct inode *inode = file->f_path.dentry->d_inode;
-	struct fuse_conn *fc = get_fuse_conn(inode);
 	struct fuse_file *ff = file->private_data;
+	struct fuse_conn *fc = ff->fc;
 	struct fuse_req *req;
 	struct fuse_flush_in inarg;
 	int err;
 
-	if (is_bad_inode(inode))
+	if (fuse_file_bad(ff))
 		return -EIO;
 
 	if (fc->no_flush)
@@ -291,7 +287,7 @@ static int fuse_flush(struct file *file, fl_owner_t id)
 	inarg.fh = ff->fh;
 	inarg.lock_owner = fuse_lock_owner_id(fc, id);
 	req->in.h.opcode = FUSE_FLUSH;
-	req->in.h.nodeid = get_node_id(inode);
+	req->in.h.nodeid = fuse_file_nodeid(ff);
 	req->in.numargs = 1;
 	req->in.args[0].size = sizeof(inarg);
 	req->in.args[0].value = &inarg;
@@ -315,23 +311,20 @@ static int fuse_flush(struct file *file, fl_owner_t id)
  * This must be called under i_mutex, otherwise the FUSE_NOWRITE usage
  * could conflict with truncation.
  */
-static void fuse_sync_writes(struct inode *inode)
+static void fuse_sync_writes(struct fuse_inode *fi)
 {
-	fuse_set_nowrite(inode);
-	fuse_release_nowrite(inode);
+	fuse_set_nowrite(fi);
+	fuse_release_nowrite(fi);
 }
 
-int fuse_fsync_common(struct file *file, struct dentry *de, int datasync,
-		      int isdir)
+int fuse_fsync_common(struct fuse_file *ff, int datasync, int isdir)
 {
-	struct inode *inode = de->d_inode;
-	struct fuse_conn *fc = get_fuse_conn(inode);
-	struct fuse_file *ff = file->private_data;
+	struct fuse_conn *fc = ff->fc;
 	struct fuse_req *req;
 	struct fuse_fsync_in inarg;
 	int err;
 
-	if (is_bad_inode(inode))
+	if (fuse_file_bad(ff))
 		return -EIO;
 
 	if ((!isdir && fc->no_fsync) || (isdir && fc->no_fsyncdir))
@@ -342,11 +335,11 @@ int fuse_fsync_common(struct file *file, struct dentry *de, int datasync,
 	 * wait for all outstanding writes, before sending the FSYNC
 	 * request.
 	 */
-	err = write_inode_now(inode, 0);
+	err = write_inode_now(fuse_file_vfs_inode(ff), 0);
 	if (err)
 		return err;
 
-	fuse_sync_writes(inode);
+	fuse_sync_writes(ff->fi);
 
 	req = fuse_get_req(fc);
 	if (IS_ERR(req))
@@ -356,7 +349,7 @@ int fuse_fsync_common(struct file *file, struct dentry *de, int datasync,
 	inarg.fh = ff->fh;
 	inarg.fsync_flags = datasync ? 1 : 0;
 	req->in.h.opcode = isdir ? FUSE_FSYNCDIR : FUSE_FSYNC;
-	req->in.h.nodeid = get_node_id(inode);
+	req->in.h.nodeid = fuse_file_nodeid(ff);
 	req->in.numargs = 1;
 	req->in.args[0].size = sizeof(inarg);
 	req->in.args[0].value = &inarg;
@@ -375,21 +368,23 @@ int fuse_fsync_common(struct file *file, struct dentry *de, int datasync,
 
 static int fuse_fsync(struct file *file, struct dentry *de, int datasync)
 {
-	return fuse_fsync_common(file, de, datasync, 0);
+	struct fuse_file *ff = file->private_data;
+
+	return fuse_fsync_common(ff, datasync, 0);
 }
 
-void fuse_read_fill(struct fuse_req *req, struct file *file,
-		    struct inode *inode, loff_t pos, size_t count, int opcode)
+void fuse_read_fill(struct fuse_req *req, struct fuse_file *ff,
+		    loff_t pos, size_t count, int opcode)
+
 {
 	struct fuse_read_in *inarg = &req->misc.read.in;
-	struct fuse_file *ff = file->private_data;
 
 	inarg->fh = ff->fh;
 	inarg->offset = pos;
 	inarg->size = count;
-	inarg->flags = file->f_flags;
+	inarg->flags = ff->file->f_flags;
 	req->in.h.opcode = opcode;
-	req->in.h.nodeid = get_node_id(inode);
+	req->in.h.nodeid = fuse_file_nodeid(ff);
 	req->in.numargs = 1;
 	req->in.args[0].size = sizeof(struct fuse_read_in);
 	req->in.args[0].value = inarg;
@@ -398,41 +393,38 @@ void fuse_read_fill(struct fuse_req *req, struct file *file,
 	req->out.args[0].size = count;
 }
 
-static size_t fuse_send_read(struct fuse_req *req, struct file *file,
-			     struct inode *inode, loff_t pos, size_t count,
-			     fl_owner_t owner)
+static size_t fuse_send_read(struct fuse_req *req, struct fuse_file *ff,
+			     loff_t pos, size_t count, fl_owner_t owner)
 {
-	struct fuse_conn *fc = get_fuse_conn(inode);
-
-	fuse_read_fill(req, file, inode, pos, count, FUSE_READ);
+	fuse_read_fill(req, ff, pos, count, FUSE_READ);
 	if (owner != NULL) {
 		struct fuse_read_in *inarg = &req->misc.read.in;
 
 		inarg->read_flags |= FUSE_READ_LOCKOWNER;
-		inarg->lock_owner = fuse_lock_owner_id(fc, owner);
+		inarg->lock_owner = fuse_lock_owner_id(ff->fc, owner);
 	}
-	fuse_request_send(fc, req);
+	fuse_request_send(ff->fc, req);
 	return req->out.args[0].size;
 }
 
-static void fuse_read_update_size(struct inode *inode, loff_t size,
+static void fuse_read_update_size(struct fuse_file *ff, loff_t size,
 				  u64 attr_ver)
 {
-	struct fuse_conn *fc = get_fuse_conn(inode);
-	struct fuse_inode *fi = get_fuse_inode(inode);
+	struct fuse_conn *fc = ff->fc;
+	struct fuse_inode *fi = ff->fi;
 
 	spin_lock(&fc->lock);
-	if (attr_ver == fi->attr_version && size < inode->i_size) {
+	if (attr_ver == fi->attr_version && size < fi->inode.i_size) {
 		fi->attr_version = ++fc->attr_version;
-		i_size_write(inode, size);
+		i_size_write(&fi->inode, size);
 	}
 	spin_unlock(&fc->lock);
 }
 
 static int fuse_readpage(struct file *file, struct page *page)
 {
-	struct inode *inode = page->mapping->host;
-	struct fuse_conn *fc = get_fuse_conn(inode);
+	struct fuse_file *ff = file->private_data;
+	struct fuse_conn *fc = ff->fc;
 	struct fuse_req *req;
 	size_t num_read;
 	loff_t pos = page_offset(page);
@@ -441,7 +433,7 @@ static int fuse_readpage(struct file *file, struct page *page)
 	int err;
 
 	err = -EIO;
-	if (is_bad_inode(inode))
+	if (fuse_file_bad(ff))
 		goto out;
 
 	/*
@@ -449,7 +441,7 @@ static int fuse_readpage(struct file *file, struct page *page)
 	 * page-cache page, so make sure we read a properly synced
 	 * page.
 	 */
-	fuse_wait_on_page_writeback(inode, page->index);
+	fuse_wait_on_page_writeback(ff->fi, page->index);
 
 	req = fuse_get_req(fc);
 	err = PTR_ERR(req);
@@ -462,7 +454,7 @@ static int fuse_readpage(struct file *file, struct page *page)
 	req->out.argpages = 1;
 	req->num_pages = 1;
 	req->pages[0] = page;
-	num_read = fuse_send_read(req, file, inode, pos, count, NULL);
+	num_read = fuse_send_read(req, ff, pos, count, NULL);
 	err = req->out.h.error;
 	fuse_put_request(fc, req);
 
@@ -471,12 +463,12 @@ static int fuse_readpage(struct file *file, struct page *page)
 		 * Short read means EOF.  If file size is larger, truncate it
 		 */
 		if (num_read < count)
-			fuse_read_update_size(inode, pos + num_read, attr_ver);
+			fuse_read_update_size(ff, pos + num_read, attr_ver);
 
 		SetPageUptodate(page);
 	}
 
-	fuse_invalidate_attr(inode); /* atime changed */
+	fuse_invalidate_attr(ff->fi); /* atime changed */
  out:
 	unlock_page(page);
 	return err;
@@ -485,19 +477,19 @@ static int fuse_readpage(struct file *file, struct page *page)
 static void fuse_readpages_end(struct fuse_conn *fc, struct fuse_req *req)
 {
 	int i;
+	struct fuse_file *ff = req->ff;
 	size_t count = req->misc.read.in.size;
 	size_t num_read = req->out.args[0].size;
-	struct inode *inode = req->pages[0]->mapping->host;
 
 	/*
 	 * Short read means EOF.  If file size is larger, truncate it
 	 */
 	if (!req->out.h.error && num_read < count) {
 		loff_t pos = page_offset(req->pages[0]) + num_read;
-		fuse_read_update_size(inode, pos, req->misc.read.attr_ver);
+		fuse_read_update_size(ff, pos, req->misc.read.attr_ver);
 	}
 
-	fuse_invalidate_attr(inode); /* atime changed */
+	fuse_invalidate_attr(ff->fi); /* atime changed */
 
 	for (i = 0; i < req->num_pages; i++) {
 		struct page *page = req->pages[i];
@@ -507,54 +499,49 @@ static void fuse_readpages_end(struct fuse_conn *fc, struct fuse_req *req)
 			SetPageError(page);
 		unlock_page(page);
 	}
-	if (req->ff)
+
+	if (fc->async_read)
 		fuse_file_put(req->ff);
 }
 
-static void fuse_send_readpages(struct fuse_req *req, struct file *file,
-				struct inode *inode)
+static void fuse_send_readpages(struct fuse_req *req, struct fuse_file *ff)
 {
-	struct fuse_conn *fc = get_fuse_conn(inode);
+	struct fuse_conn *fc = ff->fc;
 	loff_t pos = page_offset(req->pages[0]);
 	size_t count = req->num_pages << PAGE_CACHE_SHIFT;
 
 	req->out.argpages = 1;
 	req->out.page_zeroing = 1;
-	fuse_read_fill(req, file, inode, pos, count, FUSE_READ);
+	fuse_read_fill(req, ff, pos, count, FUSE_READ);
 	req->misc.read.attr_ver = fuse_get_attr_version(fc);
+
 	if (fc->async_read) {
-		struct fuse_file *ff = file->private_data;
 		req->ff = fuse_file_get(ff);
 		req->end = fuse_readpages_end;
 		fuse_request_send_background(fc, req);
 	} else {
+		req->ff = ff;
 		fuse_request_send(fc, req);
 		fuse_readpages_end(fc, req);
 		fuse_put_request(fc, req);
 	}
 }
 
-struct fuse_fill_data {
-	struct fuse_req *req;
-	struct file *file;
-	struct inode *inode;
-};
-
-static int fuse_readpages_fill(void *_data, struct page *page)
+static int fuse_readpages_fill(void *data, struct page *page)
 {
-	struct fuse_fill_data *data = _data;
-	struct fuse_req *req = data->req;
-	struct inode *inode = data->inode;
-	struct fuse_conn *fc = get_fuse_conn(inode);
+	struct fuse_req **reqp = data;
+	struct fuse_req *req = *reqp;
+	struct fuse_file *ff = req->ff;
+	struct fuse_conn *fc = ff->fc;
 
-	fuse_wait_on_page_writeback(inode, page->index);
+	fuse_wait_on_page_writeback(ff->fi, page->index);
 
 	if (req->num_pages &&
 	    (req->num_pages == FUSE_MAX_PAGES_PER_REQ ||
 	     (req->num_pages + 1) * PAGE_CACHE_SIZE > fc->max_read ||
 	     req->pages[req->num_pages - 1]->index + 1 != page->index)) {
-		fuse_send_readpages(req, data->file, inode);
-		data->req = req = fuse_get_req(fc);
+		fuse_send_readpages(req, ff);
+		*reqp = req = fuse_get_req(fc);
 		if (IS_ERR(req)) {
 			unlock_page(page);
 			return PTR_ERR(req);
@@ -568,28 +555,27 @@ static int fuse_readpages_fill(void *_data, struct page *page)
 static int fuse_readpages(struct file *file, struct address_space *mapping,
 			  struct list_head *pages, unsigned nr_pages)
 {
-	struct inode *inode = mapping->host;
-	struct fuse_conn *fc = get_fuse_conn(inode);
-	struct fuse_fill_data data;
+	struct fuse_file *ff = file->private_data;
+	struct fuse_conn *fc = ff->fc;
+	struct fuse_req *req;
 	int err;
 
 	err = -EIO;
-	if (is_bad_inode(inode))
+	if (fuse_file_bad(ff))
 		goto out;
 
-	data.file = file;
-	data.inode = inode;
-	data.req = fuse_get_req(fc);
-	err = PTR_ERR(data.req);
-	if (IS_ERR(data.req))
+	req = fuse_get_req(fc);
+	err = PTR_ERR(req);
+	if (IS_ERR(req))
 		goto out;
 
-	err = read_cache_pages(mapping, pages, fuse_readpages_fill, &data);
+	req->ff = ff;
+	err = read_cache_pages(mapping, pages, fuse_readpages_fill, &req);
 	if (!err) {
-		if (data.req->num_pages)
-			fuse_send_readpages(data.req, file, inode);
+		if (req->num_pages)
+			fuse_send_readpages(req, ff);
 		else
-			fuse_put_request(fc, data.req);
+			fuse_put_request(fc, req);
 	}
 out:
 	return err;
@@ -598,15 +584,16 @@ out:
 static ssize_t fuse_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
 				  unsigned long nr_segs, loff_t pos)
 {
-	struct inode *inode = iocb->ki_filp->f_mapping->host;
+	struct fuse_file *ff = iocb->ki_filp->private_data;
+	struct fuse_inode *fi = ff->fi;
 
-	if (pos + iov_length(iov, nr_segs) > i_size_read(inode)) {
+	if (pos + iov_length(iov, nr_segs) > i_size_read(&fi->inode)) {
 		int err;
 		/*
 		 * If trying to read past EOF, make sure the i_size
 		 * attribute is up-to-date.
 		 */
-		err = fuse_update_attributes(inode, NULL, iocb->ki_filp, NULL);
+		err = fuse_update_attributes(fi, NULL, iocb->ki_filp, NULL);
 		if (err)
 			return err;
 	}
@@ -614,11 +601,11 @@ static ssize_t fuse_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
 	return generic_file_aio_read(iocb, iov, nr_segs, pos);
 }
 
-static void fuse_write_fill(struct fuse_req *req, struct file *file,
-			    struct fuse_file *ff, struct inode *inode,
-			    loff_t pos, size_t count, int writepage)
+static void fuse_write_fill(struct fuse_req *req, struct fuse_file *ff,
+			    unsigned int f_flags, loff_t pos, size_t count,
+			    int writepage)
 {
-	struct fuse_conn *fc = get_fuse_conn(inode);
+	struct fuse_conn *fc = ff->fc;
 	struct fuse_write_in *inarg = &req->misc.write.in;
 	struct fuse_write_out *outarg = &req->misc.write.out;
 
@@ -627,9 +614,9 @@ static void fuse_write_fill(struct fuse_req *req, struct file *file,
 	inarg->offset = pos;
 	inarg->size = count;
 	inarg->write_flags = writepage ? FUSE_WRITE_CACHE : 0;
-	inarg->flags = file ? file->f_flags : 0;
+	inarg->flags = f_flags;
 	req->in.h.opcode = FUSE_WRITE;
-	req->in.h.nodeid = get_node_id(inode);
+	req->in.h.nodeid = fuse_file_nodeid(ff);
 	req->in.numargs = 2;
 	if (fc->minor < 9)
 		req->in.args[0].size = FUSE_COMPAT_WRITE_IN_SIZE;
@@ -642,12 +629,12 @@ static void fuse_write_fill(struct fuse_req *req, struct file *file,
 	req->out.args[0].value = outarg;
 }
 
-static size_t fuse_send_write(struct fuse_req *req, struct file *file,
-			      struct inode *inode, loff_t pos, size_t count,
-			      fl_owner_t owner)
+static size_t fuse_send_write(struct fuse_req *req, struct fuse_file *ff,
+			      loff_t pos, size_t count, fl_owner_t owner)
 {
-	struct fuse_conn *fc = get_fuse_conn(inode);
-	fuse_write_fill(req, file, file->private_data, inode, pos, count, 0);
+	struct fuse_conn *fc = ff->fc;
+
+	fuse_write_fill(req, ff, ff->file->f_flags, pos, count, 0);
 	if (owner != NULL) {
 		struct fuse_write_in *inarg = &req->misc.write.in;
 		inarg->write_flags |= FUSE_WRITE_LOCKOWNER;
@@ -669,35 +656,35 @@ static int fuse_write_begin(struct file *file, struct address_space *mapping,
 	return 0;
 }
 
-static void fuse_write_update_size(struct inode *inode, loff_t pos)
+static void fuse_write_update_size(struct fuse_file *ff, loff_t pos)
 {
-	struct fuse_conn *fc = get_fuse_conn(inode);
-	struct fuse_inode *fi = get_fuse_inode(inode);
+	struct fuse_conn *fc = ff->fc;
+	struct fuse_inode *fi = ff->fi;
 
 	spin_lock(&fc->lock);
 	fi->attr_version = ++fc->attr_version;
-	if (pos > inode->i_size)
-		i_size_write(inode, pos);
+	if (pos > fi->inode.i_size)
+		i_size_write(&fi->inode, pos);
 	spin_unlock(&fc->lock);
 }
 
-static int fuse_buffered_write(struct file *file, struct inode *inode,
-			       loff_t pos, unsigned count, struct page *page)
+static int fuse_buffered_write(struct fuse_file *ff, loff_t pos, unsigned count,
+			       struct page *page)
 {
 	int err;
 	size_t nres;
-	struct fuse_conn *fc = get_fuse_conn(inode);
+	struct fuse_conn *fc = ff->fc;
 	unsigned offset = pos & (PAGE_CACHE_SIZE - 1);
 	struct fuse_req *req;
 
-	if (is_bad_inode(inode))
+	if (fuse_file_bad(ff))
 		return -EIO;
 
 	/*
 	 * Make sure writepages on the same page are not mixed up with
 	 * plain writes.
 	 */
-	fuse_wait_on_page_writeback(inode, page->index);
+	fuse_wait_on_page_writeback(ff->fi, page->index);
 
 	req = fuse_get_req(fc);
 	if (IS_ERR(req))
@@ -707,18 +694,18 @@ static int fuse_buffered_write(struct file *file, struct inode *inode,
 	req->num_pages = 1;
 	req->pages[0] = page;
 	req->page_offset = offset;
-	nres = fuse_send_write(req, file, inode, pos, count, NULL);
+	nres = fuse_send_write(req, ff, pos, count, NULL);
 	err = req->out.h.error;
 	fuse_put_request(fc, req);
 	if (!err && !nres)
 		err = -EIO;
 	if (!err) {
 		pos += nres;
-		fuse_write_update_size(inode, pos);
+		fuse_write_update_size(ff, pos);
 		if (count == PAGE_CACHE_SIZE)
 			SetPageUptodate(page);
 	}
-	fuse_invalidate_attr(inode);
+	fuse_invalidate_attr(ff->fi);
 	return err ? err : nres;
 }
 
@@ -726,29 +713,28 @@ static int fuse_write_end(struct file *file, struct address_space *mapping,
 			loff_t pos, unsigned len, unsigned copied,
 			struct page *page, void *fsdata)
 {
-	struct inode *inode = mapping->host;
+	struct fuse_file *ff = file->private_data;
 	int res = 0;
 
 	if (copied)
-		res = fuse_buffered_write(file, inode, pos, copied, page);
+		res = fuse_buffered_write(ff, pos, copied, page);
 
 	unlock_page(page);
 	page_cache_release(page);
 	return res;
 }
 
-static size_t fuse_send_write_pages(struct fuse_req *req, struct file *file,
-				    struct inode *inode, loff_t pos,
-				    size_t count)
+static size_t fuse_send_write_pages(struct fuse_req *req, struct fuse_file *ff,
+				    loff_t pos, size_t count)
 {
 	size_t res;
 	unsigned offset;
 	unsigned i;
 
 	for (i = 0; i < req->num_pages; i++)
-		fuse_wait_on_page_writeback(inode, req->pages[i]->index);
+		fuse_wait_on_page_writeback(ff->fi, req->pages[i]->index);
 
-	res = fuse_send_write(req, file, inode, pos, count, NULL);
+	res = fuse_send_write(req, ff, pos, count, NULL);
 
 	offset = req->page_offset;
 	count = res;
@@ -771,11 +757,11 @@ static size_t fuse_send_write_pages(struct fuse_req *req, struct file *file,
 	return res;
 }
 
-static ssize_t fuse_fill_write_pages(struct fuse_req *req,
-			       struct address_space *mapping,
-			       struct iov_iter *ii, loff_t pos)
+static ssize_t fuse_fill_write_pages(struct fuse_req *req, struct fuse_file *ff,
+				     struct iov_iter *ii, loff_t pos)
 {
-	struct fuse_conn *fc = get_fuse_conn(mapping->host);
+	struct fuse_conn *fc = ff->fc;
+	struct address_space *mapping = ff->file->f_mapping;
 	unsigned offset = pos & (PAGE_CACHE_SIZE - 1);
 	size_t count = 0;
 	int err;
@@ -833,16 +819,14 @@ static ssize_t fuse_fill_write_pages(struct fuse_req *req,
 	return count > 0 ? count : err;
 }
 
-static ssize_t fuse_perform_write(struct file *file,
-				  struct address_space *mapping,
-				  struct iov_iter *ii, loff_t pos)
+static ssize_t fuse_perform_write(struct fuse_file *ff, struct iov_iter *ii,
+				  loff_t pos)
 {
-	struct inode *inode = mapping->host;
-	struct fuse_conn *fc = get_fuse_conn(inode);
+	struct fuse_conn *fc = ff->fc;
 	int err = 0;
 	ssize_t res = 0;
 
-	if (is_bad_inode(inode))
+	if (fuse_file_bad(ff))
 		return -EIO;
 
 	do {
@@ -855,14 +839,14 @@ static ssize_t fuse_perform_write(struct file *file,
 			break;
 		}
 
-		count = fuse_fill_write_pages(req, mapping, ii, pos);
+		count = fuse_fill_write_pages(req, ff, ii, pos);
 		if (count <= 0) {
 			err = count;
 		} else {
 			size_t num_written;
 
-			num_written = fuse_send_write_pages(req, file, inode,
-							    pos, count);
+			num_written = fuse_send_write_pages(req, ff, pos,
+							    count);
 			err = req->out.h.error;
 			if (!err) {
 				res += num_written;
@@ -877,9 +861,9 @@ static ssize_t fuse_perform_write(struct file *file,
 	} while (!err && iov_iter_count(ii));
 
 	if (res > 0)
-		fuse_write_update_size(inode, pos);
+		fuse_write_update_size(ff, pos);
 
-	fuse_invalidate_attr(inode);
+	fuse_invalidate_attr(ff->fi);
 
 	return res > 0 ? res : err;
 }
@@ -887,11 +871,11 @@ static ssize_t fuse_perform_write(struct file *file,
 static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
 				   unsigned long nr_segs, loff_t pos)
 {
-	struct file *file = iocb->ki_filp;
-	struct address_space *mapping = file->f_mapping;
+	struct fuse_file *ff = iocb->ki_filp->private_data;
+	struct address_space *mapping = ff->file->f_mapping;
 	size_t count = 0;
 	ssize_t written = 0;
-	struct inode *inode = mapping->host;
+	struct inode *vfs_inode = fuse_file_vfs_inode(ff);
 	ssize_t err;
 	struct iov_iter i;
 
@@ -901,33 +885,34 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
 	if (err)
 		return err;
 
-	mutex_lock(&inode->i_mutex);
-	vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
+	mutex_lock(&vfs_inode->i_mutex);
+	vfs_check_frozen(vfs_inode->i_sb, SB_FREEZE_WRITE);
 
 	/* We can write back this queue in page reclaim */
 	current->backing_dev_info = mapping->backing_dev_info;
 
-	err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode));
+	err = generic_write_checks(ff->file, &pos, &count,
+				   S_ISBLK(vfs_inode->i_mode));
 	if (err)
 		goto out;
 
 	if (count == 0)
 		goto out;
 
-	err = file_remove_suid(file);
+	err = file_remove_suid(ff->file);
 	if (err)
 		goto out;
 
-	file_update_time(file);
+	file_update_time(ff->file);
 
 	iov_iter_init(&i, iov, nr_segs, count, 0);
-	written = fuse_perform_write(file, mapping, &i, pos);
+	written = fuse_perform_write(ff, &i, pos);
 	if (written >= 0)
 		iocb->ki_pos = pos + written;
 
 out:
 	current->backing_dev_info = NULL;
-	mutex_unlock(&inode->i_mutex);
+	mutex_unlock(&vfs_inode->i_mutex);
 
 	return written ? written : err;
 }
@@ -986,17 +971,16 @@ static int fuse_get_user_pages(struct fuse_req *req, const char __user *buf,
 	return 0;
 }
 
-static ssize_t fuse_direct_io(struct file *file, const char __user *buf,
+static ssize_t fuse_direct_io(struct fuse_file *ff, const char __user *buf,
 			      size_t count, loff_t *ppos, int write)
 {
-	struct inode *inode = file->f_path.dentry->d_inode;
-	struct fuse_conn *fc = get_fuse_conn(inode);
+	struct fuse_conn *fc = ff->fc;
 	size_t nmax = write ? fc->max_write : fc->max_read;
 	loff_t pos = *ppos;
 	ssize_t res = 0;
 	struct fuse_req *req;
 
-	if (is_bad_inode(inode))
+	if (fuse_file_bad(ff))
 		return -EIO;
 
 	req = fuse_get_req(fc);
@@ -1013,10 +997,10 @@ static ssize_t fuse_direct_io(struct file *file, const char __user *buf,
 		}
 
 		if (write)
-			nres = fuse_send_write(req, file, inode, pos, nbytes,
+			nres = fuse_send_write(req, ff, pos, nbytes,
 					       current->files);
 		else
-			nres = fuse_send_read(req, file, inode, pos, nbytes,
+			nres = fuse_send_read(req, ff, pos, nbytes,
 					      current->files);
 		fuse_release_user_pages(req, !write);
 		if (req->out.h.error) {
@@ -1043,10 +1027,10 @@ static ssize_t fuse_direct_io(struct file *file, const char __user *buf,
 	fuse_put_request(fc, req);
 	if (res > 0) {
 		if (write)
-			fuse_write_update_size(inode, pos);
+			fuse_write_update_size(ff, pos);
 		*ppos = pos;
 	}
-	fuse_invalidate_attr(inode);
+	fuse_invalidate_attr(ff->fi);
 
 	return res;
 }
@@ -1054,20 +1038,24 @@ static ssize_t fuse_direct_io(struct file *file, const char __user *buf,
 static ssize_t fuse_direct_read(struct file *file, char __user *buf,
 				     size_t count, loff_t *ppos)
 {
-	return fuse_direct_io(file, buf, count, ppos, 0);
+	struct fuse_file *ff = file->private_data;
+
+	return fuse_direct_io(ff, buf, count, ppos, 0);
 }
 
 static ssize_t fuse_direct_write(struct file *file, const char __user *buf,
 				 size_t count, loff_t *ppos)
 {
-	struct inode *inode = file->f_path.dentry->d_inode;
+	struct fuse_file *ff = file->private_data;
+	struct inode *vfs_inode = fuse_file_vfs_inode(ff);
 	ssize_t res;
+
 	/* Don't allow parallel writes to the same file */
-	mutex_lock(&inode->i_mutex);
+	mutex_lock(&vfs_inode->i_mutex);
 	res = generic_write_checks(file, ppos, &count, 0);
 	if (!res)
-		res = fuse_direct_io(file, buf, count, ppos, 1);
-	mutex_unlock(&inode->i_mutex);
+		res = fuse_direct_io(ff, buf, count, ppos, 1);
+	mutex_unlock(&vfs_inode->i_mutex);
 	return res;
 }
 
@@ -1079,9 +1067,8 @@ static void fuse_writepage_free(struct fuse_conn *fc, struct fuse_req *req)
 
 static void fuse_writepage_finish(struct fuse_conn *fc, struct fuse_req *req)
 {
-	struct inode *inode = req->inode;
-	struct fuse_inode *fi = get_fuse_inode(inode);
-	struct backing_dev_info *bdi = inode->i_mapping->backing_dev_info;
+	struct fuse_inode *fi = req->fi;
+	struct backing_dev_info *bdi = fi->inode.i_mapping->backing_dev_info;
 
 	list_del(&req->writepages_entry);
 	dec_bdi_stat(bdi, BDI_WRITEBACK);
@@ -1095,8 +1082,8 @@ static void fuse_send_writepage(struct fuse_conn *fc, struct fuse_req *req)
 __releases(&fc->lock)
 __acquires(&fc->lock)
 {
-	struct fuse_inode *fi = get_fuse_inode(req->inode);
-	loff_t size = i_size_read(req->inode);
+	struct fuse_inode *fi = req->fi;
+	loff_t size = i_size_read(&fi->inode);
 	struct fuse_write_in *inarg = &req->misc.write.in;
 
 	if (!fc->connected)
@@ -1130,12 +1117,11 @@ __acquires(&fc->lock)
  *
  * Called with fc->lock
  */
-void fuse_flush_writepages(struct inode *inode)
+void fuse_flush_writepages(struct fuse_inode *fi)
 __releases(&fc->lock)
 __acquires(&fc->lock)
 {
-	struct fuse_conn *fc = get_fuse_conn(inode);
-	struct fuse_inode *fi = get_fuse_inode(inode);
+	struct fuse_conn *fc = get_fuse_conn(&fi->inode);
 	struct fuse_req *req;
 
 	while (fi->writectr >= 0 && !list_empty(&fi->queued_writes)) {
@@ -1147,10 +1133,9 @@ __acquires(&fc->lock)
 
 static void fuse_writepage_end(struct fuse_conn *fc, struct fuse_req *req)
 {
-	struct inode *inode = req->inode;
-	struct fuse_inode *fi = get_fuse_inode(inode);
+	struct fuse_inode *fi = req->fi;
 
-	mapping_set_error(inode->i_mapping, req->out.h.error);
+	mapping_set_error(fi->inode.i_mapping, req->out.h.error);
 	spin_lock(&fc->lock);
 	fi->writectr--;
 	fuse_writepage_finish(fc, req);
@@ -1184,7 +1169,7 @@ static int fuse_writepage_locked(struct page *page)
 	req->ff = fuse_file_get(ff);
 	spin_unlock(&fc->lock);
 
-	fuse_write_fill(req, NULL, ff, inode, page_offset(page), 0, 1);
+	fuse_write_fill(req, ff, 0, page_offset(page), 0, 1);
 
 	copy_highpage(tmp_page, page);
 	req->in.argpages = 1;
@@ -1192,7 +1177,7 @@ static int fuse_writepage_locked(struct page *page)
 	req->pages[0] = tmp_page;
 	req->page_offset = 0;
 	req->end = fuse_writepage_end;
-	req->inode = inode;
+	req->fi = fi;
 
 	inc_bdi_stat(mapping->backing_dev_info, BDI_WRITEBACK);
 	inc_zone_page_state(tmp_page, NR_WRITEBACK_TEMP);
@@ -1201,7 +1186,7 @@ static int fuse_writepage_locked(struct page *page)
 	spin_lock(&fc->lock);
 	list_add(&req->writepages_entry, &fi->writepages);
 	list_add_tail(&req->list, &fi->queued_writes);
-	fuse_flush_writepages(inode);
+	fuse_flush_writepages(fi);
 	spin_unlock(&fc->lock);
 
 	return 0;
@@ -1227,10 +1212,11 @@ static int fuse_launder_page(struct page *page)
 {
 	int err = 0;
 	if (clear_page_dirty_for_io(page)) {
-		struct inode *inode = page->mapping->host;
+		struct fuse_inode *fi = get_fuse_inode(page->mapping->host);
+
 		err = fuse_writepage_locked(page);
 		if (!err)
-			fuse_wait_on_page_writeback(inode, page->index);
+			fuse_wait_on_page_writeback(fi, page->index);
 	}
 	return err;
 }
@@ -1266,9 +1252,9 @@ static int fuse_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
 	 * Don't use page->mapping as it may become NULL from a
 	 * concurrent truncate.
 	 */
-	struct inode *inode = vma->vm_file->f_mapping->host;
+	struct fuse_inode *fi = get_fuse_inode(vma->vm_file->f_mapping->host);
 
-	fuse_wait_on_page_writeback(inode, page->index);
+	fuse_wait_on_page_writeback(fi, page->index);
 	return 0;
 }
 
@@ -1281,17 +1267,15 @@ static struct vm_operations_struct fuse_file_vm_ops = {
 static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma)
 {
 	if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE)) {
-		struct inode *inode = file->f_dentry->d_inode;
-		struct fuse_conn *fc = get_fuse_conn(inode);
-		struct fuse_inode *fi = get_fuse_inode(inode);
 		struct fuse_file *ff = file->private_data;
+		struct fuse_conn *fc = ff->fc;
 		/*
 		 * file may be written through mmap, so chain it onto the
 		 * inodes's write_file list
 		 */
 		spin_lock(&fc->lock);
 		if (list_empty(&ff->write_entry))
-			list_add(&ff->write_entry, &fi->write_files);
+			list_add(&ff->write_entry, &ff->fi->write_files);
 		spin_unlock(&fc->lock);
 	}
 	file_accessed(file);
@@ -1333,13 +1317,11 @@ static int convert_fuse_file_lock(const struct fuse_file_lock *ffl,
 	return 0;
 }
 
-static void fuse_lk_fill(struct fuse_req *req, struct file *file,
+static void fuse_lk_fill(struct fuse_req *req, struct fuse_file *ff,
 			 const struct file_lock *fl, int opcode, pid_t pid,
 			 int flock)
 {
-	struct inode *inode = file->f_path.dentry->d_inode;
-	struct fuse_conn *fc = get_fuse_conn(inode);
-	struct fuse_file *ff = file->private_data;
+	struct fuse_conn *fc = ff->fc;
 	struct fuse_lk_in *arg = &req->misc.lk_in;
 
 	arg->fh = ff->fh;
@@ -1351,16 +1333,15 @@ static void fuse_lk_fill(struct fuse_req *req, struct file *file,
 	if (flock)
 		arg->lk_flags |= FUSE_LK_FLOCK;
 	req->in.h.opcode = opcode;
-	req->in.h.nodeid = get_node_id(inode);
+	req->in.h.nodeid = fuse_file_nodeid(ff);
 	req->in.numargs = 1;
 	req->in.args[0].size = sizeof(*arg);
 	req->in.args[0].value = arg;
 }
 
-static int fuse_getlk(struct file *file, struct file_lock *fl)
+static int fuse_getlk(struct fuse_file *ff, struct file_lock *fl)
 {
-	struct inode *inode = file->f_path.dentry->d_inode;
-	struct fuse_conn *fc = get_fuse_conn(inode);
+	struct fuse_conn *fc = ff->fc;
 	struct fuse_req *req;
 	struct fuse_lk_out outarg;
 	int err;
@@ -1369,7 +1350,7 @@ static int fuse_getlk(struct file *file, struct file_lock *fl)
 	if (IS_ERR(req))
 		return PTR_ERR(req);
 
-	fuse_lk_fill(req, file, fl, FUSE_GETLK, 0, 0);
+	fuse_lk_fill(req, ff, fl, FUSE_GETLK, 0, 0);
 	req->out.numargs = 1;
 	req->out.args[0].size = sizeof(outarg);
 	req->out.args[0].value = &outarg;
@@ -1382,10 +1363,9 @@ static int fuse_getlk(struct file *file, struct file_lock *fl)
 	return err;
 }
 
-static int fuse_setlk(struct file *file, struct file_lock *fl, int flock)
+static int fuse_setlk(struct fuse_file *ff, struct file_lock *fl, int flock)
 {
-	struct inode *inode = file->f_path.dentry->d_inode;
-	struct fuse_conn *fc = get_fuse_conn(inode);
+	struct fuse_conn *fc = ff->fc;
 	struct fuse_req *req;
 	int opcode = (fl->fl_flags & FL_SLEEP) ? FUSE_SETLKW : FUSE_SETLK;
 	pid_t pid = fl->fl_type != F_UNLCK ? current->tgid : 0;
@@ -1404,7 +1384,7 @@ static int fuse_setlk(struct file *file, struct file_lock *fl, int flock)
 	if (IS_ERR(req))
 		return PTR_ERR(req);
 
-	fuse_lk_fill(req, file, fl, opcode, pid, flock);
+	fuse_lk_fill(req, ff, fl, opcode, pid, flock);
 	fuse_request_send(fc, req);
 	err = req->out.h.error;
 	/* locking is restartable */
@@ -1416,8 +1396,8 @@ static int fuse_setlk(struct file *file, struct file_lock *fl, int flock)
 
 static int fuse_file_lock(struct file *file, int cmd, struct file_lock *fl)
 {
-	struct inode *inode = file->f_path.dentry->d_inode;
-	struct fuse_conn *fc = get_fuse_conn(inode);
+	struct fuse_file *ff = file->private_data;
+	struct fuse_conn *fc = ff->fc;
 	int err;
 
 	if (cmd == F_CANCELLK) {
@@ -1427,20 +1407,20 @@ static int fuse_file_lock(struct file *file, int cmd, struct file_lock *fl)
 			posix_test_lock(file, fl);
 			err = 0;
 		} else
-			err = fuse_getlk(file, fl);
+			err = fuse_getlk(ff, fl);
 	} else {
 		if (fc->no_lock)
 			err = posix_lock_file(file, fl, NULL);
 		else
-			err = fuse_setlk(file, fl, 0);
+			err = fuse_setlk(ff, fl, 0);
 	}
 	return err;
 }
 
 static int fuse_file_flock(struct file *file, int cmd, struct file_lock *fl)
 {
-	struct inode *inode = file->f_path.dentry->d_inode;
-	struct fuse_conn *fc = get_fuse_conn(inode);
+	struct fuse_file *ff = file->private_data;
+	struct fuse_conn *fc = ff->fc;
 	int err;
 
 	if (fc->no_lock) {
@@ -1448,7 +1428,7 @@ static int fuse_file_flock(struct file *file, int cmd, struct file_lock *fl)
 	} else {
 		/* emulate flock with POSIX locks */
 		fl->fl_owner = (fl_owner_t) file;
-		err = fuse_setlk(file, fl, 1);
+		err = fuse_setlk(ff, fl, 1);
 	}
 
 	return err;
@@ -1493,21 +1473,22 @@ static sector_t fuse_bmap(struct address_space *mapping, sector_t block)
 static loff_t fuse_file_llseek(struct file *file, loff_t offset, int origin)
 {
 	loff_t retval;
-	struct inode *inode = file->f_path.dentry->d_inode;
+	struct fuse_file *ff = file->private_data;
+	struct inode *vfs_inode = fuse_file_vfs_inode(ff);
 
-	mutex_lock(&inode->i_mutex);
+	mutex_lock(&vfs_inode->i_mutex);
 	switch (origin) {
 	case SEEK_END:
-		retval = fuse_update_attributes(inode, NULL, file, NULL);
+		retval = fuse_update_attributes(ff->fi, NULL, file, NULL);
 		if (retval)
 			goto exit;
-		offset += i_size_read(inode);
+		offset += i_size_read(vfs_inode);
 		break;
 	case SEEK_CUR:
 		offset += file->f_pos;
 	}
 	retval = -EINVAL;
-	if (offset >= 0 && offset <= inode->i_sb->s_maxbytes) {
+	if (offset >= 0 && offset <= vfs_inode->i_sb->s_maxbytes) {
 		if (offset != file->f_pos) {
 			file->f_pos = offset;
 			file->f_version = 0;
@@ -1515,7 +1496,7 @@ static loff_t fuse_file_llseek(struct file *file, loff_t offset, int origin)
 		retval = offset;
 	}
 exit:
-	mutex_unlock(&inode->i_mutex);
+	mutex_unlock(&vfs_inode->i_mutex);
 	return retval;
 }
 
@@ -1608,12 +1589,10 @@ static int fuse_ioctl_copy_user(struct page **pages, struct iovec *iov,
  * limits ioctl data transfers to well-formed ioctls and is the forced
  * behavior for all FUSE servers.
  */
-static long fuse_file_ioctl_common(struct file *file, unsigned int cmd,
+static long fuse_file_ioctl_common(struct fuse_file *ff, unsigned int cmd,
 				   unsigned long arg, unsigned int flags)
 {
-	struct inode *inode = file->f_dentry->d_inode;
-	struct fuse_file *ff = file->private_data;
-	struct fuse_conn *fc = get_fuse_conn(inode);
+	struct fuse_conn *fc = ff->fc;
 	struct fuse_ioctl_in inarg = {
 		.fh = ff->fh,
 		.cmd = cmd,
@@ -1636,7 +1615,7 @@ static long fuse_file_ioctl_common(struct file *file, unsigned int cmd,
 		return -EACCES;
 
 	err = -EIO;
-	if (is_bad_inode(inode))
+	if (fuse_file_bad(ff))
 		goto out;
 
 	err = -ENOMEM;
@@ -1699,7 +1678,7 @@ static long fuse_file_ioctl_common(struct file *file, unsigned int cmd,
 
 	/* okay, let's send it to the client */
 	req->in.h.opcode = FUSE_IOCTL;
-	req->in.h.nodeid = get_node_id(inode);
+	req->in.h.nodeid = fuse_file_nodeid(ff);
 	req->in.numargs = 1;
 	req->in.args[0].size = sizeof(inarg);
 	req->in.args[0].value = &inarg;
@@ -1786,13 +1765,17 @@ static long fuse_file_ioctl_common(struct file *file, unsigned int cmd,
 static long fuse_file_ioctl(struct file *file, unsigned int cmd,
 			    unsigned long arg)
 {
-	return fuse_file_ioctl_common(file, cmd, arg, 0);
+	struct fuse_file *ff = file->private_data;
+
+	return fuse_file_ioctl_common(ff, cmd, arg, 0);
 }
 
 static long fuse_file_compat_ioctl(struct file *file, unsigned int cmd,
 				   unsigned long arg)
 {
-	return fuse_file_ioctl_common(file, cmd, arg, FUSE_IOCTL_COMPAT);
+	struct fuse_file *ff = file->private_data;
+
+	return fuse_file_ioctl_common(ff, cmd, arg, FUSE_IOCTL_COMPAT);
 }
 
 /*
@@ -1831,9 +1814,10 @@ static struct rb_node **fuse_find_polled_node(struct fuse_conn *fc, u64 kh,
  * not removed before the file is released.  This is because a file
  * polled once is likely to be polled again.
  */
-static void fuse_register_polled_file(struct fuse_conn *fc,
-				      struct fuse_file *ff)
+static void fuse_register_polled_file(struct fuse_file *ff)
 {
+	struct fuse_conn *fc = ff->fc;
+
 	spin_lock(&fc->lock);
 	if (RB_EMPTY_NODE(&ff->polled_node)) {
 		struct rb_node **link, *parent;
@@ -1848,9 +1832,8 @@ static void fuse_register_polled_file(struct fuse_conn *fc,
 
 static unsigned fuse_file_poll(struct file *file, poll_table *wait)
 {
-	struct inode *inode = file->f_dentry->d_inode;
 	struct fuse_file *ff = file->private_data;
-	struct fuse_conn *fc = get_fuse_conn(inode);
+	struct fuse_conn *fc = ff->fc;
 	struct fuse_poll_in inarg = { .fh = ff->fh, .kh = ff->kh };
 	struct fuse_poll_out outarg;
 	struct fuse_req *req;
@@ -1867,7 +1850,7 @@ static unsigned fuse_file_poll(struct file *file, poll_table *wait)
 	 */
 	if (waitqueue_active(&ff->poll_wait)) {
 		inarg.flags |= FUSE_POLL_SCHEDULE_NOTIFY;
-		fuse_register_polled_file(fc, ff);
+		fuse_register_polled_file(ff);
 	}
 
 	req = fuse_get_req(fc);
@@ -1875,7 +1858,7 @@ static unsigned fuse_file_poll(struct file *file, poll_table *wait)
 		return PTR_ERR(req);
 
 	req->in.h.opcode = FUSE_POLL;
-	req->in.h.nodeid = get_node_id(inode);
+	req->in.h.nodeid = fuse_file_nodeid(ff);
 	req->in.numargs = 1;
 	req->in.args[0].size = sizeof(inarg);
 	req->in.args[0].value = &inarg;
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 6fc5aed..c9ffaa0 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -102,6 +102,15 @@ struct fuse_file {
 	/** Request reserved for flush and release */
 	struct fuse_req *reserved_req;
 
+	/** The fuse_conn this ff is associated with */
+	struct fuse_conn *fc;
+
+	/** The fuse_inode this ff is associated with */
+	struct fuse_inode *fi;
+
+	/** The VFS file this ff is associated with */
+	struct file *file;
+
 	/** Kernel file handle guaranteed to be unique */
 	u64 kh;
 
@@ -277,7 +286,7 @@ struct fuse_req {
 	struct fuse_file *ff;
 
 	/** Inode used in the request or NULL */
-	struct inode *inode;
+	struct fuse_inode *fi;
 
 	/** Link on fi->writepages */
 	struct list_head writepages_entry;
@@ -490,6 +499,21 @@ static inline u64 get_node_id(struct inode *inode)
 	return get_fuse_inode(inode)->nodeid;
 }
 
+static inline u64 fuse_file_nodeid(struct fuse_file *ff)
+{
+	return ff->fi->nodeid;
+}
+
+static inline struct inode *fuse_file_vfs_inode(struct fuse_file *ff)
+{
+	return ff->file->f_dentry->d_inode;
+}
+
+static inline bool fuse_file_bad(struct fuse_file *ff)
+{
+	return is_bad_inode(fuse_file_vfs_inode(ff));
+}
+
 /** Device operations */
 extern const struct file_operations fuse_dev_operations;
 
@@ -514,18 +538,19 @@ void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req,
 /**
  * Initialize READ or READDIR request
  */
-void fuse_read_fill(struct fuse_req *req, struct file *file,
-		    struct inode *inode, loff_t pos, size_t count, int opcode);
+void fuse_read_fill(struct fuse_req *req, struct fuse_file *ff,
+		    loff_t pos, size_t count, int opcode);
 
 /**
  * Send OPEN or OPENDIR request
  */
-int fuse_open_common(struct inode *inode, struct file *file, int isdir);
+int fuse_open_common(struct fuse_conn *fc, struct fuse_inode *fi,
+		     struct inode *inode, struct file *file, int isdir);
 
-struct fuse_file *fuse_file_alloc(struct fuse_conn *fc);
+struct fuse_file *fuse_file_alloc(struct fuse_conn *fc, struct fuse_inode *fi,
+				  struct file *file);
 void fuse_file_free(struct fuse_file *ff);
-void fuse_finish_open(struct inode *inode, struct file *file,
-		      struct fuse_file *ff, struct fuse_open_out *outarg);
+void fuse_finish_open(struct fuse_file *ff, struct fuse_open_out *outarg);
 
 /** Fill in ff->reserved_req with a RELEASE request */
 void fuse_release_fill(struct fuse_file *ff, u64 nodeid, int flags, int opcode);
@@ -533,13 +558,12 @@ void fuse_release_fill(struct fuse_file *ff, u64 nodeid, int flags, int opcode);
 /**
  * Send RELEASE or RELEASEDIR request
  */
-int fuse_release_common(struct inode *inode, struct file *file, int isdir);
+void fuse_release_common(struct fuse_file *ff, int isdir);
 
 /**
  * Send FSYNC or FSYNCDIR request
  */
-int fuse_fsync_common(struct file *file, struct dentry *de, int datasync,
-		      int isdir);
+int fuse_fsync_common(struct fuse_file *ff, int datasync, int isdir);
 
 /**
  * Notify poll wakeup
@@ -643,7 +667,7 @@ void fuse_abort_conn(struct fuse_conn *fc);
 /**
  * Invalidate inode attributes
  */
-void fuse_invalidate_attr(struct inode *inode);
+void fuse_invalidate_attr(struct fuse_inode *fi);
 
 void fuse_invalidate_entry_cache(struct dentry *entry);
 
@@ -684,13 +708,13 @@ int fuse_allow_task(struct fuse_conn *fc, struct task_struct *task);
 
 u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id);
 
-int fuse_update_attributes(struct inode *inode, struct kstat *stat,
+int fuse_update_attributes(struct fuse_inode *fi, struct kstat *stat,
 			   struct file *file, bool *refreshed);
 
-void fuse_flush_writepages(struct inode *inode);
+void fuse_flush_writepages(struct fuse_inode *fi);
 
-void fuse_set_nowrite(struct inode *inode);
-void fuse_release_nowrite(struct inode *inode);
+void fuse_set_nowrite(struct fuse_inode *fi);
+void fuse_release_nowrite(struct fuse_inode *fi);
 
 u64 fuse_get_attr_version(struct fuse_conn *fc);
 
-- 
1.6.0.2


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 3/6] FUSE: make ff->fi optional
  2009-04-14  1:54 [PATCHSET] CUSE: implement CUSE, take #3 Tejun Heo
  2009-04-14  1:54 ` [PATCH 1/6] FUSE: misc cleanups Tejun Heo
  2009-04-14  1:54 ` [PATCH 2/6] FUSE: use fuse objects as the main objects in file handling functions Tejun Heo
@ 2009-04-14  1:54 ` Tejun Heo
  2009-04-14  1:54 ` [PATCH 4/6] FUSE: update fuse_conn_init() and separate out fuse_conn_kill() Tejun Heo
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 17+ messages in thread
From: Tejun Heo @ 2009-04-14  1:54 UTC (permalink / raw)
  To: linux-kernel, fuse-devel, miklos; +Cc: Tejun Heo

Allow ff->fi to be NULL.  fi related operations are skipped if ff->fi
is unavailable and nodeid is assumed to be zero.

Signed-off-by: Tejun Heo <tj@kernel.org>
---
 fs/fuse/file.c   |   58 +++++++++++++++++++++++++++++++++++++----------------
 fs/fuse/fuse_i.h |    2 +-
 2 files changed, 41 insertions(+), 19 deletions(-)

diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index f32d2c8..54ad406 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -339,7 +339,8 @@ int fuse_fsync_common(struct fuse_file *ff, int datasync, int isdir)
 	if (err)
 		return err;
 
-	fuse_sync_writes(ff->fi);
+	if (ff->fi)
+		fuse_sync_writes(ff->fi);
 
 	req = fuse_get_req(fc);
 	if (IS_ERR(req))
@@ -413,6 +414,9 @@ static void fuse_read_update_size(struct fuse_file *ff, loff_t size,
 	struct fuse_conn *fc = ff->fc;
 	struct fuse_inode *fi = ff->fi;
 
+	if (!fi)
+		return;
+
 	spin_lock(&fc->lock);
 	if (attr_ver == fi->attr_version && size < fi->inode.i_size) {
 		fi->attr_version = ++fc->attr_version;
@@ -441,7 +445,8 @@ static int fuse_readpage(struct file *file, struct page *page)
 	 * page-cache page, so make sure we read a properly synced
 	 * page.
 	 */
-	fuse_wait_on_page_writeback(ff->fi, page->index);
+	if (ff->fi)
+		fuse_wait_on_page_writeback(ff->fi, page->index);
 
 	req = fuse_get_req(fc);
 	err = PTR_ERR(req);
@@ -468,7 +473,8 @@ static int fuse_readpage(struct file *file, struct page *page)
 		SetPageUptodate(page);
 	}
 
-	fuse_invalidate_attr(ff->fi); /* atime changed */
+	if (ff->fi)
+		fuse_invalidate_attr(ff->fi); /* atime changed */
  out:
 	unlock_page(page);
 	return err;
@@ -489,7 +495,8 @@ static void fuse_readpages_end(struct fuse_conn *fc, struct fuse_req *req)
 		fuse_read_update_size(ff, pos, req->misc.read.attr_ver);
 	}
 
-	fuse_invalidate_attr(ff->fi); /* atime changed */
+	if (ff->fi)
+		fuse_invalidate_attr(ff->fi); /* atime changed */
 
 	for (i = 0; i < req->num_pages; i++) {
 		struct page *page = req->pages[i];
@@ -534,7 +541,8 @@ static int fuse_readpages_fill(void *data, struct page *page)
 	struct fuse_file *ff = req->ff;
 	struct fuse_conn *fc = ff->fc;
 
-	fuse_wait_on_page_writeback(ff->fi, page->index);
+	if (ff->fi)
+		fuse_wait_on_page_writeback(ff->fi, page->index);
 
 	if (req->num_pages &&
 	    (req->num_pages == FUSE_MAX_PAGES_PER_REQ ||
@@ -587,7 +595,7 @@ static ssize_t fuse_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
 	struct fuse_file *ff = iocb->ki_filp->private_data;
 	struct fuse_inode *fi = ff->fi;
 
-	if (pos + iov_length(iov, nr_segs) > i_size_read(&fi->inode)) {
+	if (fi && pos + iov_length(iov, nr_segs) > i_size_read(&fi->inode)) {
 		int err;
 		/*
 		 * If trying to read past EOF, make sure the i_size
@@ -661,6 +669,9 @@ static void fuse_write_update_size(struct fuse_file *ff, loff_t pos)
 	struct fuse_conn *fc = ff->fc;
 	struct fuse_inode *fi = ff->fi;
 
+	if (!fi)
+		return;
+
 	spin_lock(&fc->lock);
 	fi->attr_version = ++fc->attr_version;
 	if (pos > fi->inode.i_size)
@@ -684,7 +695,8 @@ static int fuse_buffered_write(struct fuse_file *ff, loff_t pos, unsigned count,
 	 * Make sure writepages on the same page are not mixed up with
 	 * plain writes.
 	 */
-	fuse_wait_on_page_writeback(ff->fi, page->index);
+	if (ff->fi)
+		fuse_wait_on_page_writeback(ff->fi, page->index);
 
 	req = fuse_get_req(fc);
 	if (IS_ERR(req))
@@ -705,7 +717,8 @@ static int fuse_buffered_write(struct fuse_file *ff, loff_t pos, unsigned count,
 		if (count == PAGE_CACHE_SIZE)
 			SetPageUptodate(page);
 	}
-	fuse_invalidate_attr(ff->fi);
+	if (ff->fi)
+		fuse_invalidate_attr(ff->fi);
 	return err ? err : nres;
 }
 
@@ -731,8 +744,10 @@ static size_t fuse_send_write_pages(struct fuse_req *req, struct fuse_file *ff,
 	unsigned offset;
 	unsigned i;
 
-	for (i = 0; i < req->num_pages; i++)
-		fuse_wait_on_page_writeback(ff->fi, req->pages[i]->index);
+	if (ff->fi)
+		for (i = 0; i < req->num_pages; i++)
+			fuse_wait_on_page_writeback(ff->fi,
+						    req->pages[i]->index);
 
 	res = fuse_send_write(req, ff, pos, count, NULL);
 
@@ -863,7 +878,8 @@ static ssize_t fuse_perform_write(struct fuse_file *ff, struct iov_iter *ii,
 	if (res > 0)
 		fuse_write_update_size(ff, pos);
 
-	fuse_invalidate_attr(ff->fi);
+	if (ff->fi)
+		fuse_invalidate_attr(ff->fi);
 
 	return res > 0 ? res : err;
 }
@@ -1030,7 +1046,8 @@ static ssize_t fuse_direct_io(struct fuse_file *ff, const char __user *buf,
 			fuse_write_update_size(ff, pos);
 		*ppos = pos;
 	}
-	fuse_invalidate_attr(ff->fi);
+	if (ff->fi)
+		fuse_invalidate_attr(ff->fi);
 
 	return res;
 }
@@ -1266,9 +1283,11 @@ static struct vm_operations_struct fuse_file_vm_ops = {
 
 static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma)
 {
-	if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE)) {
-		struct fuse_file *ff = file->private_data;
-		struct fuse_conn *fc = ff->fc;
+	struct fuse_file *ff = file->private_data;
+	struct fuse_conn *fc = ff->fc;
+
+	if (ff->fi &&
+	    (vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE)) {
 		/*
 		 * file may be written through mmap, so chain it onto the
 		 * inodes's write_file list
@@ -1479,9 +1498,12 @@ static loff_t fuse_file_llseek(struct file *file, loff_t offset, int origin)
 	mutex_lock(&vfs_inode->i_mutex);
 	switch (origin) {
 	case SEEK_END:
-		retval = fuse_update_attributes(ff->fi, NULL, file, NULL);
-		if (retval)
-			goto exit;
+		if (ff->fi) {
+			retval = fuse_update_attributes(ff->fi, NULL, file,
+							NULL);
+			if (retval)
+				goto exit;
+		}
 		offset += i_size_read(vfs_inode);
 		break;
 	case SEEK_CUR:
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index c9ffaa0..016a745 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -501,7 +501,7 @@ static inline u64 get_node_id(struct inode *inode)
 
 static inline u64 fuse_file_nodeid(struct fuse_file *ff)
 {
-	return ff->fi->nodeid;
+	return ff->fi ? ff->fi->nodeid : 0;
 }
 
 static inline struct inode *fuse_file_vfs_inode(struct fuse_file *ff)
-- 
1.6.0.2


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 4/6] FUSE: update fuse_conn_init() and separate out fuse_conn_kill()
  2009-04-14  1:54 [PATCHSET] CUSE: implement CUSE, take #3 Tejun Heo
                   ` (2 preceding siblings ...)
  2009-04-14  1:54 ` [PATCH 3/6] FUSE: make ff->fi optional Tejun Heo
@ 2009-04-14  1:54 ` Tejun Heo
  2009-04-14  1:54 ` [PATCH 5/6] FUSE: export symbols to be used by CUSE Tejun Heo
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 17+ messages in thread
From: Tejun Heo @ 2009-04-14  1:54 UTC (permalink / raw)
  To: linux-kernel, fuse-devel, miklos; +Cc: Tejun Heo

Update fuse_conn_init() such that it takes @devt and @bdev instead of
@sb and skip bdi registration if device number is not available.
Also, separate out fuse_conn_kill() from fuse_put_super() and export
it.

These will be used to implement cuse.

Signed-off-by: Tejun Heo <tj@kernel.org>
---
 fs/fuse/fuse_i.h |    7 ++++++-
 fs/fuse/inode.c  |   49 +++++++++++++++++++++++++++++--------------------
 2 files changed, 35 insertions(+), 21 deletions(-)

diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 016a745..d2643df 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -679,7 +679,12 @@ struct fuse_conn *fuse_conn_get(struct fuse_conn *fc);
 /**
  * Initialize fuse_conn
  */
-int fuse_conn_init(struct fuse_conn *fc, struct super_block *sb);
+int fuse_conn_init(struct fuse_conn *fc, dev_t devt, struct block_device *bdev);
+
+/**
+ * Destroy fuse_conn
+ */
+void fuse_conn_kill(struct fuse_conn *fc);
 
 /**
  * Release reference to fuse_conn
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 459b73d..699b228 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -279,20 +279,7 @@ static void fuse_put_super(struct super_block *sb)
 	struct fuse_conn *fc = get_fuse_conn_super(sb);
 
 	fuse_send_destroy(fc);
-	spin_lock(&fc->lock);
-	fc->connected = 0;
-	fc->blocked = 0;
-	spin_unlock(&fc->lock);
-	/* Flush all readers on this fs */
-	kill_fasync(&fc->fasync, SIGIO, POLL_IN);
-	wake_up_all(&fc->waitq);
-	wake_up_all(&fc->blocked_waitq);
-	wake_up_all(&fc->reserved_req_waitq);
-	mutex_lock(&fuse_mutex);
-	list_del(&fc->entry);
-	fuse_ctl_remove_conn(fc);
-	mutex_unlock(&fuse_mutex);
-	bdi_destroy(&fc->bdi);
+	fuse_conn_kill(fc);
 	fuse_conn_put(fc);
 }
 
@@ -463,7 +450,7 @@ static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt)
 	return 0;
 }
 
-int fuse_conn_init(struct fuse_conn *fc, struct super_block *sb)
+int fuse_conn_init(struct fuse_conn *fc, dev_t devt, struct block_device *bdev)
 {
 	int err;
 
@@ -487,18 +474,21 @@ int fuse_conn_init(struct fuse_conn *fc, struct super_block *sb)
 	fc->bdi.capabilities = BDI_CAP_NO_ACCT_WB;
 	fc->khctr = 0;
 	fc->polled_files = RB_ROOT;
-	fc->dev = sb->s_dev;
+	fc->dev = devt;
 	err = bdi_init(&fc->bdi);
+
 	if (err)
 		goto error_mutex_destroy;
-	if (sb->s_bdev) {
+
+	if (bdev)
 		err = bdi_register(&fc->bdi, NULL, "%u:%u-fuseblk",
 				   MAJOR(fc->dev), MINOR(fc->dev));
-	} else {
+	else if (fc->dev)
 		err = bdi_register_dev(&fc->bdi, fc->dev);
-	}
+
 	if (err)
 		goto error_bdi_destroy;
+
 	/*
 	 * For a single fuse filesystem use max 1% of dirty +
 	 * writeback threshold.
@@ -527,6 +517,25 @@ int fuse_conn_init(struct fuse_conn *fc, struct super_block *sb)
 }
 EXPORT_SYMBOL_GPL(fuse_conn_init);
 
+void fuse_conn_kill(struct fuse_conn *fc)
+{
+	spin_lock(&fc->lock);
+	fc->connected = 0;
+	fc->blocked = 0;
+	spin_unlock(&fc->lock);
+	/* Flush all readers on this fs */
+	kill_fasync(&fc->fasync, SIGIO, POLL_IN);
+	wake_up_all(&fc->waitq);
+	wake_up_all(&fc->blocked_waitq);
+	wake_up_all(&fc->reserved_req_waitq);
+	mutex_lock(&fuse_mutex);
+	list_del(&fc->entry);
+	fuse_ctl_remove_conn(fc);
+	mutex_unlock(&fuse_mutex);
+	bdi_destroy(&fc->bdi);
+}
+EXPORT_SYMBOL_GPL(fuse_conn_kill);
+
 void fuse_conn_put(struct fuse_conn *fc)
 {
 	if (atomic_dec_and_test(&fc->count)) {
@@ -840,7 +849,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
 	if (!fc)
 		goto err_fput;
 
-	err = fuse_conn_init(fc, sb);
+	err = fuse_conn_init(fc, sb->s_dev, sb->s_bdev);
 	if (err) {
 		kfree(fc);
 		goto err_fput;
-- 
1.6.0.2


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 5/6] FUSE: export symbols to be used by CUSE
  2009-04-14  1:54 [PATCHSET] CUSE: implement CUSE, take #3 Tejun Heo
                   ` (3 preceding siblings ...)
  2009-04-14  1:54 ` [PATCH 4/6] FUSE: update fuse_conn_init() and separate out fuse_conn_kill() Tejun Heo
@ 2009-04-14  1:54 ` Tejun Heo
  2009-04-14  1:54 ` [PATCH 6/6] CUSE: implement CUSE - Character device in Userspace Tejun Heo
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 17+ messages in thread
From: Tejun Heo @ 2009-04-14  1:54 UTC (permalink / raw)
  To: linux-kernel, fuse-devel, miklos; +Cc: Tejun Heo

Export the following symbols for CUSE.

fuse_conn_put()
fuse_conn_get()
fuse_send_init()
fuse_flush()
fuse_fsync()
fuse_direct_io()
fuse_direct_read()
fuse_file_ioctl_common()
fuse_file_poll()
fuse_request_alloc()
fuse_get_req()
fuse_put_request()
fuse_request_send()
fuse_abort_conn()
fuse_dev_release()
fuse_dev_operations

Signed-off-by: Tejun Heo <tj@kernel.org>
---
 fs/fuse/dev.c    |    9 ++++++++-
 fs/fuse/file.c   |   27 ++++++++++++++++++---------
 fs/fuse/fuse_i.h |   39 +++++++++++++++++++++++++++++++++++++++
 fs/fuse/inode.c  |    5 ++++-
 4 files changed, 69 insertions(+), 11 deletions(-)

diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index ba76b68..2a17249 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -46,6 +46,7 @@ struct fuse_req *fuse_request_alloc(void)
 		fuse_request_init(req);
 	return req;
 }
+EXPORT_SYMBOL_GPL(fuse_request_alloc);
 
 struct fuse_req *fuse_request_alloc_nofs(void)
 {
@@ -124,6 +125,7 @@ struct fuse_req *fuse_get_req(struct fuse_conn *fc)
 	atomic_dec(&fc->num_waiting);
 	return ERR_PTR(err);
 }
+EXPORT_SYMBOL_GPL(fuse_get_req);
 
 /*
  * Return request in fuse_file->reserved_req.  However that may
@@ -208,6 +210,7 @@ void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
 			fuse_request_free(req);
 	}
 }
+EXPORT_SYMBOL_GPL(fuse_put_request);
 
 static unsigned len_args(unsigned numargs, struct fuse_arg *args)
 {
@@ -400,6 +403,7 @@ void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
 	}
 	spin_unlock(&fc->lock);
 }
+EXPORT_SYMBOL_GPL(fuse_request_send);
 
 static void fuse_request_send_nowait_locked(struct fuse_conn *fc,
 					    struct fuse_req *req)
@@ -1105,8 +1109,9 @@ void fuse_abort_conn(struct fuse_conn *fc)
 	}
 	spin_unlock(&fc->lock);
 }
+EXPORT_SYMBOL_GPL(fuse_abort_conn);
 
-static int fuse_dev_release(struct inode *inode, struct file *file)
+int fuse_dev_release(struct inode *inode, struct file *file)
 {
 	struct fuse_conn *fc = fuse_get_conn(file);
 	if (fc) {
@@ -1120,6 +1125,7 @@ static int fuse_dev_release(struct inode *inode, struct file *file)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(fuse_dev_release);
 
 static int fuse_dev_fasync(int fd, struct file *file, int on)
 {
@@ -1142,6 +1148,7 @@ const struct file_operations fuse_dev_operations = {
 	.release	= fuse_dev_release,
 	.fasync		= fuse_dev_fasync,
 };
+EXPORT_SYMBOL_GPL(fuse_dev_operations);
 
 static struct miscdevice fuse_miscdevice = {
 	.minor = FUSE_MINOR,
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 54ad406..700cdf9 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -12,6 +12,7 @@
 #include <linux/slab.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/module.h>
 
 static const struct file_operations fuse_direct_io_file_operations;
 
@@ -149,6 +150,7 @@ int fuse_open_common(struct fuse_conn *fc, struct fuse_inode *fi,
 
 	return err;
 }
+EXPORT_SYMBOL_GPL(fuse_open_common);
 
 void fuse_release_fill(struct fuse_file *ff, u64 nodeid, int flags, int opcode)
 {
@@ -190,6 +192,7 @@ void fuse_release_common(struct fuse_file *ff, int isdir)
 	 */
 	fuse_file_put(ff);
 }
+EXPORT_SYMBOL_GPL(fuse_release_common);
 
 static int fuse_open(struct inode *inode, struct file *file)
 {
@@ -268,7 +271,7 @@ static int fuse_wait_on_page_writeback(struct fuse_inode *fi, pgoff_t index)
 	return 0;
 }
 
-static int fuse_flush(struct file *file, fl_owner_t id)
+int fuse_flush(struct file *file, fl_owner_t id)
 {
 	struct fuse_file *ff = file->private_data;
 	struct fuse_conn *fc = ff->fc;
@@ -301,6 +304,7 @@ static int fuse_flush(struct file *file, fl_owner_t id)
 	}
 	return err;
 }
+EXPORT_SYMBOL_GPL(fuse_flush);
 
 /*
  * Wait for all pending writepages on the inode to finish.
@@ -367,12 +371,13 @@ int fuse_fsync_common(struct fuse_file *ff, int datasync, int isdir)
 	return err;
 }
 
-static int fuse_fsync(struct file *file, struct dentry *de, int datasync)
+int fuse_fsync(struct file *file, struct dentry *de, int datasync)
 {
 	struct fuse_file *ff = file->private_data;
 
 	return fuse_fsync_common(ff, datasync, 0);
 }
+EXPORT_SYMBOL_GPL(fuse_fsync);
 
 void fuse_read_fill(struct fuse_req *req, struct fuse_file *ff,
 		    loff_t pos, size_t count, int opcode)
@@ -987,8 +992,8 @@ static int fuse_get_user_pages(struct fuse_req *req, const char __user *buf,
 	return 0;
 }
 
-static ssize_t fuse_direct_io(struct fuse_file *ff, const char __user *buf,
-			      size_t count, loff_t *ppos, int write)
+ssize_t fuse_direct_io(struct fuse_file *ff, const char __user *buf,
+		       size_t count, loff_t *ppos, int write)
 {
 	struct fuse_conn *fc = ff->fc;
 	size_t nmax = write ? fc->max_write : fc->max_read;
@@ -1051,14 +1056,16 @@ static ssize_t fuse_direct_io(struct fuse_file *ff, const char __user *buf,
 
 	return res;
 }
+EXPORT_SYMBOL_GPL(fuse_direct_io);
 
-static ssize_t fuse_direct_read(struct file *file, char __user *buf,
-				     size_t count, loff_t *ppos)
+ssize_t fuse_direct_read(struct file *file, char __user *buf, size_t count,
+			 loff_t *ppos)
 {
 	struct fuse_file *ff = file->private_data;
 
 	return fuse_direct_io(ff, buf, count, ppos, 0);
 }
+EXPORT_SYMBOL_GPL(fuse_direct_read);
 
 static ssize_t fuse_direct_write(struct file *file, const char __user *buf,
 				 size_t count, loff_t *ppos)
@@ -1611,8 +1618,8 @@ static int fuse_ioctl_copy_user(struct page **pages, struct iovec *iov,
  * limits ioctl data transfers to well-formed ioctls and is the forced
  * behavior for all FUSE servers.
  */
-static long fuse_file_ioctl_common(struct fuse_file *ff, unsigned int cmd,
-				   unsigned long arg, unsigned int flags)
+long fuse_file_ioctl_common(struct fuse_file *ff, unsigned int cmd,
+			    unsigned long arg, unsigned int flags)
 {
 	struct fuse_conn *fc = ff->fc;
 	struct fuse_ioctl_in inarg = {
@@ -1783,6 +1790,7 @@ static long fuse_file_ioctl_common(struct fuse_file *ff, unsigned int cmd,
 
 	return err ? err : outarg.result;
 }
+EXPORT_SYMBOL_GPL(fuse_file_ioctl_common);
 
 static long fuse_file_ioctl(struct file *file, unsigned int cmd,
 			    unsigned long arg)
@@ -1852,7 +1860,7 @@ static void fuse_register_polled_file(struct fuse_file *ff)
 	spin_unlock(&fc->lock);
 }
 
-static unsigned fuse_file_poll(struct file *file, poll_table *wait)
+unsigned fuse_file_poll(struct file *file, poll_table *wait)
 {
 	struct fuse_file *ff = file->private_data;
 	struct fuse_conn *fc = ff->fc;
@@ -1899,6 +1907,7 @@ static unsigned fuse_file_poll(struct file *file, poll_table *wait)
 	}
 	return POLLERR;
 }
+EXPORT_SYMBOL_GPL(fuse_file_poll);
 
 /*
  * This is called from fuse_handle_notify() on FUSE_NOTIFY_POLL and
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index d2643df..cdab92d 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -530,6 +530,11 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, struct qstr *name,
 		     struct fuse_entry_out *outarg, struct inode **inode);
 
 /**
+ * Send INIT command
+ */
+void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req);
+
+/**
  * Send FORGET command
  */
 void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req,
@@ -566,6 +571,35 @@ void fuse_release_common(struct fuse_file *ff, int isdir);
 int fuse_fsync_common(struct fuse_file *ff, int datasync, int isdir);
 
 /**
+ * Send READ or WRITE request for direct IO
+ */
+ssize_t fuse_direct_io(struct fuse_file *ff, const char __user *buf,
+		       size_t count, loff_t *ppos, int write);
+ssize_t fuse_direct_read(struct file *file, char __user *buf, size_t count,
+			 loff_t *ppos);
+
+/**
+ * Send FLUSH request
+ */
+int fuse_flush(struct file *file, fl_owner_t id);
+
+/**
+ * Send FSYNCDIR or FSYNC request
+ */
+int fuse_fsync(struct file *file, struct dentry *de, int datasync);
+
+/**
+ * Send IOCTL request
+ */
+long fuse_file_ioctl_common(struct fuse_file *ff, unsigned int cmd,
+			    unsigned long arg, unsigned int flags);
+
+/**
+ * Send POLL request
+ */
+unsigned fuse_file_poll(struct file *file, poll_table *wait);
+
+/**
  * Notify poll wakeup
  */
 int fuse_notify_poll_wakeup(struct fuse_conn *fc,
@@ -608,6 +642,11 @@ void fuse_truncate(struct address_space *mapping, loff_t offset);
 int fuse_dev_init(void);
 
 /**
+ * Release the client device
+ */
+int fuse_dev_release(struct inode *inode, struct file *file);
+
+/**
  * Cleanup the client device
  */
 void fuse_dev_cleanup(void);
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 699b228..a01f52c 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -545,12 +545,14 @@ void fuse_conn_put(struct fuse_conn *fc)
 		fc->release(fc);
 	}
 }
+EXPORT_SYMBOL_GPL(fuse_conn_put);
 
 struct fuse_conn *fuse_conn_get(struct fuse_conn *fc)
 {
 	atomic_inc(&fc->count);
 	return fc;
 }
+EXPORT_SYMBOL_GPL(fuse_conn_get);
 
 static struct inode *fuse_get_root_inode(struct super_block *sb, unsigned mode)
 {
@@ -774,7 +776,7 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
 	wake_up_all(&fc->blocked_waitq);
 }
 
-static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
+void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
 {
 	struct fuse_init_in *arg = &req->misc.init_in;
 
@@ -797,6 +799,7 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
 	req->end = process_init_reply;
 	fuse_request_send_background(fc, req);
 }
+EXPORT_SYMBOL_GPL(fuse_send_init);
 
 static void fuse_free_conn(struct fuse_conn *fc)
 {
-- 
1.6.0.2


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 6/6] CUSE: implement CUSE - Character device in Userspace
  2009-04-14  1:54 [PATCHSET] CUSE: implement CUSE, take #3 Tejun Heo
                   ` (4 preceding siblings ...)
  2009-04-14  1:54 ` [PATCH 5/6] FUSE: export symbols to be used by CUSE Tejun Heo
@ 2009-04-14  1:54 ` Tejun Heo
  2009-04-21 16:43 ` [PATCHSET] CUSE: implement CUSE, take #3 Tejun Heo
  2009-04-22 11:39 ` Miklos Szeredi
  7 siblings, 0 replies; 17+ messages in thread
From: Tejun Heo @ 2009-04-14  1:54 UTC (permalink / raw)
  To: linux-kernel, fuse-devel, miklos; +Cc: Tejun Heo

CUSE enables implementing character devices in userspace.  With recent
additions of ioctl and poll support, FUSE already has most of what's
necessary to implement character devices.  All CUSE has to do is
bonding all those components - FUSE, chardev and the driver model -
nicely.

When client opens /dev/cuse, kernel starts conversation with
CUSE_INIT.  The client tells CUSE which device it wants to create.  As
the previous patch made fuse_file usable without associated
fuse_inode, CUSE doesn't create super block or inodes.  It attaches
fuse_file to cdev file->private_data during open and set ff->fi to
NULL.  The rest of the operation is almost identical to FUSE direct IO
case.

Each CUSE device has a corresponding directory /sys/class/cuse/DEVNAME
(which is symlink to /sys/devices/virtual/class/DEVNAME if
SYSFS_DEPRECATED is turned off) which hosts "waiting" and "abort"
among other things.  Those two files have the same meaning as the FUSE
control files.

The only notable lacking feature compared to in-kernel implementation
is mmap support.

Signed-off-by: Tejun Heo <tj@kernel.org>
---
 fs/Kconfig            |   10 +
 fs/fuse/Makefile      |    1 +
 fs/fuse/cuse.c        |  675 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/fuse/file.c        |    4 +-
 include/linux/cuse.h  |   49 ++++
 include/linux/fuse.h  |    2 +
 include/linux/magic.h |    5 +-
 7 files changed, 743 insertions(+), 3 deletions(-)
 create mode 100644 fs/fuse/cuse.c
 create mode 100644 include/linux/cuse.h

diff --git a/fs/Kconfig b/fs/Kconfig
index 9f7270f..525da2e 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -62,6 +62,16 @@ source "fs/autofs/Kconfig"
 source "fs/autofs4/Kconfig"
 source "fs/fuse/Kconfig"
 
+config CUSE
+	tristate "Character device in Userpace support"
+	depends on FUSE_FS
+	help
+	  This FUSE extension allows character devices to be
+	  implemented in userspace.
+
+	  If you want to develop or use userspace character device
+	  based on CUSE, answer Y or M.
+
 config GENERIC_ACL
 	bool
 	select FS_POSIX_ACL
diff --git a/fs/fuse/Makefile b/fs/fuse/Makefile
index 7243706..e95eeb4 100644
--- a/fs/fuse/Makefile
+++ b/fs/fuse/Makefile
@@ -3,5 +3,6 @@
 #
 
 obj-$(CONFIG_FUSE_FS) += fuse.o
+obj-$(CONFIG_CUSE) += cuse.o
 
 fuse-objs := dev.o dir.o file.o inode.o control.o
diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c
new file mode 100644
index 0000000..2238016
--- /dev/null
+++ b/fs/fuse/cuse.c
@@ -0,0 +1,675 @@
+/*
+ * CUSE: Character device in Userspace
+ *
+ * Copyright (C) 2008-2009  SUSE Linux Products GmbH
+ * Copyright (C) 2008-2009  Tejun Heo <tj@kernel.org>
+ *
+ * This file is released under the GPLv2.
+ *
+ * CUSE enables character devices to be implemented from userland much
+ * like FUSE allows filesystems.  On initialization /dev/cuse is
+ * created.  By opening the file and replying to the CUSE_INIT request
+ * userland CUSE server can create a character device.  After that the
+ * operation is very similar to FUSE.
+ *
+ * A CUSE instance involves the following objects.
+ *
+ * cuse_conn	: contains fuse_conn and serves as bonding structure
+ * channel	: file handle connected to the userland CUSE server
+ * cdev		: the implemented character device
+ * dev		: generic device for cdev
+ *
+ * Note that 'channel' is what 'dev' is in FUSE.  As CUSE deals with
+ * devices, it's called 'channel' to reduce confusion.
+ *
+ * channel determines when the character device dies.  When channel is
+ * closed, everything begins to destruct.  The cuse_conn is taken off
+ * the lookup table preventing further access from cdev, cdev and
+ * generic device are removed and the base reference of cuse_conn is
+ * put.
+ *
+ * On each open, the matching cuse_conn is looked up and if found an
+ * additional reference is taken which is released when the file is
+ * closed.
+ */
+
+#include <linux/cuse.h>
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/kdev_t.h>
+#include <linux/kthread.h>
+#include <linux/list.h>
+#include <linux/magic.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/stat.h>
+
+#include "fuse_i.h"
+
+#define CUSE_CONNTBL_LEN	64
+
+struct cuse_conn {
+	struct list_head	list;	/* linked on cuse_conntbl */
+	struct fuse_conn	fc;	/* fuse connection */
+	struct cdev		*cdev;	/* associated character device */
+	struct device		*dev;	/* device representing @cdev */
+
+	/* init parameters, set once during initialization */
+	bool			unrestricted_ioctl;
+
+	/* the following field is protected by cuse_disconnect_mutex */
+	bool			disconnected;	/* channel disconnected */
+};
+
+static DEFINE_SPINLOCK(cuse_lock);		/* protects cuse_conntbl */
+static DEFINE_MUTEX(cuse_disconnect_mutex);	/* protects cc->disconnected */
+static struct list_head cuse_conntbl[CUSE_CONNTBL_LEN];
+static struct class *cuse_class;
+
+static struct cuse_conn *fc_to_cc(struct fuse_conn *fc)
+{
+	return container_of(fc, struct cuse_conn, fc);
+}
+
+static struct list_head *cuse_conntbl_head(dev_t devt)
+{
+	return &cuse_conntbl[(MAJOR(devt) + MINOR(devt)) % CUSE_CONNTBL_LEN];
+}
+
+
+/**************************************************************************
+ * CUSE frontend operations
+ *
+ * These are file operations for the character device.
+ *
+ * On open, CUSE opens a file from the FUSE mnt and stores it to
+ * private_data of the open file.  All other ops call FUSE ops on the
+ * FUSE file.
+ */
+
+static ssize_t cuse_direct_write(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	struct fuse_file *ff = file->private_data;
+
+	/*
+	 * No locking or generic_write_checks(), the server is
+	 * responsible for locking and sanity checks.
+	 */
+	return fuse_direct_io(ff, buf, count, ppos, 1);
+}
+
+static int cuse_open(struct inode *inode, struct file *file)
+{
+	dev_t devt = inode->i_cdev->dev;
+	struct cuse_conn *cc = NULL, *pos;
+	int rc;
+
+	/* look up and get the connection */
+	spin_lock(&cuse_lock);
+	list_for_each_entry(pos, cuse_conntbl_head(devt), list)
+		if (pos->dev->devt == devt) {
+			fuse_conn_get(&pos->fc);
+			cc = pos;
+			break;
+		}
+	spin_unlock(&cuse_lock);
+
+	/* dead? */
+	if (!cc)
+		return -ENODEV;
+
+	/*
+	 * Generic permission check is already done against the chrdev
+	 * file, proceed to open.
+	 */
+	rc = fuse_open_common(&cc->fc, NULL, inode, file, 0);
+	if (rc)
+		fuse_conn_put(&cc->fc);
+	return rc;
+}
+
+static int cuse_release(struct inode *inode, struct file *file)
+{
+	struct fuse_file *ff = file->private_data;
+	struct fuse_conn *fc = ff->fc;
+
+	fuse_release_common(ff, 0);
+	fuse_conn_put(fc);
+	return 0;
+}
+
+static long cuse_file_ioctl(struct file *file, unsigned int cmd,
+			    unsigned long arg)
+{
+	struct fuse_file *ff = file->private_data;
+	struct cuse_conn *cc = fc_to_cc(ff->fc);
+	unsigned int flags = 0;
+
+	if (cc->unrestricted_ioctl)
+		flags |= FUSE_IOCTL_UNRESTRICTED;
+
+	return fuse_file_ioctl_common(ff, cmd, arg, flags);
+}
+
+static long cuse_file_compat_ioctl(struct file *file, unsigned int cmd,
+				   unsigned long arg)
+{
+	struct fuse_file *ff = file->private_data;
+	struct cuse_conn *cc = fc_to_cc(ff->fc);
+	unsigned int flags = FUSE_IOCTL_COMPAT;
+
+	if (cc->unrestricted_ioctl)
+		flags |= FUSE_IOCTL_UNRESTRICTED;
+
+	return fuse_file_ioctl_common(ff, cmd, arg, flags);
+}
+
+static const struct file_operations cuse_frontend_fops = {
+	.owner			= THIS_MODULE,
+	.read			= fuse_direct_read,
+	.write			= cuse_direct_write,
+	.open			= cuse_open,
+	.flush			= fuse_flush,
+	.release		= cuse_release,
+	.fsync			= fuse_fsync,
+	.unlocked_ioctl		= cuse_file_ioctl,
+	.compat_ioctl		= cuse_file_compat_ioctl,
+	.poll			= fuse_file_poll,
+};
+
+
+/**************************************************************************
+ * CUSE channel initialization and destruction
+ */
+
+struct cuse_devinfo {
+	const char		*name;
+};
+
+/**
+ * cuse_parse_one - parse one key=value pair
+ * @pp: i/o parameter for the current position
+ * @end: points to one past the end of the packed string
+ * @keyp: out parameter for key
+ * @valp: out parameter for value
+ *
+ * *@pp points to packed strings - "key0=val0\0key1=val1\0" which ends
+ * at @end - 1.  This function parses one pair and set *@keyp to the
+ * start of the key and *@valp to the start of the value.  Note that
+ * the original string is modified such that the key string is
+ * terminated with '\0'.  *@pp is updated to point to the next string.
+ *
+ * RETURNS:
+ * 1 on successful parse, 0 on EOF, -errno on failure.
+ */
+static int cuse_parse_one(char **pp, char *end, char **keyp, char **valp)
+{
+	char *p = *pp;
+	char *key, *val;
+
+	while (p < end && *p == '\0')
+		p++;
+	if (p == end)
+		return 0;
+
+	if (end[-1] != '\0') {
+		printk(KERN_ERR "CUSE: info not properly terminated\n");
+		return -EINVAL;
+	}
+
+	key = val = p;
+	p += strlen(p);
+
+	if (valp) {
+		strsep(&val, "=");
+		if (!val)
+			val = key + strlen(key);
+		key = strstrip(key);
+		val = strstrip(val);
+	} else
+		key = strstrip(key);
+
+	if (!strlen(key)) {
+		printk(KERN_ERR "CUSE: zero length info key specified\n");
+		return -EINVAL;
+	}
+
+	*pp = p;
+	*keyp = key;
+	if (valp)
+		*valp = val;
+
+	return 1;
+}
+
+/**
+ * cuse_parse_dev_info - parse device info
+ * @p: device info string
+ * @len: length of device info string
+ * @devinfo: out parameter for parsed device info
+ *
+ * Parse @p to extract device info and store it into @devinfo.  String
+ * pointed to by @p is modified by parsing and @devinfo points into
+ * them, so @p shouldn't be freed while @devinfo is in use.
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+static int cuse_parse_devinfo(char *p, size_t len, struct cuse_devinfo *devinfo)
+{
+	char *end = p + len;
+	char *key, *val;
+	int rc;
+
+	while (true) {
+		rc = cuse_parse_one(&p, end, &key, &val);
+		if (rc < 0)
+			return rc;
+		if (!rc)
+			break;
+		if (strcmp(key, "DEVNAME") == 0)
+			devinfo->name = val;
+		else
+			printk(KERN_WARNING "CUSE: unknown device info \"%s\"\n",
+			       key);
+	}
+
+	if (!devinfo->name || !strlen(devinfo->name)) {
+		printk(KERN_ERR "CUSE: DEVNAME unspecified\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void cuse_gendev_release(struct device *dev)
+{
+	kfree(dev);
+}
+
+/**
+ * cuse_init_worker - worker to finish initializing CUSE channel
+ * @data: cuse_conn to initialize
+ *
+ * CUSE channel is created by opening /dev/cuse but the open itself
+ * can't do much of initialization as it must return to the userland
+ * so that CUSE server and kernel can talk via the open file, so
+ * cuse_channel_open() performs minimal initialization and schedules
+ * this function on a kthread to finish up initialization.
+ *
+ * This function queries userland CUSE server which character device
+ * it wants to create and creates the character device and sets up all
+ * the required data structures for it.  Please read the comment at
+ * the top of this file for high level overview.
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+static int cuse_init_worker(void *data)
+{
+	struct cuse_conn *cc = data;
+	struct cuse_init_in iin = { };
+	struct cuse_init_out iout = { };
+	struct cuse_devinfo devinfo = { };
+	struct fuse_req *req = NULL;
+	struct page *page = NULL;
+	struct device *dev;
+	struct cdev *cdev;
+	bool disconnected;
+	dev_t devt;
+	int rc;
+
+	BUILD_BUG_ON(CUSE_INIT_INFO_MAX > PAGE_SIZE);
+
+	/* identify ourself and query what the CUSE server wants */
+	req = fuse_get_req(&cc->fc);
+	if (IS_ERR(req)) {
+		rc = PTR_ERR(req);
+		goto out;
+	}
+
+	rc = -ENOMEM;
+	page = alloc_pages(GFP_KERNEL | __GFP_ZERO, 1);
+	if (!page)
+		goto out;
+
+	req->pages[0] = nth_page(page, 0);
+	req->pages[1] = nth_page(page, 1);
+	req->num_pages = 2;
+
+	req->in.h.opcode = CUSE_INIT;
+	req->in.h.nodeid = 0;
+	req->in.numargs = 1;
+	req->in.args[0].size = sizeof(iin);
+	req->in.args[0].value = &iin;
+
+	iin.ver_major = CUSE_KERNEL_VERSION;
+	iin.ver_minor = CUSE_KERNEL_MINOR_VERSION;
+
+	req->out.numargs = 2;
+	req->out.args[0].size = sizeof(iout);
+	req->out.args[0].value = &iout;
+	req->out.args[1].size = CUSE_INIT_INFO_MAX;
+	req->out.argpages = 1;
+	req->out.argvar = 1;
+
+	fuse_request_send(&cc->fc, req);
+	rc = req->out.h.error;
+	if (rc)
+		goto out;
+
+	/* parse init reply */
+	cc->unrestricted_ioctl = iout.flags & CUSE_UNRESTRICTED_IOCTL;
+
+	rc = cuse_parse_devinfo(page_address(page), req->out.args[1].size,
+				&devinfo);
+	if (rc)
+		goto out;
+
+	/* determine and reserve devt */
+	devt = MKDEV(iout.dev_major, iout.dev_minor);
+	if (!MAJOR(devt))
+		rc = alloc_chrdev_region(&devt, MINOR(devt), 1, devinfo.name);
+	else
+		rc = register_chrdev_region(devt, 1, devinfo.name);
+	if (rc) {
+		printk(KERN_ERR "CUSE: failed to register chrdev region\n");
+		goto out;
+	}
+
+	/* devt determined, create device */
+	rc = -ENOMEM;
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		goto out_region;
+
+	device_initialize(dev);
+	dev_set_uevent_suppress(dev, 1);
+	dev->class = cuse_class;
+	dev->devt = devt;
+	dev->release = cuse_gendev_release;
+	dev_set_drvdata(dev, cc);
+	dev_set_name(dev, "%s", devinfo.name);
+
+	rc = device_add(dev);
+	if (rc)
+		goto out_device;
+
+	/* register cdev */
+	rc = -ENOMEM;
+	cdev = cdev_alloc();
+	if (!cdev)
+		goto out_device;
+
+	cdev->owner = THIS_MODULE;
+	cdev->ops = &cuse_frontend_fops;
+
+	rc = cdev_add(cdev, devt, 1);
+	if (rc)
+		goto out_cdev;
+
+	/* transfer objects to the connection and register it */
+	mutex_lock(&cuse_disconnect_mutex);
+
+	/* did we lose to channel release? */
+	disconnected = cc->disconnected;
+	if (!disconnected) {
+		cc->dev = dev;
+		cc->cdev = cdev;
+
+		/* make the device available */
+		spin_lock(&cuse_lock);
+		list_add(&cc->list, cuse_conntbl_head(devt));
+		spin_unlock(&cuse_lock);
+
+		/* announce device availability */
+		dev_set_uevent_suppress(dev, 0);
+		kobject_uevent(&dev->kobj, KOBJ_ADD);
+	}
+
+	mutex_unlock(&cuse_disconnect_mutex);
+
+	rc = -ENODEV;
+	if (disconnected)
+		goto out_cdev;
+
+	/* okay, everything is good, notify init completion */
+	fuse_put_request(&cc->fc, req);
+	req = fuse_get_req(&cc->fc);
+	if (IS_ERR(req)) {
+		rc = PTR_ERR(req);
+		goto out_cdev;
+	}
+
+	req->in.h.opcode = CUSE_INIT_DONE;
+	req->in.h.nodeid = 0;
+
+	fuse_request_send(&cc->fc, req);
+	rc = req->out.h.error;
+	goto out;
+
+out_cdev:
+	cdev_del(cdev);
+out_device:
+	put_device(dev);
+out_region:
+	unregister_chrdev_region(devt, 1);
+out:
+	if (req && !IS_ERR(req))
+		fuse_put_request(&cc->fc, req);
+	if (page)
+		__free_pages(page, 1);
+
+	if (rc)
+		fuse_abort_conn(&cc->fc);
+
+	fuse_conn_put(&cc->fc);
+	return rc;
+}
+
+static void cuse_fc_release(struct fuse_conn *fc)
+{
+	struct cuse_conn *cc = fc_to_cc(fc);
+
+	kfree(cc);
+}
+
+/**
+ * cuse_channel_open - open method for /dev/cuse
+ * @inode: inode for /dev/cuse
+ * @file: file struct being opened
+ *
+ * Userland CUSE server can create a CUSE device by opening /dev/cuse
+ * and replying to the initilaization request kernel sends.  This
+ * function is responsible for handling CUSE device initialization.
+ * Because the fd opened by this function is used during
+ * initialization, this function only creates cuse_conn and sends
+ * init.  The rest is delegated to a kthread.
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+static int cuse_channel_open(struct inode *inode, struct file *file)
+{
+	struct cuse_conn *cc;
+	struct fuse_req *init_req;
+	struct task_struct *worker;
+	int rc;
+
+	/* set up cuse_conn */
+	cc = kzalloc(sizeof(*cc), GFP_KERNEL);
+	if (!cc)
+		return -ENOMEM;
+
+	rc = fuse_conn_init(&cc->fc, 0, NULL);
+	if (rc) {
+		kfree(cc);
+		return rc;
+	}
+
+	/* cuse isn't accessible to mortal users, give it some latitude */
+	INIT_LIST_HEAD(&cc->list);
+	cc->fc.flags = FUSE_ALLOW_OTHER;
+	cc->fc.user_id = current_euid();
+	cc->fc.group_id = current_egid();
+	cc->fc.max_read = FUSE_MAX_PAGES_PER_REQ * PAGE_SIZE;
+	cc->fc.release = cuse_fc_release;
+
+	/* let's send fuse init request */
+	rc = -ENOMEM;
+	init_req = fuse_request_alloc();
+	if (!init_req)
+		goto err_put;
+
+	cc->fc.connected = 1;
+	file->private_data = &cc->fc;	/* channel owns base reference to cc */
+	fuse_send_init(&cc->fc, init_req);
+
+	/*
+	 * Okay, FUSE part of initialization is complete.  The rest of
+	 * the initialization is a bit more involved and requires
+	 * conversing with userland.  Start a kthread.
+	 */
+	fuse_conn_get(&cc->fc);
+	worker = kthread_run(cuse_init_worker, cc, "cuse-init-pid%d",
+			     current->pid);
+	if (IS_ERR(worker)) {
+		fuse_conn_put(&cc->fc);
+		rc = PTR_ERR(worker);
+		goto err_put;
+	}
+	return 0;
+
+err_put:
+	fuse_conn_put(&cc->fc);
+	return rc;
+}
+
+/**
+ * cuse_channel_release - release method for /dev/cuse
+ * @inode: inode for /dev/cuse
+ * @file: file struct being closed
+ *
+ * Disconnect the channel, deregister CUSE device and initiate
+ * destruction by putting the default reference.
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+static int cuse_channel_release(struct inode *inode, struct file *file)
+{
+	struct cuse_conn *cc = fc_to_cc(file->private_data);
+	int rc;
+
+	mutex_lock(&cuse_disconnect_mutex);
+
+	/* remove from the conntbl, no more access from this point on */
+	spin_lock(&cuse_lock);
+	list_del_init(&cc->list);
+	spin_unlock(&cuse_lock);
+
+	/* remove device */
+	if (cc->dev)
+		device_unregister(cc->dev);
+	if (cc->cdev) {
+		unregister_chrdev_region(cc->cdev->dev, 1);
+		cdev_del(cc->cdev);
+	}
+
+	/* in case init isn't done yet, mark that the conn is already dead */
+	cc->disconnected = true;
+
+	/* kill connection and shutdown channel */
+	fuse_conn_kill(&cc->fc);
+	rc = fuse_dev_release(inode, file);	/* puts the base reference */
+
+	mutex_unlock(&cuse_disconnect_mutex);
+
+	return rc;
+}
+
+static struct file_operations cuse_channel_fops; /* initialized during init */
+
+
+/**************************************************************************
+ * Misc stuff and module initializatiion
+ *
+ * CUSE exports the same set of attributes to sysfs as fusectl.
+ */
+
+ssize_t cuse_class_waiting_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct cuse_conn *cc = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d\n", atomic_read(&cc->fc.num_waiting));
+}
+
+ssize_t cuse_class_abort_store(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	struct cuse_conn *cc = dev_get_drvdata(dev);
+
+	fuse_abort_conn(&cc->fc);
+	return count;
+}
+
+static struct device_attribute cuse_class_dev_attrs[] = {
+	__ATTR(waiting, S_IFREG | 0400, cuse_class_waiting_show, NULL),
+	__ATTR(abort, S_IFREG | 0200, NULL, cuse_class_abort_store),
+	{ }
+};
+
+static struct miscdevice cuse_miscdev = {
+	.minor		= MISC_DYNAMIC_MINOR,
+	.name		= "cuse",
+	.fops		= &cuse_channel_fops,
+};
+
+static int __init cuse_init(void)
+{
+	int i, rc;
+
+	/* init conntbl */
+	for (i = 0; i < CUSE_CONNTBL_LEN; i++)
+		INIT_LIST_HEAD(&cuse_conntbl[i]);
+
+	/* inherit and extend fuse_dev_operations */
+	cuse_channel_fops		= fuse_dev_operations;
+	cuse_channel_fops.owner		= THIS_MODULE;
+	cuse_channel_fops.open		= cuse_channel_open;
+	cuse_channel_fops.release	= cuse_channel_release;
+
+	cuse_class = class_create(THIS_MODULE, "cuse");
+	if (IS_ERR(cuse_class))
+		return PTR_ERR(cuse_class);
+
+	cuse_class->dev_attrs = cuse_class_dev_attrs;
+
+	rc = misc_register(&cuse_miscdev);
+	if (rc) {
+		class_destroy(cuse_class);
+		return rc;
+	}
+
+	return 0;
+}
+
+static void __exit cuse_exit(void)
+{
+	misc_deregister(&cuse_miscdev);
+	class_destroy(cuse_class);
+}
+
+module_init(cuse_init);
+module_exit(cuse_exit);
+
+MODULE_AUTHOR("Tejun Heo <tj@kernel.org>");
+MODULE_DESCRIPTION("Character device in Userspace");
+MODULE_LICENSE("GPL");
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 700cdf9..7492577 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -14,6 +14,7 @@
 #include <linux/sched.h>
 #include <linux/module.h>
 
+static const struct file_operations fuse_file_operations;
 static const struct file_operations fuse_direct_io_file_operations;
 
 static int fuse_send_open(struct fuse_file *ff, int isdir,
@@ -110,7 +111,8 @@ void fuse_finish_open(struct fuse_file *ff, struct fuse_open_out *outarg)
 {
 	struct inode *vfs_inode = fuse_file_vfs_inode(ff);
 
-	if (outarg->open_flags & FOPEN_DIRECT_IO)
+	if (outarg->open_flags & FOPEN_DIRECT_IO &&
+	    ff->file->f_op == &fuse_file_operations)
 		ff->file->f_op = &fuse_direct_io_file_operations;
 	if (!(outarg->open_flags & FOPEN_KEEP_CACHE))
 		invalidate_inode_pages2(vfs_inode->i_mapping);
diff --git a/include/linux/cuse.h b/include/linux/cuse.h
new file mode 100644
index 0000000..17926e3
--- /dev/null
+++ b/include/linux/cuse.h
@@ -0,0 +1,49 @@
+/*
+ * CUSE: Character device in Userspace
+ *
+ * Copyright (C) 2008-2009  SUSE Linux Products GmbH
+ * Copyright (C) 2008-2009  Tejun Heo <tj@kernel.org>
+ *
+ * This file is released under the GPLv2.
+ */
+
+#ifndef _CUSE_H_
+#define _CUSE_H_
+
+#include <linux/major.h>
+#include <linux/miscdevice.h>
+#include <linux/fuse.h>
+
+#define CUSE_KERNEL_VERSION		0
+#define CUSE_KERNEL_MINOR_VERSION	1
+
+#define CUSE_KERNEL_MAJOR		MISC_MAJOR
+#define CUSE_KERNEL_MINOR		MISC_DYNAMIC_MINOR
+
+#define CUSE_INIT_INFO_MAX		4096
+
+/*
+ * CUSE INIT request/reply flags
+ */
+#define CUSE_UNRESTRICTED_IOCTL		(1 << 0) /* use unrestricted ioctl */
+
+enum cuse_opcode {
+	CUSE_INIT	= CUSE_BASE,
+	CUSE_INIT_DONE,
+};
+
+struct cuse_init_in {
+	__u32	ver_major;
+	__u32	ver_minor;
+	__u32	flags;
+	__u32	padding;
+};
+
+struct cuse_init_out {
+	__u32	dev_major;		/* chardev major */
+	__u32	dev_minor;		/* chardev minor */
+	__u32	flags;
+	__u32	padding;
+};
+
+#endif /*_CUSE_H_*/
diff --git a/include/linux/fuse.h b/include/linux/fuse.h
index 162e5de..cc51548 100644
--- a/include/linux/fuse.h
+++ b/include/linux/fuse.h
@@ -210,6 +210,8 @@ enum fuse_opcode {
 	FUSE_DESTROY       = 38,
 	FUSE_IOCTL         = 39,
 	FUSE_POLL          = 40,
+
+	CUSE_BASE          = 4096,
 };
 
 enum fuse_notify_code {
diff --git a/include/linux/magic.h b/include/linux/magic.h
index 5b4e28b..4596c9e 100644
--- a/include/linux/magic.h
+++ b/include/linux/magic.h
@@ -3,10 +3,11 @@
 
 #define ADFS_SUPER_MAGIC	0xadf5
 #define AFFS_SUPER_MAGIC	0xadff
-#define AFS_SUPER_MAGIC                0x5346414F
+#define AFS_SUPER_MAGIC		0x5346414F
 #define AUTOFS_SUPER_MAGIC	0x0187
 #define CODA_SUPER_MAGIC	0x73757245
-#define DEBUGFS_MAGIC          0x64626720
+#define CUSE_SUPER_MAGIC	0x43555345
+#define DEBUGFS_MAGIC		0x64626720
 #define SYSFS_MAGIC		0x62656572
 #define SECURITYFS_MAGIC	0x73636673
 #define TMPFS_MAGIC		0x01021994
-- 
1.6.0.2


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCHSET] CUSE: implement CUSE, take #3
  2009-04-14  1:54 [PATCHSET] CUSE: implement CUSE, take #3 Tejun Heo
                   ` (5 preceding siblings ...)
  2009-04-14  1:54 ` [PATCH 6/6] CUSE: implement CUSE - Character device in Userspace Tejun Heo
@ 2009-04-21 16:43 ` Tejun Heo
  2009-04-22 11:39 ` Miklos Szeredi
  7 siblings, 0 replies; 17+ messages in thread
From: Tejun Heo @ 2009-04-21 16:43 UTC (permalink / raw)
  To: linux-kernel, fuse-devel, miklos

Ping.

-- 
tejun

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCHSET] CUSE: implement CUSE, take #3
  2009-04-14  1:54 [PATCHSET] CUSE: implement CUSE, take #3 Tejun Heo
                   ` (6 preceding siblings ...)
  2009-04-21 16:43 ` [PATCHSET] CUSE: implement CUSE, take #3 Tejun Heo
@ 2009-04-22 11:39 ` Miklos Szeredi
  2009-04-23  0:09   ` Tejun Heo
  7 siblings, 1 reply; 17+ messages in thread
From: Miklos Szeredi @ 2009-04-22 11:39 UTC (permalink / raw)
  To: tj; +Cc: linux-kernel, fuse-devel, miklos

Hello Tejun,

Sorry for the delay, I was busy reimplementing some of the stuff in
your patchset.  The current state is here:

  git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git cuse-test

  http://git.kernel.org/?p=linux/kernel/git/mszeredi/fuse.git;a=shortlog;h=cuse-test

What I changed (IIRC):

 - Don't store pointer to struct inode, struct fuse_inode, and struct
   file in struct fuse_file.  Instead store nodeid in struct
   fuse_file, no worries about object lifetimes this way.

 - Don't implement fsync and flush in CUSE, they don't really make
   sense on character devices.  At least I can't think of any use for
   them.

 - release() in CUSE can be synchronous.  Asynchronous release causes
   many headaches in fuse, but is necessary due to the possible DoS
   that an unprivileged filesystem could do with an unrelated process.
   Since CUSE servers are always privileged, this should not be an
   issue.

 - Other cleanups.

I still think that the INIT sequence in CUSE is way too complicated
and that the kernel thread is unnecessary.  How about doing the init
with a single CUSE_INIT message which embeds the fuse_init_in and
fuse_init_out structures.  I also don't see the purpose of the
CUSE_INIT_DONE message.  Isn't it enough not to send any other
messages while the initialization is not complete?  This is what fuse
does.

I'm currently testing with ossp and some things do work, but I'm also
having issues with some apps.  But that's a topic for another mail...

Thanks,
Miklos

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCHSET] CUSE: implement CUSE, take #3
  2009-04-22 11:39 ` Miklos Szeredi
@ 2009-04-23  0:09   ` Tejun Heo
  2009-04-23 10:08     ` Miklos Szeredi
  0 siblings, 1 reply; 17+ messages in thread
From: Tejun Heo @ 2009-04-23  0:09 UTC (permalink / raw)
  To: Miklos Szeredi; +Cc: linux-kernel, fuse-devel

Hello,

Miklos Szeredi wrote:
> Hello Tejun,
> 
> Sorry for the delay, I was busy reimplementing some of the stuff in
> your patchset.  The current state is here:
> 
>   git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git cuse-test
> 
>   http://git.kernel.org/?p=linux/kernel/git/mszeredi/fuse.git;a=shortlog;h=cuse-test
> 
> What I changed (IIRC):
> 
>  - Don't store pointer to struct inode, struct fuse_inode, and struct
>    file in struct fuse_file.  Instead store nodeid in struct
>    fuse_file, no worries about object lifetimes this way.

Hmm... there really isn't any object lifetime issue with or without
the pointers as both fuse_file and fuse_inode follow VFS lifetime
rules.  I tried several different approaches including adding nodeid
but what pushed me toward adding the pointers was that the parameter
passing and which uses/depends on what becomes a bit too complex with
other approaches.  e.g. How do you discern between needing vfs inode
and fuse inode?

>  - Don't implement fsync and flush in CUSE, they don't really make
>    sense on character devices.  At least I can't think of any use for
>    them.
>  - release() in CUSE can be synchronous.  Asynchronous release causes
>    many headaches in fuse, but is necessary due to the possible DoS
>    that an unprivileged filesystem could do with an unrelated process.
>    Since CUSE servers are always privileged, this should not be an
>    issue.

Cool.

>  - Other cleanups.
> 
> I still think that the INIT sequence in CUSE is way too complicated
> and that the kernel thread is unnecessary.  How about doing the init
> with a single CUSE_INIT message which embeds the fuse_init_in and
> fuse_init_out structures.

I don't see why the kernel thread is such a big problem.  It's just a
kernel thread.  We can surely fold FUSE_INIT inside FUSE_INIT tho but
one way or the other doesn't make whole lot of difference.

> I also don't see the purpose of the CUSE_INIT_DONE message.  Isn't
> it enough not to send any other messages while the initialization is
> not complete?  This is what fuse does.

The difference between FUSE and CUSE in this regard is that the
synchornous part of initialization is much less for CUSE.  ie. For
FUSE, after the mount is complete, all accesses go to FUSE, so it's
all good.  However, for CUSE, the device nodes are setup
asynchronously, so there's no such inherent synchronization point as
mount.  This becomes a problem when CUSE is started from modprobe to
emulate devices on demand because modprobe should not return before
the device nodes are setup.

Thanks.

-- 
tejun

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCHSET] CUSE: implement CUSE, take #3
  2009-04-23  0:09   ` Tejun Heo
@ 2009-04-23 10:08     ` Miklos Szeredi
  2009-04-23 10:32       ` Tejun Heo
  0 siblings, 1 reply; 17+ messages in thread
From: Miklos Szeredi @ 2009-04-23 10:08 UTC (permalink / raw)
  To: tj; +Cc: miklos, linux-kernel, fuse-devel

On Thu, 23 Apr 2009, Tejun Heo wrote:
> Miklos Szeredi wrote:

> > What I changed (IIRC):
> > 
> >  - Don't store pointer to struct inode, struct fuse_inode, and struct
> >    file in struct fuse_file.  Instead store nodeid in struct
> >    fuse_file, no worries about object lifetimes this way.
> 
> Hmm... there really isn't any object lifetime issue with or without
> the pointers as both fuse_file and fuse_inode follow VFS lifetime
> rules.

I did find some problems with lifetimes (it was I think struct file
still being used in release code, but it may have been something
different).

>   I tried several different approaches including adding nodeid
> but what pushed me toward adding the pointers was that the parameter
> passing and which uses/depends on what becomes a bit too complex with
> other approaches.  e.g. How do you discern between needing vfs inode
> and fuse inode?

VFS inode should never be needed by the CUSE codepaths.  Well, maybe
for setting access/modification times, but in any case those need not
be shared with fuse code.

> 
> >  - Don't implement fsync and flush in CUSE, they don't really make
> >    sense on character devices.  At least I can't think of any use for
> >    them.
> >  - release() in CUSE can be synchronous.  Asynchronous release causes
> >    many headaches in fuse, but is necessary due to the possible DoS
> >    that an unprivileged filesystem could do with an unrelated process.
> >    Since CUSE servers are always privileged, this should not be an
> >    issue.
> 
> Cool.
> 
> >  - Other cleanups.
> > 
> > I still think that the INIT sequence in CUSE is way too complicated
> > and that the kernel thread is unnecessary.  How about doing the init
> > with a single CUSE_INIT message which embeds the fuse_init_in and
> > fuse_init_out structures.
> 
> I don't see why the kernel thread is such a big problem.  It's just a
> kernel thread.

I think it's an unnecessary complication.  Threads are useful if
something needs to be _done_, but in this case nothing is done, just
waiting for the reply.  One example why I find it hackish is that you
are returning error values from the thread, as if it was a synchronous
thing, but those errors are obviously lost.

I don't see any problems with doing the device initialization in the
context of the replying process (which will usually be the same as the
one opening the device anyway).

>  We can surely fold FUSE_INIT inside FUSE_INIT tho but
> one way or the other doesn't make whole lot of difference.

I just think it would be cleaner, both code-wise and protocol-wise.  I
can do the above cleanups if you have no objection.

> > I also don't see the purpose of the CUSE_INIT_DONE message.  Isn't
> > it enough not to send any other messages while the initialization is
> > not complete?  This is what fuse does.
> 
> The difference between FUSE and CUSE in this regard is that the
> synchornous part of initialization is much less for CUSE.  ie. For
> FUSE, after the mount is complete, all accesses go to FUSE, so it's
> all good.  However, for CUSE, the device nodes are setup
> asynchronously, so there's no such inherent synchronization point as
> mount.  This becomes a problem when CUSE is started from modprobe to
> emulate devices on demand because modprobe should not return before
> the device nodes are setup.

Hmm, OK.

Thanks,
Miklos

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCHSET] CUSE: implement CUSE, take #3
  2009-04-23 10:08     ` Miklos Szeredi
@ 2009-04-23 10:32       ` Tejun Heo
  2009-04-28 15:43         ` Miklos Szeredi
  0 siblings, 1 reply; 17+ messages in thread
From: Tejun Heo @ 2009-04-23 10:32 UTC (permalink / raw)
  To: Miklos Szeredi; +Cc: linux-kernel, fuse-devel

Hello,

Miklos Szeredi wrote:
> VFS inode should never be needed by the CUSE codepaths.  Well, maybe
> for setting access/modification times, but in any case those need not
> be shared with fuse code.

One more usage: locking.

>>> I still think that the INIT sequence in CUSE is way too complicated
>>> and that the kernel thread is unnecessary.  How about doing the init
>>> with a single CUSE_INIT message which embeds the fuse_init_in and
>>> fuse_init_out structures.
>> I don't see why the kernel thread is such a big problem.  It's just a
>> kernel thread.
> 
> I think it's an unnecessary complication.  Threads are useful if
> something needs to be _done_, but in this case nothing is done, just
> waiting for the reply.  One example why I find it hackish is that you
> are returning error values from the thread, as if it was a synchronous
> thing, but those errors are obviously lost.

That doesn't have anything to do with whether it's done in kernel
thread or not.  If that bothers you, just make it return 0 with
comment /* return value ignored */.

> I don't see any problems with doing the device initialization in the
> context of the replying process (which will usually be the same as
> the one opening the device anyway).

I don't know.  If it's simple, sure why not.  If it makes things even
marginally more complex (state saving and so on), why go that way?

In my first job, I used to work on a quite complex state machine based
server which had to do almost everything in multiple functions.  In
the end, it was extremely ugly and ultimately the wrong kind of
optimization, so I much prefer having a stack whenever it makes things
simpler and the overhead isn't an issue.  What are the actual benefits
of doing things in several different functions in this case?
Performance definitely is not a concern here.

>>  We can surely fold FUSE_INIT inside FUSE_INIT tho but
>> one way or the other doesn't make whole lot of difference.
> 
> I just think it would be cleaner, both code-wise and protocol-wise.  I
> can do the above cleanups if you have no objection.

Sure, if you think things would be better that way.  Please go ahead.

Thanks.

-- 
tejun

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCHSET] CUSE: implement CUSE, take #3
  2009-04-23 10:32       ` Tejun Heo
@ 2009-04-28 15:43         ` Miklos Szeredi
  2009-04-30  2:10           ` Tejun Heo
  0 siblings, 1 reply; 17+ messages in thread
From: Miklos Szeredi @ 2009-04-28 15:43 UTC (permalink / raw)
  To: tj; +Cc: miklos, linux-kernel, fuse-devel

Hi Tejun,

On Thu, 23 Apr 2009, Tejun Heo wrote:
> Miklos Szeredi wrote:
> > VFS inode should never be needed by the CUSE codepaths.  Well, maybe
> > for setting access/modification times, but in any case those need not
> > be shared with fuse code.
> 
> One more usage: locking.

You mean inode->i_mutex locking?  It doesn't look to me as if cuse
would need to do any of that.

> > I don't see any problems with doing the device initialization in the
> > context of the replying process (which will usually be the same as
> > the one opening the device anyway).
> 
> I don't know.  If it's simple, sure why not.  If it makes things even
> marginally more complex (state saving and so on), why go that way?

OK, I folded FUSE_INIT into CUSE_INIT, and moved the CUSE_INIT reply
processing into the ->end() callback.  This also allows us to remove
the CUSE_INIT_DONE message, since now the write(2) on the cuse device
will only return when the device initialization is complete.

Overall it results in some 100 lines less code (well, some of that is
because I folded <linux/cuse.h> into <linux/fuse.h>).  But the simpler
initialization sequence is, I think, definitely worth it.

The patches are pushed out to the 'cuse' and 'for-next' branches of

  git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git

I'll post the updated libfuse patch as well.  Please let me know if
you find anything wrong with the current patches.

Thanks,
Miklos

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCHSET] CUSE: implement CUSE, take #3
  2009-04-28 15:43         ` Miklos Szeredi
@ 2009-04-30  2:10           ` Tejun Heo
  2009-04-30  2:13             ` Tejun Heo
  0 siblings, 1 reply; 17+ messages in thread
From: Tejun Heo @ 2009-04-30  2:10 UTC (permalink / raw)
  To: Miklos Szeredi; +Cc: linux-kernel, fuse-devel

Hello,

Miklos Szeredi wrote:
>>> VFS inode should never be needed by the CUSE codepaths.  Well, maybe
>>> for setting access/modification times, but in any case those need not
>>> be shared with fuse code.
>> One more usage: locking.
> 
> You mean inode->i_mutex locking?  It doesn't look to me as if cuse
> would need to do any of that.

Well, there are a few places in file.c and dir.c IIRC and I think
there's one path which CUSE uses (or used, it might have gone away
with your changes).  I was concerned with the different assumptions
FUSE code paths are under depending on whether the code is used by
CUSE or that, which is not at all obvious from looking at the FUSE
code only, so the large update to put FUSE file handling paths under
the same assumptions which are safe for CUSE.  I think it's too subtle
but it's also true that the paths used by CUSE are quite limited, so
as long as you can keep a close eye on it I guess it would be okay.

>>> I don't see any problems with doing the device initialization in the
>>> context of the replying process (which will usually be the same as
>>> the one opening the device anyway).
>> I don't know.  If it's simple, sure why not.  If it makes things even
>> marginally more complex (state saving and so on), why go that way?
> 
> OK, I folded FUSE_INIT into CUSE_INIT, and moved the CUSE_INIT reply
> processing into the ->end() callback.  This also allows us to remove
> the CUSE_INIT_DONE message, since now the write(2) on the cuse device
> will only return when the device initialization is complete.
> 
> Overall it results in some 100 lines less code (well, some of that is
> because I folded <linux/cuse.h> into <linux/fuse.h>).  But the simpler
> initialization sequence is, I think, definitely worth it.
> 
> The patches are pushed out to the 'cuse' and 'for-next' branches of
> 
>   git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git
>
> I'll post the updated libfuse patch as well.  Please let me know if
> you find anything wrong with the current patches.

Looks good to me.  Much prettier with two inits merged.

Thanks.

-- 
tejun

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCHSET] CUSE: implement CUSE, take #3
  2009-04-30  2:10           ` Tejun Heo
@ 2009-04-30  2:13             ` Tejun Heo
  2009-05-06  9:33               ` Miklos Szeredi
  0 siblings, 1 reply; 17+ messages in thread
From: Tejun Heo @ 2009-04-30  2:13 UTC (permalink / raw)
  To: Miklos Szeredi; +Cc: linux-kernel, fuse-devel

Tejun Heo wrote:
>> I'll post the updated libfuse patch as well.  Please let me know if
>> you find anything wrong with the current patches.
> 
> Looks good to me.  Much prettier with two inits merged.

Oh... what happened to CUSE_INIT_DONE?

-- 
tejun

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCHSET] CUSE: implement CUSE, take #3
  2009-04-30  2:13             ` Tejun Heo
@ 2009-05-06  9:33               ` Miklos Szeredi
  2009-05-08 23:35                 ` Tejun Heo
  0 siblings, 1 reply; 17+ messages in thread
From: Miklos Szeredi @ 2009-05-06  9:33 UTC (permalink / raw)
  To: tj; +Cc: miklos, linux-kernel, fuse-devel

On Thu, 30 Apr 2009, Tejun Heo wrote:
> Tejun Heo wrote:
> >> I'll post the updated libfuse patch as well.  Please let me know if
> >> you find anything wrong with the current patches.
> > 
> > Looks good to me.  Much prettier with two inits merged.
> 
> Oh... what happened to CUSE_INIT_DONE?

By moving the CUSE_INIT reply processing into the ->end() callback,
the device initialization becomes synchronous with the process writing
the message into the cuse device.

This means, that the process can simply wait for the write() on the
device to complete before proceeding.

I've updated the libfuse patch so it calls ->init_done() after sending
the CUSE_INIT reply.

Thanks,
Miklos

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCHSET] CUSE: implement CUSE, take #3
  2009-05-06  9:33               ` Miklos Szeredi
@ 2009-05-08 23:35                 ` Tejun Heo
  0 siblings, 0 replies; 17+ messages in thread
From: Tejun Heo @ 2009-05-08 23:35 UTC (permalink / raw)
  To: Miklos Szeredi; +Cc: linux-kernel, fuse-devel

Miklos Szeredi wrote:
> On Thu, 30 Apr 2009, Tejun Heo wrote:
>> Tejun Heo wrote:
>>>> I'll post the updated libfuse patch as well.  Please let me know if
>>>> you find anything wrong with the current patches.
>>> Looks good to me.  Much prettier with two inits merged.
>> Oh... what happened to CUSE_INIT_DONE?
> 
> By moving the CUSE_INIT reply processing into the ->end() callback,
> the device initialization becomes synchronous with the process writing
> the message into the cuse device.
> 
> This means, that the process can simply wait for the write() on the
> device to complete before proceeding.
> 
> I've updated the libfuse patch so it calls ->init_done() after sending
> the CUSE_INIT reply.

Ah... cool.  Thanks.

-- 
tejun

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2009-05-08 23:36 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-04-14  1:54 [PATCHSET] CUSE: implement CUSE, take #3 Tejun Heo
2009-04-14  1:54 ` [PATCH 1/6] FUSE: misc cleanups Tejun Heo
2009-04-14  1:54 ` [PATCH 2/6] FUSE: use fuse objects as the main objects in file handling functions Tejun Heo
2009-04-14  1:54 ` [PATCH 3/6] FUSE: make ff->fi optional Tejun Heo
2009-04-14  1:54 ` [PATCH 4/6] FUSE: update fuse_conn_init() and separate out fuse_conn_kill() Tejun Heo
2009-04-14  1:54 ` [PATCH 5/6] FUSE: export symbols to be used by CUSE Tejun Heo
2009-04-14  1:54 ` [PATCH 6/6] CUSE: implement CUSE - Character device in Userspace Tejun Heo
2009-04-21 16:43 ` [PATCHSET] CUSE: implement CUSE, take #3 Tejun Heo
2009-04-22 11:39 ` Miklos Szeredi
2009-04-23  0:09   ` Tejun Heo
2009-04-23 10:08     ` Miklos Szeredi
2009-04-23 10:32       ` Tejun Heo
2009-04-28 15:43         ` Miklos Szeredi
2009-04-30  2:10           ` Tejun Heo
2009-04-30  2:13             ` Tejun Heo
2009-05-06  9:33               ` Miklos Szeredi
2009-05-08 23:35                 ` Tejun Heo

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.