Contents of /alx-src/tags/kernel26-2.6.12-alx-r9/fs/file.c
Parent Directory | Revision Log
Revision 630 -
(show annotations)
(download)
Wed Mar 4 11:03:09 2009 UTC (15 years, 6 months ago) by niro
File MIME type: text/plain
File size: 5653 byte(s)
Wed Mar 4 11:03:09 2009 UTC (15 years, 6 months ago) by niro
File MIME type: text/plain
File size: 5653 byte(s)
Tag kernel26-2.6.12-alx-r9
1 | /* |
2 | * linux/fs/file.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/time.h> |
12 | #include <linux/slab.h> |
13 | #include <linux/vmalloc.h> |
14 | #include <linux/file.h> |
15 | #include <linux/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 "free_fd_array: 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 | static int expand_fd_array(struct files_struct *files, int nr) |
57 | __releases(files->file_lock) |
58 | __acquires(files->file_lock) |
59 | { |
60 | struct file **new_fds; |
61 | int error, nfds; |
62 | |
63 | |
64 | error = -EMFILE; |
65 | if (files->max_fds >= NR_OPEN || nr >= NR_OPEN) |
66 | goto out; |
67 | |
68 | nfds = files->max_fds; |
69 | spin_unlock(&files->file_lock); |
70 | |
71 | /* |
72 | * Expand to the max in easy steps, and keep expanding it until |
73 | * we have enough for the requested fd array size. |
74 | */ |
75 | |
76 | do { |
77 | #if NR_OPEN_DEFAULT < 256 |
78 | if (nfds < 256) |
79 | nfds = 256; |
80 | else |
81 | #endif |
82 | if (nfds < (PAGE_SIZE / sizeof(struct file *))) |
83 | nfds = PAGE_SIZE / sizeof(struct file *); |
84 | else { |
85 | nfds = nfds * 2; |
86 | if (nfds > NR_OPEN) |
87 | nfds = NR_OPEN; |
88 | } |
89 | } while (nfds <= nr); |
90 | |
91 | error = -ENOMEM; |
92 | new_fds = alloc_fd_array(nfds); |
93 | spin_lock(&files->file_lock); |
94 | if (!new_fds) |
95 | goto out; |
96 | |
97 | /* Copy the existing array and install the new pointer */ |
98 | |
99 | if (nfds > files->max_fds) { |
100 | struct file **old_fds; |
101 | int i; |
102 | |
103 | old_fds = xchg(&files->fd, new_fds); |
104 | i = xchg(&files->max_fds, nfds); |
105 | |
106 | /* Don't copy/clear the array if we are creating a new |
107 | fd array for fork() */ |
108 | if (i) { |
109 | memcpy(new_fds, old_fds, i * sizeof(struct file *)); |
110 | /* clear the remainder of the array */ |
111 | memset(&new_fds[i], 0, |
112 | (nfds-i) * sizeof(struct file *)); |
113 | |
114 | spin_unlock(&files->file_lock); |
115 | free_fd_array(old_fds, i); |
116 | spin_lock(&files->file_lock); |
117 | } |
118 | } else { |
119 | /* Somebody expanded the array while we slept ... */ |
120 | spin_unlock(&files->file_lock); |
121 | free_fd_array(new_fds, nfds); |
122 | spin_lock(&files->file_lock); |
123 | } |
124 | error = 0; |
125 | out: |
126 | return error; |
127 | } |
128 | |
129 | /* |
130 | * Allocate an fdset array, using kmalloc or vmalloc. |
131 | * Note: the array isn't cleared at allocation time. |
132 | */ |
133 | fd_set * alloc_fdset(int num) |
134 | { |
135 | fd_set *new_fdset; |
136 | int size = num / 8; |
137 | |
138 | if (size <= PAGE_SIZE) |
139 | new_fdset = (fd_set *) kmalloc(size, GFP_KERNEL); |
140 | else |
141 | new_fdset = (fd_set *) vmalloc(size); |
142 | return new_fdset; |
143 | } |
144 | |
145 | void free_fdset(fd_set *array, int num) |
146 | { |
147 | int size = num / 8; |
148 | |
149 | if (num <= __FD_SETSIZE) /* Don't free an embedded fdset */ |
150 | return; |
151 | else if (size <= PAGE_SIZE) |
152 | kfree(array); |
153 | else |
154 | vfree(array); |
155 | } |
156 | |
157 | /* |
158 | * Expand the fdset in the files_struct. Called with the files spinlock |
159 | * held for write. |
160 | */ |
161 | static int expand_fdset(struct files_struct *files, int nr) |
162 | __releases(file->file_lock) |
163 | __acquires(file->file_lock) |
164 | { |
165 | fd_set *new_openset = NULL, *new_execset = NULL; |
166 | int error, nfds = 0; |
167 | |
168 | error = -EMFILE; |
169 | if (files->max_fdset >= NR_OPEN || nr >= NR_OPEN) |
170 | goto out; |
171 | |
172 | nfds = files->max_fdset; |
173 | spin_unlock(&files->file_lock); |
174 | |
175 | /* Expand to the max in easy steps */ |
176 | do { |
177 | if (nfds < (PAGE_SIZE * 8)) |
178 | nfds = PAGE_SIZE * 8; |
179 | else { |
180 | nfds = nfds * 2; |
181 | if (nfds > NR_OPEN) |
182 | nfds = NR_OPEN; |
183 | } |
184 | } while (nfds <= nr); |
185 | |
186 | error = -ENOMEM; |
187 | new_openset = alloc_fdset(nfds); |
188 | new_execset = alloc_fdset(nfds); |
189 | spin_lock(&files->file_lock); |
190 | if (!new_openset || !new_execset) |
191 | goto out; |
192 | |
193 | error = 0; |
194 | |
195 | /* Copy the existing tables and install the new pointers */ |
196 | if (nfds > files->max_fdset) { |
197 | int i = files->max_fdset / (sizeof(unsigned long) * 8); |
198 | int count = (nfds - files->max_fdset) / 8; |
199 | |
200 | /* |
201 | * Don't copy the entire array if the current fdset is |
202 | * not yet initialised. |
203 | */ |
204 | if (i) { |
205 | memcpy (new_openset, files->open_fds, files->max_fdset/8); |
206 | memcpy (new_execset, files->close_on_exec, files->max_fdset/8); |
207 | memset (&new_openset->fds_bits[i], 0, count); |
208 | memset (&new_execset->fds_bits[i], 0, count); |
209 | } |
210 | |
211 | nfds = xchg(&files->max_fdset, nfds); |
212 | new_openset = xchg(&files->open_fds, new_openset); |
213 | new_execset = xchg(&files->close_on_exec, new_execset); |
214 | spin_unlock(&files->file_lock); |
215 | free_fdset (new_openset, nfds); |
216 | free_fdset (new_execset, nfds); |
217 | spin_lock(&files->file_lock); |
218 | return 0; |
219 | } |
220 | /* Somebody expanded the array while we slept ... */ |
221 | |
222 | out: |
223 | spin_unlock(&files->file_lock); |
224 | if (new_openset) |
225 | free_fdset(new_openset, nfds); |
226 | if (new_execset) |
227 | free_fdset(new_execset, nfds); |
228 | spin_lock(&files->file_lock); |
229 | return error; |
230 | } |
231 | |
232 | /* |
233 | * Expand files. |
234 | * Return <0 on error; 0 nothing done; 1 files expanded, we may have blocked. |
235 | * Should be called with the files->file_lock spinlock held for write. |
236 | */ |
237 | int expand_files(struct files_struct *files, int nr) |
238 | { |
239 | int err, expand = 0; |
240 | |
241 | if (nr >= files->max_fdset) { |
242 | expand = 1; |
243 | if ((err = expand_fdset(files, nr))) |
244 | goto out; |
245 | } |
246 | if (nr >= files->max_fds) { |
247 | expand = 1; |
248 | if ((err = expand_fd_array(files, nr))) |
249 | goto out; |
250 | } |
251 | err = expand; |
252 | out: |
253 | return err; |
254 | } |