diff options
author | Tejun Heo <tj@kernel.org> | 2008-11-26 06:03:55 -0500 |
---|---|---|
committer | Miklos Szeredi <miklos@szeredi.hu> | 2008-11-26 06:03:55 -0500 |
commit | 95668a69a4bb862063c4d28a746e55107dee7b98 (patch) | |
tree | 0679e0f43274648bad70e694a9efb8bcfed55adc /fs | |
parent | 8599396b5062bf6bd2a0b433503849e2322df1c2 (diff) |
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: Tejun Heo <tj@kernel.org>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/fuse/dev.c | 19 | ||||
-rw-r--r-- | fs/fuse/file.c | 132 | ||||
-rw-r--r-- | fs/fuse/fuse_i.h | 20 | ||||
-rw-r--r-- | fs/fuse/inode.c | 1 |
4 files changed, 172 insertions, 0 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index ffd670bb8c8c..6176e444c76e 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c | |||
@@ -816,10 +816,29 @@ static ssize_t fuse_dev_read(struct kiocb *iocb, const struct iovec *iov, | |||
816 | return err; | 816 | return err; |
817 | } | 817 | } |
818 | 818 | ||
819 | static int fuse_notify_poll(struct fuse_conn *fc, unsigned int size, | ||
820 | struct fuse_copy_state *cs) | ||
821 | { | ||
822 | struct fuse_notify_poll_wakeup_out outarg; | ||
823 | int err; | ||
824 | |||
825 | if (size != sizeof(outarg)) | ||
826 | return -EINVAL; | ||
827 | |||
828 | err = fuse_copy_one(cs, &outarg, sizeof(outarg)); | ||
829 | if (err) | ||
830 | return err; | ||
831 | |||
832 | return fuse_notify_poll_wakeup(fc, &outarg); | ||
833 | } | ||
834 | |||
819 | static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, | 835 | static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, |
820 | unsigned int size, struct fuse_copy_state *cs) | 836 | unsigned int size, struct fuse_copy_state *cs) |
821 | { | 837 | { |
822 | switch (code) { | 838 | switch (code) { |
839 | case FUSE_NOTIFY_POLL: | ||
840 | return fuse_notify_poll(fc, size, cs); | ||
841 | |||
823 | default: | 842 | default: |
824 | return -EINVAL; | 843 | return -EINVAL; |
825 | } | 844 | } |
diff --git a/fs/fuse/file.c b/fs/fuse/file.c index a28ced678d38..b3a944e4bb9c 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c | |||
@@ -62,6 +62,8 @@ struct fuse_file *fuse_file_alloc(struct fuse_conn *fc) | |||
62 | ff->kh = ++fc->khctr; | 62 | ff->kh = ++fc->khctr; |
63 | spin_unlock(&fc->lock); | 63 | spin_unlock(&fc->lock); |
64 | } | 64 | } |
65 | RB_CLEAR_NODE(&ff->polled_node); | ||
66 | init_waitqueue_head(&ff->poll_wait); | ||
65 | } | 67 | } |
66 | return ff; | 68 | return ff; |
67 | } | 69 | } |
@@ -170,7 +172,11 @@ int fuse_release_common(struct inode *inode, struct file *file, int isdir) | |||
170 | 172 | ||
171 | spin_lock(&fc->lock); | 173 | spin_lock(&fc->lock); |
172 | list_del(&ff->write_entry); | 174 | list_del(&ff->write_entry); |
175 | if (!RB_EMPTY_NODE(&ff->polled_node)) | ||
176 | rb_erase(&ff->polled_node, &fc->polled_files); | ||
173 | spin_unlock(&fc->lock); | 177 | spin_unlock(&fc->lock); |
178 | |||
179 | wake_up_interruptible_sync(&ff->poll_wait); | ||
174 | /* | 180 | /* |
175 | * Normally this will send the RELEASE request, | 181 | * Normally this will send the RELEASE request, |
176 | * however if some asynchronous READ or WRITE requests | 182 | * however if some asynchronous READ or WRITE requests |
@@ -1749,6 +1755,130 @@ static long fuse_file_compat_ioctl(struct file *file, unsigned int cmd, | |||
1749 | return fuse_file_do_ioctl(file, cmd, arg, FUSE_IOCTL_COMPAT); | 1755 | return fuse_file_do_ioctl(file, cmd, arg, FUSE_IOCTL_COMPAT); |
1750 | } | 1756 | } |
1751 | 1757 | ||
1758 | /* | ||
1759 | * All files which have been polled are linked to RB tree | ||
1760 | * fuse_conn->polled_files which is indexed by kh. Walk the tree and | ||
1761 | * find the matching one. | ||
1762 | */ | ||
1763 | static struct rb_node **fuse_find_polled_node(struct fuse_conn *fc, u64 kh, | ||
1764 | struct rb_node **parent_out) | ||
1765 | { | ||
1766 | struct rb_node **link = &fc->polled_files.rb_node; | ||
1767 | struct rb_node *last = NULL; | ||
1768 | |||
1769 | while (*link) { | ||
1770 | struct fuse_file *ff; | ||
1771 | |||
1772 | last = *link; | ||
1773 | ff = rb_entry(last, struct fuse_file, polled_node); | ||
1774 | |||
1775 | if (kh < ff->kh) | ||
1776 | link = &last->rb_left; | ||
1777 | else if (kh > ff->kh) | ||
1778 | link = &last->rb_right; | ||
1779 | else | ||
1780 | return link; | ||
1781 | } | ||
1782 | |||
1783 | if (parent_out) | ||
1784 | *parent_out = last; | ||
1785 | return link; | ||
1786 | } | ||
1787 | |||
1788 | /* | ||
1789 | * The file is about to be polled. Make sure it's on the polled_files | ||
1790 | * RB tree. Note that files once added to the polled_files tree are | ||
1791 | * not removed before the file is released. This is because a file | ||
1792 | * polled once is likely to be polled again. | ||
1793 | */ | ||
1794 | static void fuse_register_polled_file(struct fuse_conn *fc, | ||
1795 | struct fuse_file *ff) | ||
1796 | { | ||
1797 | spin_lock(&fc->lock); | ||
1798 | if (RB_EMPTY_NODE(&ff->polled_node)) { | ||
1799 | struct rb_node **link, *parent; | ||
1800 | |||
1801 | link = fuse_find_polled_node(fc, ff->kh, &parent); | ||
1802 | BUG_ON(*link); | ||
1803 | rb_link_node(&ff->polled_node, parent, link); | ||
1804 | rb_insert_color(&ff->polled_node, &fc->polled_files); | ||
1805 | } | ||
1806 | spin_unlock(&fc->lock); | ||
1807 | } | ||
1808 | |||
1809 | static unsigned fuse_file_poll(struct file *file, poll_table *wait) | ||
1810 | { | ||
1811 | struct inode *inode = file->f_dentry->d_inode; | ||
1812 | struct fuse_file *ff = file->private_data; | ||
1813 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
1814 | struct fuse_poll_in inarg = { .fh = ff->fh, .kh = ff->kh }; | ||
1815 | struct fuse_poll_out outarg; | ||
1816 | struct fuse_req *req; | ||
1817 | int err; | ||
1818 | |||
1819 | if (fc->no_poll) | ||
1820 | return DEFAULT_POLLMASK; | ||
1821 | |||
1822 | poll_wait(file, &ff->poll_wait, wait); | ||
1823 | |||
1824 | /* | ||
1825 | * Ask for notification iff there's someone waiting for it. | ||
1826 | * The client may ignore the flag and always notify. | ||
1827 | */ | ||
1828 | if (waitqueue_active(&ff->poll_wait)) { | ||
1829 | inarg.flags |= FUSE_POLL_SCHEDULE_NOTIFY; | ||
1830 | fuse_register_polled_file(fc, ff); | ||
1831 | } | ||
1832 | |||
1833 | req = fuse_get_req(fc); | ||
1834 | if (IS_ERR(req)) | ||
1835 | return PTR_ERR(req); | ||
1836 | |||
1837 | req->in.h.opcode = FUSE_POLL; | ||
1838 | req->in.h.nodeid = get_node_id(inode); | ||
1839 | req->in.numargs = 1; | ||
1840 | req->in.args[0].size = sizeof(inarg); | ||
1841 | req->in.args[0].value = &inarg; | ||
1842 | req->out.numargs = 1; | ||
1843 | req->out.args[0].size = sizeof(outarg); | ||
1844 | req->out.args[0].value = &outarg; | ||
1845 | request_send(fc, req); | ||
1846 | err = req->out.h.error; | ||
1847 | fuse_put_request(fc, req); | ||
1848 | |||
1849 | if (!err) | ||
1850 | return outarg.revents; | ||
1851 | if (err == -ENOSYS) { | ||
1852 | fc->no_poll = 1; | ||
1853 | return DEFAULT_POLLMASK; | ||
1854 | } | ||
1855 | return POLLERR; | ||
1856 | } | ||
1857 | |||
1858 | /* | ||
1859 | * This is called from fuse_handle_notify() on FUSE_NOTIFY_POLL and | ||
1860 | * wakes up the poll waiters. | ||
1861 | */ | ||
1862 | int fuse_notify_poll_wakeup(struct fuse_conn *fc, | ||
1863 | struct fuse_notify_poll_wakeup_out *outarg) | ||
1864 | { | ||
1865 | u64 kh = outarg->kh; | ||
1866 | struct rb_node **link; | ||
1867 | |||
1868 | spin_lock(&fc->lock); | ||
1869 | |||
1870 | link = fuse_find_polled_node(fc, kh, NULL); | ||
1871 | if (*link) { | ||
1872 | struct fuse_file *ff; | ||
1873 | |||
1874 | ff = rb_entry(*link, struct fuse_file, polled_node); | ||
1875 | wake_up_interruptible_sync(&ff->poll_wait); | ||
1876 | } | ||
1877 | |||
1878 | spin_unlock(&fc->lock); | ||
1879 | return 0; | ||
1880 | } | ||
1881 | |||
1752 | static const struct file_operations fuse_file_operations = { | 1882 | static const struct file_operations fuse_file_operations = { |
1753 | .llseek = fuse_file_llseek, | 1883 | .llseek = fuse_file_llseek, |
1754 | .read = do_sync_read, | 1884 | .read = do_sync_read, |
@@ -1765,6 +1895,7 @@ static const struct file_operations fuse_file_operations = { | |||
1765 | .splice_read = generic_file_splice_read, | 1895 | .splice_read = generic_file_splice_read, |
1766 | .unlocked_ioctl = fuse_file_ioctl, | 1896 | .unlocked_ioctl = fuse_file_ioctl, |
1767 | .compat_ioctl = fuse_file_compat_ioctl, | 1897 | .compat_ioctl = fuse_file_compat_ioctl, |
1898 | .poll = fuse_file_poll, | ||
1768 | }; | 1899 | }; |
1769 | 1900 | ||
1770 | static const struct file_operations fuse_direct_io_file_operations = { | 1901 | static const struct file_operations fuse_direct_io_file_operations = { |
@@ -1779,6 +1910,7 @@ static const struct file_operations fuse_direct_io_file_operations = { | |||
1779 | .flock = fuse_file_flock, | 1910 | .flock = fuse_file_flock, |
1780 | .unlocked_ioctl = fuse_file_ioctl, | 1911 | .unlocked_ioctl = fuse_file_ioctl, |
1781 | .compat_ioctl = fuse_file_compat_ioctl, | 1912 | .compat_ioctl = fuse_file_compat_ioctl, |
1913 | .poll = fuse_file_poll, | ||
1782 | /* no mmap and splice_read */ | 1914 | /* no mmap and splice_read */ |
1783 | }; | 1915 | }; |
1784 | 1916 | ||
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 86f013303828..986fbd4c1ff5 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h | |||
@@ -19,6 +19,8 @@ | |||
19 | #include <linux/backing-dev.h> | 19 | #include <linux/backing-dev.h> |
20 | #include <linux/mutex.h> | 20 | #include <linux/mutex.h> |
21 | #include <linux/rwsem.h> | 21 | #include <linux/rwsem.h> |
22 | #include <linux/rbtree.h> | ||
23 | #include <linux/poll.h> | ||
22 | 24 | ||
23 | /** Max number of pages that can be used in a single read request */ | 25 | /** Max number of pages that can be used in a single read request */ |
24 | #define FUSE_MAX_PAGES_PER_REQ 32 | 26 | #define FUSE_MAX_PAGES_PER_REQ 32 |
@@ -111,6 +113,12 @@ struct fuse_file { | |||
111 | 113 | ||
112 | /** Entry on inode's write_files list */ | 114 | /** Entry on inode's write_files list */ |
113 | struct list_head write_entry; | 115 | struct list_head write_entry; |
116 | |||
117 | /** RB node to be linked on fuse_conn->polled_files */ | ||
118 | struct rb_node polled_node; | ||
119 | |||
120 | /** Wait queue head for poll */ | ||
121 | wait_queue_head_t poll_wait; | ||
114 | }; | 122 | }; |
115 | 123 | ||
116 | /** One input argument of a request */ | 124 | /** One input argument of a request */ |
@@ -328,6 +336,9 @@ struct fuse_conn { | |||
328 | /** The next unique kernel file handle */ | 336 | /** The next unique kernel file handle */ |
329 | u64 khctr; | 337 | u64 khctr; |
330 | 338 | ||
339 | /** rbtree of fuse_files waiting for poll events indexed by ph */ | ||
340 | struct rb_root polled_files; | ||
341 | |||
331 | /** Number of requests currently in the background */ | 342 | /** Number of requests currently in the background */ |
332 | unsigned num_background; | 343 | unsigned num_background; |
333 | 344 | ||
@@ -416,6 +427,9 @@ struct fuse_conn { | |||
416 | /** Is bmap not implemented by fs? */ | 427 | /** Is bmap not implemented by fs? */ |
417 | unsigned no_bmap:1; | 428 | unsigned no_bmap:1; |
418 | 429 | ||
430 | /** Is poll not implemented by fs? */ | ||
431 | unsigned no_poll:1; | ||
432 | |||
419 | /** Do multi-page cached writes */ | 433 | /** Do multi-page cached writes */ |
420 | unsigned big_writes:1; | 434 | unsigned big_writes:1; |
421 | 435 | ||
@@ -525,6 +539,12 @@ int fuse_fsync_common(struct file *file, struct dentry *de, int datasync, | |||
525 | int isdir); | 539 | int isdir); |
526 | 540 | ||
527 | /** | 541 | /** |
542 | * Notify poll wakeup | ||
543 | */ | ||
544 | int fuse_notify_poll_wakeup(struct fuse_conn *fc, | ||
545 | struct fuse_notify_poll_wakeup_out *outarg); | ||
546 | |||
547 | /** | ||
528 | * Initialize file operations on a regular file | 548 | * Initialize file operations on a regular file |
529 | */ | 549 | */ |
530 | void fuse_init_file_inode(struct inode *inode); | 550 | void fuse_init_file_inode(struct inode *inode); |
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 0e15bc221d23..ba7256128084 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c | |||
@@ -486,6 +486,7 @@ static struct fuse_conn *new_conn(struct super_block *sb) | |||
486 | /* fuse does it's own writeback accounting */ | 486 | /* fuse does it's own writeback accounting */ |
487 | fc->bdi.capabilities = BDI_CAP_NO_ACCT_WB; | 487 | fc->bdi.capabilities = BDI_CAP_NO_ACCT_WB; |
488 | fc->khctr = 0; | 488 | fc->khctr = 0; |
489 | fc->polled_files = RB_ROOT; | ||
489 | fc->dev = sb->s_dev; | 490 | fc->dev = sb->s_dev; |
490 | err = bdi_init(&fc->bdi); | 491 | err = bdi_init(&fc->bdi); |
491 | if (err) | 492 | if (err) |