Import changeset
[linux-flexiantxendom0-3.2.10.git] / fs / file.c
1 /*
2  *  linux/fs/open.c
3  *
4  *  Copyright (C) 1998-1999, Stephen Tweedie and Bill Hawes
5  *
6  *  Manage the dynamic fd arrays in the process files_struct.
7  */
8
9 #include <linux/fs.h>
10 #include <linux/mm.h>
11 #include <linux/sched.h>
12 #include <linux/malloc.h>
13 #include <linux/vmalloc.h>
14
15 #include <asm/bitops.h>
16
17
18 /*
19  * Allocate an fd array, using kmalloc or vmalloc.
20  * Note: the array isn't cleared at allocation time.
21  */
22 struct file ** alloc_fd_array(int num)
23 {
24         struct file **new_fds;
25         int size = num * sizeof(struct file *);
26
27         if (size <= PAGE_SIZE)
28                 new_fds = (struct file **) kmalloc(size, GFP_KERNEL);
29         else 
30                 new_fds = (struct file **) vmalloc(size);
31         return new_fds;
32 }
33
34 void free_fd_array(struct file **array, int num)
35 {
36         int size = num * sizeof(struct file *);
37
38         if (!array) {
39                 printk (KERN_ERR __FUNCTION__ "array = 0 (num = %d)\n", num);
40                 return;
41         }
42
43         if (num <= NR_OPEN_DEFAULT) /* Don't free the embedded fd array! */
44                 return;
45         else if (size <= PAGE_SIZE)
46                 kfree(array);
47         else
48                 vfree(array);
49 }
50
51 /*
52  * Expand the fd array in the files_struct.  Called with the files
53  * spinlock held for write.
54  */
55
56 int expand_fd_array(struct files_struct *files, int nr)
57 {
58         struct file **new_fds;
59         int error, nfds;
60
61         
62         error = -EMFILE;
63         if (files->max_fds >= NR_OPEN || nr >= NR_OPEN)
64                 goto out;
65
66         nfds = files->max_fds;
67         write_unlock(&files->file_lock);
68
69         /* 
70          * Expand to the max in easy steps, and keep expanding it until
71          * we have enough for the requested fd array size. 
72          */
73
74         do {
75 #if NR_OPEN_DEFAULT < 256
76                 if (nfds < 256)
77                         nfds = 256;
78                 else 
79 #endif
80                 if (nfds < (PAGE_SIZE / sizeof(struct file *)))
81                         nfds = PAGE_SIZE / sizeof(struct file *);
82                 else {
83                         nfds = nfds * 2;
84                         if (nfds > NR_OPEN)
85                                 nfds = NR_OPEN;
86                 }
87         } while (nfds <= nr);
88
89         error = -ENOMEM;
90         new_fds = alloc_fd_array(nfds);
91         write_lock(&files->file_lock);
92         if (!new_fds)
93                 goto out;
94
95         /* Copy the existing array and install the new pointer */
96
97         if (nfds > files->max_fds) {
98                 struct file **old_fds;
99                 int i;
100                 
101                 old_fds = xchg(&files->fd, new_fds);
102                 i = xchg(&files->max_fds, nfds);
103
104                 /* Don't copy/clear the array if we are creating a new
105                    fd array for fork() */
106                 if (i) {
107                         memcpy(new_fds, old_fds, i * sizeof(struct file *));
108                         /* clear the remainder of the array */
109                         memset(&new_fds[i], 0,
110                                (nfds-i) * sizeof(struct file *)); 
111
112                         write_unlock(&files->file_lock);
113                         free_fd_array(old_fds, i);
114                         write_lock(&files->file_lock);
115                 }
116         } else {
117                 /* Somebody expanded the array while we slept ... */
118                 write_unlock(&files->file_lock);
119                 free_fd_array(new_fds, nfds);
120                 write_lock(&files->file_lock);
121         }
122         error = 0;
123 out:
124         return error;
125 }
126
127 /*
128  * Allocate an fdset array, using kmalloc or vmalloc.
129  * Note: the array isn't cleared at allocation time.
130  */
131 fd_set * alloc_fdset(int num)
132 {
133         fd_set *new_fdset;
134         int size = num / 8;
135
136         if (size <= PAGE_SIZE)
137                 new_fdset = (fd_set *) kmalloc(size, GFP_KERNEL);
138         else
139                 new_fdset = (fd_set *) vmalloc(size);
140         return new_fdset;
141 }
142
143 void free_fdset(fd_set *array, int num)
144 {
145         int size = num / 8;
146
147         if (!array) {
148                 printk (KERN_ERR __FUNCTION__ "array = 0 (num = %d)\n", num);
149                 return;
150         }
151         
152         if (num <= __FD_SETSIZE) /* Don't free an embedded fdset */
153                 return;
154         else if (size <= PAGE_SIZE)
155                 kfree(array);
156         else
157                 vfree(array);
158 }
159
160 /*
161  * Expand the fdset in the files_struct.  Called with the files spinlock
162  * held for write.
163  */
164 int expand_fdset(struct files_struct *files, int nr)
165 {
166         fd_set *new_openset = 0, *new_execset = 0;
167         int error, nfds = 0;
168
169         error = -EMFILE;
170         if (files->max_fdset >= NR_OPEN || nr >= NR_OPEN)
171                 goto out;
172
173         nfds = files->max_fdset;
174         write_unlock(&files->file_lock);
175
176         /* Expand to the max in easy steps */
177         do {
178                 if (nfds < (PAGE_SIZE * 8))
179                         nfds = PAGE_SIZE * 8;
180                 else {
181                         nfds = nfds * 2;
182                         if (nfds > NR_OPEN)
183                                 nfds = NR_OPEN;
184                 }
185         } while (nfds <= nr);
186
187         error = -ENOMEM;
188         new_openset = alloc_fdset(nfds);
189         new_execset = alloc_fdset(nfds);
190         write_lock(&files->file_lock);
191         if (!new_openset || !new_execset)
192                 goto out;
193
194         error = 0;
195         
196         /* Copy the existing tables and install the new pointers */
197         if (nfds > files->max_fdset) {
198                 int i = files->max_fdset / (sizeof(unsigned long) * 8);
199                 int count = (nfds - files->max_fdset) / 8;
200                 
201                 /* 
202                  * Don't copy the entire array if the current fdset is
203                  * not yet initialised.  
204                  */
205                 if (i) {
206                         memcpy (new_openset, files->open_fds, files->max_fdset/8);
207                         memcpy (new_execset, files->close_on_exec, files->max_fdset/8);
208                         memset (&new_openset->fds_bits[i], 0, count);
209                         memset (&new_execset->fds_bits[i], 0, count);
210                 }
211                 
212                 nfds = xchg(&files->max_fdset, nfds);
213                 new_openset = xchg(&files->open_fds, new_openset);
214                 new_execset = xchg(&files->close_on_exec, new_execset);
215                 write_unlock(&files->file_lock);
216                 free_fdset (new_openset, nfds);
217                 free_fdset (new_execset, nfds);
218                 write_lock(&files->file_lock);
219                 return 0;
220         } 
221         /* Somebody expanded the array while we slept ... */
222
223 out:
224         write_unlock(&files->file_lock);
225         if (new_openset)
226                 free_fdset(new_openset, nfds);
227         if (new_execset)
228                 free_fdset(new_execset, nfds);
229         write_lock(&files->file_lock);
230         return error;
231 }
232