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

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