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

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