diff options
Diffstat (limited to 'fs/file.c')
| -rw-r--r-- | fs/file.c | 98 |
1 files changed, 43 insertions, 55 deletions
| @@ -240,13 +240,9 @@ static struct fdtable *alloc_fdtable(int nr) | |||
| 240 | if (!fdt) | 240 | if (!fdt) |
| 241 | goto out; | 241 | goto out; |
| 242 | 242 | ||
| 243 | nfds = 8 * L1_CACHE_BYTES; | 243 | nfds = max_t(int, 8 * L1_CACHE_BYTES, roundup_pow_of_two(nr + 1)); |
| 244 | /* Expand to the max in easy steps */ | 244 | if (nfds > NR_OPEN) |
| 245 | while (nfds <= nr) { | 245 | nfds = NR_OPEN; |
| 246 | nfds = nfds * 2; | ||
| 247 | if (nfds > NR_OPEN) | ||
| 248 | nfds = NR_OPEN; | ||
| 249 | } | ||
| 250 | 246 | ||
| 251 | new_openset = alloc_fdset(nfds); | 247 | new_openset = alloc_fdset(nfds); |
| 252 | new_execset = alloc_fdset(nfds); | 248 | new_execset = alloc_fdset(nfds); |
| @@ -277,86 +273,78 @@ static struct fdtable *alloc_fdtable(int nr) | |||
| 277 | } while (nfds <= nr); | 273 | } while (nfds <= nr); |
| 278 | new_fds = alloc_fd_array(nfds); | 274 | new_fds = alloc_fd_array(nfds); |
| 279 | if (!new_fds) | 275 | if (!new_fds) |
| 280 | goto out; | 276 | goto out2; |
| 281 | fdt->fd = new_fds; | 277 | fdt->fd = new_fds; |
| 282 | fdt->max_fds = nfds; | 278 | fdt->max_fds = nfds; |
| 283 | fdt->free_files = NULL; | 279 | fdt->free_files = NULL; |
| 284 | return fdt; | 280 | return fdt; |
| 281 | out2: | ||
| 282 | nfds = fdt->max_fdset; | ||
| 285 | out: | 283 | out: |
| 286 | if (new_openset) | 284 | free_fdset(new_openset, nfds); |
| 287 | free_fdset(new_openset, nfds); | 285 | free_fdset(new_execset, nfds); |
| 288 | if (new_execset) | ||
| 289 | free_fdset(new_execset, nfds); | ||
| 290 | kfree(fdt); | 286 | kfree(fdt); |
| 291 | return NULL; | 287 | return NULL; |
| 292 | } | 288 | } |
| 293 | 289 | ||
| 294 | /* | 290 | /* |
| 295 | * Expands the file descriptor table - it will allocate a new fdtable and | 291 | * Expand the file descriptor table. |
| 296 | * both fd array and fdset. It is expected to be called with the | 292 | * This function will allocate a new fdtable and both fd array and fdset, of |
| 297 | * files_lock held. | 293 | * the given size. |
| 294 | * Return <0 error code on error; 1 on successful completion. | ||
| 295 | * The files->file_lock should be held on entry, and will be held on exit. | ||
| 298 | */ | 296 | */ |
| 299 | static int expand_fdtable(struct files_struct *files, int nr) | 297 | static int expand_fdtable(struct files_struct *files, int nr) |
| 300 | __releases(files->file_lock) | 298 | __releases(files->file_lock) |
| 301 | __acquires(files->file_lock) | 299 | __acquires(files->file_lock) |
| 302 | { | 300 | { |
| 303 | int error = 0; | 301 | struct fdtable *new_fdt, *cur_fdt; |
| 304 | struct fdtable *fdt; | ||
| 305 | struct fdtable *nfdt = NULL; | ||
| 306 | 302 | ||
| 307 | spin_unlock(&files->file_lock); | 303 | spin_unlock(&files->file_lock); |
| 308 | nfdt = alloc_fdtable(nr); | 304 | new_fdt = alloc_fdtable(nr); |
| 309 | if (!nfdt) { | ||
| 310 | error = -ENOMEM; | ||
| 311 | spin_lock(&files->file_lock); | ||
| 312 | goto out; | ||
| 313 | } | ||
| 314 | |||
| 315 | spin_lock(&files->file_lock); | 305 | spin_lock(&files->file_lock); |
| 316 | fdt = files_fdtable(files); | 306 | if (!new_fdt) |
| 307 | return -ENOMEM; | ||
| 317 | /* | 308 | /* |
| 318 | * Check again since another task may have expanded the | 309 | * Check again since another task may have expanded the fd table while |
| 319 | * fd table while we dropped the lock | 310 | * we dropped the lock |
| 320 | */ | 311 | */ |
| 321 | if (nr >= fdt->max_fds || nr >= fdt->max_fdset) { | 312 | cur_fdt = files_fdtable(files); |
| 322 | copy_fdtable(nfdt, fdt); | 313 | if (nr >= cur_fdt->max_fds || nr >= cur_fdt->max_fdset) { |
| 314 | /* Continue as planned */ | ||
| 315 | copy_fdtable(new_fdt, cur_fdt); | ||
| 316 | rcu_assign_pointer(files->fdt, new_fdt); | ||
| 317 | free_fdtable(cur_fdt); | ||
| 323 | } else { | 318 | } else { |
| 324 | /* Somebody expanded while we dropped file_lock */ | 319 | /* Somebody else expanded, so undo our attempt */ |
| 325 | spin_unlock(&files->file_lock); | 320 | __free_fdtable(new_fdt); |
| 326 | __free_fdtable(nfdt); | ||
| 327 | spin_lock(&files->file_lock); | ||
| 328 | goto out; | ||
| 329 | } | 321 | } |
| 330 | rcu_assign_pointer(files->fdt, nfdt); | 322 | return 1; |
| 331 | free_fdtable(fdt); | ||
| 332 | out: | ||
| 333 | return error; | ||
| 334 | } | 323 | } |
| 335 | 324 | ||
| 336 | /* | 325 | /* |
| 337 | * Expand files. | 326 | * Expand files. |
| 338 | * Return <0 on error; 0 nothing done; 1 files expanded, we may have blocked. | 327 | * This function will expand the file structures, if the requested size exceeds |
| 339 | * Should be called with the files->file_lock spinlock held for write. | 328 | * the current capacity and there is room for expansion. |
| 329 | * Return <0 error code on error; 0 when nothing done; 1 when files were | ||
| 330 | * expanded and execution may have blocked. | ||
| 331 | * The files->file_lock should be held on entry, and will be held on exit. | ||
| 340 | */ | 332 | */ |
| 341 | int expand_files(struct files_struct *files, int nr) | 333 | int expand_files(struct files_struct *files, int nr) |
| 342 | { | 334 | { |
| 343 | int err, expand = 0; | ||
| 344 | struct fdtable *fdt; | 335 | struct fdtable *fdt; |
| 345 | 336 | ||
| 346 | fdt = files_fdtable(files); | 337 | fdt = files_fdtable(files); |
| 347 | if (nr >= fdt->max_fdset || nr >= fdt->max_fds) { | 338 | /* Do we need to expand? */ |
| 348 | if (fdt->max_fdset >= NR_OPEN || | 339 | if (nr < fdt->max_fdset && nr < fdt->max_fds) |
| 349 | fdt->max_fds >= NR_OPEN || nr >= NR_OPEN) { | 340 | return 0; |
| 350 | err = -EMFILE; | 341 | /* Can we expand? */ |
| 351 | goto out; | 342 | if (fdt->max_fdset >= NR_OPEN || fdt->max_fds >= NR_OPEN || |
| 352 | } | 343 | nr >= NR_OPEN) |
| 353 | expand = 1; | 344 | return -EMFILE; |
| 354 | if ((err = expand_fdtable(files, nr))) | 345 | |
| 355 | goto out; | 346 | /* All good, so we try */ |
| 356 | } | 347 | return expand_fdtable(files, nr); |
| 357 | err = expand; | ||
| 358 | out: | ||
| 359 | return err; | ||
| 360 | } | 348 | } |
| 361 | 349 | ||
| 362 | static void __devinit fdtable_defer_list_init(int cpu) | 350 | static void __devinit fdtable_defer_list_init(int cpu) |
