diff options
Diffstat (limited to 'kernel/fork.c')
| -rw-r--r-- | kernel/fork.c | 101 |
1 files changed, 67 insertions, 34 deletions
diff --git a/kernel/fork.c b/kernel/fork.c index 7e1ead9a6ba4..8149f3602881 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
| @@ -35,6 +35,7 @@ | |||
| 35 | #include <linux/syscalls.h> | 35 | #include <linux/syscalls.h> |
| 36 | #include <linux/jiffies.h> | 36 | #include <linux/jiffies.h> |
| 37 | #include <linux/futex.h> | 37 | #include <linux/futex.h> |
| 38 | #include <linux/rcupdate.h> | ||
| 38 | #include <linux/ptrace.h> | 39 | #include <linux/ptrace.h> |
| 39 | #include <linux/mount.h> | 40 | #include <linux/mount.h> |
| 40 | #include <linux/audit.h> | 41 | #include <linux/audit.h> |
| @@ -176,6 +177,7 @@ static struct task_struct *dup_task_struct(struct task_struct *orig) | |||
| 176 | 177 | ||
| 177 | /* One for us, one for whoever does the "release_task()" (usually parent) */ | 178 | /* One for us, one for whoever does the "release_task()" (usually parent) */ |
| 178 | atomic_set(&tsk->usage,2); | 179 | atomic_set(&tsk->usage,2); |
| 180 | atomic_set(&tsk->fs_excl, 0); | ||
| 179 | return tsk; | 181 | return tsk; |
| 180 | } | 182 | } |
| 181 | 183 | ||
| @@ -564,24 +566,53 @@ static inline int copy_fs(unsigned long clone_flags, struct task_struct * tsk) | |||
| 564 | return 0; | 566 | return 0; |
| 565 | } | 567 | } |
| 566 | 568 | ||
| 567 | static int count_open_files(struct files_struct *files, int size) | 569 | static int count_open_files(struct fdtable *fdt) |
| 568 | { | 570 | { |
| 571 | int size = fdt->max_fdset; | ||
| 569 | int i; | 572 | int i; |
| 570 | 573 | ||
| 571 | /* Find the last open fd */ | 574 | /* Find the last open fd */ |
| 572 | for (i = size/(8*sizeof(long)); i > 0; ) { | 575 | for (i = size/(8*sizeof(long)); i > 0; ) { |
| 573 | if (files->open_fds->fds_bits[--i]) | 576 | if (fdt->open_fds->fds_bits[--i]) |
| 574 | break; | 577 | break; |
| 575 | } | 578 | } |
| 576 | i = (i+1) * 8 * sizeof(long); | 579 | i = (i+1) * 8 * sizeof(long); |
| 577 | return i; | 580 | return i; |
| 578 | } | 581 | } |
| 579 | 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 = &newf->fdtab; | ||
| 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 | INIT_RCU_HEAD(&fdt->rcu); | ||
| 603 | fdt->free_files = NULL; | ||
| 604 | fdt->next = NULL; | ||
| 605 | rcu_assign_pointer(newf->fdt, fdt); | ||
| 606 | out: | ||
| 607 | return newf; | ||
| 608 | } | ||
| 609 | |||
| 580 | static int copy_files(unsigned long clone_flags, struct task_struct * tsk) | 610 | static int copy_files(unsigned long clone_flags, struct task_struct * tsk) |
| 581 | { | 611 | { |
| 582 | struct files_struct *oldf, *newf; | 612 | struct files_struct *oldf, *newf; |
| 583 | struct file **old_fds, **new_fds; | 613 | struct file **old_fds, **new_fds; |
| 584 | int open_files, size, i, error = 0, expand; | 614 | int open_files, size, i, error = 0, expand; |
| 615 | struct fdtable *old_fdt, *new_fdt; | ||
| 585 | 616 | ||
| 586 | /* | 617 | /* |
| 587 | * A background process may not have any files ... | 618 | * A background process may not have any files ... |
| @@ -602,35 +633,27 @@ static int copy_files(unsigned long clone_flags, struct task_struct * tsk) | |||
| 602 | */ | 633 | */ |
| 603 | tsk->files = NULL; | 634 | tsk->files = NULL; |
| 604 | error = -ENOMEM; | 635 | error = -ENOMEM; |
| 605 | newf = kmem_cache_alloc(files_cachep, SLAB_KERNEL); | 636 | newf = alloc_files(); |
| 606 | if (!newf) | 637 | if (!newf) |
| 607 | goto out; | 638 | goto out; |
| 608 | 639 | ||
| 609 | atomic_set(&newf->count, 1); | ||
| 610 | |||
| 611 | spin_lock_init(&newf->file_lock); | ||
| 612 | newf->next_fd = 0; | ||
| 613 | newf->max_fds = NR_OPEN_DEFAULT; | ||
| 614 | newf->max_fdset = __FD_SETSIZE; | ||
| 615 | newf->close_on_exec = &newf->close_on_exec_init; | ||
| 616 | newf->open_fds = &newf->open_fds_init; | ||
| 617 | newf->fd = &newf->fd_array[0]; | ||
| 618 | |||
| 619 | spin_lock(&oldf->file_lock); | 640 | spin_lock(&oldf->file_lock); |
| 620 | 641 | old_fdt = files_fdtable(oldf); | |
| 621 | open_files = count_open_files(oldf, oldf->max_fdset); | 642 | new_fdt = files_fdtable(newf); |
| 643 | size = old_fdt->max_fdset; | ||
| 644 | open_files = count_open_files(old_fdt); | ||
| 622 | expand = 0; | 645 | expand = 0; |
| 623 | 646 | ||
| 624 | /* | 647 | /* |
| 625 | * Check whether we need to allocate a larger fd array or fd set. | 648 | * Check whether we need to allocate a larger fd array or fd set. |
| 626 | * Note: we're not a clone task, so the open count won't change. | 649 | * Note: we're not a clone task, so the open count won't change. |
| 627 | */ | 650 | */ |
| 628 | if (open_files > newf->max_fdset) { | 651 | if (open_files > new_fdt->max_fdset) { |
| 629 | newf->max_fdset = 0; | 652 | new_fdt->max_fdset = 0; |
| 630 | expand = 1; | 653 | expand = 1; |
| 631 | } | 654 | } |
| 632 | if (open_files > newf->max_fds) { | 655 | if (open_files > new_fdt->max_fds) { |
| 633 | newf->max_fds = 0; | 656 | new_fdt->max_fds = 0; |
| 634 | expand = 1; | 657 | expand = 1; |
| 635 | } | 658 | } |
| 636 | 659 | ||
| @@ -642,14 +665,21 @@ static int copy_files(unsigned long clone_flags, struct task_struct * tsk) | |||
| 642 | spin_unlock(&newf->file_lock); | 665 | spin_unlock(&newf->file_lock); |
| 643 | if (error < 0) | 666 | if (error < 0) |
| 644 | goto out_release; | 667 | goto out_release; |
| 668 | new_fdt = files_fdtable(newf); | ||
| 669 | /* | ||
| 670 | * Reacquire the oldf lock and a pointer to its fd table | ||
| 671 | * who knows it may have a new bigger fd table. We need | ||
| 672 | * the latest pointer. | ||
| 673 | */ | ||
| 645 | spin_lock(&oldf->file_lock); | 674 | spin_lock(&oldf->file_lock); |
| 675 | old_fdt = files_fdtable(oldf); | ||
| 646 | } | 676 | } |
| 647 | 677 | ||
| 648 | old_fds = oldf->fd; | 678 | old_fds = old_fdt->fd; |
| 649 | new_fds = newf->fd; | 679 | new_fds = new_fdt->fd; |
| 650 | 680 | ||
| 651 | memcpy(newf->open_fds->fds_bits, oldf->open_fds->fds_bits, open_files/8); | 681 | memcpy(new_fdt->open_fds->fds_bits, old_fdt->open_fds->fds_bits, open_files/8); |
| 652 | memcpy(newf->close_on_exec->fds_bits, oldf->close_on_exec->fds_bits, open_files/8); | 682 | memcpy(new_fdt->close_on_exec->fds_bits, old_fdt->close_on_exec->fds_bits, open_files/8); |
| 653 | 683 | ||
| 654 | for (i = open_files; i != 0; i--) { | 684 | for (i = open_files; i != 0; i--) { |
| 655 | struct file *f = *old_fds++; | 685 | struct file *f = *old_fds++; |
| @@ -662,24 +692,24 @@ static int copy_files(unsigned long clone_flags, struct task_struct * tsk) | |||
| 662 | * is partway through open(). So make sure that this | 692 | * is partway through open(). So make sure that this |
| 663 | * fd is available to the new process. | 693 | * fd is available to the new process. |
| 664 | */ | 694 | */ |
| 665 | FD_CLR(open_files - i, newf->open_fds); | 695 | FD_CLR(open_files - i, new_fdt->open_fds); |
| 666 | } | 696 | } |
| 667 | *new_fds++ = f; | 697 | rcu_assign_pointer(*new_fds++, f); |
| 668 | } | 698 | } |
| 669 | spin_unlock(&oldf->file_lock); | 699 | spin_unlock(&oldf->file_lock); |
| 670 | 700 | ||
| 671 | /* compute the remainder to be cleared */ | 701 | /* compute the remainder to be cleared */ |
| 672 | size = (newf->max_fds - open_files) * sizeof(struct file *); | 702 | size = (new_fdt->max_fds - open_files) * sizeof(struct file *); |
| 673 | 703 | ||
| 674 | /* This is long word aligned thus could use a optimized version */ | 704 | /* This is long word aligned thus could use a optimized version */ |
| 675 | memset(new_fds, 0, size); | 705 | memset(new_fds, 0, size); |
| 676 | 706 | ||
| 677 | if (newf->max_fdset > open_files) { | 707 | if (new_fdt->max_fdset > open_files) { |
| 678 | int left = (newf->max_fdset-open_files)/8; | 708 | int left = (new_fdt->max_fdset-open_files)/8; |
| 679 | int start = open_files / (8 * sizeof(unsigned long)); | 709 | int start = open_files / (8 * sizeof(unsigned long)); |
| 680 | 710 | ||
| 681 | memset(&newf->open_fds->fds_bits[start], 0, left); | 711 | memset(&new_fdt->open_fds->fds_bits[start], 0, left); |
| 682 | memset(&newf->close_on_exec->fds_bits[start], 0, left); | 712 | memset(&new_fdt->close_on_exec->fds_bits[start], 0, left); |
| 683 | } | 713 | } |
| 684 | 714 | ||
| 685 | tsk->files = newf; | 715 | tsk->files = newf; |
| @@ -688,9 +718,9 @@ out: | |||
| 688 | return error; | 718 | return error; |
| 689 | 719 | ||
| 690 | out_release: | 720 | out_release: |
| 691 | free_fdset (newf->close_on_exec, newf->max_fdset); | 721 | free_fdset (new_fdt->close_on_exec, new_fdt->max_fdset); |
| 692 | free_fdset (newf->open_fds, newf->max_fdset); | 722 | free_fdset (new_fdt->open_fds, new_fdt->max_fdset); |
| 693 | free_fd_array(newf->fd, newf->max_fds); | 723 | free_fd_array(new_fdt->fd, new_fdt->max_fds); |
| 694 | kmem_cache_free(files_cachep, newf); | 724 | kmem_cache_free(files_cachep, newf); |
| 695 | goto out; | 725 | goto out; |
| 696 | } | 726 | } |
| @@ -1115,6 +1145,9 @@ static task_t *copy_process(unsigned long clone_flags, | |||
| 1115 | __get_cpu_var(process_counts)++; | 1145 | __get_cpu_var(process_counts)++; |
| 1116 | } | 1146 | } |
| 1117 | 1147 | ||
| 1148 | if (!current->signal->tty && p->signal->tty) | ||
| 1149 | p->signal->tty = NULL; | ||
| 1150 | |||
| 1118 | nr_threads++; | 1151 | nr_threads++; |
| 1119 | total_forks++; | 1152 | total_forks++; |
| 1120 | write_unlock_irq(&tasklist_lock); | 1153 | write_unlock_irq(&tasklist_lock); |
