OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [fs/] [select.c] - Blame information for rev 1775

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1627 jcastillo
/*
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
#include <linux/file.h>
25
 
26
#include <asm/segment.h>
27
#include <asm/system.h>
28
 
29
#define ROUND_UP(x,y) (((x)+(y)-1)/(y))
30
 
31
/*
32
 * Ok, Peter made a complicated, but straightforward multiple_wait() function.
33
 * I have rewritten this, taking some shortcuts: This code may not be easy to
34
 * follow, but it should be free of race-conditions, and it's practical. If you
35
 * understand what I'm doing here, then you understand how the linux
36
 * sleep/wakeup mechanism works.
37
 *
38
 * Two very simple procedures, select_wait() and free_wait() make all the work.
39
 * select_wait() is a inline-function defined in <linux/sched.h>, as all select
40
 * functions have to call it to add an entry to the select table.
41
 */
42
 
43
/*
44
 * I rewrote this again to make the select_table size variable, take some
45
 * more shortcuts, improve responsiveness, and remove another race that
46
 * Linus noticed.  -- jrs
47
 */
48
 
49
void select_free_wait(select_table * p)
50
{
51
        struct select_table_entry * entry = p->entry + p->nr;
52
 
53
        while (p->nr > 0) {
54
                p->nr--;
55
                entry--;
56
                remove_wait_queue(entry->wait_address,&entry->wait);
57
        }
58
}
59
 
60
/*
61
 *      File handle locking
62
 */
63
 
64
static void lock_fd_bits(int n, int x)
65
{
66
        int i;
67
        for(i=0;i<__NFDBITS;i++)
68
        {
69
                if(x&(1<<i))
70
                        fget(n+i);
71
        }
72
}
73
 
74
static void unlock_fd_bits(int n, int x)
75
{
76
        int i;
77
        for(i=0;i<__NFDBITS;i++)
78
        {
79
                if(x&(1<<i))
80
                {
81
                        /* ick */
82
                        struct file *f=current->files->fd[n+i];
83
                        fput(f, f->f_inode);
84
                }
85
        }
86
}
87
 
88
/*
89
 * The check function checks the ready status of a file using the vfs layer.
90
 *
91
 * If the file was not ready we were added to its wait queue.  But in
92
 * case it became ready just after the check and just before it called
93
 * select_wait, we call it again, knowing we are already on its
94
 * wait queue this time.  The second call is not necessary if the
95
 * select_table is NULL indicating an earlier file check was ready
96
 * and we aren't going to sleep on the select_table.  -- jrs
97
 */
98
 
99
int select_check(int flag, select_table * wait, struct file * file)
100
{
101
        struct inode * inode;
102
        struct file_operations *fops;
103
        int (*select) (struct inode *, struct file *, int, select_table *);
104
 
105
        inode = file->f_inode;
106
        if ((fops = file->f_op) && (select = fops->select))
107
                return select(inode, file, flag, wait)
108
                    || (wait && select(inode, file, flag, NULL));
109
        if (flag != SEL_EX)
110
                return 1;
111
        return 0;
112
}
113
 
114
static int do_select(int n, fd_set *in, fd_set *out, fd_set *ex,
115
        fd_set *res_in, fd_set *res_out, fd_set *res_ex, fd_set *locked)
116
{
117
        int count;
118
        select_table wait_table, *wait;
119
        struct select_table_entry *entry;
120
        unsigned long set;
121
        int i,j;
122
        int max = -1;
123
        int threaded = 0;
124
        j = 0;
125
        for (;;) {
126
                i = j * __NFDBITS;
127
                if (i >= n)
128
                        break;
129
                set = in->fds_bits[j] | out->fds_bits[j] | ex->fds_bits[j];
130
                j++;
131
                for ( ; set ; i++,set >>= 1) {
132
                        if (i >= n)
133
                                goto end_check;
134
                        if (!(set & 1))
135
                                continue;
136
                        if (!current->files->fd[i])
137
                                return -EBADF;
138
                        if (!current->files->fd[i]->f_inode)
139
                                return -EBADF;
140
                        max = i;
141
                }
142
        }
143
end_check:
144
        n = max + 1;
145
 
146
        /* Now we _must_ lock the handles before we get the page otherwise
147
           they may get closed on us during the kmalloc causing explosions.. */
148
 
149
        if(current->files->count>1)
150
        {
151
 
152
                /*
153
                 *      Only for the threaded cases must we do work.
154
                 */
155
                j = 0;
156
                for (;;) {
157
                        i = j * __NFDBITS;
158
                        if (i >= n)
159
                                break;
160
                        lock_fd_bits(i,in->fds_bits[j]);
161
                        lock_fd_bits(i,out->fds_bits[j]);
162
                        lock_fd_bits(i,ex->fds_bits[j]);
163
                        j++;
164
                }
165
                threaded=1;
166
        }
167
 
168
        if(!(entry = (struct select_table_entry*) __get_free_page(GFP_KERNEL)))
169
        {
170
                count = -ENOMEM;
171
                goto bale;
172
        }
173
        count = 0;
174
        wait_table.nr = 0;
175
        wait_table.entry = entry;
176
        wait = &wait_table;
177
repeat:
178
        current->state = TASK_INTERRUPTIBLE;
179
        for (i = 0 ; i < n ; i++) {
180
                struct file * file = current->files->fd[i];
181
                if (!file)
182
                        continue;
183
                if (FD_ISSET(i,in) && select_check(SEL_IN,wait,file)) {
184
                        FD_SET(i, res_in);
185
                        count++;
186
                        wait = NULL;
187
                }
188
                if (FD_ISSET(i,out) && select_check(SEL_OUT,wait,file)) {
189
                        FD_SET(i, res_out);
190
                        count++;
191
                        wait = NULL;
192
                }
193
                if (FD_ISSET(i,ex) && select_check(SEL_EX,wait,file)) {
194
                        FD_SET(i, res_ex);
195
                        count++;
196
                        wait = NULL;
197
                }
198
        }
199
        wait = NULL;
200
        if (!count && current->timeout && !(current->signal & ~current->blocked)) {
201
                schedule();
202
                goto repeat;
203
        }
204
        select_free_wait(&wait_table);
205
        free_page((unsigned long) entry);
206
        current->state = TASK_RUNNING;
207
bale:
208
 
209
        if(threaded)
210
        {
211
                /* Unlock handles now */
212
                j = 0;
213
                for (;;) {
214
                        i = j * __NFDBITS;
215
                        if (i >= n)
216
                                break;
217
                        unlock_fd_bits(i,in->fds_bits[j]);
218
                        unlock_fd_bits(i,out->fds_bits[j]);
219
                        unlock_fd_bits(i,ex->fds_bits[j]);
220
                        j++;
221
                }
222
        }
223
        return count;
224
}
225
 
226
/*
227
 * We do a VERIFY_WRITE here even though we are only reading this time:
228
 * we'll write to it eventually..
229
 *
230
 * Use "int" accesses to let user-mode fd_set's be int-aligned.
231
 */
232
static int __get_fd_set(unsigned long nr, int * fs_pointer, int * fdset)
233
{
234
        /* round up nr to nearest "int" */
235
        nr = (nr + 8*sizeof(int)-1) / (8*sizeof(int));
236
        if (fs_pointer) {
237
                int error = verify_area(VERIFY_WRITE,fs_pointer,nr*sizeof(int));
238
                if (!error) {
239
                        while (nr) {
240
                                *fdset = get_user(fs_pointer);
241
                                nr--;
242
                                fs_pointer++;
243
                                fdset++;
244
                        }
245
                }
246
                return error;
247
        }
248
        while (nr) {
249
                *fdset = 0;
250
                nr--;
251
                fdset++;
252
        }
253
        return 0;
254
}
255
 
256
static void __set_fd_set(long nr, int * fs_pointer, int * fdset)
257
{
258
        if (!fs_pointer)
259
                return;
260
        while (nr >= 0) {
261
                put_user(*fdset, fs_pointer);
262
                nr -= 8 * sizeof(int);
263
                fdset++;
264
                fs_pointer++;
265
        }
266
}
267
 
268
/* We can do long accesses here, kernel fdsets are always long-aligned */
269
static inline void __zero_fd_set(long nr, unsigned long * fdset)
270
{
271
        while (nr >= 0) {
272
                *fdset = 0;
273
                nr -= 8 * sizeof(unsigned long);
274
                fdset++;
275
        }
276
}
277
 
278
/*
279
 * Due to kernel stack usage, we use a _limited_ fd_set type here, and once
280
 * we really start supporting >256 file descriptors we'll probably have to
281
 * allocate the kernel fd_set copies dynamically.. (The kernel select routines
282
 * are careful to touch only the defined low bits of any fd_set pointer, this
283
 * is important for performance too).
284
 *
285
 * Note a few subtleties: we use "long" for the dummy, not int, and we do a
286
 * subtract by 1 on the nr of file descriptors. The former is better for
287
 * machines with long > int, and the latter allows us to test the bit count
288
 * against "zero or positive", which can mostly be just a sign bit test..
289
 */
290
typedef struct {
291
        unsigned long dummy[NR_OPEN/(8*(sizeof(unsigned long)))];
292
} limited_fd_set;
293
 
294
#define get_fd_set(nr,fsp,fdp) \
295
__get_fd_set(nr, (int *) (fsp), (int *) (fdp))
296
 
297
#define set_fd_set(nr,fsp,fdp) \
298
__set_fd_set((nr)-1, (int *) (fsp), (int *) (fdp))
299
 
300
#define zero_fd_set(nr,fdp) \
301
__zero_fd_set((nr)-1, (unsigned long *) (fdp))
302
 
303
/*
304
 * We can actually return ERESTARTSYS instead of EINTR, but I'd
305
 * like to be certain this leads to no problems. So I return
306
 * EINTR just for safety.
307
 *
308
 * Update: ERESTARTSYS breaks at least the xview clock binary, so
309
 * I'm trying ERESTARTNOHAND which restart only when you want to.
310
 */
311
asmlinkage int sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp)
312
{
313
        int error;
314
        limited_fd_set res_in, in;
315
        limited_fd_set res_out, out;
316
        limited_fd_set res_ex, ex;
317
        limited_fd_set locked;
318
        unsigned long timeout;
319
 
320
        error = -EINVAL;
321
        if (n < 0)
322
                goto out;
323
        if (n > NR_OPEN)
324
                n = NR_OPEN;
325
        if ((error = get_fd_set(n, inp, &in)) ||
326
            (error = get_fd_set(n, outp, &out)) ||
327
            (error = get_fd_set(n, exp, &ex))) goto out;
328
        timeout = ~0UL;
329
        if (tvp) {
330
                error = verify_area(VERIFY_WRITE, tvp, sizeof(*tvp));
331
                if (error)
332
                        goto out;
333
                timeout = ROUND_UP(get_user(&tvp->tv_usec),(1000000/HZ));
334
                timeout += get_user(&tvp->tv_sec) * (unsigned long) HZ;
335
                if (timeout)
336
                        timeout += jiffies + 1;
337
        }
338
        zero_fd_set(n, &res_in);
339
        zero_fd_set(n, &res_out);
340
        zero_fd_set(n, &res_ex);
341
        current->timeout = timeout;
342
        error = do_select(n,
343
                (fd_set *) &in,
344
                (fd_set *) &out,
345
                (fd_set *) &ex,
346
                (fd_set *) &res_in,
347
                (fd_set *) &res_out,
348
                (fd_set *) &res_ex,
349
                (fd_set *) &locked);
350
        timeout = current->timeout?current->timeout - jiffies - 1:0;
351
        current->timeout = 0;
352
        if (tvp && !(current->personality & STICKY_TIMEOUTS)) {
353
                put_user(timeout/HZ, &tvp->tv_sec);
354
                timeout %= HZ;
355
                timeout *= (1000000/HZ);
356
                put_user(timeout, &tvp->tv_usec);
357
        }
358
        if (error < 0)
359
                goto out;
360
        if (!error) {
361
                error = -ERESTARTNOHAND;
362
                if (current->signal & ~current->blocked)
363
                        goto out;
364
                error = 0;
365
        }
366
        set_fd_set(n, inp, &res_in);
367
        set_fd_set(n, outp, &res_out);
368
        set_fd_set(n, exp, &res_ex);
369
out:
370
        return error;
371
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.