People, Not sure where to go from here but .... ( Yes I have read the FAQ ) For practical reasons, I have created some modification to the Linux kernel. These changes were to make our implementation of software more convenient (elegant). Essentially, I have modified the select() calls to allow the non-trivial use of directories as an 'fd'. ( Aside: this has been in a production environment since 2.4.0pre9 ) ( More info on the company and what we are doing via private emails, just ask ) As this is my virginal experience I have, understandibly, a lot of questions. o Is there actually a new experimental kernel 2.5.x? o Does this belong there, or is this appropriate for 2.4.x? o What happens if Linus accepts the patchs to fs/* but the ext2/* or reiserfs/* patches are not accepted? o Should this be the default function if no poll operation exists? (I think not) I have waited until 2.4.0 settled a little bit before charging head along. Additionally, I have avoided simply sending the patches to the appropriate people because I wanted to illicit a discussion prior to doing so. This being my first foray into linux kernel development I crave some constructive feedback. For example, I have un-'static'ed the function llseek() from fs/read_write.c because I wanted to use it in fs/readdir.c (this was easier then moving the code from fs/readdir.c -> fs/read_write.c and I am really into easy!) I thought it was more appropriate then using sys_lseek(), is this a true statement? Additionaly you may wonder why I have choosen to examine "." and ".." in the filldir instead of switching on d_type. The reason is that reiserfs always returns DT_UNKNOWN and I couldn't figure out, without doing a stat(), how to put something more meaningful into d_type. Reiserfs is our most likely candidate for our production environment so I needed both ext2 and reiserfs to work. I have avoided using the existing wait_queue (to avoid unecessary wakeups) and created a seperate poll wait_queue. Good? Bad? Ugly? The current scheme only implements the read event. That is all I currently require. More to come, however, if this generates interest. Please advise. Thanks. +- N. Jason Kleinbub Technical Architect/Product Manager Navtech Weather Systems.
--- /usr/src/linux-2.4.0/fs/read_write.c Wed Feb 7 10:17:24 2001 +++ /usr/src/linux/fs/read_write.c Thu Feb 8 12:16:47 2001 @@ -19,6 +19,7 @@ mmap: generic_file_mmap, }; + ssize_t generic_read_dir(struct file *filp, char *buf, size_t siz, loff_t *ppos) { return -EISDIR; @@ -47,7 +48,7 @@ return retval; } -static inline loff_t llseek(struct file *file, loff_t offset, int origin) +loff_t llseek(struct file *file, loff_t offset, int origin) { loff_t (*fn)(struct file *, loff_t, int); loff_t retval; --- /usr/src/linux-2.4.0/fs/readdir.c Wed Feb 7 10:17:26 2001 +++ /usr/src/linux/fs/readdir.c Thu Feb 8 12:16:47 2001 @@ -11,8 +11,17 @@ #include <linux/file.h> #include <linux/smp_lock.h> +#include <linux/poll.h> + #include <asm/uaccess.h> +struct getdents_callback { + struct linux_dirent * current_dir; + struct linux_dirent * previous; + int count; + int error; +}; + int vfs_readdir(struct file *file, filldir_t filler, void *buf) { struct inode *inode = file->f_dentry->d_inode; @@ -33,6 +42,51 @@ return res; } +static int fillpolldir(void *__buf, const char *name, int namlen, off_t offset, + ino_t ino, unsigned int d_type) +{ + struct getdents_callback *buf = (struct getdents_callback *)__buf; + + /* Best to use d_type. Difficult for non ext2 fs */ + if (name && strcmp(name, ".") && strcmp(name, "..")){ + buf->count = 1; + return -EINVAL; + } + return 0; +} + +unsigned int generic_poll_dir(struct file *filp, struct poll_table_struct *ptbl){ + unsigned int mask; + struct inode *inode = filp->f_dentry->d_inode; + loff_t curpos; + struct getdents_callback buf; + + + /* Do I really need this. The function should only be 'hooked' to dirops */ + if(!S_ISDIR(inode->i_mode)) + return 0; + + poll_wait(filp, &(inode->i_pollwait), ptbl); + + mask = 0; + + buf.current_dir = NULL; + buf.previous = NULL; + buf.count = 0; + buf.error = 0; + + /* Read check */ + curpos = filp->f_pos; + llseek(filp, 0, 0); + vfs_readdir(filp, fillpolldir, &buf); + llseek(filp, curpos, 0); + if(buf.count > 0){ + mask = POLLIN | POLLRDNORM; + } + + return mask; +} + /* * Directory is locked and all positive dentries in it are safe, since * for ramfs-type trees they can't go away without unlink() or rmdir(), @@ -176,12 +230,6 @@ char d_name[1]; }; -struct getdents_callback { - struct linux_dirent * current_dir; - struct linux_dirent * previous; - int count; - int error; -}; static int filldir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino, unsigned int d_type) --- /usr/src/linux-2.4.0/fs/inode.c Wed Feb 7 10:17:26 2001 +++ /usr/src/linux/fs/inode.c Wed Feb 7 07:32:22 2001 @@ -99,6 +99,7 @@ { memset(inode, 0, sizeof(*inode)); init_waitqueue_head(&inode->i_wait); + init_waitqueue_head(&inode->i_pollwait); INIT_LIST_HEAD(&inode->i_hash); INIT_LIST_HEAD(&inode->i_data.clean_pages); INIT_LIST_HEAD(&inode->i_data.dirty_pages); --- /usr/src/linux-2.4.0/fs/namei.c Wed Feb 7 10:17:27 2001 +++ /usr/src/linux/fs/namei.c Wed Feb 7 07:31:38 2001 @@ -917,8 +917,10 @@ unlock_kernel(); exit_lock: up(&dir->i_zombie); - if (!error) + if (!error){ inode_dir_notify(dir, DN_CREATE); + wake_up_interruptible(&(dir->i_pollwait)); + } return error; } @@ -1197,8 +1199,11 @@ unlock_kernel(); exit_lock: up(&dir->i_zombie); - if (!error) + if (!error){ inode_dir_notify(dir, DN_CREATE); + wake_up_interruptible(&(dir->i_pollwait)); + } + return error; } @@ -1266,8 +1271,11 @@ exit_lock: up(&dir->i_zombie); - if (!error) + if (!error){ inode_dir_notify(dir, DN_CREATE); + wake_up_interruptible(&(dir->i_pollwait)); + } + return error; } @@ -1358,6 +1366,7 @@ double_up(&dir->i_zombie, &dentry->d_inode->i_zombie); if (!error) { inode_dir_notify(dir, DN_DELETE); + wake_up_interruptible(&(dir->i_pollwait)); d_delete(dentry); } dput(dentry); @@ -1429,8 +1438,10 @@ } } up(&dir->i_zombie); - if (!error) + if (!error){ inode_dir_notify(dir, DN_DELETE); + wake_up_interruptible(&(dir->i_pollwait)); + } return error; } @@ -1497,8 +1508,10 @@ exit_lock: up(&dir->i_zombie); - if (!error) + if (!error){ inode_dir_notify(dir, DN_CREATE); + wake_up_interruptible(&(dir->i_pollwait)); + } return error; } @@ -1571,8 +1584,11 @@ exit_lock: up(&dir->i_zombie); - if (!error) + if (!error){ inode_dir_notify(dir, DN_CREATE); + wake_up_interruptible(&(dir->i_pollwait)); + } + return error; } @@ -1791,6 +1807,8 @@ inode_dir_notify(old_dir, DN_DELETE); inode_dir_notify(new_dir, DN_CREATE); } + wake_up_interruptible(&(old_dir->i_pollwait)); + } return error; } --- /usr/src/linux-2.4.0/fs/ext2/dir.c Wed Feb 7 10:17:27 2001 +++ /usr/src/linux/fs/ext2/dir.c Tue Feb 6 15:47:56 2001 @@ -32,6 +32,7 @@ readdir: ext2_readdir, ioctl: ext2_ioctl, fsync: ext2_sync_file, + poll: generic_poll_dir, }; int ext2_check_dir_entry (const char * function, struct inode * dir, --- /usr/src/linux-2.4.0/fs/reiserfs/dir.c Wed Feb 7 10:17:28 2001 +++ /usr/src/linux/fs/reiserfs/dir.c Tue Jan 9 19:16:27 2001 @@ -27,6 +27,7 @@ read: generic_read_dir, readdir: reiserfs_readdir, fsync: reiserfs_dir_fsync, + poll: generic_poll_dir, }; /* --- /usr/src/linux-2.4.0/include/linux/fs.h Wed Feb 7 10:17:28 2001 +++ /usr/src/linux/include/linux/fs.h Thu Feb 8 11:04:53 2001 @@ -423,6 +423,8 @@ unsigned long i_dnotify_mask; /* Directory notify events */ struct dnotify_struct *i_dnotify; /* for directory notifications */ + wait_queue_head_t i_pollwait; + unsigned long i_state; unsigned int i_flags; @@ -1185,6 +1187,8 @@ /* needed for stackable file system support */ extern loff_t default_llseek(struct file *file, loff_t offset, int origin); +extern loff_t llseek(struct file *file, loff_t offset, int origin); + extern int __user_walk(const char *, unsigned, struct nameidata *); extern int path_init(const char *, unsigned, struct nameidata *); @@ -1271,6 +1275,7 @@ extern ssize_t generic_file_write(struct file *, const char *, size_t, loff_t *); extern void do_generic_file_read(struct file *, loff_t *, read_descriptor_t *, read_actor_t); +extern unsigned int generic_poll_dir(struct file *filp, struct poll_table_struct +*ptbl); extern ssize_t generic_read_dir(struct file *, char *, size_t, loff_t *); extern struct file_operations generic_ro_fops;