home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / KERNEL-S / V1.2 / LINUX-1.2 / LINUX-1 / linux / fs / select.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-01-23  |  6.8 KB  |  260 lines

  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)
  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)
  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,
  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)
  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)
  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 )
  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.     if (current->timeout > jiffies)
  241.         timeout = current->timeout - jiffies;
  242.     else
  243.         timeout = 0;
  244.     current->timeout = 0;
  245.     if (tvp && !(current->personality & STICKY_TIMEOUTS)) {
  246.         put_fs_long(timeout/HZ, (unsigned long *) &tvp->tv_sec);
  247.         timeout %= HZ;
  248.         timeout *= (1000000/HZ);
  249.         put_fs_long(timeout, (unsigned long *) &tvp->tv_usec);
  250.     }
  251.     if (i < 0)
  252.         return i;
  253.     if (!i && (current->signal & ~current->blocked))
  254.         return -ERESTARTNOHAND;
  255.     set_fd_set(n, inp, &res_in);
  256.     set_fd_set(n, outp, &res_out);
  257.     set_fd_set(n, exp, &res_ex);
  258.     return i;
  259. }
  260.