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