diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/file.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'fs/file.c')
-rw-r--r-- | fs/file.c | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/fs/file.c b/fs/file.c new file mode 100644 index 000000000000..92b5f25985d2 --- /dev/null +++ b/fs/file.c | |||
@@ -0,0 +1,254 @@ | |||
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 | } | ||