|
|
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:
1.1.1.3 ! root 42: typedef struct select_table_struct {
! 43: int nr, woken;
! 44: struct task_struct * current;
! 45: struct select_table_struct * next_table;
1.1 root 46: wait_entry entry[NR_OPEN*3];
47: } select_table;
48:
1.1.1.3 ! root 49: static select_table * sel_tables = NULL;
! 50:
1.1 root 51: static void add_wait(struct task_struct ** wait_address, select_table * p)
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;
1.1.1.3 ! root 60: current->next_wait = NULL;
1.1 root 61: p->entry[p->nr].wait_address = wait_address;
1.1.1.3 ! root 62: p->entry[p->nr].old_task = *wait_address;
1.1 root 63: *wait_address = current;
64: p->nr++;
65: }
66:
1.1.1.3 ! root 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)
1.1 root 72: {
73: int i;
74: struct task_struct ** tpp;
75:
1.1.1.3 ! root 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;
1.1 root 81: for (i = 0; i < p->nr ; i++) {
1.1.1.3 ! root 82: wake_up(p->entry[i].wait_address);
! 83: wake_up(&p->entry[i].old_task);
1.1 root 84: }
85: p->nr = 0;
86: }
87:
1.1.1.3 ! root 88: static void free_wait(select_table * p)
! 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:
1.1.1.2 root 106: static struct tty_struct * get_tty(struct inode * inode)
1.1 root 107: {
108: int major, minor;
109:
110: if (!S_ISCHR(inode->i_mode))
111: return NULL;
1.1.1.2 root 112: if ((major = MAJOR(inode->i_rdev)) != 5 && major != 4)
1.1 root 113: return NULL;
114: if (major == 5)
115: minor = current->tty;
116: else
1.1.1.2 root 117: minor = MINOR(inode->i_rdev);
1.1 root 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: */
1.1.1.2 root 127: static int check_in(select_table * wait, struct inode * inode)
1.1 root 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)
1.1.1.2 root 137: if (!PIPE_EMPTY(*inode) || inode->i_count < 2)
1.1 root 138: return 1;
139: else
140: add_wait(&inode->i_wait, wait);
141: return 0;
142: }
143:
1.1.1.2 root 144: static int check_out(select_table * wait, struct inode * inode)
1.1 root 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:
1.1.1.2 root 161: static int check_ex(select_table * wait, struct inode * inode)
1.1 root 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,
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;
1.1.1.3 ! root 204: wait_table.woken = 0;
! 205: wait_table.current = current;
! 206: wait_table.next_table = sel_tables;
! 207: sel_tables = &wait_table;
1.1 root 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) &&
1.1.1.2 root 229: current->timeout && !count) {
1.1 root 230: current->state = TASK_INTERRUPTIBLE;
1.1.1.3 ! root 231: sti();
1.1 root 232: schedule();
1.1.1.3 ! root 233: cli();
1.1 root 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 )
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:
1.1.1.2 root 257: mask = get_fs_long(buffer++);
258: if (mask >= 32)
259: mask = ~0;
260: else
261: mask = ~((~0) << mask);
1.1 root 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: }
1.1.1.3 ! root 309: if (i)
! 310: return i;
! 311: if (current->signal & ~current->blocked)
1.1 root 312: return -EINTR;
1.1.1.3 ! root 313: return 0;
1.1 root 314: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.