diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/exit.c | 21 | ||||
-rw-r--r-- | kernel/fork.c | 82 |
2 files changed, 63 insertions, 40 deletions
diff --git a/kernel/exit.c b/kernel/exit.c index 5b0fb9f09f21..83beb1e93b18 100644 --- a/kernel/exit.c +++ b/kernel/exit.c | |||
@@ -368,17 +368,19 @@ EXPORT_SYMBOL(daemonize); | |||
368 | static inline void close_files(struct files_struct * files) | 368 | static inline void close_files(struct files_struct * files) |
369 | { | 369 | { |
370 | int i, j; | 370 | int i, j; |
371 | struct fdtable *fdt; | ||
371 | 372 | ||
372 | j = 0; | 373 | j = 0; |
374 | fdt = files_fdtable(files); | ||
373 | for (;;) { | 375 | for (;;) { |
374 | unsigned long set; | 376 | unsigned long set; |
375 | i = j * __NFDBITS; | 377 | i = j * __NFDBITS; |
376 | if (i >= files->max_fdset || i >= files->max_fds) | 378 | if (i >= fdt->max_fdset || i >= fdt->max_fds) |
377 | break; | 379 | break; |
378 | set = files->open_fds->fds_bits[j++]; | 380 | set = fdt->open_fds->fds_bits[j++]; |
379 | while (set) { | 381 | while (set) { |
380 | if (set & 1) { | 382 | if (set & 1) { |
381 | struct file * file = xchg(&files->fd[i], NULL); | 383 | struct file * file = xchg(&fdt->fd[i], NULL); |
382 | if (file) | 384 | if (file) |
383 | filp_close(file, files); | 385 | filp_close(file, files); |
384 | } | 386 | } |
@@ -403,16 +405,19 @@ struct files_struct *get_files_struct(struct task_struct *task) | |||
403 | 405 | ||
404 | void fastcall put_files_struct(struct files_struct *files) | 406 | void fastcall put_files_struct(struct files_struct *files) |
405 | { | 407 | { |
408 | struct fdtable *fdt; | ||
409 | |||
406 | if (atomic_dec_and_test(&files->count)) { | 410 | if (atomic_dec_and_test(&files->count)) { |
407 | close_files(files); | 411 | close_files(files); |
408 | /* | 412 | /* |
409 | * Free the fd and fdset arrays if we expanded them. | 413 | * Free the fd and fdset arrays if we expanded them. |
410 | */ | 414 | */ |
411 | if (files->fd != &files->fd_array[0]) | 415 | fdt = files_fdtable(files); |
412 | free_fd_array(files->fd, files->max_fds); | 416 | if (fdt->fd != &files->fd_array[0]) |
413 | if (files->max_fdset > __FD_SETSIZE) { | 417 | free_fd_array(fdt->fd, fdt->max_fds); |
414 | free_fdset(files->open_fds, files->max_fdset); | 418 | if (fdt->max_fdset > __FD_SETSIZE) { |
415 | free_fdset(files->close_on_exec, files->max_fdset); | 419 | free_fdset(fdt->open_fds, fdt->max_fdset); |
420 | free_fdset(fdt->close_on_exec, fdt->max_fdset); | ||
416 | } | 421 | } |
417 | kmem_cache_free(files_cachep, files); | 422 | kmem_cache_free(files_cachep, files); |
418 | } | 423 | } |
diff --git a/kernel/fork.c b/kernel/fork.c index b25802065031..ecc694debb50 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
@@ -568,21 +568,47 @@ static inline int copy_fs(unsigned long clone_flags, struct task_struct * tsk) | |||
568 | static int count_open_files(struct files_struct *files, int size) | 568 | static int count_open_files(struct files_struct *files, int size) |
569 | { | 569 | { |
570 | int i; | 570 | int i; |
571 | struct fdtable *fdt; | ||
571 | 572 | ||
572 | /* Find the last open fd */ | 573 | /* Find the last open fd */ |
574 | fdt = files_fdtable(files); | ||
573 | for (i = size/(8*sizeof(long)); i > 0; ) { | 575 | for (i = size/(8*sizeof(long)); i > 0; ) { |
574 | if (files->open_fds->fds_bits[--i]) | 576 | if (fdt->open_fds->fds_bits[--i]) |
575 | break; | 577 | break; |
576 | } | 578 | } |
577 | i = (i+1) * 8 * sizeof(long); | 579 | i = (i+1) * 8 * sizeof(long); |
578 | return i; | 580 | return i; |
579 | } | 581 | } |
580 | 582 | ||
583 | static struct files_struct *alloc_files(void) | ||
584 | { | ||
585 | struct files_struct *newf; | ||
586 | struct fdtable *fdt; | ||
587 | |||
588 | newf = kmem_cache_alloc(files_cachep, SLAB_KERNEL); | ||
589 | if (!newf) | ||
590 | goto out; | ||
591 | |||
592 | atomic_set(&newf->count, 1); | ||
593 | |||
594 | spin_lock_init(&newf->file_lock); | ||
595 | fdt = files_fdtable(newf); | ||
596 | fdt->next_fd = 0; | ||
597 | fdt->max_fds = NR_OPEN_DEFAULT; | ||
598 | fdt->max_fdset = __FD_SETSIZE; | ||
599 | fdt->close_on_exec = &newf->close_on_exec_init; | ||
600 | fdt->open_fds = &newf->open_fds_init; | ||
601 | fdt->fd = &newf->fd_array[0]; | ||
602 | out: | ||
603 | return newf; | ||
604 | } | ||
605 | |||
581 | static int copy_files(unsigned long clone_flags, struct task_struct * tsk) | 606 | static int copy_files(unsigned long clone_flags, struct task_struct * tsk) |
582 | { | 607 | { |
583 | struct files_struct *oldf, *newf; | 608 | struct files_struct *oldf, *newf; |
584 | struct file **old_fds, **new_fds; | 609 | struct file **old_fds, **new_fds; |
585 | int open_files, size, i, error = 0, expand; | 610 | int open_files, size, i, error = 0, expand; |
611 | struct fdtable *old_fdt, *new_fdt; | ||
586 | 612 | ||
587 | /* | 613 | /* |
588 | * A background process may not have any files ... | 614 | * A background process may not have any files ... |
@@ -603,35 +629,27 @@ static int copy_files(unsigned long clone_flags, struct task_struct * tsk) | |||
603 | */ | 629 | */ |
604 | tsk->files = NULL; | 630 | tsk->files = NULL; |
605 | error = -ENOMEM; | 631 | error = -ENOMEM; |
606 | newf = kmem_cache_alloc(files_cachep, SLAB_KERNEL); | 632 | newf = alloc_files(); |
607 | if (!newf) | 633 | if (!newf) |
608 | goto out; | 634 | goto out; |
609 | 635 | ||
610 | atomic_set(&newf->count, 1); | ||
611 | |||
612 | spin_lock_init(&newf->file_lock); | ||
613 | newf->next_fd = 0; | ||
614 | newf->max_fds = NR_OPEN_DEFAULT; | ||
615 | newf->max_fdset = __FD_SETSIZE; | ||
616 | newf->close_on_exec = &newf->close_on_exec_init; | ||
617 | newf->open_fds = &newf->open_fds_init; | ||
618 | newf->fd = &newf->fd_array[0]; | ||
619 | |||
620 | spin_lock(&oldf->file_lock); | 636 | spin_lock(&oldf->file_lock); |
621 | 637 | old_fdt = files_fdtable(oldf); | |
622 | open_files = count_open_files(oldf, oldf->max_fdset); | 638 | new_fdt = files_fdtable(newf); |
639 | size = old_fdt->max_fdset; | ||
640 | open_files = count_open_files(oldf, old_fdt->max_fdset); | ||
623 | expand = 0; | 641 | expand = 0; |
624 | 642 | ||
625 | /* | 643 | /* |
626 | * Check whether we need to allocate a larger fd array or fd set. | 644 | * Check whether we need to allocate a larger fd array or fd set. |
627 | * Note: we're not a clone task, so the open count won't change. | 645 | * Note: we're not a clone task, so the open count won't change. |
628 | */ | 646 | */ |
629 | if (open_files > newf->max_fdset) { | 647 | if (open_files > new_fdt->max_fdset) { |
630 | newf->max_fdset = 0; | 648 | new_fdt->max_fdset = 0; |
631 | expand = 1; | 649 | expand = 1; |
632 | } | 650 | } |
633 | if (open_files > newf->max_fds) { | 651 | if (open_files > new_fdt->max_fds) { |
634 | newf->max_fds = 0; | 652 | new_fdt->max_fds = 0; |
635 | expand = 1; | 653 | expand = 1; |
636 | } | 654 | } |
637 | 655 | ||
@@ -646,11 +664,11 @@ static int copy_files(unsigned long clone_flags, struct task_struct * tsk) | |||
646 | spin_lock(&oldf->file_lock); | 664 | spin_lock(&oldf->file_lock); |
647 | } | 665 | } |
648 | 666 | ||
649 | old_fds = oldf->fd; | 667 | old_fds = old_fdt->fd; |
650 | new_fds = newf->fd; | 668 | new_fds = new_fdt->fd; |
651 | 669 | ||
652 | memcpy(newf->open_fds->fds_bits, oldf->open_fds->fds_bits, open_files/8); | 670 | memcpy(new_fdt->open_fds->fds_bits, old_fdt->open_fds->fds_bits, open_files/8); |
653 | memcpy(newf->close_on_exec->fds_bits, oldf->close_on_exec->fds_bits, open_files/8); | 671 | memcpy(new_fdt->close_on_exec->fds_bits, old_fdt->close_on_exec->fds_bits, open_files/8); |
654 | 672 | ||
655 | for (i = open_files; i != 0; i--) { | 673 | for (i = open_files; i != 0; i--) { |
656 | struct file *f = *old_fds++; | 674 | struct file *f = *old_fds++; |
@@ -663,24 +681,24 @@ static int copy_files(unsigned long clone_flags, struct task_struct * tsk) | |||
663 | * is partway through open(). So make sure that this | 681 | * is partway through open(). So make sure that this |
664 | * fd is available to the new process. | 682 | * fd is available to the new process. |
665 | */ | 683 | */ |
666 | FD_CLR(open_files - i, newf->open_fds); | 684 | FD_CLR(open_files - i, new_fdt->open_fds); |
667 | } | 685 | } |
668 | *new_fds++ = f; | 686 | *new_fds++ = f; |
669 | } | 687 | } |
670 | spin_unlock(&oldf->file_lock); | 688 | spin_unlock(&oldf->file_lock); |
671 | 689 | ||
672 | /* compute the remainder to be cleared */ | 690 | /* compute the remainder to be cleared */ |
673 | size = (newf->max_fds - open_files) * sizeof(struct file *); | 691 | size = (new_fdt->max_fds - open_files) * sizeof(struct file *); |
674 | 692 | ||
675 | /* This is long word aligned thus could use a optimized version */ | 693 | /* This is long word aligned thus could use a optimized version */ |
676 | memset(new_fds, 0, size); | 694 | memset(new_fds, 0, size); |
677 | 695 | ||
678 | if (newf->max_fdset > open_files) { | 696 | if (new_fdt->max_fdset > open_files) { |
679 | int left = (newf->max_fdset-open_files)/8; | 697 | int left = (new_fdt->max_fdset-open_files)/8; |
680 | int start = open_files / (8 * sizeof(unsigned long)); | 698 | int start = open_files / (8 * sizeof(unsigned long)); |
681 | 699 | ||
682 | memset(&newf->open_fds->fds_bits[start], 0, left); | 700 | memset(&new_fdt->open_fds->fds_bits[start], 0, left); |
683 | memset(&newf->close_on_exec->fds_bits[start], 0, left); | 701 | memset(&new_fdt->close_on_exec->fds_bits[start], 0, left); |
684 | } | 702 | } |
685 | 703 | ||
686 | tsk->files = newf; | 704 | tsk->files = newf; |
@@ -689,9 +707,9 @@ out: | |||
689 | return error; | 707 | return error; |
690 | 708 | ||
691 | out_release: | 709 | out_release: |
692 | free_fdset (newf->close_on_exec, newf->max_fdset); | 710 | free_fdset (new_fdt->close_on_exec, new_fdt->max_fdset); |
693 | free_fdset (newf->open_fds, newf->max_fdset); | 711 | free_fdset (new_fdt->open_fds, new_fdt->max_fdset); |
694 | free_fd_array(newf->fd, newf->max_fds); | 712 | free_fd_array(new_fdt->fd, new_fdt->max_fds); |
695 | kmem_cache_free(files_cachep, newf); | 713 | kmem_cache_free(files_cachep, newf); |
696 | goto out; | 714 | goto out; |
697 | } | 715 | } |