diff options
Diffstat (limited to 'sys/kern/kern_descrip.c')
-rw-r--r-- | sys/kern/kern_descrip.c | 243 |
1 files changed, 147 insertions, 96 deletions
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index 571e24a4946..77d7d8fdc6a 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_descrip.c,v 1.38 2001/10/07 23:12:06 art Exp $ */ +/* $OpenBSD: kern_descrip.c,v 1.39 2001/10/26 10:39:31 art Exp $ */ /* $NetBSD: kern_descrip.c,v 1.42 1996/03/30 22:24:38 christos Exp $ */ /* @@ -205,10 +205,16 @@ sys_dup(p, v, retval) int new; int error; +restart: if ((u_int)old >= fdp->fd_nfiles || fdp->fd_ofiles[old] == NULL) return (EBADF); - if ((error = fdalloc(p, 0, &new)) != 0) + if ((error = fdalloc(p, 0, &new)) != 0) { + if (error == ENOSPC) { + fdexpand(p); + goto restart; + } return (error); + } return (finishdup(fdp, old, new, retval)); } @@ -230,6 +236,7 @@ sys_dup2(p, v, retval) int old = SCARG(uap, from), new = SCARG(uap, to); int i, error; +restart: if ((u_int)old >= fdp->fd_nfiles || fdp->fd_ofiles[old] == NULL || (u_int)new >= p->p_rlimit[RLIMIT_NOFILE].rlim_cur || (u_int)new >= maxfiles) @@ -239,12 +246,15 @@ sys_dup2(p, v, retval) return (0); } if (new >= fdp->fd_nfiles) { - if ((error = fdalloc(p, new, &i)) != 0) + if ((error = fdalloc(p, new, &i)) != 0) { + if (error == ENOSPC) { + fdexpand(p); + goto restart; + } return (error); + } if (new != i) panic("dup2: fdalloc"); - } else { - (void) fdrelease(p, new); } return (finishdup(fdp, old, new, retval)); } @@ -272,6 +282,7 @@ sys_fcntl(p, v, retval) struct flock fl; int newmin; +restart: if ((u_int)fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL) return (EBADF); @@ -282,8 +293,13 @@ sys_fcntl(p, v, retval) if ((u_int)newmin >= p->p_rlimit[RLIMIT_NOFILE].rlim_cur || (u_int)newmin >= maxfiles) return (EINVAL); - if ((error = fdalloc(p, newmin, &i)) != 0) + if ((error = fdalloc(p, newmin, &i)) != 0) { + if (error == ENOSPC) { + fdexpand(p); + goto restart; + } return (error); + } return (finishdup(fdp, fd, i, retval)); case F_GETFD: @@ -426,6 +442,8 @@ sys_fcntl(p, v, retval) /* * Common code for dup, dup2, and fcntl(F_DUPFD). + * + * XXX - should take a proc as an argument. */ int finishdup(fdp, old, new, retval) @@ -433,16 +451,26 @@ finishdup(fdp, old, new, retval) register int old, new; register_t *retval; { - register struct file *fp; + struct file *fp, *oldfp; + + oldfp = fdp->fd_ofiles[new]; fp = fdp->fd_ofiles[old]; if (fp->f_count == LONG_MAX-2) return (EDEADLK); fdp->fd_ofiles[new] = fp; - fdp->fd_ofileflags[new] = fdp->fd_ofileflags[old] &~ UF_EXCLOSE; + fdp->fd_ofileflags[new] = fdp->fd_ofileflags[old] & ~UF_EXCLOSE; fp->f_count++; - fd_used(fdp, new); + if (oldfp == NULL) + fd_used(fdp, new); *retval = new; + + if (oldfp != NULL) { + if (new < fdp->fd_knlistsize) + knote_fdclose(curproc, new); + closef(oldfp, curproc); + } + return (0); } @@ -574,110 +602,126 @@ sys_fpathconf(p, v, retval) /* * Allocate a file descriptor for the process. */ -int fdexpand; - int fdalloc(p, want, result) struct proc *p; int want; int *result; { - register struct filedesc *fdp = p->p_fd; - register int i; - int lim, last, nfiles; - struct file **newofile; - char *newofileflags; - u_int *newhimap, *newlomap, new, off; + struct filedesc *fdp = p->p_fd; + int lim, last, i; + u_int new, off; /* * Search for a free descriptor starting at the higher * of want or fd_freefile. If that fails, consider * expanding the ofile array. */ +restart: lim = min((int)p->p_rlimit[RLIMIT_NOFILE].rlim_cur, maxfiles); - for (;;) { - last = min(fdp->fd_nfiles, lim); - if ((i = want) < fdp->fd_freefile) - i = fdp->fd_freefile; - off = i >> NDENTRYSHIFT; - new = find_next_zero(fdp->fd_himap, off, - (last + NDENTRIES - 1) >> NDENTRYSHIFT); - if (new != -1) { - i = find_next_zero(&fdp->fd_lomap[new], - new > off ? 0 : i & NDENTRYMASK, - NDENTRIES); - if (i == -1) { - /* free file descriptor in this block was - * below want, try again with higher want. - */ - want = (new + 1) << NDENTRYSHIFT; - continue; - } - i += (new << NDENTRYSHIFT); - if (i < last) { - fd_used(fdp, i); - if (want <= fdp->fd_freefile) - fdp->fd_freefile = i; - *result = i; - return (0); - } + last = min(fdp->fd_nfiles, lim); + if ((i = want) < fdp->fd_freefile) + i = fdp->fd_freefile; + off = i >> NDENTRYSHIFT; + new = find_next_zero(fdp->fd_himap, off, + (last + NDENTRIES - 1) >> NDENTRYSHIFT); + if (new != -1) { + i = find_next_zero(&fdp->fd_lomap[new], + new > off ? 0 : i & NDENTRYMASK, + NDENTRIES); + if (i == -1) { + /* + * Free file descriptor in this block was + * below want, try again with higher want. + */ + want = (new + 1) << NDENTRYSHIFT; + goto restart; + } + i += (new << NDENTRYSHIFT); + if (i < last) { + fd_used(fdp, i); + if (want <= fdp->fd_freefile) + fdp->fd_freefile = i; + *result = i; + return (0); } + } + if (fdp->fd_nfiles >= lim) + return (EMFILE); - /* - * No space in current array. Expand? - */ - if (fdp->fd_nfiles >= lim) - return (EMFILE); - if (fdp->fd_nfiles < NDEXTENT) - nfiles = NDEXTENT; - else - nfiles = 2 * fdp->fd_nfiles; - nfiles = min(lim, nfiles); - newofile = malloc(nfiles * OFILESIZE, M_FILEDESC, M_WAITOK); - newofileflags = (char *) &newofile[nfiles]; + return (ENOSPC); +} - /* - * Copy the existing ofile and ofileflags arrays - * and zero the new portion of each array. - */ - bcopy(fdp->fd_ofiles, newofile, - (i = sizeof(struct file *) * fdp->fd_nfiles)); - bzero((char *)newofile + i, nfiles * sizeof(struct file *) - i); - bcopy(fdp->fd_ofileflags, newofileflags, - (i = sizeof(char) * fdp->fd_nfiles)); - bzero(newofileflags + i, nfiles * sizeof(char) - i); - - if (fdp->fd_nfiles > NDFILE) - free(fdp->fd_ofiles, M_FILEDESC); - - if (NDHISLOTS(nfiles) > NDHISLOTS(fdp->fd_nfiles)) { - newhimap = malloc(NDHISLOTS(nfiles) * sizeof(u_int), - M_FILEDESC, M_WAITOK); - newlomap = malloc( NDLOSLOTS(nfiles) * sizeof(u_int), - M_FILEDESC, M_WAITOK); - - bcopy(fdp->fd_himap, newhimap, - (i = NDHISLOTS(fdp->fd_nfiles) * sizeof(u_int))); - bzero((char *)newhimap + i, - NDHISLOTS(nfiles) * sizeof(u_int) - i); - - bcopy(fdp->fd_lomap, newlomap, - (i = NDLOSLOTS(fdp->fd_nfiles) * sizeof(u_int))); - bzero((char *)newlomap + i, - NDLOSLOTS(nfiles) * sizeof(u_int) - i); - - if (NDHISLOTS(fdp->fd_nfiles) > NDHISLOTS(NDFILE)) { - free(fdp->fd_himap, M_FILEDESC); - free(fdp->fd_lomap, M_FILEDESC); - } - fdp->fd_himap = newhimap; - fdp->fd_lomap = newlomap; +void +fdexpand(p) + struct proc *p; +{ + struct filedesc *fdp = p->p_fd; + int nfiles, i; + struct file **newofile; + char *newofileflags; + u_int *newhimap, *newlomap; + + /* + * If it's already expanding just wait for that expansion to finish + * and return and let the caller retry the operation. + */ + if (lockmgr(&fdp->fd_lock, LK_EXCLUSIVE|LK_SLEEPFAIL, NULL, p)) + return; + + /* + * No space in current array. + */ + if (fdp->fd_nfiles < NDEXTENT) + nfiles = NDEXTENT; + else + nfiles = 2 * fdp->fd_nfiles; + + newofile = malloc(nfiles * OFILESIZE, M_FILEDESC, M_WAITOK); + newofileflags = (char *) &newofile[nfiles]; + + /* + * Copy the existing ofile and ofileflags arrays + * and zero the new portion of each array. + */ + bcopy(fdp->fd_ofiles, newofile, + (i = sizeof(struct file *) * fdp->fd_nfiles)); + bzero((char *)newofile + i, nfiles * sizeof(struct file *) - i); + bcopy(fdp->fd_ofileflags, newofileflags, + (i = sizeof(char) * fdp->fd_nfiles)); + bzero(newofileflags + i, nfiles * sizeof(char) - i); + + if (fdp->fd_nfiles > NDFILE) + free(fdp->fd_ofiles, M_FILEDESC); + + if (NDHISLOTS(nfiles) > NDHISLOTS(fdp->fd_nfiles)) { + newhimap = malloc(NDHISLOTS(nfiles) * sizeof(u_int), + M_FILEDESC, M_WAITOK); + newlomap = malloc(NDLOSLOTS(nfiles) * sizeof(u_int), + M_FILEDESC, M_WAITOK); + + bcopy(fdp->fd_himap, newhimap, + (i = NDHISLOTS(fdp->fd_nfiles) * sizeof(u_int))); + bzero((char *)newhimap + i, + NDHISLOTS(nfiles) * sizeof(u_int) - i); + + bcopy(fdp->fd_lomap, newlomap, + (i = NDLOSLOTS(fdp->fd_nfiles) * sizeof(u_int))); + bzero((char *)newlomap + i, + NDLOSLOTS(nfiles) * sizeof(u_int) - i); + + if (NDHISLOTS(fdp->fd_nfiles) > NDHISLOTS(NDFILE)) { + free(fdp->fd_himap, M_FILEDESC); + free(fdp->fd_lomap, M_FILEDESC); } - fdp->fd_ofiles = newofile; - fdp->fd_ofileflags = newofileflags; - fdp->fd_nfiles = nfiles; - fdexpand++; + fdp->fd_himap = newhimap; + fdp->fd_lomap = newlomap; } + fdp->fd_ofiles = newofile; + fdp->fd_ofileflags = newofileflags; + fdp->fd_nfiles = nfiles; + + lockmgr(&fdp->fd_lock, LK_RELEASE, NULL, p); } /* @@ -716,8 +760,14 @@ falloc(p, resultfp, resultfd) register struct file *fp, *fq; int error, i; - if ((error = fdalloc(p, 0, &i)) != 0) +restart: + if ((error = fdalloc(p, 0, &i)) != 0) { + if (error == ENOSPC) { + fdexpand(p); + goto restart; + } return (error); + } if (nfiles >= maxfiles) { fd_unused(p->p_fd, i); tablefull("file"); @@ -782,6 +832,7 @@ fdinit(p) newfdp->fd_fd.fd_rdir = fdp->fd_rdir; if (newfdp->fd_fd.fd_rdir) VREF(newfdp->fd_fd.fd_rdir); + lockinit(&newfdp->fd_fd.fd_lock, PLOCK, "fdexpand", 0, 0); /* Create the file descriptor table. */ newfdp->fd_fd.fd_refcnt = 1; |