Messages in this thread Patch in this message | | | Date | Mon, 28 Jun 1999 09:35:20 +1000 | From | Richard Gooch <> | Subject | Re: VFS event hooks |
| |
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/
| |