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