patch-2.2.12 linux/kernel/fork.c

Next file: linux/mm/mlock.c
Previous file: linux/kernel/exit.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.2.11/linux/kernel/fork.c linux/kernel/fork.c
@@ -362,6 +362,9 @@
 
 	if (clone_flags & CLONE_VM) {
 		mmget(current->mm);
+		tsk->min_flt = tsk->maj_flt = 0;
+		tsk->cmin_flt = tsk->cmaj_flt = 0;
+		tsk->nswap = tsk->cnswap = 0;
 		/*
 		 * Set up the LDT descriptor for the clone task.
 		 */
@@ -414,32 +417,11 @@
 	return 0;
 }
 
-/*
- * Copy a fd_set and compute the maximum fd it contains. 
- */
-static inline int __copy_fdset(unsigned long *d, unsigned long *src)
-{
-	int i; 
-	unsigned long *p = src; 
-	unsigned long *max = src; 
-
-	for (i = __FDSET_LONGS; i; --i) {
-		if ((*d++ = *p++) != 0) 
-			max = p; 
-	}
-	return (max - src)*sizeof(long)*8; 
-}
-
-static inline int copy_fdset(fd_set *dst, fd_set *src)
-{
-	return __copy_fdset(dst->fds_bits, src->fds_bits);  
-}
-
 static int copy_files(unsigned long clone_flags, struct task_struct * tsk)
 {
 	struct files_struct *oldf, *newf;
 	struct file **old_fds, **new_fds;
-	int size, i, error = 0;
+	int nfds, size, i, error = 0;
 
 	/*
 	 * A background process may not have any files ...
@@ -459,25 +441,74 @@
 	if (!newf) 
 		goto out;
 
-	/*
-	 * Allocate the fd array, using get_free_page() if possible.
-	 * Eventually we want to make the array size variable ...
-	 */
-	size = NR_OPEN * sizeof(struct file *);
-	if (size == PAGE_SIZE)
-		new_fds = (struct file **) __get_free_page(GFP_KERNEL);
-	else
-		new_fds = (struct file **) kmalloc(size, GFP_KERNEL);
-	if (!new_fds)
-		goto out_release;
+	size = oldf->max_fdset;
+	nfds = NR_OPEN_DEFAULT;
 
+#ifdef FDSET_DEBUG	
+	printk (KERN_ERR __FUNCTION__ " size = %d/%d\n",
+		oldf->max_fds, oldf->max_fdset);
+#endif
 	atomic_set(&newf->count, 1);
-	newf->max_fds = NR_OPEN;
-	newf->fd = new_fds;
-	newf->close_on_exec = oldf->close_on_exec;
-	i = copy_fdset(&newf->open_fds, &oldf->open_fds);
 
+	newf->next_fd	    = 0;
+	newf->max_fds	    = NR_OPEN_DEFAULT;
+	newf->max_fdset	    = __FD_SETSIZE;
+	newf->close_on_exec = &newf->close_on_exec_init;
+	newf->open_fds	    = &newf->open_fds_init;
+	newf->fd	    = &newf->fd_array[0];
+
+	/* Even if the old fdset gets grown here, we'll only copy "size" fds */
+	if (size > __FD_SETSIZE) {
+		newf->max_fdset = 0;
+		error = expand_fdset(newf, size);
+		if (error)
+			goto out_release;
+	}
+	memcpy(newf->open_fds->fds_bits, oldf->open_fds->fds_bits, size/8);
+	memcpy(newf->close_on_exec->fds_bits, oldf->close_on_exec->fds_bits, size/8);
+	if (newf->max_fdset > size) {
+		int left = (newf->max_fdset-size)/8;
+		int start = size / (8 * sizeof(unsigned long));
+		
+		memset(&newf->open_fds->fds_bits[start], 0, left);
+		memset(&newf->close_on_exec->fds_bits[start], 0, left);
+	}
+
+	/* Find the last open fd */
+	for (i = size/(8*sizeof(long)); i > 0; ) {
+		if (newf->open_fds->fds_bits[--i])
+			break;
+	}
+	i = (i+1) * 8 * sizeof(long);
+
+#ifdef FDSET_DEBUG	
+	printk (KERN_ERR __FUNCTION__ " first-free = %d/%d\n", i, size);
+#endif
+
+	/* Do a sanity check ... */
+	if (i > oldf->max_fds)
+		printk(KERN_ERR 
+		       "copy_files: pid %d, open files %d exceeds max %d!\n",
+		       current->pid, i, oldf->max_fds);
+
+	/*
+	 * Check whether we need to allocate a larger fd array.
+	 * Note: we're not a clone task, so the open count won't
+	 * change.
+	 */
+	if (i > NR_OPEN_DEFAULT) {
+		newf->max_fds = 0;
+		error = expand_fd_array(newf, i);
+		if (error)
+			goto out_release;
+		nfds = newf->max_fds;
+	}
+
+	/* compute the remainder to be cleared */
+	size = (nfds - i) * sizeof(struct file *);
 	old_fds = oldf->fd;
+	new_fds = newf->fd;
+
 	for (; i != 0; i--) {
 		struct file *f = *old_fds++;
 		*new_fds = f;
@@ -486,14 +517,20 @@
 		new_fds++;
 	}
 	/* This is long word aligned thus could use a optimized version */ 
-	memset(new_fds, 0, (char *)newf->fd + size - (char *)new_fds); 
+	memset(new_fds, 0, size); 
       
 	tsk->files = newf;
 	error = 0;
 out:
+#ifdef FDSET_DEBUG	
+	if (error)
+		printk (KERN_ERR "copy_files: return %d\n", error);
+#endif
 	return error;
 
 out_release:
+	free_fdset (newf->close_on_exec, newf->max_fdset);
+	free_fdset (newf->open_fds, newf->max_fdset);
 	kmem_cache_free(files_cachep, newf);
 	goto out;
 }
@@ -553,13 +590,14 @@
 	if (p->user) {
 		if (atomic_read(&p->user->count) >= p->rlim[RLIMIT_NPROC].rlim_cur)
 			goto bad_fork_free;
+		atomic_inc(&p->user->count);
 	}
 
 	{
 		struct task_struct **tslot;
 		tslot = find_empty_process();
 		if (!tslot)
-			goto bad_fork_free;
+			goto bad_fork_cleanup_count;
 		p->tarray_ptr = tslot;
 		*tslot = p;
 		nr = tslot - &task[0];
@@ -632,7 +670,7 @@
 		goto bad_fork_cleanup_sighand;
 	retval = copy_thread(nr, clone_flags, usp, p, regs);
 	if (retval)
-		goto bad_fork_cleanup_sighand;
+		goto bad_fork_cleanup_mm;
 	p->semundo = NULL;
 
 	/* ok, now we should be set up.. */
@@ -663,8 +701,6 @@
 		write_unlock_irq(&tasklist_lock);
 
 		nr_tasks++;
-		if (p->user)
-			atomic_inc(&p->user->count);
 
 		p->next_run = NULL;
 		p->prev_run = NULL;
@@ -679,6 +715,9 @@
 		down(&sem);
 	return retval;
 
+bad_fork_cleanup_mm:
+	mmput(p->mm);
+	p->mm = NULL;
 bad_fork_cleanup_sighand:
 	exit_sighand(p);
 bad_fork_cleanup_fs:
@@ -692,6 +731,9 @@
 		__MOD_DEC_USE_COUNT(p->binfmt->module);
 
 	add_free_taskslot(p->tarray_ptr);
+bad_fork_cleanup_count:
+	if (p->user)
+		free_uid(p);
 bad_fork_free:
 	free_task_struct(p);
 	goto bad_fork;

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)