diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c index 82e6e56..9e9e1bd 100644 --- a/sys/amd64/amd64/machdep.c +++ b/sys/amd64/amd64/machdep.c @@ -1137,7 +1137,7 @@ hammer_time(u_int64_t modulep, u_int64_t physfree) * This may be done better later if it gets more high level * components in it. If so just link td->td_proc here. */ - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); preload_metadata = (caddr_t)(uintptr_t)(modulep + KERNBASE); preload_bootstrap_relocate(KERNBASE); diff --git a/sys/amd64/amd64/pmap.c b/sys/amd64/amd64/pmap.c index 894e880..32ed747 100644 --- a/sys/amd64/amd64/pmap.c +++ b/sys/amd64/amd64/pmap.c @@ -1186,7 +1186,7 @@ pmap_pinit0(pmap_t pmap) * Initialize a preallocated and zeroed pmap structure, * such as one in a vmspace structure. */ -void +int pmap_pinit(pmap_t pmap) { vm_page_t pml4pg; @@ -1216,6 +1216,8 @@ pmap_pinit(pmap_t pmap) pmap->pm_active = 0; TAILQ_INIT(&pmap->pm_pvchunk); bzero(&pmap->pm_stats, sizeof pmap->pm_stats); + + return (1); } /* diff --git a/sys/arm/arm/pmap.c b/sys/arm/arm/pmap.c index e831cc5..a536639 100644 --- a/sys/arm/arm/pmap.c +++ b/sys/arm/arm/pmap.c @@ -3814,7 +3814,7 @@ pmap_extract_and_hold(pmap_t pmap, vm_offset_t va, vm_prot_t prot) * such as one in a vmspace structure. */ -void +int pmap_pinit(pmap_t pmap) { PDEBUG(1, printf("pmap_pinit: pmap = %08x\n", (uint32_t) pmap)); @@ -3833,6 +3833,7 @@ pmap_pinit(pmap_t pmap) pmap_enter(pmap, vector_page, PHYS_TO_VM_PAGE(systempage.pv_pa), VM_PROT_READ, 1); } + return (1); } diff --git a/sys/arm/at91/kb920x_machdep.c b/sys/arm/at91/kb920x_machdep.c index 61fe4dc..4efd0ed 100644 --- a/sys/arm/at91/kb920x_machdep.c +++ b/sys/arm/at91/kb920x_machdep.c @@ -449,7 +449,7 @@ initarm(void *arg, void *arg2) undefined_handler_address = (u_int)undefinedinstruction_bounce; undefined_init(); - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); thread0.td_kstack = kernelstack.pv_va; thread0.td_pcb = (struct pcb *) (thread0.td_kstack + KSTACK_PAGES * PAGE_SIZE) - 1; diff --git a/sys/arm/sa11x0/assabet_machdep.c b/sys/arm/sa11x0/assabet_machdep.c index 22b15f4..da2fd37 100644 --- a/sys/arm/sa11x0/assabet_machdep.c +++ b/sys/arm/sa11x0/assabet_machdep.c @@ -422,7 +422,7 @@ initarm(void *arg, void *arg2) /* Set stack for exception handlers */ - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); thread0.td_kstack = kernelstack.pv_va; thread0.td_pcb = (struct pcb *) (thread0.td_kstack + KSTACK_PAGES * PAGE_SIZE) - 1; diff --git a/sys/arm/xscale/i80321/ep80219_machdep.c b/sys/arm/xscale/i80321/ep80219_machdep.c index 6961ffa..432f23f 100644 --- a/sys/arm/xscale/i80321/ep80219_machdep.c +++ b/sys/arm/xscale/i80321/ep80219_machdep.c @@ -426,7 +426,7 @@ initarm(void *arg, void *arg2) undefined_handler_address = (u_int)undefinedinstruction_bounce; undefined_init(); - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); thread0.td_kstack = kernelstack.pv_va; thread0.td_pcb = (struct pcb *) (thread0.td_kstack + KSTACK_PAGES * PAGE_SIZE) - 1; diff --git a/sys/arm/xscale/i80321/iq31244_machdep.c b/sys/arm/xscale/i80321/iq31244_machdep.c index 589e778..42c0cf8 100644 --- a/sys/arm/xscale/i80321/iq31244_machdep.c +++ b/sys/arm/xscale/i80321/iq31244_machdep.c @@ -424,7 +424,7 @@ initarm(void *arg, void *arg2) undefined_handler_address = (u_int)undefinedinstruction_bounce; undefined_init(); - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); thread0.td_kstack = kernelstack.pv_va; thread0.td_pcb = (struct pcb *) (thread0.td_kstack + KSTACK_PAGES * PAGE_SIZE) - 1; diff --git a/sys/arm/xscale/i8134x/crb_machdep.c b/sys/arm/xscale/i8134x/crb_machdep.c index a448628..f7b85d5 100644 --- a/sys/arm/xscale/i8134x/crb_machdep.c +++ b/sys/arm/xscale/i8134x/crb_machdep.c @@ -402,7 +402,7 @@ initarm(void *arg, void *arg2) #ifdef KSE proc_linkup(&proc0, &ksegrp0, &thread0); #else - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); #endif thread0.td_kstack = kernelstack.pv_va; thread0.td_pcb = (struct pcb *) diff --git a/sys/arm/xscale/ixp425/avila_machdep.c b/sys/arm/xscale/ixp425/avila_machdep.c index 8aa4352..cb135e2 100644 --- a/sys/arm/xscale/ixp425/avila_machdep.c +++ b/sys/arm/xscale/ixp425/avila_machdep.c @@ -490,7 +490,7 @@ initarm(void *arg, void *arg2) undefined_handler_address = (u_int)undefinedinstruction_bounce; undefined_init(); - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); thread0.td_kstack = kernelstack.pv_va; thread0.td_pcb = (struct pcb *) (thread0.td_kstack + KSTACK_PAGES * PAGE_SIZE) - 1; diff --git a/sys/compat/pecoff/imgact_pecoff.c b/sys/compat/pecoff/imgact_pecoff.c index be0359e..be376c6 100644 --- a/sys/compat/pecoff/imgact_pecoff.c +++ b/sys/compat/pecoff/imgact_pecoff.c @@ -416,7 +416,11 @@ exec_pecoff_coff_prep_zmagic(struct image_params * imgp, wp = (void *) ((char *) ap + sizeof(struct coff_aouthdr)); error = pecoff_read_from(FIRST_THREAD_IN_PROC(imgp->proc), imgp->vp, peofs + PECOFF_HDR_SIZE, (caddr_t) sh, scnsiz); + if (error) + return (error); exec_new_vmspace(imgp, &pecoff_sysvec); + if (error) + return (error); vmspace = imgp->proc->p_vmspace; for (i = 0; i < fp->f_nscns; i++) { prot = VM_PROT_WRITE; /* XXX for relocation? */ diff --git a/sys/compat/svr4/imgact_svr4.c b/sys/compat/svr4/imgact_svr4.c index a082ca3..f89afba 100644 --- a/sys/compat/svr4/imgact_svr4.c +++ b/sys/compat/svr4/imgact_svr4.c @@ -120,7 +120,9 @@ exec_svr4_imgact(imgp) /* * Destroy old process VM and create a new one (with a new stack) */ - exec_new_vmspace(imgp, &svr4_sysvec); + error = exec_new_vmspace(imgp, &svr4_sysvec); + if (error) + goto fail; vmspace = imgp->proc->p_vmspace; /* diff --git a/sys/i386/i386/machdep.c b/sys/i386/i386/machdep.c index 14b33f4..5d7a9b5 100644 --- a/sys/i386/i386/machdep.c +++ b/sys/i386/i386/machdep.c @@ -2088,7 +2088,7 @@ init386(first) * This may be done better later if it gets more high level * components in it. If so just link td->td_proc here. */ - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); metadata_missing = 0; if (bootinfo.bi_modulep) { diff --git a/sys/i386/i386/pmap.c b/sys/i386/i386/pmap.c index f05d7d4..2492d85 100644 --- a/sys/i386/i386/pmap.c +++ b/sys/i386/i386/pmap.c @@ -1223,11 +1223,15 @@ pmap_pinit0(pmap_t pmap) mtx_unlock_spin(&allpmaps_lock); } +static int i386_pmap_pdir_pages; +SYSCTL_INT(_vm, OID_AUTO, i386_pmap_pdir_pages, + CTLFLAG_RW, &i386_pmap_pdir_pages, 0, "Pages reserved for pmap page dirs"); + /* * Initialize a preallocated and zeroed pmap structure, * such as one in a vmspace structure. */ -void +int pmap_pinit(pmap_t pmap) { vm_page_t m, ptdpg[NPGPTD]; @@ -1244,6 +1248,12 @@ pmap_pinit(pmap_t pmap) if (pmap->pm_pdir == NULL) { pmap->pm_pdir = (pd_entry_t *)kmem_alloc_nofault(kernel_map, NBPTD); + i386_pmap_pdir_pages += NBPTD / PAGE_SIZE; + + if (pmap->pm_pdir == NULL) { + PMAP_LOCK_DESTROY(pmap); + return (0); + } #ifdef PAE pmap->pm_pdpt = uma_zalloc(pdptzone, M_WAITOK | M_ZERO); KASSERT(((vm_offset_t)pmap->pm_pdpt & @@ -1297,6 +1307,8 @@ pmap_pinit(pmap_t pmap) pmap->pm_active = 0; TAILQ_INIT(&pmap->pm_pvchunk); bzero(&pmap->pm_stats, sizeof pmap->pm_stats); + + return (1); } /* diff --git a/sys/i386/ibcs2/imgact_coff.c b/sys/i386/ibcs2/imgact_coff.c index 8a418f4..3f2feda 100644 --- a/sys/i386/ibcs2/imgact_coff.c +++ b/sys/i386/ibcs2/imgact_coff.c @@ -337,7 +337,9 @@ exec_coff_imgact(imgp) VOP_UNLOCK(imgp->vp, 0, td); - exec_new_vmspace(imgp, &ibcs2_svr3_sysvec); + error = exec_new_vmspace(imgp, &ibcs2_svr3_sysvec); + if (error) + goto fail; vmspace = imgp->proc->p_vmspace; for (i = 0; i < nscns; i++) { diff --git a/sys/i386/linux/imgact_linux.c b/sys/i386/linux/imgact_linux.c index 7d5d9df..066fa38 100644 --- a/sys/i386/linux/imgact_linux.c +++ b/sys/i386/linux/imgact_linux.c @@ -119,7 +119,9 @@ exec_linux_imgact(struct image_params *imgp) /* * Destroy old process VM and create a new one (with a new stack) */ - exec_new_vmspace(imgp, &linux_sysvec); + error = exec_new_vmspace(imgp, &linux_sysvec); + if (error) + goto fail; vmspace = imgp->proc->p_vmspace; /* diff --git a/sys/ia64/ia64/machdep.c b/sys/ia64/ia64/machdep.c index 6d79775..2ba9500 100644 --- a/sys/ia64/ia64/machdep.c +++ b/sys/ia64/ia64/machdep.c @@ -789,7 +789,7 @@ ia64_init(void) msgbufp = (struct msgbuf *)pmap_steal_memory(MSGBUF_SIZE); msgbufinit(msgbufp, MSGBUF_SIZE); - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); /* * Init mapping for kernel stack for proc 0 */ diff --git a/sys/ia64/ia64/pmap.c b/sys/ia64/ia64/pmap.c index 39ef9b7..7b89b16 100644 --- a/sys/ia64/ia64/pmap.c +++ b/sys/ia64/ia64/pmap.c @@ -721,6 +721,7 @@ pmap_pinit(struct pmap *pmap) pmap->pm_active = 0; TAILQ_INIT(&pmap->pm_pvlist); bzero(&pmap->pm_stats, sizeof pmap->pm_stats); + return (1); } /*************************************************** diff --git a/sys/kern/imgact_aout.c b/sys/kern/imgact_aout.c index dfd5dad..b0e9b1a 100644 --- a/sys/kern/imgact_aout.c +++ b/sys/kern/imgact_aout.c @@ -198,9 +198,11 @@ exec_aout_imgact(imgp) /* * Destroy old process VM and create a new one (with a new stack) */ - exec_new_vmspace(imgp, &aout_sysvec); + error = exec_new_vmspace(imgp, &aout_sysvec); vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY, td); + if (error) + return (error); /* * The vm space can be changed by exec_new_vmspace diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c index 4ae4596..e6a9c5d 100644 --- a/sys/kern/imgact_elf.c +++ b/sys/kern/imgact_elf.c @@ -482,7 +482,9 @@ __elfN(load_file)(struct proc *p, const char *file, u_long *addr, if (curthread->td_proc != p) panic("elf_load_file - thread"); /* XXXKSE DIAGNOSTIC */ - tempdata = malloc(sizeof(*tempdata), M_TEMP, M_WAITOK); + tempdata = malloc(sizeof(*tempdata), M_TEMP, M_WAITOK | M_FAILOK); + if (tempdata == NULL) + return (ENOMEM); nd = &tempdata->nd; attr = &tempdata->attr; imgp = &tempdata->image_params; @@ -666,10 +668,12 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) */ VOP_UNLOCK(imgp->vp, 0, td); - exec_new_vmspace(imgp, sv); + error = exec_new_vmspace(imgp, sv); imgp->proc->p_sysent = sv; vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY, td); + if (error) + return (error); vmspace = imgp->proc->p_vmspace; @@ -789,7 +793,11 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) VOP_UNLOCK(imgp->vp, 0, td); if (brand_info->emul_path != NULL && brand_info->emul_path[0] != '\0') { - path = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); + path = malloc(MAXPATHLEN, M_TEMP, M_WAITOK | M_FAILOK); + if (path == NULL) { + vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY, td); + return (ENOMEM); + } snprintf(path, MAXPATHLEN, "%s%s", brand_info->emul_path, interp); error = __elfN(load_file)(imgp->proc, path, &addr, @@ -812,7 +820,9 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) /* * Construct auxargs table (used by the fixup routine) */ - elf_auxargs = malloc(sizeof(Elf_Auxargs), M_TEMP, M_WAITOK); + elf_auxargs = malloc(sizeof(Elf_Auxargs), M_TEMP, M_WAITOK | M_FAILOK); + if (elf_auxargs == NULL) + return (ENOMEM); elf_auxargs->execfd = -1; elf_auxargs->phdr = proghdr; elf_auxargs->phent = hdr->e_phentsize; @@ -926,7 +936,7 @@ __elfN(coredump)(td, vp, limit) * Allocate memory for building the header, fill it up, * and write it out. */ - hdr = malloc(hdrsize, M_TEMP, M_WAITOK); + hdr = malloc(hdrsize, M_TEMP, M_WAITOK | M_FAILOK); if (hdr == NULL) { return (EINVAL); } diff --git a/sys/kern/imgact_gzip.c b/sys/kern/imgact_gzip.c index d4fb7e9..cec6169 100644 --- a/sys/kern/imgact_gzip.c +++ b/sys/kern/imgact_gzip.c @@ -239,9 +239,13 @@ do_aout_hdr(struct imgact_gzip * gz) /* * Destroy old process VM and create a new one (with a new stack) */ - exec_new_vmspace(gz->ip, &aout_sysvec); + error = exec_new_vmspace(gz->ip, &aout_sysvec); vn_lock(gz->ip->vp, LK_EXCLUSIVE | LK_RETRY, td); + if (error) { + gz->where = __LINE__; + return (error); + } vmspace = gz->ip->proc->p_vmspace; diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index eaf05a0..75aaba3 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -459,7 +459,7 @@ proc0_init(void *dummy __unused) siginit(&proc0); /* Create the file descriptor table. */ - p->p_fd = fdinit(NULL); + p->p_fd = fdinit(NULL, fdprealloc()); p->p_fdtol = NULL; /* Create the limits structures. */ diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index 941a4e5..bf4cd7e 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -1414,16 +1414,29 @@ falloc(struct thread *td, struct file **resultfp, int *resultfd) return (0); } +void * +fdprealloc() +{ + return (malloc(sizeof(struct filedesc0), M_FILEDESC, + M_WAITOK | M_ZERO | M_FAILOK)); +} + +void +fdprealloc_free(void *prealloc) +{ + free(prealloc, M_FILEDESC); +} + /* * Build a new filedesc structure from another. * Copy the current, root, and jail root vnode references. */ struct filedesc * -fdinit(struct filedesc *fdp) +fdinit(struct filedesc *fdp, void *prealloc) { struct filedesc0 *newfdp; - newfdp = malloc(sizeof *newfdp, M_FILEDESC, M_WAITOK | M_ZERO); + newfdp = (struct filedesc0 *)prealloc; FILEDESC_LOCK_INIT(&newfdp->fd_fd); if (fdp != NULL) { FILEDESC_XLOCK(fdp); @@ -1496,7 +1509,7 @@ fdshare(struct filedesc *fdp) * Unshare a filedesc structure, if necessary by making a copy */ void -fdunshare(struct proc *p, struct thread *td) +fdunshare(struct proc *p, struct thread *td, void **prealloc) { FILEDESC_XLOCK(p->p_fd); @@ -1504,7 +1517,8 @@ fdunshare(struct proc *p, struct thread *td) struct filedesc *tmp; FILEDESC_XUNLOCK(p->p_fd); - tmp = fdcopy(p->p_fd); + tmp = fdcopy(p->p_fd, *prealloc); + *prealloc = NULL; fdfree(td); p->p_fd = tmp; } else @@ -1516,7 +1530,7 @@ fdunshare(struct proc *p, struct thread *td) * this is to ease callers, not catch errors. */ struct filedesc * -fdcopy(struct filedesc *fdp) +fdcopy(struct filedesc *fdp, void *prealloc) { struct filedesc *newfdp; int i; @@ -1525,7 +1539,7 @@ fdcopy(struct filedesc *fdp) if (fdp == NULL) return (NULL); - newfdp = fdinit(fdp); + newfdp = fdinit(fdp, prealloc); FILEDESC_SLOCK(fdp); while (fdp->fd_lastfile >= newfdp->fd_nfiles) { FILEDESC_SUNLOCK(fdp); diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c index 348effe..65dab61 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -288,12 +288,13 @@ do_execve(td, args, mac_p) struct vattr attr; int (*img_first)(struct image_params *); struct pargs *oldargs = NULL, *newargs = NULL; - struct sigacts *oldsigacts, *newsigacts; + struct sigacts *oldsigacts, *newsigacts = NULL; #ifdef KTRACE struct vnode *tracevp = NULL; struct ucred *tracecred = NULL; #endif struct vnode *textvp = NULL; + void *fdpmem = NULL; int credential_changing; int vfslocked; int textset; @@ -470,23 +471,44 @@ interpret: else suword(--stack_base, imgp->args->argc); + i = imgp->args->begin_envv - imgp->args->begin_argv; + /* Cache arguments if they fit inside our allowance */ + if (ps_arg_cache_limit >= i + sizeof(struct pargs)) { + newargs = pargs_alloc(i); + if (newargs == NULL) { + error = ENOMEM; + goto exec_fail_dealloc; + } + bcopy(imgp->args->begin_argv, newargs->ar_args, i); + } + + PROC_LOCK(p); + if (sigacts_shared(p->p_sigacts)) { + PROC_UNLOCK(p); + newsigacts = sigacts_alloc(); + if (newsigacts == NULL) { + error = ENOMEM; + goto exec_fail_dealloc; + } + } else + PROC_UNLOCK(p); + /* * For security and other reasons, the file descriptor table cannot * be shared after an exec. */ - fdunshare(p, td); + fdpmem = fdprealloc(); + if (fdpmem == NULL) { + error = ENOMEM; + goto exec_fail_dealloc; + } + fdunshare(p, td, &fdpmem); /* * Malloc things before we need locks. */ newcred = crget(); euip = uifind(attr.va_uid); - i = imgp->args->begin_envv - imgp->args->begin_argv; - /* Cache arguments if they fit inside our allowance */ - if (ps_arg_cache_limit >= i + sizeof(struct pargs)) { - newargs = pargs_alloc(i); - bcopy(imgp->args->begin_argv, newargs->ar_args, i); - } /* close files on exec */ VOP_UNLOCK(imgp->vp, 0, td); @@ -506,10 +528,10 @@ interpret: if (sigacts_shared(p->p_sigacts)) { oldsigacts = p->p_sigacts; PROC_UNLOCK(p); - newsigacts = sigacts_alloc(); sigacts_copy(newsigacts, oldsigacts); PROC_LOCK(p); p->p_sigacts = newsigacts; + newsigacts = NULL; } else oldsigacts = NULL; @@ -749,8 +771,6 @@ done1: vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY, td); if (oldargs != NULL) pargs_drop(oldargs); - if (newargs != NULL) - pargs_drop(newargs); if (oldsigacts != NULL) sigacts_free(oldsigacts); @@ -759,6 +779,15 @@ exec_fail_dealloc: /* * free various allocated resources */ + if (newargs != NULL) + pargs_drop(newargs); + + if (fdpmem != NULL) + fdprealloc_free(fdpmem); + + if (newsigacts != NULL) + sigacts_free(newsigacts); + if (imgp->firstpage != NULL) exec_unmap_first_page(imgp); @@ -914,7 +943,9 @@ exec_new_vmspace(imgp, sv) pmap_remove_pages(vmspace_pmap(vmspace)); vm_map_remove(map, vm_map_min(map), vm_map_max(map)); } else { - vmspace_exec(p, sv->sv_minuser, sv->sv_maxuser); + error = vmspace_exec(p, sv->sv_minuser, sv->sv_maxuser); + if (error) + return (error); vmspace = p->p_vmspace; map = &vmspace->vm_map; } diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index 8d0eb49..3596ace 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -195,6 +195,8 @@ fork1(td, flags, pages, procp) struct filedesc_to_leader *fdtol; struct thread *td2; struct sigacts *newsigacts; + struct vmspace *vm2; + void *fdpmem1, *fdpmem2; int error; /* Can't copy and clear. */ @@ -218,14 +220,35 @@ fork1(td, flags, pages, procp) PROC_UNLOCK(p1); } - vm_forkproc(td, NULL, NULL, flags); + if (flags & RFCFDG) { + fdpmem1 = fdprealloc(); + if (fdpmem1 == NULL) { + error = ENOMEM; + goto norfproc_fail; + } + } else + fdpmem1 = NULL; + + if (flags & RFFDG) { + fdpmem2 = fdprealloc(); + if (fdpmem2 == NULL) { + error = ENOMEM; + goto norfproc_fail; + } + } else + fdpmem2 = NULL; + + error = vm_forkproc(td, NULL, NULL, NULL, flags); + if (error) + goto norfproc_fail; /* * Close all file descriptors. */ if (flags & RFCFDG) { struct filedesc *fdtmp; - fdtmp = fdinit(td->td_proc->p_fd); + fdtmp = fdinit(td->td_proc->p_fd, fdpmem1); + fdpmem1 = NULL; fdfree(td); p1->p_fd = fdtmp; } @@ -234,8 +257,13 @@ fork1(td, flags, pages, procp) * Unshare file descriptors (from parent). */ if (flags & RFFDG) - fdunshare(p1, td); + fdunshare(p1, td, &fdpmem2); +norfproc_fail: + if (fdpmem1 != NULL) + fdprealloc_free(fdpmem1); + if (fdpmem2 != NULL) + fdprealloc_free(fdpmem2); if ((p1->p_flag & P_HADTHREADS) && (flags & (RFCFDG | RFFDG))) { PROC_LOCK(p1); @@ -243,7 +271,7 @@ fork1(td, flags, pages, procp) PROC_UNLOCK(p1); } *procp = NULL; - return (0); + return (error); } /* @@ -274,8 +302,58 @@ fork1(td, flags, pages, procp) */ } + /* + * Malloc things while we don't hold any locks. + */ + if (flags & RFSIGSHARE) + newsigacts = NULL; + else { + newsigacts = sigacts_alloc(); + if (newsigacts == NULL) { + error = ENOMEM; + goto fail1; + } + } + if (flags & (RFCFDG | RFFDG)) { + fdpmem1 = fdprealloc(); + if (fdpmem1 == NULL) { + error = ENOMEM; + goto fail2; + } + } else + fdpmem1 = NULL; + /* Allocate new proc. */ - newproc = uma_zalloc(proc_zone, M_WAITOK); + newproc = uma_zalloc(proc_zone, M_WAITOK | M_FAILOK); + if (newproc == NULL) { + error = ENOMEM; + goto fail2; + } + if (TAILQ_EMPTY(&newproc->p_threads)) { + td2 = thread_alloc(); + if (td2 == NULL) { + error = ENOMEM; + goto fail3; + } + proc_linkup(newproc, td2); + sched_newproc(newproc, td2); + } else + td2 = FIRST_THREAD_IN_PROC(newproc); + /* Allocate and switch to an alternate kstack if specified. */ + if (pages != 0) { + if (!vm_thread_new_altkstack(td2, pages)) { + error = ENOMEM; + goto fail3; + } + } + if ((flags & RFMEM) == 0) { + vm2 = vmspace_fork(p1->p_vmspace); + if (vm2 == NULL) { + error = ENOMEM; + goto fail3; + } + } else + vm2 = NULL; #ifdef MAC mac_init_proc(newproc); #endif @@ -402,7 +480,6 @@ again: lastpid = trypid; p2 = newproc; - td2 = FIRST_THREAD_IN_PROC(newproc); p2->p_state = PRS_NEW; /* protect against others */ p2->p_pid = trypid; /* @@ -431,21 +508,15 @@ again: PROC_UNLOCK(p2); /* - * Malloc things while we don't hold any locks. - */ - if (flags & RFSIGSHARE) - newsigacts = NULL; - else - newsigacts = sigacts_alloc(); - - /* * Copy filedesc. */ if (flags & RFCFDG) { - fd = fdinit(p1->p_fd); + fd = fdinit(p1->p_fd, fdpmem1); + fdpmem1 = NULL; fdtol = NULL; } else if (flags & RFFDG) { - fd = fdcopy(p1->p_fd); + fd = fdcopy(p1->p_fd, fdpmem1); + fdpmem1 = NULL; fdtol = NULL; } else { fd = fdshare(p1->p_fd); @@ -478,9 +549,6 @@ again: * Start by zeroing the section of proc that is zero-initialized, * then copy the section that is copied directly from the parent. */ - /* Allocate and switch to an alternate kstack if specified. */ - if (pages != 0) - vm_thread_new_altkstack(td2, pages); PROC_LOCK(p2); PROC_LOCK(p1); @@ -653,7 +721,7 @@ again: * Finish creating the child process. It will return via a different * execution path later. (ie: directly into user mode) */ - vm_forkproc(td, p2, td2, flags); + vm_forkproc(td, p2, td2, vm2, flags); if (flags == (RFFDG | RFPROC)) { PCPU_INC(cnt.v_forks); @@ -745,7 +813,14 @@ fail: #ifdef MAC mac_destroy_proc(newproc); #endif +fail3: uma_zfree(proc_zone, newproc); +fail2: + if (fdpmem1 != NULL) + fdprealloc_free(fdpmem1); +fail1: + if (newsigacts != NULL) + sigacts_free(newsigacts); if (p1->p_flag & P_HADTHREADS) { PROC_LOCK(p1); thread_single_end(); diff --git a/sys/kern/kern_kse.c b/sys/kern/kern_kse.c index 63514c2..fa3bd7f 100644 --- a/sys/kern/kern_kse.c +++ b/sys/kern/kern_kse.c @@ -43,6 +43,7 @@ __FBSDID("$FreeBSD: src/sys/kern/kern_kse.c,v 1.234 2007/07/27 09:21:17 attilio #include #include #include +#include #include #include #include @@ -64,7 +65,7 @@ TAILQ_HEAD(, kse_upcall) zombie_upcalls = TAILQ_HEAD_INITIALIZER(zombie_upcalls); static int thread_update_usr_ticks(struct thread *td); -static void thread_alloc_spare(struct thread *td); +static int thread_alloc_spare(struct thread *td); static struct thread *thread_schedule_upcall(struct thread *td, struct kse_upcall *ku); static struct kse_upcall *upcall_alloc(void); @@ -648,6 +649,15 @@ kse_create(struct thread *td, struct kse_create_args *uap) PROC_UNLOCK(p); } + /* + * For the first call this may not have been set. + * Of course nor may it actually be needed. + * thread_schedule_upcall() will look for it. + */ + if (td->td_standin == NULL) { + if (!thread_alloc_spare(td)) + return (ENOMEM); + } /* * Even bound LWPs get a mailbox and an upcall to hold it. * XXX This should change. @@ -657,13 +667,6 @@ kse_create(struct thread *td, struct kse_create_args *uap) newku->ku_func = mbx.km_func; bcopy(&mbx.km_stack, &newku->ku_stack, sizeof(stack_t)); - /* - * For the first call this may not have been set. - * Of course nor may it actually be needed. - * thread_schedule_upcall() will look for it. - */ - if (td->td_standin == NULL) - thread_alloc_spare(td); PROC_LOCK(p); PROC_SLOCK(p); /* @@ -989,19 +992,22 @@ error: * XXX BUG.. we need to get the cr ref after the thread has * checked and chenged its own, not 6 months before... */ -void +int thread_alloc_spare(struct thread *td) { struct thread *spare; if (td->td_standin) - return; + return (1); spare = thread_alloc(); + if (spare == NULL) + return (0); td->td_standin = spare; bzero(&spare->td_startzero, __rangeof(struct thread, td_startzero, td_endzero)); spare->td_proc = td->td_proc; spare->td_ucred = crhold(td->td_ucred); + return (1); } /* @@ -1170,8 +1176,18 @@ thread_user_enter(struct thread *td) KASSERT(ku->ku_owner == td, ("wrong owner")); KASSERT(!TD_CAN_UNBIND(td), ("can unbind")); - if (td->td_standin == NULL) - thread_alloc_spare(td); + if (td->td_standin == NULL) { + if (!thread_alloc_spare(td)) { + PROC_LOCK(p); + if (kern_logsigexit) + log(LOG_INFO, + "pid %d (%s), uid %d: thread_alloc_spare failed\n", + p->p_pid, p->p_comm, + td->td_ucred ? td->td_ucred->cr_uid : -1); + sigexit(td, SIGSEGV); /* XXX ? */ + /* panic("thread_user_enter: thread_alloc_spare failed"); */ + } + } ku->ku_mflags = fuword32((void *)&ku->ku_mailbox->km_flags); tmbx = (void *)fuword((void *)&ku->ku_mailbox->km_curthread); if ((tmbx == NULL) || (tmbx == (void *)-1L) || @@ -1385,7 +1401,7 @@ out: * for when we re-enter the kernel. */ if (td->td_standin == NULL) - thread_alloc_spare(td); + thread_alloc_spare(td); /* XXX care of failure ? */ } ku->ku_mflags = 0; diff --git a/sys/kern/kern_malloc.c b/sys/kern/kern_malloc.c index 3d89243..eb48669 100644 --- a/sys/kern/kern_malloc.c +++ b/sys/kern/kern_malloc.c @@ -365,7 +365,8 @@ malloc(unsigned long size, struct malloc_type *mtp, int flags) malloc_type_allocated(mtp, va == NULL ? 0 : size); } if (flags & M_WAITOK) - KASSERT(va != NULL, ("malloc(M_WAITOK) returned NULL")); + KASSERT((flags & M_FAILOK) || va != NULL, + ("malloc(M_WAITOK) returned NULL")); else if (va == NULL) t_malloc_fail = time_uptime; #ifdef DIAGNOSTIC @@ -737,7 +738,9 @@ restart: mtx_unlock(&malloc_mtx); buflen = sizeof(mtsh) + count * (sizeof(mth) + sizeof(struct malloc_type_stats) * MAXCPU) + 1; - buffer = malloc(buflen, M_TEMP, M_WAITOK | M_ZERO); + buffer = malloc(buflen, M_TEMP, M_WAITOK | M_ZERO | M_FAILOK); + if (buffer == NULL) + return (ENOMEM); mtx_lock(&malloc_mtx); if (count < kmemcount) { free(buffer, M_TEMP); diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c index 10f5662..a5c95d9 100644 --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -145,20 +145,21 @@ proc_dtor(void *mem, int size, void *arg) /* INVARIANTS checks go here */ p = (struct proc *)mem; td = FIRST_THREAD_IN_PROC(p); + if (td != NULL) { #ifdef INVARIANTS - KASSERT((p->p_numthreads == 1), - ("bad number of threads in exiting process")); - KASSERT((td != NULL), ("proc_dtor: bad thread pointer")); - KASSERT(STAILQ_EMPTY(&p->p_ktr), ("proc_dtor: non-empty p_ktr")); + KASSERT((p->p_numthreads == 1), + ("bad number of threads in exiting process")); + KASSERT(STAILQ_EMPTY(&p->p_ktr), ("proc_dtor: non-empty p_ktr")); #endif - /* Dispose of an alternate kstack, if it exists. - * XXX What if there are more than one thread in the proc? - * The first thread in the proc is special and not - * freed, so you gotta do this here. - */ - if (((p->p_flag & P_KTHREAD) != 0) && (td->td_altkstack != 0)) - vm_thread_dispose_altkstack(td); + /* Dispose of an alternate kstack, if it exists. + * XXX What if there are more than one thread in the proc? + * The first thread in the proc is special and not + * freed, so you gotta do this here. + */ + if (((p->p_flag & P_KTHREAD) != 0) && (td->td_altkstack != 0)) + vm_thread_dispose_altkstack(td); + } if (p->p_ksi != NULL) KASSERT(! KSI_ONQ(p->p_ksi), ("SIGCHLD queue")); } @@ -170,17 +171,14 @@ static int proc_init(void *mem, int size, int flags) { struct proc *p; - struct thread *td; p = (struct proc *)mem; p->p_sched = (struct p_sched *)&p[1]; - td = thread_alloc(); bzero(&p->p_mtx, sizeof(struct mtx)); mtx_init(&p->p_mtx, "process lock", NULL, MTX_DEF | MTX_DUPOK); mtx_init(&p->p_slock, "process slock", NULL, MTX_SPIN | MTX_RECURSE); + TAILQ_INIT(&p->p_threads); /* all threads in proc */ p->p_stats = pstats_alloc(); - proc_linkup(p, td); - sched_newproc(p, td); return (0); } @@ -1118,7 +1116,9 @@ pargs_alloc(int len) struct pargs *pa; MALLOC(pa, struct pargs *, sizeof(struct pargs) + len, M_PARGS, - M_WAITOK); + M_WAITOK | M_FAILOK); + if (pa == NULL) + return (pa); refcount_init(&pa->ar_ref, 1); pa->ar_length = len; return (pa); @@ -1194,6 +1194,8 @@ sysctl_kern_proc_args(SYSCTL_HANDLER_ARGS) if (req->newlen + sizeof(struct pargs) > ps_arg_cache_limit) return (ENOMEM); newpa = pargs_alloc(req->newlen); + if (newpa == NULL) + return (ENOMEM); error = SYSCTL_IN(req, newpa->ar_args, req->newlen); if (error != 0) { pargs_free(newpa); diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c index 2f196cb..9eada8f 100644 --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -102,7 +102,7 @@ static uma_zone_t ksiginfo_zone = NULL; struct filterops sig_filtops = { 0, filt_sigattach, filt_sigdetach, filt_signal }; -static int kern_logsigexit = 1; +int kern_logsigexit = 1; SYSCTL_INT(_kern, KERN_LOGSIGEXIT, logsigexit, CTLFLAG_RW, &kern_logsigexit, 0, "Log processes quitting on abnormal signals to syslog(3)"); @@ -3253,7 +3253,10 @@ sigacts_alloc(void) { struct sigacts *ps; - ps = malloc(sizeof(struct sigacts), M_SUBPROC, M_WAITOK | M_ZERO); + ps = malloc(sizeof(struct sigacts), M_SUBPROC, M_WAITOK | M_ZERO | + M_FAILOK); + if (ps == NULL) + return (NULL); ps->ps_refcnt = 1; mtx_init(&ps->ps_mtx, "sigacts", NULL, MTX_DEF); return (ps); diff --git a/sys/kern/kern_thr.c b/sys/kern/kern_thr.c index 07602cd..bdd90c6 100644 --- a/sys/kern/kern_thr.c +++ b/sys/kern/kern_thr.c @@ -175,6 +175,8 @@ create_thread(struct thread *td, mcontext_t *ctx, /* Initialize our td */ newtd = thread_alloc(); + if (newtd == NULL) + return (ENOMEM); /* * Try the copyout as soon as we allocate the td so we don't diff --git a/sys/kern/kern_thread.c b/sys/kern/kern_thread.c index 348db77..2221309 100644 --- a/sys/kern/kern_thread.c +++ b/sys/kern/kern_thread.c @@ -114,23 +114,6 @@ thread_ctor(void *mem, int size, void *arg, int flags) struct thread *td; td = (struct thread *)mem; - td->td_state = TDS_INACTIVE; - td->td_oncpu = NOCPU; - - td->td_tid = alloc_unr(tid_unrhdr); - td->td_syscalls = 0; - - /* - * Note that td_critnest begins life as 1 because the thread is not - * running and is thereby implicitly waiting to be on the receiving - * end of a context switch. - */ - td->td_critnest = 1; - -#ifdef AUDIT - audit_thread_alloc(td); -#endif - umtx_thread_alloc(td); return (0); } @@ -140,35 +123,6 @@ thread_ctor(void *mem, int size, void *arg, int flags) static void thread_dtor(void *mem, int size, void *arg) { - struct thread *td; - - td = (struct thread *)mem; - -#ifdef INVARIANTS - /* Verify that this thread is in a safe state to free. */ - switch (td->td_state) { - case TDS_INHIBITED: - case TDS_RUNNING: - case TDS_CAN_RUN: - case TDS_RUNQ: - /* - * We must never unlink a thread that is in one of - * these states, because it is currently active. - */ - panic("bad state for thread unlinking"); - /* NOTREACHED */ - case TDS_INACTIVE: - break; - default: - panic("bad thread state"); - /* NOTREACHED */ - } -#endif -#ifdef AUDIT - audit_thread_free(td); -#endif - free_unr(tid_unrhdr, td->td_tid); - sched_newthread(td); } /* @@ -180,14 +134,7 @@ thread_init(void *mem, int size, int flags) struct thread *td; td = (struct thread *)mem; - - vm_thread_new(td, 0); - cpu_thread_setup(td); - td->td_sleepqueue = sleepq_alloc(); - td->td_turnstile = turnstile_alloc(); - td->td_sched = (struct td_sched *)&td[1]; - sched_newthread(td); - umtx_thread_init(td); + td->td_kstack = 0; return (0); } @@ -200,10 +147,11 @@ thread_fini(void *mem, int size) struct thread *td; td = (struct thread *)mem; - turnstile_free(td->td_turnstile); - sleepq_free(td->td_sleepqueue); - umtx_thread_fini(td); - vm_thread_dispose(td); + if (td->td_turnstile) { + turnstile_free(td->td_turnstile); + sleepq_free(td->td_sleepqueue); + umtx_thread_fini(td); + } } /* @@ -215,10 +163,16 @@ thread_fini(void *mem, int size) * proc_init() */ void +proc_linkup0(struct proc *p, struct thread *td) +{ + TAILQ_INIT(&p->p_threads); /* all threads in proc */ + proc_linkup(p, td); +} + +void proc_linkup(struct proc *p, struct thread *td) { - TAILQ_INIT(&p->p_threads); /* all threads in proc */ #ifdef KSE TAILQ_INIT(&p->p_upcalls); /* upcall list */ #endif @@ -246,6 +200,7 @@ threadinit(void) thread_zone = uma_zcreate("THREAD", sched_sizeof_thread(), thread_ctor, thread_dtor, thread_init, thread_fini, 16 - 1, 0); +/* uma_zone_set_max(thread_zone, XXX); */ #ifdef KSE kseinit(); /* set up kse specific stuff e.g. upcall zone*/ #endif @@ -310,9 +265,60 @@ thread_reap(void) struct thread * thread_alloc(void) { + struct thread *td; thread_reap(); /* check if any zombies to get */ - return (uma_zalloc(thread_zone, M_WAITOK)); + + td = (struct thread *)uma_zalloc(thread_zone, M_WAITOK | M_FAILOK); + if (td == NULL) + return (NULL); + KASSERT(td->td_kstack == 0, ("thread_alloc got initialized thread")); + if (!vm_thread_new(td, 0)) { + uma_zfree(thread_zone, td); + return (NULL); + } + cpu_thread_setup(td); + if (td->td_sleepqueue == NULL) { + td->td_sleepqueue = sleepq_alloc(); + if (td->td_sleepqueue == NULL) + goto fail1; + } + if (td->td_turnstile == NULL) { + td->td_turnstile = turnstile_alloc(); + if (td->td_turnstile == NULL) + goto fail2; + } + td->td_sched = (struct td_sched *)&td[1]; + if (!umtx_thread_init(td)) + goto fail3; + sched_newthread(td); + + td->td_state = TDS_INACTIVE; + td->td_oncpu = NOCPU; + + td->td_tid = alloc_unr(tid_unrhdr); + td->td_syscalls = 0; + + /* + * Note that td_critnest begins life as 1 because the thread is not + * running and is thereby implicitly waiting to be on the receiving + * end of a context switch. + */ + td->td_critnest = 1; + +#ifdef AUDIT + audit_thread_alloc(td); +#endif + umtx_thread_alloc(td); + + return (td); +fail3: + turnstile_free(td->td_turnstile); + td->td_turnstile = NULL; +fail2: + sleepq_free(td->td_sleepqueue); +fail1: + return (NULL); } @@ -324,6 +330,35 @@ thread_free(struct thread *td) { cpu_thread_clean(td); +#ifdef INVARIANTS + /* Verify that this thread is in a safe state to free. */ + switch (td->td_state) { + case TDS_INHIBITED: + case TDS_RUNNING: + case TDS_CAN_RUN: + case TDS_RUNQ: + /* + * We must never unlink a thread that is in one of + * these states, because it is currently active. + */ + panic("bad state for thread unlinking"); + /* NOTREACHED */ + case TDS_INACTIVE: + break; + default: + panic("bad thread state"); + /* NOTREACHED */ + } +#endif +#ifdef AUDIT + audit_thread_free(td); +#endif + free_unr(tid_unrhdr, td->td_tid); + sched_newthread(td); + if (td->td_altkstack != 0) + vm_thread_dispose_altkstack(td); + if (td->td_kstack != 0) + vm_thread_dispose(td); uma_zfree(thread_zone, td); } diff --git a/sys/kern/kern_umtx.c b/sys/kern/kern_umtx.c index 6229cd9..007d420 100644 --- a/sys/kern/kern_umtx.c +++ b/sys/kern/kern_umtx.c @@ -245,7 +245,8 @@ umtxq_alloc(void) { struct umtx_q *uq; - uq = malloc(sizeof(struct umtx_q), M_UMTX, M_WAITOK | M_ZERO); + uq = malloc(sizeof(struct umtx_q), M_UMTX, + M_WAITOK | M_FAILOK | M_ZERO); TAILQ_INIT(&uq->uq_pi_contested); uq->uq_inherited_pri = PRI_MAX; return (uq); @@ -2639,11 +2640,13 @@ freebsd32_umtx_op(struct thread *td, struct freebsd32_umtx_op_args *uap) } #endif -void +int umtx_thread_init(struct thread *td) { - td->td_umtxq = umtxq_alloc(); + if ((td->td_umtxq = umtxq_alloc()) == NULL) + return (0); td->td_umtxq->uq_thread = td; + return (1); } void diff --git a/sys/kern/subr_sleepqueue.c b/sys/kern/subr_sleepqueue.c index 88ad13e..ae62df3 100644 --- a/sys/kern/subr_sleepqueue.c +++ b/sys/kern/subr_sleepqueue.c @@ -207,7 +207,7 @@ struct sleepqueue * sleepq_alloc(void) { - return (uma_zalloc(sleepq_zone, M_WAITOK)); + return (uma_zalloc(sleepq_zone, M_WAITOK | M_FAILOK)); } /* diff --git a/sys/kern/subr_turnstile.c b/sys/kern/subr_turnstile.c index 9dd08b8..9788ded 100644 --- a/sys/kern/subr_turnstile.c +++ b/sys/kern/subr_turnstile.c @@ -511,7 +511,7 @@ struct turnstile * turnstile_alloc(void) { - return (uma_zalloc(turnstile_zone, M_WAITOK)); + return (uma_zalloc(turnstile_zone, M_WAITOK | M_FAILOK)); } /* diff --git a/sys/pc98/pc98/machdep.c b/sys/pc98/pc98/machdep.c index 1cf2cfa..9a8f7de 100644 --- a/sys/pc98/pc98/machdep.c +++ b/sys/pc98/pc98/machdep.c @@ -1917,7 +1917,7 @@ init386(first) * This may be done better later if it gets more high level * components in it. If so just link td->td_proc here. */ - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); /* * Initialize DMAC diff --git a/sys/powerpc/powerpc/machdep.c b/sys/powerpc/powerpc/machdep.c index 515539f..fcf4231 100644 --- a/sys/powerpc/powerpc/machdep.c +++ b/sys/powerpc/powerpc/machdep.c @@ -287,7 +287,7 @@ powerpc_init(u_int startkernel, u_int endkernel, u_int basekernel, void *mdp) /* * Start initializing proc0 and thread0. */ - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); thread0.td_frame = &frame0; /* diff --git a/sys/powerpc/powerpc/pmap_dispatch.c b/sys/powerpc/powerpc/pmap_dispatch.c index 37bff4a..a668e07 100644 --- a/sys/powerpc/powerpc/pmap_dispatch.c +++ b/sys/powerpc/powerpc/pmap_dispatch.c @@ -197,6 +197,7 @@ void pmap_pinit(pmap_t pmap) { MMU_PINIT(mmu_obj, pmap); + return (1); } void diff --git a/sys/sparc64/sparc64/machdep.c b/sys/sparc64/sparc64/machdep.c index 16bedb2..0e12ea5 100644 --- a/sys/sparc64/sparc64/machdep.c +++ b/sys/sparc64/sparc64/machdep.c @@ -399,7 +399,7 @@ sparc64_init(caddr_t mdp, u_long o1, u_long o2, u_long o3, ofw_vec_t *vec) /* * Initialize proc0 stuff (p_contested needs to be done early). */ - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); proc0.p_md.md_sigtramp = NULL; proc0.p_md.md_utrap = NULL; thread0.td_kstack = kstack0; diff --git a/sys/sparc64/sparc64/pmap.c b/sys/sparc64/sparc64/pmap.c index 6f27e3d..0cb16fb 100644 --- a/sys/sparc64/sparc64/pmap.c +++ b/sys/sparc64/sparc64/pmap.c @@ -1021,6 +1021,10 @@ pmap_pinit(pmap_t pm) if (pm->pm_tsb == NULL) { pm->pm_tsb = (struct tte *)kmem_alloc_nofault(kernel_map, TSB_BSIZE); + if (pm->pm_tsb == NULL) { + PMAP_LOCK_DESTROY(pm); + return (0); + } } /* @@ -1044,6 +1048,7 @@ pmap_pinit(pmap_t pm) pm->pm_context[i] = -1; pm->pm_active = 0; bzero(&pm->pm_stats, sizeof(pm->pm_stats)); + return (1); } /* diff --git a/sys/sun4v/sun4v/machdep.c b/sys/sun4v/sun4v/machdep.c index e184f2c..93b15c8 100644 --- a/sys/sun4v/sun4v/machdep.c +++ b/sys/sun4v/sun4v/machdep.c @@ -364,7 +364,7 @@ sparc64_init(caddr_t mdp, u_long o1, u_long o2, u_long o3, ofw_vec_t *vec) * Initialize proc0 stuff (p_contested needs to be done early). */ - proc_linkup(&proc0, &thread0); + proc_linkup0(&proc0, &thread0); proc0.p_md.md_sigtramp = NULL; proc0.p_md.md_utrap = NULL; frame0.tf_tstate = TSTATE_IE | TSTATE_PEF | TSTATE_PRIV; diff --git a/sys/sun4v/sun4v/pmap.c b/sys/sun4v/sun4v/pmap.c index 60f88c3..553c11b 100644 --- a/sys/sun4v/sun4v/pmap.c +++ b/sys/sun4v/sun4v/pmap.c @@ -1723,6 +1723,7 @@ pmap_pinit(pmap_t pmap) TAILQ_INIT(&pmap->pm_pvlist); PMAP_LOCK_INIT(pmap); bzero(&pmap->pm_stats, sizeof pmap->pm_stats); + return (1); } /* diff --git a/sys/sys/filedesc.h b/sys/sys/filedesc.h index 48db9d8..41e2092 100644 --- a/sys/sys/filedesc.h +++ b/sys/sys/filedesc.h @@ -117,10 +117,12 @@ int fdavail(struct thread *td, int n); int fdcheckstd(struct thread *td); void fdclose(struct filedesc *fdp, struct file *fp, int idx, struct thread *td); void fdcloseexec(struct thread *td); -struct filedesc *fdcopy(struct filedesc *fdp); -void fdunshare(struct proc *p, struct thread *td); +struct filedesc *fdcopy(struct filedesc *fdp, void *fdpmem); +void fdunshare(struct proc *p, struct thread *td, void **prealloc); void fdfree(struct thread *td); -struct filedesc *fdinit(struct filedesc *fdp); +struct filedesc *fdinit(struct filedesc *fdp, void *prealloc); +void * fdprealloc(void); +void fdprealloc_free(void *prealloc); struct filedesc *fdshare(struct filedesc *fdp); struct filedesc_to_leader * filedesc_to_leader_alloc(struct filedesc_to_leader *old, diff --git a/sys/sys/malloc.h b/sys/sys/malloc.h index 05e1381..21a7cf8 100644 --- a/sys/sys/malloc.h +++ b/sys/sys/malloc.h @@ -47,6 +47,8 @@ */ #define M_NOWAIT 0x0001 /* do not block */ #define M_WAITOK 0x0002 /* ok to block */ +#define M_FAILOK 0x0004 /* despite M_WAITOK, fail when + no addr space */ #define M_ZERO 0x0100 /* bzero the allocation */ #define M_NOVM 0x0200 /* don't ask VM for pages */ #define M_USE_RESERVE 0x0400 /* can alloc out of reserve memory */ diff --git a/sys/sys/proc.h b/sys/sys/proc.h index 138e43e..0d5d589 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -841,6 +841,7 @@ void pargs_drop(struct pargs *pa); void pargs_free(struct pargs *pa); void pargs_hold(struct pargs *pa); void procinit(void); +void proc_linkup0(struct proc *p, struct thread *td); void proc_linkup(struct proc *p, struct thread *td); void proc_reparent(struct proc *child, struct proc *newparent); struct pstats *pstats_alloc(void); diff --git a/sys/sys/signalvar.h b/sys/sys/signalvar.h index f955dc7..5a81b56 100644 --- a/sys/sys/signalvar.h +++ b/sys/sys/signalvar.h @@ -299,6 +299,7 @@ struct mtx; extern int sugid_coredump; /* Sysctl variable kern.sugid_coredump */ extern struct mtx sigio_lock; +extern int kern_logsigexit; /* Sysctl variable kern.logsigexit */ /* * Lock the pointers for a sigio object in the underlying objects of diff --git a/sys/sys/umtx.h b/sys/sys/umtx.h index c4f6029..75a24d8 100644 --- a/sys/sys/umtx.h +++ b/sys/sys/umtx.h @@ -171,7 +171,7 @@ struct umtx_q *umtxq_alloc(void); void umtxq_free(struct umtx_q *); int kern_umtx_wake(struct thread *td, void *uaddr, int n_wake); void umtx_pi_adjust(struct thread *td, u_char oldpri); -void umtx_thread_init(struct thread *td); +int umtx_thread_init(struct thread *td); void umtx_thread_fini(struct thread *td); void umtx_thread_alloc(struct thread *td); void umtx_thread_exit(struct thread *td); diff --git a/sys/vm/pmap.h b/sys/vm/pmap.h index d05e85e..3a53726 100644 --- a/sys/vm/pmap.h +++ b/sys/vm/pmap.h @@ -114,7 +114,7 @@ void pmap_object_init_pt(pmap_t pmap, vm_offset_t addr, vm_object_t object, vm_pindex_t pindex, vm_size_t size); boolean_t pmap_page_exists_quick(pmap_t pmap, vm_page_t m); void pmap_page_init(vm_page_t m); -void pmap_pinit(pmap_t); +int pmap_pinit(pmap_t); void pmap_pinit0(pmap_t); void pmap_protect(pmap_t, vm_offset_t, vm_offset_t, vm_prot_t); void pmap_qenter(vm_offset_t, vm_page_t *, int); diff --git a/sys/vm/uma_core.c b/sys/vm/uma_core.c index c192106..78d102a 100644 --- a/sys/vm/uma_core.c +++ b/sys/vm/uma_core.c @@ -2870,7 +2870,9 @@ restart: buflen = sizeof(ush) + count * (sizeof(uth) + sizeof(ups) * (mp_maxid + 1)) + 1; - buffer = malloc(buflen, M_TEMP, M_WAITOK | M_ZERO); + buffer = malloc(buflen, M_TEMP, M_WAITOK | M_ZERO | M_FAILOK); + if (buffer == NULL) + return (ENOMEM); mtx_lock(&uma_mtx); i = 0; diff --git a/sys/vm/vm_extern.h b/sys/vm/vm_extern.h index 6c310bd..380101a 100644 --- a/sys/vm/vm_extern.h +++ b/sys/vm/vm_extern.h @@ -70,14 +70,14 @@ int vm_fault(vm_map_t, vm_offset_t, vm_prot_t, int); void vm_fault_copy_entry(vm_map_t, vm_map_t, vm_map_entry_t, vm_map_entry_t); void vm_fault_unwire(vm_map_t, vm_offset_t, vm_offset_t, boolean_t); int vm_fault_wire(vm_map_t, vm_offset_t, vm_offset_t, boolean_t, boolean_t); -void vm_forkproc(struct thread *, struct proc *, struct thread *, int); +int vm_forkproc(struct thread *, struct proc *, struct thread *, struct vmspace *, int); void vm_waitproc(struct proc *); int vm_mmap(vm_map_t, vm_offset_t *, vm_size_t, vm_prot_t, vm_prot_t, int, objtype_t, void *, vm_ooffset_t); void vm_set_page_size(void); struct vmspace *vmspace_alloc(vm_offset_t, vm_offset_t); struct vmspace *vmspace_fork(struct vmspace *); -void vmspace_exec(struct proc *, vm_offset_t, vm_offset_t); -void vmspace_unshare(struct proc *); +int vmspace_exec(struct proc *, vm_offset_t, vm_offset_t); +int vmspace_unshare(struct proc *); void vmspace_exit(struct thread *); struct vmspace *vmspace_acquire_ref(struct proc *); void vmspace_free(struct vmspace *); @@ -92,8 +92,8 @@ struct sf_buf *vm_imgact_map_page(vm_object_t object, vm_ooffset_t offset); void vm_imgact_unmap_page(struct sf_buf *sf); void vm_thread_dispose(struct thread *td); void vm_thread_dispose_altkstack(struct thread *td); -void vm_thread_new(struct thread *td, int pages); -void vm_thread_new_altkstack(struct thread *td, int pages); +int vm_thread_new(struct thread *td, int pages); +int vm_thread_new_altkstack(struct thread *td, int pages); void vm_thread_swapin(struct thread *td); void vm_thread_swapout(struct thread *td); #endif /* _KERNEL */ diff --git a/sys/vm/vm_glue.c b/sys/vm/vm_glue.c index ee0a019..7f84931 100644 --- a/sys/vm/vm_glue.c +++ b/sys/vm/vm_glue.c @@ -315,12 +315,16 @@ vm_imgact_unmap_page(struct sf_buf *sf) #define KSTACK_MAX_PAGES 32 #endif +static int thread_kstack_pages; +SYSCTL_INT(_vm, OID_AUTO, thread_kstack_pages, + CTLFLAG_RW, &thread_kstack_pages, 0, "Pages reserved for thread kernel stacks"); + /* * Create the kernel stack (including pcb for i386) for a new thread. * This routine directly affects the fork perf for a process and * create performance for a thread. */ -void +int vm_thread_new(struct thread *td, int pages) { vm_object_t ksobj; @@ -337,18 +341,23 @@ vm_thread_new(struct thread *td, int pages) * Allocate an object for the kstack. */ ksobj = vm_object_allocate(OBJT_DEFAULT, pages); - td->td_kstack_obj = ksobj; /* * Get a kernel virtual address for this thread's kstack. */ ks = kmem_alloc_nofault(kernel_map, (pages + KSTACK_GUARD_PAGES) * PAGE_SIZE); - if (ks == 0) - panic("vm_thread_new: kstack allocation failed"); + if (ks == 0) { + printf("vm_thread_new: kstack allocation failed\n"); + vm_object_deallocate(ksobj); + return (0); + } + thread_kstack_pages += pages + KSTACK_GUARD_PAGES; + if (KSTACK_GUARD_PAGES != 0) { pmap_qremove(ks, KSTACK_GUARD_PAGES); ks += KSTACK_GUARD_PAGES * PAGE_SIZE; } + td->td_kstack_obj = ksobj; td->td_kstack = ks; /* * Knowing the number of pages allocated is useful when you @@ -371,6 +380,7 @@ vm_thread_new(struct thread *td, int pages) } VM_OBJECT_UNLOCK(ksobj); pmap_qenter(ks, ma, pages); + return (1); } /* @@ -402,6 +412,8 @@ vm_thread_dispose(struct thread *td) vm_object_deallocate(ksobj); kmem_free(kernel_map, ks - (KSTACK_GUARD_PAGES * PAGE_SIZE), (pages + KSTACK_GUARD_PAGES) * PAGE_SIZE); + thread_kstack_pages -= pages + KSTACK_GUARD_PAGES; + td->td_kstack = 0; } /* @@ -467,7 +479,7 @@ vm_thread_swapin(struct thread *td) /* * Set up a variable-sized alternate kstack. */ -void +int vm_thread_new_altkstack(struct thread *td, int pages) { @@ -475,7 +487,7 @@ vm_thread_new_altkstack(struct thread *td, int pages) td->td_altkstack_obj = td->td_kstack_obj; td->td_altkstack_pages = td->td_kstack_pages; - vm_thread_new(td, pages); + return (vm_thread_new(td, pages)); } /* @@ -503,14 +515,16 @@ vm_thread_dispose_altkstack(struct thread *td) * ready to run. The new process is set up so that it returns directly * to user mode to avoid stack copying and relocation problems. */ -void -vm_forkproc(td, p2, td2, flags) +int +vm_forkproc(td, p2, td2, vm2, flags) struct thread *td; struct proc *p2; struct thread *td2; + struct vmspace *vm2; int flags; { struct proc *p1 = td->td_proc; + int error; if ((flags & RFPROC) == 0) { /* @@ -520,11 +534,13 @@ vm_forkproc(td, p2, td2, flags) */ if ((flags & RFMEM) == 0) { if (p1->p_vmspace->vm_refcnt > 1) { - vmspace_unshare(p1); + error = vmspace_unshare(p1); + if (error) + return (error); } } cpu_fork(td, p2, td2, flags); - return; + return (0); } if (flags & RFMEM) { @@ -537,7 +553,7 @@ vm_forkproc(td, p2, td2, flags) } if ((flags & RFMEM) == 0) { - p2->p_vmspace = vmspace_fork(p1->p_vmspace); + p2->p_vmspace = vm2; if (p1->p_vmspace->vm_shm) shmfork(p1, p2); } @@ -547,6 +563,7 @@ vm_forkproc(td, p2, td2, flags) * and make the child ready to run. */ cpu_fork(td, p2, td2, flags); + return (0); } /* diff --git a/sys/vm/vm_kern.c b/sys/vm/vm_kern.c index 116ae9b..a08444b 100644 --- a/sys/vm/vm_kern.c +++ b/sys/vm/vm_kern.c @@ -302,8 +302,12 @@ kmem_malloc(map, size, flags) vm_map_lock(map); if (vm_map_findspace(map, vm_map_min(map), size, &addr)) { vm_map_unlock(map); - panic("kmem_malloc(%ld): kmem_map too small: %ld total allocated", - (long)size, (long)map->size); + if (flags & M_FAILOK) + return (0); + else + panic("kmem_malloc(%ld): kmem_map too " + "small: %ld total allocated", + (long)size, (long)map->size); } } else { return (0); diff --git a/sys/vm/vm_map.c b/sys/vm/vm_map.c index fdff905..5f0a035 100644 --- a/sys/vm/vm_map.c +++ b/sys/vm/vm_map.c @@ -197,7 +197,6 @@ vmspace_zfini(void *mem, int size) struct vmspace *vm; vm = (struct vmspace *)mem; - pmap_release(vmspace_pmap(vm)); vm_map_zfini(&vm->vm_map, sizeof(vm->vm_map)); } @@ -208,8 +207,8 @@ vmspace_zinit(void *mem, int size, int flags) vm = (struct vmspace *)mem; + vm->vm_map.pmap = NULL; (void)vm_map_zinit(&vm->vm_map, sizeof(vm->vm_map), flags); - pmap_pinit(vmspace_pmap(vm)); return (0); } @@ -272,6 +271,10 @@ vmspace_alloc(min, max) struct vmspace *vm; vm = uma_zalloc(vmspace_zone, M_WAITOK); + if (vm->vm_map.pmap == NULL && !pmap_pinit(vmspace_pmap(vm))) { + uma_zfree(vmspace_zone, vm); + return (NULL); + } CTR1(KTR_VM, "vmspace_alloc: %p", vm); _vm_map_init(&vm->vm_map, min, max); vm->vm_map.pmap = vmspace_pmap(vm); /* XXX */ @@ -321,6 +324,12 @@ vmspace_dofree(struct vmspace *vm) (void)vm_map_remove(&vm->vm_map, vm->vm_map.min_offset, vm->vm_map.max_offset); + /* + * XXX Comment out the pmap_release call for now. The + * vmspace_zone is marked as UMA_ZONE_NOFREE, and bugs cause + * pmap.resident_count to be != 0 on exit sometimes. + */ +/* pmap_release(vmspace_pmap(vm)); */ uma_zfree(vmspace_zone, vm); } @@ -2586,6 +2595,8 @@ vmspace_fork(struct vmspace *vm1) vm_map_lock(old_map); vm2 = vmspace_alloc(old_map->min_offset, old_map->max_offset); + if (vm2 == NULL) + goto unlock_and_return; vm2->vm_taddr = vm1->vm_taddr; vm2->vm_daddr = vm1->vm_daddr; vm2->vm_maxsaddr = vm1->vm_maxsaddr; @@ -2677,7 +2688,7 @@ vmspace_fork(struct vmspace *vm1) } old_entry = old_entry->next; } - +unlock_and_return: vm_map_unlock(old_map); return (vm2); @@ -3005,13 +3016,15 @@ Retry: * Unshare the specified VM space for exec. If other processes are * mapped to it, then create a new one. The new vmspace is null. */ -void +int vmspace_exec(struct proc *p, vm_offset_t minuser, vm_offset_t maxuser) { struct vmspace *oldvmspace = p->p_vmspace; struct vmspace *newvmspace; newvmspace = vmspace_alloc(minuser, maxuser); + if (newvmspace == NULL) + return (ENOMEM); newvmspace->vm_swrss = oldvmspace->vm_swrss; /* * This code is written like this for prototype purposes. The @@ -3026,27 +3039,31 @@ vmspace_exec(struct proc *p, vm_offset_t minuser, vm_offset_t maxuser) if (p == curthread->td_proc) /* XXXKSE ? */ pmap_activate(curthread); vmspace_free(oldvmspace); + return (0); } /* * Unshare the specified VM space for forcing COW. This * is called by rfork, for the (RFMEM|RFPROC) == 0 case. */ -void +int vmspace_unshare(struct proc *p) { struct vmspace *oldvmspace = p->p_vmspace; struct vmspace *newvmspace; if (oldvmspace->vm_refcnt == 1) - return; + return (0); newvmspace = vmspace_fork(oldvmspace); + if (newvmspace == NULL) + return (ENOMEM); PROC_VMSPACE_LOCK(p); p->p_vmspace = newvmspace; PROC_VMSPACE_UNLOCK(p); if (p == curthread->td_proc) /* XXXKSE ? */ pmap_activate(curthread); vmspace_free(oldvmspace); + return (0); } /*