root/fs/select.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. free_wait
  2. check
  3. do_select
  4. __get_fd_set
  5. __set_fd_set
  6. sys_select

   1 /*
   2  * This file contains the procedures for the handling of select
   3  *
   4  * Created for Linux based loosely upon Mathius Lattner's minix
   5  * patches by Peter MacDonald. Heavily edited by Linus.
   6  */
   7 
   8 #include <linux/types.h>
   9 #include <linux/time.h>
  10 #include <linux/fs.h>
  11 #include <linux/kernel.h>
  12 #include <linux/sched.h>
  13 #include <linux/string.h>
  14 #include <linux/stat.h>
  15 #include <linux/signal.h>
  16 #include <linux/errno.h>
  17 
  18 #include <asm/segment.h>
  19 #include <asm/system.h>
  20 
  21 /*
  22  * Ok, Peter made a complicated, but straightforward multiple_wait() function.
  23  * I have rewritten this, taking some shortcuts: This code may not be easy to
  24  * follow, but it should be free of race-conditions, and it's practical. If you
  25  * understand what I'm doing here, then you understand how the linux
  26  * sleep/wakeup mechanism works.
  27  *
  28  * Two very simple procedures, select_wait() and free_wait() make all the work.
  29  * select_wait() is a inline-function defined in <linux/sched.h>, as all select
  30  * functions have to call it to add an entry to the select table.
  31  */
  32 
  33 /*
  34  * I rewrote this again to make the select_table size variable, take some
  35  * more shortcuts, improve responsiveness, and remove another race that
  36  * Linus noticed.  -- jrs
  37  */
  38 
  39 static void free_wait(select_table * p)
     /* [previous][next][first][last][top][bottom][index][help] */
  40 {
  41         struct select_table_entry * entry = p->entry + p->nr;
  42 
  43         while (p->nr > 0) {
  44                 p->nr--;
  45                 entry--;
  46                 remove_wait_queue(entry->wait_address,&entry->wait);
  47         }
  48 }
  49 
  50 /*
  51  * The check function checks the ready status of a file using the vfs layer.
  52  *
  53  * If the file was not ready we were added to its wait queue.  But in
  54  * case it became ready just after the check and just before it called
  55  * select_wait, we call it again, knowing we are already on its
  56  * wait queue this time.  The second call is not necessary if the
  57  * select_table is NULL indicating an earlier file check was ready
  58  * and we aren't going to sleep on the select_table.  -- jrs
  59  */
  60 
  61 static int check(int flag, select_table * wait, struct file * file)
     /* [previous][next][first][last][top][bottom][index][help] */
  62 {
  63         struct inode * inode;
  64         struct file_operations *fops;
  65         int (*select) (struct inode *, struct file *, int, select_table *);
  66 
  67         inode = file->f_inode;
  68         if ((fops = file->f_op) && (select = fops->select))
  69                 return select(inode, file, flag, wait)
  70                     || (wait && select(inode, file, flag, NULL));
  71         if (S_ISREG(inode->i_mode))
  72                 return 1;
  73         return 0;
  74 }
  75 
  76 int do_select(int n, fd_set *in, fd_set *out, fd_set *ex,
     /* [previous][next][first][last][top][bottom][index][help] */
  77         fd_set *res_in, fd_set *res_out, fd_set *res_ex)
  78 {
  79         int count;
  80         select_table wait_table, *wait;
  81         struct select_table_entry *entry;
  82         int i;
  83         int max;
  84 
  85         max = -1;
  86         for (i = 0 ; i < n ; i++) {
  87                 if (!FD_ISSET(i, in) &&
  88                     !FD_ISSET(i, out) &&
  89                     !FD_ISSET(i, ex))
  90                         continue;
  91                 if (!current->filp[i])
  92                         return -EBADF;
  93                 if (!current->filp[i]->f_inode)
  94                         return -EBADF;
  95                 max = i;
  96         }
  97         n = max + 1;
  98         entry = (struct select_table_entry *) get_free_page(GFP_KERNEL);
  99         if (!entry)
 100                 return -ENOMEM;
 101         FD_ZERO(res_in);
 102         FD_ZERO(res_out);
 103         FD_ZERO(res_ex);
 104         count = 0;
 105         wait_table.nr = 0;
 106         wait_table.entry = entry;
 107         wait = &wait_table;
 108 repeat:
 109         current->state = TASK_INTERRUPTIBLE;
 110         for (i = 0 ; i < n ; i++) {
 111                 if (FD_ISSET(i,in) && check(SEL_IN,wait,current->filp[i])) {
 112                         FD_SET(i, res_in);
 113                         count++;
 114                         wait = NULL;
 115                 }
 116                 if (FD_ISSET(i,out) && check(SEL_OUT,wait,current->filp[i])) {
 117                         FD_SET(i, res_out);
 118                         count++;
 119                         wait = NULL;
 120                 }
 121                 if (FD_ISSET(i,ex) && check(SEL_EX,wait,current->filp[i])) {
 122                         FD_SET(i, res_ex);
 123                         count++;
 124                         wait = NULL;
 125                 }
 126         }
 127         wait = NULL;
 128         if (!count && current->timeout && !(current->signal & ~current->blocked)) {
 129                 schedule();
 130                 goto repeat;
 131         }
 132         free_wait(&wait_table);
 133         free_page((unsigned long) entry);
 134         current->state = TASK_RUNNING;
 135         return count;
 136 }
 137 
 138 static void __get_fd_set(int nr, unsigned long * fs_pointer, unsigned long * fdset)
     /* [previous][next][first][last][top][bottom][index][help] */
 139 {
 140         FD_ZERO(fdset);
 141         if (!fs_pointer)
 142                 return;
 143         while (nr > 0) {
 144                 *fdset = get_fs_long(fs_pointer);
 145                 fdset++;
 146                 fs_pointer++;
 147                 nr -= 32;
 148         }
 149 }
 150 
 151 static void __set_fd_set(int nr, unsigned long * fs_pointer, unsigned long * fdset)
     /* [previous][next][first][last][top][bottom][index][help] */
 152 {
 153         if (!fs_pointer)
 154                 return;
 155         verify_area(fs_pointer, sizeof(fd_set));
 156         while (nr > 0) {
 157                 put_fs_long(*fdset, fs_pointer);
 158                 fdset++;
 159                 fs_pointer++;
 160                 nr -= 32;
 161         }
 162 }
 163 
 164 #define get_fd_set(nr,fsp,fdp) \
 165 __get_fd_set(nr, (unsigned long *) (fsp), (unsigned long *) (fdp))
 166 
 167 #define set_fd_set(nr,fsp,fdp) \
 168 __set_fd_set(nr, (unsigned long *) (fsp), (unsigned long *) (fdp))
 169 
 170 /*
 171  * We can actually return ERESTARTSYS insetad of EINTR, but I'd
 172  * like to be certain this leads to no problems. So I return
 173  * EINTR just for safety.
 174  */
 175 int sys_select( unsigned long *buffer )
     /* [previous][next][first][last][top][bottom][index][help] */
 176 {
 177 /* Perform the select(nd, in, out, ex, tv) system call. */
 178         int i;
 179         fd_set res_in, in, *inp;
 180         fd_set res_out, out, *outp;
 181         fd_set res_ex, ex, *exp;
 182         int n;
 183         struct timeval *tvp;
 184         unsigned long timeout;
 185 
 186         n = get_fs_long(buffer++);
 187         if (n < 0)
 188                 return -EINVAL;
 189         if (n > NR_OPEN)
 190                 n = NR_OPEN;
 191         inp = (fd_set *) get_fs_long(buffer++);
 192         outp = (fd_set *) get_fs_long(buffer++);
 193         exp = (fd_set *) get_fs_long(buffer++);
 194         tvp = (struct timeval *) get_fs_long(buffer);
 195         get_fd_set(n, inp, &in);
 196         get_fd_set(n, outp, &out);
 197         get_fd_set(n, exp, &ex);
 198         timeout = 0xffffffff;
 199         if (tvp) {
 200                 timeout = jiffies;
 201                 timeout += get_fs_long((unsigned long *)&tvp->tv_usec)/(1000000/HZ);
 202                 timeout += get_fs_long((unsigned long *)&tvp->tv_sec) * HZ;
 203                 if (timeout <= jiffies)
 204                         timeout = 0;
 205         }
 206         current->timeout = timeout;
 207         i = do_select(n, &in, &out, &ex, &res_in, &res_out, &res_ex);
 208         if (current->timeout > jiffies)
 209                 timeout = current->timeout - jiffies;
 210         else
 211                 timeout = 0;
 212         current->timeout = 0;
 213         if (tvp) {
 214                 verify_area(tvp, sizeof(*tvp));
 215                 put_fs_long(timeout/HZ, (unsigned long *) &tvp->tv_sec);
 216                 timeout %= HZ;
 217                 timeout *= (1000000/HZ);
 218                 put_fs_long(timeout, (unsigned long *) &tvp->tv_usec);
 219         }
 220         if (i < 0)
 221                 return i;
 222         if (!i && (current->signal & ~current->blocked))
 223                 return -EINTR;
 224         set_fd_set(n, inp, &res_in);
 225         set_fd_set(n, outp, &res_out);
 226         set_fd_set(n, exp, &res_ex);
 227         return i;
 228 }

/* [previous][next][first][last][top][bottom][index][help] */