Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 95668a69 authored by Tejun Heo's avatar Tejun Heo Committed by Miklos Szeredi
Browse files

fuse: implement poll support

Implement poll support.  Polled files are indexed using kh in a RB
tree rooted at fuse_conn->polled_files.

Client should send FUSE_NOTIFY_POLL notification once after processing
FUSE_POLL which has FUSE_POLL_SCHEDULE_NOTIFY set.  Sending
notification unconditionally after the latest poll or everytime file
content might have changed is inefficient but won't cause malfunction.

fuse_file_poll() can sleep and requires patches from the following
thread which allows f_op->poll() to sleep.

  http://thread.gmane.org/gmane.linux.kernel/726176



Signed-off-by: default avatarTejun Heo <tj@kernel.org>
Signed-off-by: default avatarMiklos Szeredi <mszeredi@suse.cz>
parent 8599396b
Loading
Loading
Loading
Loading
+19 −0
Original line number Original line Diff line number Diff line
@@ -816,10 +816,29 @@ static ssize_t fuse_dev_read(struct kiocb *iocb, const struct iovec *iov,
	return err;
	return err;
}
}


static int fuse_notify_poll(struct fuse_conn *fc, unsigned int size,
			    struct fuse_copy_state *cs)
{
	struct fuse_notify_poll_wakeup_out outarg;
	int err;

	if (size != sizeof(outarg))
		return -EINVAL;

	err = fuse_copy_one(cs, &outarg, sizeof(outarg));
	if (err)
		return err;

	return fuse_notify_poll_wakeup(fc, &outarg);
}

static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
		       unsigned int size, struct fuse_copy_state *cs)
		       unsigned int size, struct fuse_copy_state *cs)
{
{
	switch (code) {
	switch (code) {
	case FUSE_NOTIFY_POLL:
		return fuse_notify_poll(fc, size, cs);

	default:
	default:
		return -EINVAL;
		return -EINVAL;
	}
	}
+132 −0
Original line number Original line Diff line number Diff line
@@ -62,6 +62,8 @@ struct fuse_file *fuse_file_alloc(struct fuse_conn *fc)
			ff->kh = ++fc->khctr;
			ff->kh = ++fc->khctr;
			spin_unlock(&fc->lock);
			spin_unlock(&fc->lock);
		}
		}
		RB_CLEAR_NODE(&ff->polled_node);
		init_waitqueue_head(&ff->poll_wait);
	}
	}
	return ff;
	return ff;
}
}
@@ -170,7 +172,11 @@ int fuse_release_common(struct inode *inode, struct file *file, int isdir)


		spin_lock(&fc->lock);
		spin_lock(&fc->lock);
		list_del(&ff->write_entry);
		list_del(&ff->write_entry);
		if (!RB_EMPTY_NODE(&ff->polled_node))
			rb_erase(&ff->polled_node, &fc->polled_files);
		spin_unlock(&fc->lock);
		spin_unlock(&fc->lock);

		wake_up_interruptible_sync(&ff->poll_wait);
		/*
		/*
		 * Normally this will send the RELEASE request,
		 * Normally this will send the RELEASE request,
		 * however if some asynchronous READ or WRITE requests
		 * however if some asynchronous READ or WRITE requests
@@ -1749,6 +1755,130 @@ static long fuse_file_compat_ioctl(struct file *file, unsigned int cmd,
	return fuse_file_do_ioctl(file, cmd, arg, FUSE_IOCTL_COMPAT);
	return fuse_file_do_ioctl(file, cmd, arg, FUSE_IOCTL_COMPAT);
}
}


/*
 * All files which have been polled are linked to RB tree
 * fuse_conn->polled_files which is indexed by kh.  Walk the tree and
 * find the matching one.
 */
static struct rb_node **fuse_find_polled_node(struct fuse_conn *fc, u64 kh,
					      struct rb_node **parent_out)
{
	struct rb_node **link = &fc->polled_files.rb_node;
	struct rb_node *last = NULL;

	while (*link) {
		struct fuse_file *ff;

		last = *link;
		ff = rb_entry(last, struct fuse_file, polled_node);

		if (kh < ff->kh)
			link = &last->rb_left;
		else if (kh > ff->kh)
			link = &last->rb_right;
		else
			return link;
	}

	if (parent_out)
		*parent_out = last;
	return link;
}

/*
 * The file is about to be polled.  Make sure it's on the polled_files
 * RB tree.  Note that files once added to the polled_files tree are
 * 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)
{
	spin_lock(&fc->lock);
	if (RB_EMPTY_NODE(&ff->polled_node)) {
		struct rb_node **link, *parent;

		link = fuse_find_polled_node(fc, ff->kh, &parent);
		BUG_ON(*link);
		rb_link_node(&ff->polled_node, parent, link);
		rb_insert_color(&ff->polled_node, &fc->polled_files);
	}
	spin_unlock(&fc->lock);
}

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_poll_in inarg = { .fh = ff->fh, .kh = ff->kh };
	struct fuse_poll_out outarg;
	struct fuse_req *req;
	int err;

	if (fc->no_poll)
		return DEFAULT_POLLMASK;

	poll_wait(file, &ff->poll_wait, wait);

	/*
	 * Ask for notification iff there's someone waiting for it.
	 * The client may ignore the flag and always notify.
	 */
	if (waitqueue_active(&ff->poll_wait)) {
		inarg.flags |= FUSE_POLL_SCHEDULE_NOTIFY;
		fuse_register_polled_file(fc, ff);
	}

	req = fuse_get_req(fc);
	if (IS_ERR(req))
		return PTR_ERR(req);

	req->in.h.opcode = FUSE_POLL;
	req->in.h.nodeid = get_node_id(inode);
	req->in.numargs = 1;
	req->in.args[0].size = sizeof(inarg);
	req->in.args[0].value = &inarg;
	req->out.numargs = 1;
	req->out.args[0].size = sizeof(outarg);
	req->out.args[0].value = &outarg;
	request_send(fc, req);
	err = req->out.h.error;
	fuse_put_request(fc, req);

	if (!err)
		return outarg.revents;
	if (err == -ENOSYS) {
		fc->no_poll = 1;
		return DEFAULT_POLLMASK;
	}
	return POLLERR;
}

/*
 * This is called from fuse_handle_notify() on FUSE_NOTIFY_POLL and
 * wakes up the poll waiters.
 */
int fuse_notify_poll_wakeup(struct fuse_conn *fc,
			    struct fuse_notify_poll_wakeup_out *outarg)
{
	u64 kh = outarg->kh;
	struct rb_node **link;

	spin_lock(&fc->lock);

	link = fuse_find_polled_node(fc, kh, NULL);
	if (*link) {
		struct fuse_file *ff;

		ff = rb_entry(*link, struct fuse_file, polled_node);
		wake_up_interruptible_sync(&ff->poll_wait);
	}

	spin_unlock(&fc->lock);
	return 0;
}

static const struct file_operations fuse_file_operations = {
static const struct file_operations fuse_file_operations = {
	.llseek		= fuse_file_llseek,
	.llseek		= fuse_file_llseek,
	.read		= do_sync_read,
	.read		= do_sync_read,
@@ -1765,6 +1895,7 @@ static const struct file_operations fuse_file_operations = {
	.splice_read	= generic_file_splice_read,
	.splice_read	= generic_file_splice_read,
	.unlocked_ioctl	= fuse_file_ioctl,
	.unlocked_ioctl	= fuse_file_ioctl,
	.compat_ioctl	= fuse_file_compat_ioctl,
	.compat_ioctl	= fuse_file_compat_ioctl,
	.poll		= fuse_file_poll,
};
};


static const struct file_operations fuse_direct_io_file_operations = {
static const struct file_operations fuse_direct_io_file_operations = {
@@ -1779,6 +1910,7 @@ static const struct file_operations fuse_direct_io_file_operations = {
	.flock		= fuse_file_flock,
	.flock		= fuse_file_flock,
	.unlocked_ioctl	= fuse_file_ioctl,
	.unlocked_ioctl	= fuse_file_ioctl,
	.compat_ioctl	= fuse_file_compat_ioctl,
	.compat_ioctl	= fuse_file_compat_ioctl,
	.poll		= fuse_file_poll,
	/* no mmap and splice_read */
	/* no mmap and splice_read */
};
};


+20 −0
Original line number Original line Diff line number Diff line
@@ -19,6 +19,8 @@
#include <linux/backing-dev.h>
#include <linux/backing-dev.h>
#include <linux/mutex.h>
#include <linux/mutex.h>
#include <linux/rwsem.h>
#include <linux/rwsem.h>
#include <linux/rbtree.h>
#include <linux/poll.h>


/** Max number of pages that can be used in a single read request */
/** Max number of pages that can be used in a single read request */
#define FUSE_MAX_PAGES_PER_REQ 32
#define FUSE_MAX_PAGES_PER_REQ 32
@@ -111,6 +113,12 @@ struct fuse_file {


	/** Entry on inode's write_files list */
	/** Entry on inode's write_files list */
	struct list_head write_entry;
	struct list_head write_entry;

	/** RB node to be linked on fuse_conn->polled_files */
	struct rb_node polled_node;

	/** Wait queue head for poll */
	wait_queue_head_t poll_wait;
};
};


/** One input argument of a request */
/** One input argument of a request */
@@ -328,6 +336,9 @@ struct fuse_conn {
	/** The next unique kernel file handle */
	/** The next unique kernel file handle */
	u64 khctr;
	u64 khctr;


	/** rbtree of fuse_files waiting for poll events indexed by ph */
	struct rb_root polled_files;

	/** Number of requests currently in the background */
	/** Number of requests currently in the background */
	unsigned num_background;
	unsigned num_background;


@@ -416,6 +427,9 @@ struct fuse_conn {
	/** Is bmap not implemented by fs? */
	/** Is bmap not implemented by fs? */
	unsigned no_bmap:1;
	unsigned no_bmap:1;


	/** Is poll not implemented by fs? */
	unsigned no_poll:1;

	/** Do multi-page cached writes */
	/** Do multi-page cached writes */
	unsigned big_writes:1;
	unsigned big_writes:1;


@@ -524,6 +538,12 @@ int fuse_release_common(struct inode *inode, struct file *file, int isdir);
int fuse_fsync_common(struct file *file, struct dentry *de, int datasync,
int fuse_fsync_common(struct file *file, struct dentry *de, int datasync,
		      int isdir);
		      int isdir);


/**
 * Notify poll wakeup
 */
int fuse_notify_poll_wakeup(struct fuse_conn *fc,
			    struct fuse_notify_poll_wakeup_out *outarg);

/**
/**
 * Initialize file operations on a regular file
 * Initialize file operations on a regular file
 */
 */
+1 −0
Original line number Original line Diff line number Diff line
@@ -486,6 +486,7 @@ static struct fuse_conn *new_conn(struct super_block *sb)
		/* fuse does it's own writeback accounting */
		/* fuse does it's own writeback accounting */
		fc->bdi.capabilities = BDI_CAP_NO_ACCT_WB;
		fc->bdi.capabilities = BDI_CAP_NO_ACCT_WB;
		fc->khctr = 0;
		fc->khctr = 0;
		fc->polled_files = RB_ROOT;
		fc->dev = sb->s_dev;
		fc->dev = sb->s_dev;
		err = bdi_init(&fc->bdi);
		err = bdi_init(&fc->bdi);
		if (err)
		if (err)
+25 −0
Original line number Original line Diff line number Diff line
@@ -163,6 +163,13 @@ struct fuse_file_lock {


#define FUSE_IOCTL_MAX_IOV	256
#define FUSE_IOCTL_MAX_IOV	256


/**
 * Poll flags
 *
 * FUSE_POLL_SCHEDULE_NOTIFY: request poll notify
 */
#define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0)

enum fuse_opcode {
enum fuse_opcode {
	FUSE_LOOKUP	   = 1,
	FUSE_LOOKUP	   = 1,
	FUSE_FORGET	   = 2,  /* no reply */
	FUSE_FORGET	   = 2,  /* no reply */
@@ -201,9 +208,11 @@ enum fuse_opcode {
	FUSE_BMAP          = 37,
	FUSE_BMAP          = 37,
	FUSE_DESTROY       = 38,
	FUSE_DESTROY       = 38,
	FUSE_IOCTL         = 39,
	FUSE_IOCTL         = 39,
	FUSE_POLL          = 40,
};
};


enum fuse_notify_code {
enum fuse_notify_code {
	FUSE_NOTIFY_POLL   = 1,
	FUSE_NOTIFY_CODE_MAX,
	FUSE_NOTIFY_CODE_MAX,
};
};


@@ -421,6 +430,22 @@ struct fuse_ioctl_out {
	__u32	out_iovs;
	__u32	out_iovs;
};
};


struct fuse_poll_in {
	__u64	fh;
	__u64	kh;
	__u32	flags;
	__u32   padding;
};

struct fuse_poll_out {
	__u32	revents;
	__u32	padding;
};

struct fuse_notify_poll_wakeup_out {
	__u64	kh;
};

struct fuse_in_header {
struct fuse_in_header {
	__u32	len;
	__u32	len;
	__u32	opcode;
	__u32	opcode;