Re: VFS event hooks

Richard Gooch (rgooch@atnf.csiro.au)
Mon, 28 Jun 1999 09:35:20 +1000


Alan Cox writes:
> > Note that the poll() approach would simply mean that the file browser
> > would have each directory that it displays open - and running poll on
> > them. Whenever poll indicates that the directory changed, it re-reads the
> > directory and updates as necessary.
>
> Ted Tso pointed something out at Expo we'd missed. There is a problem with
> using poll() as there is no "changed since last time" case. That means there
> is no way to do
>
> {
> if(its_changed()
> do_thing
> poll()
> }
>
> as the poll won't be working for "since last poll".
>
> SIGIO instead is the model that is uglier but probably right -
> especially now we have rt signals that pass the fd back

This is what I thought at first, too. But using SIGIO (IIRC) would
limit you to one process per inode which can receive change events.
This is definately no good: there could well be multiple file browsers
pointed at the same directory.

Also, you can define the semantics for the new poll() flag to be:
"something changed since the last poll()".

I've coded up a quick "demonstration of principle" patch which I've
appended. It works a treat!

It patches against 2.3.5 and 2.3.8. Currently only ext2fs directories
support inode change events, but it gives you the idea. I considered
hacking do_select() and do_poll() to call vfs_poll() directly, but
that would have introduced more tests to the critical path.

Linus: how does this look?

Regards,

Richard....

diff -urN linux-2.3.5/fs/Makefile linux/fs/Makefile
--- linux-2.3.5/fs/Makefile Wed Jun 23 23:54:54 1999
+++ linux/fs/Makefile Mon Jun 28 08:07:17 1999
@@ -13,7 +13,7 @@
O_OBJS = open.o read_write.o devices.o file_table.o buffer.o \
super.o block_dev.o stat.o exec.o pipe.o namei.o fcntl.o \
ioctl.o readdir.o select.o fifo.o locks.o filesystems.o \
- dcache.o inode.o attr.o bad_inode.o $(BINFMTS)
+ dcache.o inode.o attr.o bad_inode.o vfs_poll.o $(BINFMTS)

MOD_LIST_NAME := FS_MODULES
ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos ntfs \
diff -urN linux-2.3.5/fs/ext2/dir.c linux/fs/ext2/dir.c
--- linux-2.3.5/fs/ext2/dir.c Sat Apr 24 14:20:37 1999
+++ linux/fs/ext2/dir.c Mon Jun 28 08:55:18 1999
@@ -39,12 +39,12 @@
ext2_dir_read, /* read */
NULL, /* write - bad */
ext2_readdir, /* readdir */
- NULL, /* poll - default */
+ vfs_poll, /* poll - use VFS */
ext2_ioctl, /* ioctl */
NULL, /* mmap */
NULL, /* no special open code */
NULL, /* flush */
- NULL, /* no special release code */
+ vfs_release, /* for vfs_poll() */
ext2_sync_file, /* fsync */
NULL, /* fasync */
NULL, /* check_media_change */
diff -urN linux-2.3.5/fs/namei.c linux/fs/namei.c
--- linux-2.3.5/fs/namei.c Sun May 16 16:43:05 1999
+++ linux/fs/namei.c Mon Jun 28 08:53:04 1999
@@ -16,6 +16,7 @@
#include <linux/proc_fs.h>
#include <linux/smp_lock.h>
#include <linux/quotaops.h>
+#include <linux/poll.h>

#include <asm/uaccess.h>
#include <asm/unaligned.h>
@@ -647,6 +648,8 @@

DQUOT_INIT(dir);
error = dir->i_op->create(dir, dentry, mode);
+ if (!error)
+ vfs_poll_notify (dir, POLLWRINODE);
exit_lock:
return error;
}
@@ -825,6 +828,8 @@

DQUOT_INIT(dir->d_inode);
error = dir->d_inode->i_op->mknod(dir->d_inode, dentry, mode, dev);
+ if (!error)
+ vfs_poll_notify (dir->d_inode, POLLWRINODE);
exit_lock:
retval = ERR_PTR(error);
if (!error)
@@ -917,6 +922,8 @@
DQUOT_INIT(dir->d_inode);
mode &= (S_IRWXUGO|S_ISVTX) & ~current->fs->umask;
error = dir->d_inode->i_op->mkdir(dir->d_inode, dentry, mode);
+ if (!error)
+ vfs_poll_notify (dir->d_inode, POLLWRINODE);

exit_lock:
unlock_dir(dir);
@@ -1012,6 +1019,8 @@
error = -ENOENT;
if (check_parent(dir, dentry))
error = vfs_rmdir(dir->d_inode, dentry);
+ if (!error)
+ vfs_poll_notify (dir->d_inode, POLLWRINODE);

double_unlock(dentry, dir);
exit_dput:
@@ -1066,6 +1075,8 @@
error = -ENOENT;
if (check_parent(dir, dentry))
error = vfs_unlink(dir->d_inode, dentry);
+ if (!error)
+ vfs_poll_notify (dir->d_inode, POLLWRINODE);

unlock_dir(dir);
dput(dentry);
@@ -1116,6 +1127,8 @@

DQUOT_INIT(dir->d_inode);
error = dir->d_inode->i_op->symlink(dir->d_inode, dentry, oldname);
+ if (!error)
+ vfs_poll_notify (dir->d_inode, POLLWRINODE);

exit_lock:
unlock_dir(dir);
@@ -1202,6 +1215,8 @@

DQUOT_INIT(dir->d_inode);
error = dir->d_inode->i_op->link(old_dentry, dir->d_inode, new_dentry);
+ if (!error)
+ vfs_poll_notify (dir->d_inode, POLLWRINODE);

exit_lock:
unlock_dir(dir);
@@ -1374,7 +1389,11 @@
if (check_parent(old_dir, old_dentry) && check_parent(new_dir, new_dentry))
error = vfs_rename(old_dir->d_inode, old_dentry,
new_dir->d_inode, new_dentry);
-
+ if (!error) {
+ vfs_poll_notify (old_dir->d_inode, POLLWRINODE);
+ vfs_poll_notify (new_dir->d_inode, POLLWRINODE);
+ }
+
double_unlock(new_dir, old_dir);
dput(new_dentry);
exit_old:
diff -urN linux-2.3.5/fs/vfs_poll.c linux/fs/vfs_poll.c
--- linux-2.3.5/fs/vfs_poll.c Thu Jan 1 10:00:00 1970
+++ linux/fs/vfs_poll.c Mon Jun 28 09:28:28 1999
@@ -0,0 +1,52 @@
+#include <linux/fs.h>
+#include <linux/malloc.h>
+#include <linux/poll.h>
+
+unsigned int vfs_poll (struct file *file, struct poll_table_struct *wait)
+{
+ unsigned int mask;
+ struct vfs_poll_info *entry = file->private_data;
+
+ if (!entry)
+ {
+ struct inode *inode = file->f_dentry->d_inode;
+
+ entry = kmalloc (sizeof *entry, GFP_KERNEL);
+ if (!entry)
+ {
+ printk ("vfs_poll(): could not allocate\n");
+ return 0;
+ }
+ file->private_data = entry;
+ memset (entry, sizeof *entry);
+ init_waitqueue_head (&entry->wait);
+ entry->next = inode->i_vfs_poll;
+ inode->i_vfs_poll = entry;
+ }
+ poll_wait (file, &entry->wait, wait);
+ mask = entry->events;
+ entry->events = 0;
+ return mask;
+} /* End Function vfs_poll */
+
+int vfs_release (struct inode *inode, struct file *file)
+{
+ struct vfs_poll_info *entry = file->private_data;
+
+ if (!entry) return 0;
+ if (entry->prev) entry->prev->next = entry->next;
+ else inode->i_vfs_poll = entry->next;
+ if (entry->next) entry->next->prev = entry->prev;
+ kfree (entry);
+} /* End Function vfs_release */
+
+void __vfs_poll_notify (struct inode *inode, unsigned int events)
+{
+ struct vfs_poll_info *curr;
+
+ for (curr = inode->i_vfs_poll; curr; curr = curr->next)
+ {
+ curr->events |= events;
+ wake_up_interruptible (&curr->wait);
+ }
+} /* End Function __vfs_notify */
diff -urN linux-2.3.5/include/linux/fs.h linux/include/linux/fs.h
--- linux-2.3.5/include/linux/fs.h Fri Jun 25 11:00:33 1999
+++ linux/include/linux/fs.h Mon Jun 28 08:11:01 1999
@@ -336,6 +336,13 @@
#include <linux/quota.h>
#include <linux/mount.h>

+struct vfs_poll_info {
+ unsigned short events;
+ wait_queue_head_t wait;
+ struct vfs_poll_info *prev;
+ struct vfs_poll_info *next;
+};
+
struct inode {
struct list_head i_hash;
struct list_head i_list;
@@ -365,6 +372,7 @@
struct file_lock *i_flock;
struct vm_area_struct *i_mmap;
struct page *i_pages;
+ struct vfs_poll_info *i_vfs_poll;
struct dquot *i_dquot[MAXQUOTAS];

unsigned long i_state;
@@ -400,6 +408,17 @@
void *generic_ip;
} u;
};
+
+extern unsigned int vfs_poll (struct file *, struct poll_table_struct *);
+extern int vfs_release (struct inode *, struct file *);
+extern void __vfs_poll_notify (struct inode *inode, unsigned int events);
+
+static inline void vfs_poll_notify (struct inode *inode, unsigned int events)
+{
+ if (!inode->i_vfs_poll)
+ return;
+ __vfs_poll_notify (inode, events);
+}

/* Inode state bits.. */
#define I_DIRTY 1
diff -urN linux-2.3.5/include/linux/poll.h linux/include/linux/poll.h
--- linux-2.3.5/include/linux/poll.h Fri Jun 25 11:00:35 1999
+++ linux/include/linux/poll.h Mon Jun 28 08:11:13 1999
@@ -3,6 +3,9 @@

#include <asm/poll.h>

+/* Linux-specific poll events: valid for all architectures */
+#define POLLWRINODE 0x8000
+
#ifdef __KERNEL__

#include <linux/wait.h>

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/