root/fs/select.c

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

DEFINITIONS

This source file includes following definitions.
  1. add_wait
  2. free_one_table
  3. free_wait
  4. get_tty
  5. check_in
  6. check_out
  7. check_ex
  8. do_select
  9. 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/fs.h>
   9 #include <linux/kernel.h>
  10 #include <linux/tty.h>
  11 #include <linux/sched.h>
  12 
  13 #include <asm/segment.h>
  14 #include <asm/system.h>
  15 
  16 #include <sys/stat.h>
  17 #include <sys/types.h>
  18 #include <string.h>
  19 #include <const.h>
  20 #include <errno.h>
  21 #include <sys/time.h>
  22 #include <signal.h>
  23 
  24 /*
  25  * Ok, Peter made a complicated, but straightforward multiple_wait() function.
  26  * I have rewritten this, taking some shortcuts: This code may not be easy to
  27  * follow, but it should be free of race-conditions, and it's practical. If you
  28  * understand what I'm doing here, then you understand how the linux sleep/wakeup
  29  * mechanism works.
  30  *
  31  * Two very simple procedures, add_wait() and free_wait() make all the work. We
  32  * have to have interrupts disabled throughout the select, but that's not really
  33  * such a loss: sleeping automatically frees interrupts when we aren't in this
  34  * task.
  35  */
  36 
  37 typedef struct {
  38         struct task_struct * old_task;
  39         struct task_struct ** wait_address;
  40 } wait_entry;
  41 
  42 typedef struct select_table_struct {
  43         int nr, woken;
  44         struct task_struct * current;
  45         struct select_table_struct * next_table;
  46         wait_entry entry[NR_OPEN*3];
  47 } select_table;
  48 
  49 static select_table * sel_tables = NULL;
  50 
  51 static void add_wait(struct task_struct ** wait_address, select_table * p)
     /* [previous][next][first][last][top][bottom][index][help] */
  52 {
  53         int i;
  54 
  55         if (!wait_address)
  56                 return;
  57         for (i = 0 ; i < p->nr ; i++)
  58                 if (p->entry[i].wait_address == wait_address)
  59                         return;
  60         current->next_wait = NULL;
  61         p->entry[p->nr].wait_address = wait_address;
  62         p->entry[p->nr].old_task = *wait_address;
  63         *wait_address = current;
  64         p->nr++;
  65 }
  66 
  67 /*
  68  * free_wait removes the current task from any wait-queues and then
  69  * wakes up the queues.
  70  */
  71 static void free_one_table(select_table * p)
     /* [previous][next][first][last][top][bottom][index][help] */
  72 {
  73         int i;
  74         struct task_struct ** tpp;
  75 
  76         for(tpp = &LAST_TASK ; tpp > &FIRST_TASK ; --tpp)
  77                 if (*tpp && ((*tpp)->next_wait == p->current))
  78                         (*tpp)->next_wait = NULL;
  79         if (!p->nr)
  80                 return;
  81         for (i = 0; i < p->nr ; i++) {
  82                 wake_up(p->entry[i].wait_address);
  83                 wake_up(&p->entry[i].old_task);
  84         }
  85         p->nr = 0;
  86 }
  87 
  88 static void free_wait(select_table * p)
     /* [previous][next][first][last][top][bottom][index][help] */
  89 {
  90         select_table * tmp;
  91 
  92         if (p->woken)
  93                 return;
  94         p = sel_tables;
  95         sel_tables = NULL;
  96         while (p) {
  97                 wake_up(&p->current);
  98                 p->woken = 1;
  99                 tmp = p->next_table;
 100                 p->next_table = NULL;
 101                 free_one_table(p);
 102                 p = tmp;
 103         }
 104 }
 105 
 106 static struct tty_struct * get_tty(struct inode * inode)
     /* [previous][next][first][last][top][bottom][index][help] */
 107 {
 108         int major, minor;
 109 
 110         if (!S_ISCHR(inode->i_mode))
 111                 return NULL;
 112         if ((major = MAJOR(inode->i_rdev)) != 5 && major != 4)
 113                 return NULL;
 114         if (major == 5)
 115                 minor = current->tty;
 116         else
 117                 minor = MINOR(inode->i_rdev);
 118         if (minor < 0)
 119                 return NULL;
 120         return TTY_TABLE(minor);
 121 }
 122 
 123 /*
 124  * The check_XX functions check out a file. We know it's either
 125  * a pipe, a character device or a fifo (fifo's not implemented)
 126  */
 127 static int check_in(select_table * wait, struct inode * inode)
     /* [previous][next][first][last][top][bottom][index][help] */
 128 {
 129         struct tty_struct * tty;
 130 
 131         if (tty = get_tty(inode))
 132                 if (!EMPTY(tty->secondary))
 133                         return 1;
 134                 else
 135                         add_wait(&tty->secondary->proc_list, wait);
 136         else if (inode->i_pipe)
 137                 if (!PIPE_EMPTY(*inode) || inode->i_count < 2)
 138                         return 1;
 139                 else
 140                         add_wait(&inode->i_wait, wait);
 141         return 0;
 142 }
 143 
 144 static int check_out(select_table * wait, struct inode * inode)
     /* [previous][next][first][last][top][bottom][index][help] */
 145 {
 146         struct tty_struct * tty;
 147 
 148         if (tty = get_tty(inode))
 149                 if (!FULL(tty->write_q))
 150                         return 1;
 151                 else
 152                         add_wait(&tty->write_q->proc_list, wait);
 153         else if (inode->i_pipe)
 154                 if (!PIPE_FULL(*inode))
 155                         return 1;
 156                 else
 157                         add_wait(&inode->i_wait, wait);
 158         return 0;
 159 }
 160 
 161 static int check_ex(select_table * wait, struct inode * inode)
     /* [previous][next][first][last][top][bottom][index][help] */
 162 {
 163         struct tty_struct * tty;
 164 
 165         if (tty = get_tty(inode))
 166                 if (!FULL(tty->write_q))
 167                         return 0;
 168                 else
 169                         return 0;
 170         else if (inode->i_pipe)
 171                 if (inode->i_count < 2)
 172                         return 1;
 173                 else
 174                         add_wait(&inode->i_wait,wait);
 175         return 0;
 176 }
 177 
 178 int do_select(fd_set in, fd_set out, fd_set ex,
     /* [previous][next][first][last][top][bottom][index][help] */
 179         fd_set *inp, fd_set *outp, fd_set *exp)
 180 {
 181         int count;
 182         select_table wait_table;
 183         int i;
 184         fd_set mask;
 185 
 186         mask = in | out | ex;
 187         for (i = 0 ; i < NR_OPEN ; i++,mask >>= 1) {
 188                 if (!(mask & 1))
 189                         continue;
 190                 if (!current->filp[i])
 191                         return -EBADF;
 192                 if (!current->filp[i]->f_inode)
 193                         return -EBADF;
 194                 if (current->filp[i]->f_inode->i_pipe)
 195                         continue;
 196                 if (S_ISCHR(current->filp[i]->f_inode->i_mode))
 197                         continue;
 198                 if (S_ISFIFO(current->filp[i]->f_inode->i_mode))
 199                         continue;
 200                 return -EBADF;
 201         }
 202 repeat:
 203         wait_table.nr = 0;
 204         wait_table.woken = 0;
 205         wait_table.current = current;
 206         wait_table.next_table = sel_tables;
 207         sel_tables = &wait_table;
 208         *inp = *outp = *exp = 0;
 209         count = 0;
 210         mask = 1;
 211         for (i = 0 ; i < NR_OPEN ; i++, mask += mask) {
 212                 if (mask & in)
 213                         if (check_in(&wait_table,current->filp[i]->f_inode)) {
 214                                 *inp |= mask;
 215                                 count++;
 216                         }
 217                 if (mask & out)
 218                         if (check_out(&wait_table,current->filp[i]->f_inode)) {
 219                                 *outp |= mask;
 220                                 count++;
 221                         }
 222                 if (mask & ex)
 223                         if (check_ex(&wait_table,current->filp[i]->f_inode)) {
 224                                 *exp |= mask;
 225                                 count++;
 226                         }
 227         }
 228         if (!(current->signal & ~current->blocked) &&
 229             current->timeout && !count) {
 230                 current->state = TASK_INTERRUPTIBLE;
 231                 sti();
 232                 schedule();
 233                 cli();
 234                 free_wait(&wait_table);
 235                 goto repeat;
 236         }
 237         free_wait(&wait_table);
 238         return count;
 239 }
 240 
 241 /*
 242  * Note that we cannot return -ERESTARTSYS, as we change our input
 243  * parameters. Sad, but there you are. We could do some tweaking in
 244  * the library function ...
 245  */
 246 int sys_select( unsigned long *buffer )
     /* [previous][next][first][last][top][bottom][index][help] */
 247 {
 248 /* Perform the select(nd, in, out, ex, tv) system call. */
 249         int i;
 250         fd_set res_in, in = 0, *inp;
 251         fd_set res_out, out = 0, *outp;
 252         fd_set res_ex, ex = 0, *exp;
 253         fd_set mask;
 254         struct timeval *tvp;
 255         unsigned long timeout;
 256 
 257         mask = get_fs_long(buffer++);
 258         if (mask >= 32)
 259                 mask = ~0;
 260         else
 261                 mask = ~((~0) << mask);
 262         inp = (fd_set *) get_fs_long(buffer++);
 263         outp = (fd_set *) get_fs_long(buffer++);
 264         exp = (fd_set *) get_fs_long(buffer++);
 265         tvp = (struct timeval *) get_fs_long(buffer);
 266 
 267         if (inp)
 268                 in = mask & get_fs_long(inp);
 269         if (outp)
 270                 out = mask & get_fs_long(outp);
 271         if (exp)
 272                 ex = mask & get_fs_long(exp);
 273         timeout = 0xffffffff;
 274         if (tvp) {
 275                 timeout = get_fs_long((unsigned long *)&tvp->tv_usec)/(1000000/HZ);
 276                 timeout += get_fs_long((unsigned long *)&tvp->tv_sec) * HZ;
 277                 timeout += jiffies;
 278         }
 279         current->timeout = timeout;
 280         cli();
 281         i = do_select(in, out, ex, &res_in, &res_out, &res_ex);
 282         if (current->timeout > jiffies)
 283                 timeout = current->timeout - jiffies;
 284         else
 285                 timeout = 0;
 286         sti();
 287         current->timeout = 0;
 288         if (i < 0)
 289                 return i;
 290         if (inp) {
 291                 verify_area(inp, 4);
 292                 put_fs_long(res_in,inp);
 293         }
 294         if (outp) {
 295                 verify_area(outp,4);
 296                 put_fs_long(res_out,outp);
 297         }
 298         if (exp) {
 299                 verify_area(exp,4);
 300                 put_fs_long(res_ex,exp);
 301         }
 302         if (tvp) {
 303                 verify_area(tvp, sizeof(*tvp));
 304                 put_fs_long(timeout/HZ, (unsigned long *) &tvp->tv_sec);
 305                 timeout %= HZ;
 306                 timeout *= (1000000/HZ);
 307                 put_fs_long(timeout, (unsigned long *) &tvp->tv_usec);
 308         }
 309         if (i)
 310                 return i;
 311         if (current->signal & ~current->blocked)
 312                 return -EINTR;
 313         return 0;
 314 }

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