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_INTS ; j++) {
  96                 i = j * __NFDBITS;
  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 int * fs_pointer, fd_set * fdset)
     /* [previous][next][first][last][top][bottom][index][help] */
 158 {
 159         int error, i;
 160         unsigned int * tmp;
 161 
 162         FD_ZERO(fdset);
 163         if (!fs_pointer)
 164                 return 0;
 165         error = verify_area(VERIFY_WRITE,fs_pointer,sizeof(fd_set));
 166         if (error)
 167                 return error;
 168         tmp = fdset->fds_bits;
 169         for (i = __FDSET_INTS; i > 0; i--) {
 170                 if (nr <= 0)
 171                         break;
 172                 *tmp = get_user(fs_pointer);
 173                 tmp++;
 174                 fs_pointer++;
 175                 nr -= 8 * sizeof(unsigned int);
 176         }
 177         return 0;
 178 }
 179 
 180 static void __set_fd_set(int nr, unsigned int * fs_pointer, unsigned int * fdset)
     /* [previous][next][first][last][top][bottom][index][help] */
 181 {
 182         int i;
 183 
 184         if (!fs_pointer)
 185                 return;
 186         for (i = __FDSET_INTS; i > 0; i--) {
 187                 if (nr <= 0)
 188                         break;
 189                 put_user(*fdset, fs_pointer);
 190                 fdset++;
 191                 fs_pointer++;
 192                 nr -= 8 * sizeof(unsigned int);
 193         }
 194 }
 195 
 196 #define get_fd_set(nr,fsp,fdp) \
 197 __get_fd_set(nr, (unsigned int *) (fsp), fdp)
 198 
 199 #define set_fd_set(nr,fsp,fdp) \
 200 __set_fd_set(nr, (unsigned int *) (fsp), (unsigned int *) (fdp))
 201 
 202 /*
 203  * We can actually return ERESTARTSYS instead of EINTR, but I'd
 204  * like to be certain this leads to no problems. So I return
 205  * EINTR just for safety.
 206  *
 207  * Update: ERESTARTSYS breaks at least the xview clock binary, so
 208  * I'm trying ERESTARTNOHAND which restart only when you want to.
 209  */
 210 asmlinkage int sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp)
     /* [previous][next][first][last][top][bottom][index][help] */
 211 {
 212         int i;
 213         fd_set res_in, in;
 214         fd_set res_out, out;
 215         fd_set res_ex, ex;
 216         unsigned long timeout;
 217 
 218         if (n < 0)
 219                 return -EINVAL;
 220         if (n > NR_OPEN)
 221                 n = NR_OPEN;
 222         if ((i = get_fd_set(n, inp, &in)) ||
 223             (i = get_fd_set(n, outp, &out)) ||
 224             (i = get_fd_set(n, exp, &ex))) return i;
 225         timeout = ~0UL;
 226         if (tvp) {
 227                 i = verify_area(VERIFY_WRITE, tvp, sizeof(*tvp));
 228                 if (i)
 229                         return i;
 230                 timeout = ROUND_UP(get_user(&tvp->tv_usec),(1000000/HZ));
 231                 timeout += get_user(&tvp->tv_sec) * (unsigned long) HZ;
 232                 if (timeout)
 233                         timeout += jiffies + 1;
 234         }
 235         current->timeout = timeout;
 236         i = do_select(n, &in, &out, &ex, &res_in, &res_out, &res_ex);
 237         timeout = current->timeout - jiffies - 1;
 238         current->timeout = 0;
 239         if ((long) timeout < 0)
 240                 timeout = 0;
 241         if (tvp && !(current->personality & STICKY_TIMEOUTS)) {
 242                 put_user(timeout/HZ, &tvp->tv_sec);
 243                 timeout %= HZ;
 244                 timeout *= (1000000/HZ);
 245                 put_user(timeout, &tvp->tv_usec);
 246         }
 247         if (i < 0)
 248                 return i;
 249         if (!i && (current->signal & ~current->blocked))
 250                 return -ERESTARTNOHAND;
 251         set_fd_set(n, inp, &res_in);
 252         set_fd_set(n, outp, &res_out);
 253         set_fd_set(n, exp, &res_ex);
 254         return i;
 255 }

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