|
|
1.1 ! root 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 { ! 43: int nr; ! 44: wait_entry entry[NR_OPEN*3]; ! 45: } select_table; ! 46: ! 47: static void add_wait(struct task_struct ** wait_address, select_table * p) ! 48: { ! 49: int i; ! 50: ! 51: if (!wait_address) ! 52: return; ! 53: for (i = 0 ; i < p->nr ; i++) ! 54: if (p->entry[i].wait_address == wait_address) ! 55: return; ! 56: p->entry[p->nr].wait_address = wait_address; ! 57: p->entry[p->nr].old_task = * wait_address; ! 58: *wait_address = current; ! 59: p->nr++; ! 60: } ! 61: ! 62: static void free_wait(select_table * p) ! 63: { ! 64: int i; ! 65: struct task_struct ** tpp; ! 66: ! 67: for (i = 0; i < p->nr ; i++) { ! 68: tpp = p->entry[i].wait_address; ! 69: while (*tpp && *tpp != current) { ! 70: (*tpp)->state = 0; ! 71: current->state = TASK_UNINTERRUPTIBLE; ! 72: schedule(); ! 73: } ! 74: if (!*tpp) ! 75: printk("free_wait: NULL"); ! 76: if (*tpp = p->entry[i].old_task) ! 77: (**tpp).state = 0; ! 78: } ! 79: p->nr = 0; ! 80: } ! 81: ! 82: static struct tty_struct * get_tty(struct m_inode * inode) ! 83: { ! 84: int major, minor; ! 85: ! 86: if (!S_ISCHR(inode->i_mode)) ! 87: return NULL; ! 88: if ((major = MAJOR(inode->i_zone[0])) != 5 && major != 4) ! 89: return NULL; ! 90: if (major == 5) ! 91: minor = current->tty; ! 92: else ! 93: minor = MINOR(inode->i_zone[0]); ! 94: if (minor < 0) ! 95: return NULL; ! 96: return TTY_TABLE(minor); ! 97: } ! 98: ! 99: /* ! 100: * The check_XX functions check out a file. We know it's either ! 101: * a pipe, a character device or a fifo (fifo's not implemented) ! 102: */ ! 103: static int check_in(select_table * wait, struct m_inode * inode) ! 104: { ! 105: struct tty_struct * tty; ! 106: ! 107: if (tty = get_tty(inode)) ! 108: if (!EMPTY(tty->secondary)) ! 109: return 1; ! 110: else ! 111: add_wait(&tty->secondary->proc_list, wait); ! 112: else if (inode->i_pipe) ! 113: if (!PIPE_EMPTY(*inode)) ! 114: return 1; ! 115: else ! 116: add_wait(&inode->i_wait, wait); ! 117: return 0; ! 118: } ! 119: ! 120: static int check_out(select_table * wait, struct m_inode * inode) ! 121: { ! 122: struct tty_struct * tty; ! 123: ! 124: if (tty = get_tty(inode)) ! 125: if (!FULL(tty->write_q)) ! 126: return 1; ! 127: else ! 128: add_wait(&tty->write_q->proc_list, wait); ! 129: else if (inode->i_pipe) ! 130: if (!PIPE_FULL(*inode)) ! 131: return 1; ! 132: else ! 133: add_wait(&inode->i_wait, wait); ! 134: return 0; ! 135: } ! 136: ! 137: static int check_ex(select_table * wait, struct m_inode * inode) ! 138: { ! 139: struct tty_struct * tty; ! 140: ! 141: if (tty = get_tty(inode)) ! 142: if (!FULL(tty->write_q)) ! 143: return 0; ! 144: else ! 145: return 0; ! 146: else if (inode->i_pipe) ! 147: if (inode->i_count < 2) ! 148: return 1; ! 149: else ! 150: add_wait(&inode->i_wait,wait); ! 151: return 0; ! 152: } ! 153: ! 154: int do_select(fd_set in, fd_set out, fd_set ex, ! 155: fd_set *inp, fd_set *outp, fd_set *exp) ! 156: { ! 157: int count; ! 158: select_table wait_table; ! 159: int i; ! 160: fd_set mask; ! 161: ! 162: mask = in | out | ex; ! 163: for (i = 0 ; i < NR_OPEN ; i++,mask >>= 1) { ! 164: if (!(mask & 1)) ! 165: continue; ! 166: if (!current->filp[i]) ! 167: return -EBADF; ! 168: if (!current->filp[i]->f_inode) ! 169: return -EBADF; ! 170: if (current->filp[i]->f_inode->i_pipe) ! 171: continue; ! 172: if (S_ISCHR(current->filp[i]->f_inode->i_mode)) ! 173: continue; ! 174: if (S_ISFIFO(current->filp[i]->f_inode->i_mode)) ! 175: continue; ! 176: return -EBADF; ! 177: } ! 178: repeat: ! 179: wait_table.nr = 0; ! 180: *inp = *outp = *exp = 0; ! 181: count = 0; ! 182: mask = 1; ! 183: for (i = 0 ; i < NR_OPEN ; i++, mask += mask) { ! 184: if (mask & in) ! 185: if (check_in(&wait_table,current->filp[i]->f_inode)) { ! 186: *inp |= mask; ! 187: count++; ! 188: } ! 189: if (mask & out) ! 190: if (check_out(&wait_table,current->filp[i]->f_inode)) { ! 191: *outp |= mask; ! 192: count++; ! 193: } ! 194: if (mask & ex) ! 195: if (check_ex(&wait_table,current->filp[i]->f_inode)) { ! 196: *exp |= mask; ! 197: count++; ! 198: } ! 199: } ! 200: if (!(current->signal & ~current->blocked) && ! 201: (wait_table.nr || current->timeout) && !count) { ! 202: current->state = TASK_INTERRUPTIBLE; ! 203: schedule(); ! 204: free_wait(&wait_table); ! 205: goto repeat; ! 206: } ! 207: free_wait(&wait_table); ! 208: return count; ! 209: } ! 210: ! 211: /* ! 212: * Note that we cannot return -ERESTARTSYS, as we change our input ! 213: * parameters. Sad, but there you are. We could do some tweaking in ! 214: * the library function ... ! 215: */ ! 216: int sys_select( unsigned long *buffer ) ! 217: { ! 218: /* Perform the select(nd, in, out, ex, tv) system call. */ ! 219: int i; ! 220: fd_set res_in, in = 0, *inp; ! 221: fd_set res_out, out = 0, *outp; ! 222: fd_set res_ex, ex = 0, *exp; ! 223: fd_set mask; ! 224: struct timeval *tvp; ! 225: unsigned long timeout; ! 226: ! 227: mask = ~((~0) << get_fs_long(buffer++)); ! 228: inp = (fd_set *) get_fs_long(buffer++); ! 229: outp = (fd_set *) get_fs_long(buffer++); ! 230: exp = (fd_set *) get_fs_long(buffer++); ! 231: tvp = (struct timeval *) get_fs_long(buffer); ! 232: ! 233: if (inp) ! 234: in = mask & get_fs_long(inp); ! 235: if (outp) ! 236: out = mask & get_fs_long(outp); ! 237: if (exp) ! 238: ex = mask & get_fs_long(exp); ! 239: timeout = 0xffffffff; ! 240: if (tvp) { ! 241: timeout = get_fs_long((unsigned long *)&tvp->tv_usec)/(1000000/HZ); ! 242: timeout += get_fs_long((unsigned long *)&tvp->tv_sec) * HZ; ! 243: timeout += jiffies; ! 244: } ! 245: current->timeout = timeout; ! 246: cli(); ! 247: i = do_select(in, out, ex, &res_in, &res_out, &res_ex); ! 248: if (current->timeout > jiffies) ! 249: timeout = current->timeout - jiffies; ! 250: else ! 251: timeout = 0; ! 252: sti(); ! 253: current->timeout = 0; ! 254: if (i < 0) ! 255: return i; ! 256: if (inp) { ! 257: verify_area(inp, 4); ! 258: put_fs_long(res_in,inp); ! 259: } ! 260: if (outp) { ! 261: verify_area(outp,4); ! 262: put_fs_long(res_out,outp); ! 263: } ! 264: if (exp) { ! 265: verify_area(exp,4); ! 266: put_fs_long(res_ex,exp); ! 267: } ! 268: if (tvp) { ! 269: verify_area(tvp, sizeof(*tvp)); ! 270: put_fs_long(timeout/HZ, (unsigned long *) &tvp->tv_sec); ! 271: timeout %= HZ; ! 272: timeout *= (1000000/HZ); ! 273: put_fs_long(timeout, (unsigned long *) &tvp->tv_usec); ! 274: } ! 275: if (!i && (current->signal & ~current->blocked)) ! 276: return -EINTR; ! 277: return i; ! 278: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.