|
|
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:
1.1.1.2 ! root 82: static struct tty_struct * get_tty(struct inode * inode)
1.1 root 83: {
84: int major, minor;
85:
86: if (!S_ISCHR(inode->i_mode))
87: return NULL;
1.1.1.2 ! root 88: if ((major = MAJOR(inode->i_rdev)) != 5 && major != 4)
1.1 root 89: return NULL;
90: if (major == 5)
91: minor = current->tty;
92: else
1.1.1.2 ! root 93: minor = MINOR(inode->i_rdev);
1.1 root 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: */
1.1.1.2 ! root 103: static int check_in(select_table * wait, struct inode * inode)
1.1 root 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)
1.1.1.2 ! root 113: if (!PIPE_EMPTY(*inode) || inode->i_count < 2)
1.1 root 114: return 1;
115: else
116: add_wait(&inode->i_wait, wait);
117: return 0;
118: }
119:
1.1.1.2 ! root 120: static int check_out(select_table * wait, struct inode * inode)
1.1 root 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:
1.1.1.2 ! root 137: static int check_ex(select_table * wait, struct inode * inode)
1.1 root 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) &&
1.1.1.2 ! root 201: current->timeout && !count) {
1.1 root 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:
1.1.1.2 ! root 227: mask = get_fs_long(buffer++);
! 228: if (mask >= 32)
! 229: mask = ~0;
! 230: else
! 231: mask = ~((~0) << mask);
1.1 root 232: inp = (fd_set *) get_fs_long(buffer++);
233: outp = (fd_set *) get_fs_long(buffer++);
234: exp = (fd_set *) get_fs_long(buffer++);
235: tvp = (struct timeval *) get_fs_long(buffer);
236:
237: if (inp)
238: in = mask & get_fs_long(inp);
239: if (outp)
240: out = mask & get_fs_long(outp);
241: if (exp)
242: ex = mask & get_fs_long(exp);
243: timeout = 0xffffffff;
244: if (tvp) {
245: timeout = get_fs_long((unsigned long *)&tvp->tv_usec)/(1000000/HZ);
246: timeout += get_fs_long((unsigned long *)&tvp->tv_sec) * HZ;
247: timeout += jiffies;
248: }
249: current->timeout = timeout;
250: cli();
251: i = do_select(in, out, ex, &res_in, &res_out, &res_ex);
252: if (current->timeout > jiffies)
253: timeout = current->timeout - jiffies;
254: else
255: timeout = 0;
256: sti();
257: current->timeout = 0;
258: if (i < 0)
259: return i;
260: if (inp) {
261: verify_area(inp, 4);
262: put_fs_long(res_in,inp);
263: }
264: if (outp) {
265: verify_area(outp,4);
266: put_fs_long(res_out,outp);
267: }
268: if (exp) {
269: verify_area(exp,4);
270: put_fs_long(res_ex,exp);
271: }
272: if (tvp) {
273: verify_area(tvp, sizeof(*tvp));
274: put_fs_long(timeout/HZ, (unsigned long *) &tvp->tv_sec);
275: timeout %= HZ;
276: timeout *= (1000000/HZ);
277: put_fs_long(timeout, (unsigned long *) &tvp->tv_usec);
278: }
279: if (!i && (current->signal & ~current->blocked))
280: return -EINTR;
281: return i;
282: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.