Selects on dirs/files.

From: N. Jason Kleinbub (jkleinbub@navtechinc.com)
Date: Tue Feb 13 2001 - 09:19:46 EST


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;

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



This archive was generated by hypermail 2b29 : Thu Feb 15 2001 - 21:00:21 EST