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

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