mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-15 10:17:20 +00:00
New file descriptor allocation code, derived from similar code introduced
in OpenBSD by Niels Provos. The patch introduces a bitmap of allocated file descriptors which is used to locate available descriptors when a new one is needed. It also moves the task of growing the file descriptor table out of fdalloc(), reducing complexity in both fdalloc() and do_dup(). Debts of gratitude are owed to tjr@ (who provided the original patch on which this work is based), grog@ (for the gdb(4) man page) and rwatson@ (for assistance with pxeboot(8)).
This commit is contained in:
parent
0161ff2670
commit
a2fe44e8cf
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=124548
@ -406,6 +406,7 @@ proc0_init(void *dummy __unused)
|
||||
siginit(&proc0);
|
||||
|
||||
/* Create the file descriptor table. */
|
||||
/* XXX this duplicates part of fdinit() */
|
||||
fdp = &filedesc0;
|
||||
p->p_fd = &fdp->fd_fd;
|
||||
p->p_fdtol = NULL;
|
||||
@ -415,6 +416,7 @@ proc0_init(void *dummy __unused)
|
||||
fdp->fd_fd.fd_ofiles = fdp->fd_dfiles;
|
||||
fdp->fd_fd.fd_ofileflags = fdp->fd_dfileflags;
|
||||
fdp->fd_fd.fd_nfiles = NDFILE;
|
||||
fdp->fd_fd.fd_map = fdp->fd_dmap;
|
||||
|
||||
/* Create the limits structures. */
|
||||
p->p_limit = &limit0;
|
||||
|
@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include "opt_compat.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/limits.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/syscallsubr.h>
|
||||
#include <sys/sysproto.h>
|
||||
@ -96,6 +97,9 @@ enum dup_type { DUP_VARIABLE, DUP_FIXED };
|
||||
|
||||
static int do_dup(struct thread *td, enum dup_type type, int old, int new,
|
||||
register_t *retval);
|
||||
static int fd_first_free(struct filedesc *, int, int);
|
||||
static int fd_last_used(struct filedesc *, int, int);
|
||||
static void fdgrowtable(struct filedesc *, int);
|
||||
|
||||
/*
|
||||
* Descriptor management.
|
||||
@ -105,6 +109,102 @@ int nfiles; /* actual number of open files */
|
||||
struct sx filelist_lock; /* sx to protect filelist */
|
||||
struct mtx sigio_lock; /* mtx to protect pointers to sigio */
|
||||
|
||||
/*
|
||||
* Find the first zero bit in the given bitmap, starting at low and not
|
||||
* exceeding size - 1.
|
||||
*/
|
||||
static int
|
||||
fd_first_free(struct filedesc *fdp, int low, int size)
|
||||
{
|
||||
NDSLOTTYPE *map = fdp->fd_map;
|
||||
NDSLOTTYPE mask;
|
||||
int off, maxoff;
|
||||
|
||||
if (low >= size)
|
||||
return (low);
|
||||
|
||||
off = NDSLOT(low);
|
||||
if (low % NDENTRIES) {
|
||||
mask = ~(~(NDSLOTTYPE)0 >> (NDENTRIES - (low % NDENTRIES)));
|
||||
if ((mask &= ~map[off]) != 0)
|
||||
return (off * NDENTRIES + ffsl(mask) - 1);
|
||||
++off;
|
||||
}
|
||||
for (maxoff = NDSLOTS(size); off < maxoff; ++off)
|
||||
if ((mask = ~map[off]) != 0)
|
||||
return (off * NDENTRIES + ffsl(mask) - 1);
|
||||
return (size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the highest non-zero bit in the given bitmap, starting at low and
|
||||
* not exceeding size - 1.
|
||||
*/
|
||||
static int
|
||||
fd_last_used(struct filedesc *fdp, int low, int size)
|
||||
{
|
||||
NDSLOTTYPE *map = fdp->fd_map;
|
||||
NDSLOTTYPE mask;
|
||||
int off, minoff;
|
||||
|
||||
if (low >= size)
|
||||
return (-1);
|
||||
|
||||
off = NDSLOT(size);
|
||||
if (size % NDENTRIES) {
|
||||
mask = ~(~(NDSLOTTYPE)0 << (size % NDENTRIES));
|
||||
if ((mask &= map[off]) != 0)
|
||||
return (off * NDENTRIES + flsl(mask) - 1);
|
||||
--off;
|
||||
}
|
||||
for (minoff = NDSLOT(low); off >= minoff; --off)
|
||||
if ((mask = map[off]) != 0)
|
||||
return (off * NDENTRIES + flsl(mask) - 1);
|
||||
return (size - 1);
|
||||
}
|
||||
|
||||
static int
|
||||
fdisused(struct filedesc *fdp, int fd)
|
||||
{
|
||||
KASSERT(fd >= 0 && fd < fdp->fd_nfiles,
|
||||
("file descriptor %d out of range (0, %d)", fd, fdp->fd_nfiles));
|
||||
return ((fdp->fd_map[NDSLOT(fd)] & NDBIT(fd)) != 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark a file descriptor as used.
|
||||
*/
|
||||
void
|
||||
fdused(struct filedesc *fdp, int fd)
|
||||
{
|
||||
FILEDESC_LOCK_ASSERT(fdp, MA_OWNED);
|
||||
KASSERT(!fdisused(fdp, fd),
|
||||
("fd already used"));
|
||||
fdp->fd_map[NDSLOT(fd)] |= NDBIT(fd);
|
||||
if (fd > fdp->fd_lastfile)
|
||||
fdp->fd_lastfile = fd;
|
||||
if (fd == fdp->fd_freefile)
|
||||
fdp->fd_freefile = fd_first_free(fdp, fd, fdp->fd_nfiles);
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark a file descriptor as unused.
|
||||
*/
|
||||
void
|
||||
fdunused(struct filedesc *fdp, int fd)
|
||||
{
|
||||
FILEDESC_LOCK_ASSERT(fdp, MA_OWNED);
|
||||
KASSERT(fdisused(fdp, fd),
|
||||
("fd is already unused"));
|
||||
KASSERT(fdp->fd_ofiles[fd] == NULL,
|
||||
("fd is still in use"));
|
||||
fdp->fd_map[NDSLOT(fd)] &= ~NDBIT(fd);
|
||||
if (fd < fdp->fd_freefile)
|
||||
fdp->fd_freefile = fd;
|
||||
if (fd == fdp->fd_lastfile)
|
||||
fdp->fd_lastfile = fd_last_used(fdp, 0, fd);
|
||||
}
|
||||
|
||||
/*
|
||||
* System calls on descriptors.
|
||||
*/
|
||||
@ -458,8 +558,10 @@ do_dup(td, type, old, new, retval)
|
||||
struct proc *p;
|
||||
struct file *fp;
|
||||
struct file *delfp;
|
||||
int error, newfd;
|
||||
int holdleaders;
|
||||
int error, holdleaders, maxfd;
|
||||
|
||||
KASSERT((type == DUP_VARIABLE || type == DUP_FIXED),
|
||||
("invalid dup type %d", type));
|
||||
|
||||
p = td->td_proc;
|
||||
fdp = p->p_fd;
|
||||
@ -468,9 +570,12 @@ do_dup(td, type, old, new, retval)
|
||||
* Verify we have a valid descriptor to dup from and possibly to
|
||||
* dup to.
|
||||
*/
|
||||
if (old < 0 || new < 0 || new >= p->p_rlimit[RLIMIT_NOFILE].rlim_cur ||
|
||||
new >= maxfilesperproc)
|
||||
if (old < 0 || new < 0)
|
||||
return (EBADF);
|
||||
maxfd = min((int)p->p_rlimit[RLIMIT_NOFILE].rlim_cur, maxfilesperproc);
|
||||
if (new >= maxfd)
|
||||
return (EMFILE);
|
||||
|
||||
FILEDESC_LOCK(fdp);
|
||||
if (old >= fdp->fd_nfiles || fdp->fd_ofiles[old] == NULL) {
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
@ -485,19 +590,26 @@ do_dup(td, type, old, new, retval)
|
||||
fhold(fp);
|
||||
|
||||
/*
|
||||
* Expand the table for the new descriptor if needed. This may
|
||||
* block and drop and reacquire the filedesc lock.
|
||||
* If the caller specified a file descriptor, make sure the file
|
||||
* table is large enough to hold it, and grab it. Otherwise, just
|
||||
* allocate a new descriptor the usual way. Since the filedesc
|
||||
* lock may be temporarily dropped in the process, we have to look
|
||||
* out for a race.
|
||||
*/
|
||||
if (type == DUP_VARIABLE || new >= fdp->fd_nfiles) {
|
||||
error = fdalloc(td, new, &newfd);
|
||||
if (error) {
|
||||
if (type == DUP_FIXED) {
|
||||
if (new >= fdp->fd_nfiles)
|
||||
fdgrowtable(fdp, new + 1);
|
||||
KASSERT(new < fdp->fd_nfiles,
|
||||
("fdgrowtable() failed to grow table"));
|
||||
if (fdp->fd_ofiles[new] == NULL)
|
||||
fdused(fdp, new);
|
||||
} else {
|
||||
if ((error = fdalloc(td, &new)) != 0) {
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
fdrop(fp, td);
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
if (type == DUP_VARIABLE)
|
||||
new = newfd;
|
||||
|
||||
/*
|
||||
* If the old file changed out from under us then treat it as a
|
||||
@ -505,39 +617,38 @@ do_dup(td, type, old, new, retval)
|
||||
* avoid this case.
|
||||
*/
|
||||
if (fdp->fd_ofiles[old] != fp) {
|
||||
if (fdp->fd_ofiles[new] == NULL) {
|
||||
if (new < fdp->fd_freefile)
|
||||
fdp->fd_freefile = new;
|
||||
while (fdp->fd_lastfile > 0 &&
|
||||
fdp->fd_ofiles[fdp->fd_lastfile] == NULL)
|
||||
fdp->fd_lastfile--;
|
||||
}
|
||||
/* we've allocated a descriptor which we won't use */
|
||||
if (fdp->fd_ofiles[new] == NULL)
|
||||
fdunused(fdp, new);
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
fdrop(fp, td);
|
||||
return (EBADF);
|
||||
}
|
||||
KASSERT(old != new, ("new fd is same as old"));
|
||||
KASSERT(old != new,
|
||||
("new fd is same as old"));
|
||||
|
||||
/*
|
||||
* Save info on the descriptor being overwritten. We have
|
||||
* to do the unmap now, but we cannot close it without
|
||||
* introducing an ownership race for the slot.
|
||||
* Save info on the descriptor being overwritten. We cannot close
|
||||
* it without introducing an ownership race for the slot, since we
|
||||
* need to drop the filedesc lock to call closef().
|
||||
*
|
||||
* XXX this duplicates parts of close().
|
||||
*/
|
||||
delfp = fdp->fd_ofiles[new];
|
||||
if (delfp != NULL && p->p_fdtol != NULL) {
|
||||
/*
|
||||
* Ask fdfree() to sleep to ensure that all relevant
|
||||
* process leaders can be traversed in closef().
|
||||
*/
|
||||
fdp->fd_holdleaderscount++;
|
||||
holdleaders = 1;
|
||||
} else
|
||||
holdleaders = 0;
|
||||
KASSERT(delfp == NULL || type == DUP_FIXED,
|
||||
("dup() picked an open file"));
|
||||
holdleaders = 0;
|
||||
if (delfp != NULL) {
|
||||
if (td->td_proc->p_fdtol != NULL) {
|
||||
/*
|
||||
* Ask fdfree() to sleep to ensure that all relevant
|
||||
* process leaders can be traversed in closef().
|
||||
*/
|
||||
fdp->fd_holdleaderscount++;
|
||||
holdleaders = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Duplicate the source descriptor, update lastfile
|
||||
* Duplicate the source descriptor
|
||||
*/
|
||||
fdp->fd_ofiles[new] = fp;
|
||||
fdp->fd_ofileflags[new] = fdp->fd_ofileflags[old] &~ UF_EXCLOSE;
|
||||
@ -550,8 +661,11 @@ do_dup(td, type, old, new, retval)
|
||||
* If we dup'd over a valid file, we now own the reference to it
|
||||
* and must dispose of it using closef() semantics (as if a
|
||||
* close() were performed on it).
|
||||
*
|
||||
* XXX this duplicates parts of close().
|
||||
*/
|
||||
if (delfp) {
|
||||
if (delfp != NULL) {
|
||||
/* XXX need to call knote_fdclose() */
|
||||
mtx_lock(&Giant);
|
||||
(void) closef(delfp, td);
|
||||
mtx_unlock(&Giant);
|
||||
@ -823,11 +937,12 @@ close(td, uap)
|
||||
if ((unsigned)fd >= fdp->fd_nfiles ||
|
||||
(fp = fdp->fd_ofiles[fd]) == NULL) {
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
error = EBADF;
|
||||
goto done2;
|
||||
mtx_unlock(&Giant);
|
||||
return (EBADF);
|
||||
}
|
||||
fdp->fd_ofiles[fd] = NULL;
|
||||
fdp->fd_ofileflags[fd] = 0;
|
||||
fdunused(fdp, fd);
|
||||
if (td->td_proc->p_fdtol != NULL) {
|
||||
/*
|
||||
* Ask fdfree() to sleep to ensure that all relevant
|
||||
@ -841,10 +956,6 @@ close(td, uap)
|
||||
* we now hold the fp reference that used to be owned by the descriptor
|
||||
* array.
|
||||
*/
|
||||
while (fdp->fd_lastfile > 0 && fdp->fd_ofiles[fdp->fd_lastfile] == NULL)
|
||||
fdp->fd_lastfile--;
|
||||
if (fd < fdp->fd_freefile)
|
||||
fdp->fd_freefile = fd;
|
||||
if (fd < fdp->fd_knlistsize) {
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
knote_fdclose(td, fd);
|
||||
@ -852,7 +963,6 @@ close(td, uap)
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
|
||||
error = closef(fp, td);
|
||||
done2:
|
||||
mtx_unlock(&Giant);
|
||||
if (holdleaders) {
|
||||
FILEDESC_LOCK(fdp);
|
||||
@ -1030,98 +1140,112 @@ fpathconf(td, uap)
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a file descriptor for the process.
|
||||
* Grow the file table to accomodate (at least) nfd descriptors. This may
|
||||
* block and drop the filedesc lock, but it will reacquire it before
|
||||
* returing.
|
||||
*/
|
||||
static int fdexpand;
|
||||
SYSCTL_INT(_debug, OID_AUTO, fdexpand, CTLFLAG_RD, &fdexpand, 0, "");
|
||||
|
||||
int
|
||||
fdalloc(td, want, result)
|
||||
struct thread *td;
|
||||
int want;
|
||||
int *result;
|
||||
static void
|
||||
fdgrowtable(struct filedesc *fdp, int nfd)
|
||||
{
|
||||
struct proc *p = td->td_proc;
|
||||
struct filedesc *fdp = td->td_proc->p_fd;
|
||||
int i;
|
||||
int lim, last, nfiles;
|
||||
struct file **newofile, **oldofile;
|
||||
char *newofileflags;
|
||||
struct file **ntable;
|
||||
char *nfileflags;
|
||||
int nnfiles, onfiles;
|
||||
NDSLOTTYPE *nmap;
|
||||
|
||||
FILEDESC_LOCK_ASSERT(fdp, MA_OWNED);
|
||||
|
||||
KASSERT(fdp->fd_nfiles > 0,
|
||||
("zero-length file table"));
|
||||
|
||||
/* compute the size of the new table */
|
||||
onfiles = fdp->fd_nfiles;
|
||||
nnfiles = NDSLOTS(nfd) * NDENTRIES; /* round up */
|
||||
if (nnfiles <= onfiles)
|
||||
/* the table is already large enough */
|
||||
return;
|
||||
|
||||
/* allocate a new table and (if required) new bitmaps */
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
MALLOC(ntable, struct file **, nnfiles * OFILESIZE,
|
||||
M_FILEDESC, M_ZERO | M_WAITOK);
|
||||
nfileflags = (char *)&ntable[nnfiles];
|
||||
if (NDSLOTS(nnfiles) > NDSLOTS(onfiles))
|
||||
MALLOC(nmap, NDSLOTTYPE *, NDSLOTS(nnfiles) * NDSLOTSIZE,
|
||||
M_FILEDESC, M_ZERO | M_WAITOK);
|
||||
else
|
||||
nmap = NULL;
|
||||
FILEDESC_LOCK(fdp);
|
||||
|
||||
/*
|
||||
* Search for a free descriptor starting at the higher
|
||||
* of want or fd_freefile. If that fails, consider
|
||||
* expanding the ofile array.
|
||||
* We now have new tables ready to go. Since we dropped the
|
||||
* filedesc lock to call malloc(), watch out for a race.
|
||||
*/
|
||||
lim = min((int)p->p_rlimit[RLIMIT_NOFILE].rlim_cur, maxfilesperproc);
|
||||
for (;;) {
|
||||
last = min(fdp->fd_nfiles, lim);
|
||||
i = max(want, fdp->fd_freefile);
|
||||
for (; i < last; i++) {
|
||||
if (fdp->fd_ofiles[i] == NULL) {
|
||||
fdp->fd_ofileflags[i] = 0;
|
||||
if (i > fdp->fd_lastfile)
|
||||
fdp->fd_lastfile = i;
|
||||
if (want <= fdp->fd_freefile)
|
||||
fdp->fd_freefile = i;
|
||||
*result = i;
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* No space in current array. Expand?
|
||||
*/
|
||||
if (i >= lim)
|
||||
return (EMFILE);
|
||||
if (fdp->fd_nfiles < NDEXTENT)
|
||||
nfiles = NDEXTENT;
|
||||
else
|
||||
nfiles = 2 * fdp->fd_nfiles;
|
||||
while (nfiles < want)
|
||||
nfiles <<= 1;
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
newofile = malloc(nfiles * OFILESIZE, M_FILEDESC, M_WAITOK);
|
||||
|
||||
/*
|
||||
* Deal with file-table extend race that might have
|
||||
* occurred while filedesc was unlocked.
|
||||
*/
|
||||
FILEDESC_LOCK(fdp);
|
||||
if (fdp->fd_nfiles >= nfiles) {
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
free(newofile, M_FILEDESC);
|
||||
FILEDESC_LOCK(fdp);
|
||||
continue;
|
||||
}
|
||||
newofileflags = (char *) &newofile[nfiles];
|
||||
/*
|
||||
* Copy the existing ofile and ofileflags arrays
|
||||
* and zero the new portion of each array.
|
||||
*/
|
||||
i = fdp->fd_nfiles * sizeof(struct file *);
|
||||
bcopy(fdp->fd_ofiles, newofile, i);
|
||||
bzero((char *)newofile + i,
|
||||
nfiles * sizeof(struct file *) - i);
|
||||
i = fdp->fd_nfiles * sizeof(char);
|
||||
bcopy(fdp->fd_ofileflags, newofileflags, i);
|
||||
bzero(newofileflags + i, nfiles * sizeof(char) - i);
|
||||
if (fdp->fd_nfiles > NDFILE)
|
||||
oldofile = fdp->fd_ofiles;
|
||||
else
|
||||
oldofile = NULL;
|
||||
fdp->fd_ofiles = newofile;
|
||||
fdp->fd_ofileflags = newofileflags;
|
||||
fdp->fd_nfiles = nfiles;
|
||||
fdexpand++;
|
||||
if (oldofile != NULL) {
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
free(oldofile, M_FILEDESC);
|
||||
FILEDESC_LOCK(fdp);
|
||||
}
|
||||
onfiles = fdp->fd_nfiles;
|
||||
if (onfiles >= nnfiles) {
|
||||
/* we lost the race, but that's OK */
|
||||
free(ntable, M_FILEDESC);
|
||||
if (nmap != NULL)
|
||||
free(nmap, M_FILEDESC);
|
||||
return;
|
||||
}
|
||||
bcopy(fdp->fd_ofiles, ntable, onfiles * sizeof(*ntable));
|
||||
bcopy(fdp->fd_ofileflags, nfileflags, onfiles);
|
||||
if (onfiles > NDFILE)
|
||||
free(fdp->fd_ofiles, M_FILEDESC);
|
||||
fdp->fd_ofiles = ntable;
|
||||
fdp->fd_ofileflags = nfileflags;
|
||||
if (NDSLOTS(nnfiles) > NDSLOTS(onfiles)) {
|
||||
bcopy(fdp->fd_map, nmap, NDSLOTS(onfiles) * sizeof(*nmap));
|
||||
if (NDSLOTS(onfiles) > NDSLOTS(NDFILE))
|
||||
free(fdp->fd_map, M_FILEDESC);
|
||||
fdp->fd_map = nmap;
|
||||
}
|
||||
fdp->fd_nfiles = nnfiles;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a file descriptor for the process.
|
||||
*/
|
||||
int
|
||||
fdalloc(struct thread *td, int *result)
|
||||
{
|
||||
struct proc *p = td->td_proc;
|
||||
struct filedesc *fdp = p->p_fd;
|
||||
int fd = -1, maxfd;
|
||||
|
||||
FILEDESC_LOCK_ASSERT(fdp, MA_OWNED);
|
||||
|
||||
maxfd = min((int)p->p_rlimit[RLIMIT_NOFILE].rlim_cur, maxfilesperproc);
|
||||
|
||||
/*
|
||||
* Search the bitmap for a free descriptor. If none is found, try
|
||||
* to grow the file table. Keep at it until we either get a file
|
||||
* descriptor or run into process or system limits; fdgrowtable()
|
||||
* may drop the filedesc lock, so we're in a race.
|
||||
*/
|
||||
for (;;) {
|
||||
fd = fd_first_free(fdp, fdp->fd_freefile, fdp->fd_nfiles);
|
||||
if (fd >= maxfd)
|
||||
return (EMFILE);
|
||||
if (fd < fdp->fd_nfiles)
|
||||
break;
|
||||
fdgrowtable(fdp, min(fdp->fd_nfiles * 2, maxfd));
|
||||
}
|
||||
FILEDESC_LOCK_ASSERT(fdp, MA_OWNED);
|
||||
|
||||
/*
|
||||
* Perform some sanity checks, then mark the file descriptor as
|
||||
* used and return it to the caller.
|
||||
*/
|
||||
KASSERT(!fdisused(fdp, fd),
|
||||
("fd_first_free() returned non-free descriptor"));
|
||||
KASSERT(fdp->fd_ofiles[fd] == NULL,
|
||||
("free descriptor isn't"));
|
||||
fdp->fd_ofileflags[fd] = 0; /* XXX needed? */
|
||||
fdused(fdp, fd);
|
||||
fdp->fd_freefile = fd_first_free(fdp, fd, fdp->fd_nfiles);
|
||||
*result = fd;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1205,7 +1329,7 @@ falloc(td, resultfp, resultfd)
|
||||
LIST_INSERT_HEAD(&filehead, fp, f_list);
|
||||
}
|
||||
sx_xunlock(&filelist_lock);
|
||||
if ((error = fdalloc(td, 0, &i))) {
|
||||
if ((error = fdalloc(td, &i))) {
|
||||
FILEDESC_UNLOCK(p->p_fd);
|
||||
fdrop(fp, td);
|
||||
if (resultfp)
|
||||
@ -1248,6 +1372,8 @@ fdinit(fdp)
|
||||
{
|
||||
struct filedesc0 *newfdp;
|
||||
|
||||
FILEDESC_LOCK_ASSERT(fdp, MA_OWNED);
|
||||
|
||||
MALLOC(newfdp, struct filedesc0 *, sizeof(struct filedesc0),
|
||||
M_FILEDESC, M_WAITOK | M_ZERO);
|
||||
mtx_init(&newfdp->fd_fd.fd_mtx, FILEDESC_LOCK_DESC, NULL, MTX_DEF);
|
||||
@ -1268,6 +1394,7 @@ fdinit(fdp)
|
||||
newfdp->fd_fd.fd_ofileflags = newfdp->fd_dfileflags;
|
||||
newfdp->fd_fd.fd_nfiles = NDFILE;
|
||||
newfdp->fd_fd.fd_knlistsize = -1;
|
||||
newfdp->fd_fd.fd_map = newfdp->fd_dmap;
|
||||
return (&newfdp->fd_fd);
|
||||
}
|
||||
|
||||
@ -1294,107 +1421,38 @@ fdcopy(fdp)
|
||||
struct filedesc *fdp;
|
||||
{
|
||||
struct filedesc *newfdp;
|
||||
struct file **fpp;
|
||||
int i, j;
|
||||
int i;
|
||||
|
||||
/* Certain daemons might not have file descriptors. */
|
||||
if (fdp == NULL)
|
||||
return (NULL);
|
||||
|
||||
FILEDESC_LOCK_ASSERT(fdp, MA_OWNED);
|
||||
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
MALLOC(newfdp, struct filedesc *, sizeof(struct filedesc0),
|
||||
M_FILEDESC, M_WAITOK);
|
||||
FILEDESC_LOCK(fdp);
|
||||
bcopy(fdp, newfdp, sizeof(struct filedesc));
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
bzero(&newfdp->fd_mtx, sizeof(newfdp->fd_mtx));
|
||||
mtx_init(&newfdp->fd_mtx, FILEDESC_LOCK_DESC, NULL, MTX_DEF);
|
||||
if (newfdp->fd_cdir)
|
||||
VREF(newfdp->fd_cdir);
|
||||
if (newfdp->fd_rdir)
|
||||
VREF(newfdp->fd_rdir);
|
||||
if (newfdp->fd_jdir)
|
||||
VREF(newfdp->fd_jdir);
|
||||
newfdp->fd_refcnt = 1;
|
||||
|
||||
/*
|
||||
* If the number of open files fits in the internal arrays
|
||||
* of the open file structure, use them, otherwise allocate
|
||||
* additional memory for the number of descriptors currently
|
||||
* in use.
|
||||
*/
|
||||
FILEDESC_LOCK(fdp);
|
||||
newfdp->fd_lastfile = fdp->fd_lastfile;
|
||||
newfdp->fd_nfiles = fdp->fd_nfiles;
|
||||
if (newfdp->fd_lastfile < NDFILE) {
|
||||
newfdp->fd_ofiles = ((struct filedesc0 *) newfdp)->fd_dfiles;
|
||||
newfdp->fd_ofileflags =
|
||||
((struct filedesc0 *) newfdp)->fd_dfileflags;
|
||||
i = NDFILE;
|
||||
} else {
|
||||
/*
|
||||
* Compute the smallest multiple of NDEXTENT needed
|
||||
* for the file descriptors currently in use,
|
||||
* allowing the table to shrink.
|
||||
*/
|
||||
retry:
|
||||
i = newfdp->fd_nfiles;
|
||||
while (i > 2 * NDEXTENT && i > newfdp->fd_lastfile * 2)
|
||||
i /= 2;
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
MALLOC(newfdp->fd_ofiles, struct file **, i * OFILESIZE,
|
||||
M_FILEDESC, M_WAITOK);
|
||||
FILEDESC_LOCK(fdp);
|
||||
newfdp->fd_lastfile = fdp->fd_lastfile;
|
||||
newfdp->fd_nfiles = fdp->fd_nfiles;
|
||||
j = newfdp->fd_nfiles;
|
||||
while (j > 2 * NDEXTENT && j > newfdp->fd_lastfile * 2)
|
||||
j /= 2;
|
||||
if (i != j) {
|
||||
/*
|
||||
* The size of the original table has changed.
|
||||
* Go over once again.
|
||||
*/
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
FREE(newfdp->fd_ofiles, M_FILEDESC);
|
||||
FILEDESC_LOCK(fdp);
|
||||
newfdp->fd_lastfile = fdp->fd_lastfile;
|
||||
newfdp->fd_nfiles = fdp->fd_nfiles;
|
||||
goto retry;
|
||||
newfdp = fdinit(fdp);
|
||||
FILEDESC_LOCK(newfdp);
|
||||
if (fdp->fd_lastfile >= newfdp->fd_nfiles)
|
||||
fdgrowtable(newfdp, fdp->fd_lastfile + 1);
|
||||
KASSERT(newfdp->fd_nfiles > fdp->fd_lastfile,
|
||||
("fdgrowtable() failed to grow table"));
|
||||
/* copy everything except kqueue descriptors */
|
||||
newfdp->fd_freefile = -1;
|
||||
for (i = 0; i <= fdp->fd_lastfile; ++i) {
|
||||
if (fdisused(fdp, i) &&
|
||||
fdp->fd_ofiles[i]->f_type != DTYPE_KQUEUE) {
|
||||
newfdp->fd_ofiles[i] = fdp->fd_ofiles[i];
|
||||
newfdp->fd_ofileflags[i] = fdp->fd_ofileflags[i];
|
||||
fdused(newfdp, i);
|
||||
fhold(newfdp->fd_ofiles[i]);
|
||||
newfdp->fd_lastfile = i;
|
||||
} else {
|
||||
if (newfdp->fd_freefile == -1)
|
||||
newfdp->fd_freefile = i;
|
||||
}
|
||||
newfdp->fd_ofileflags = (char *) &newfdp->fd_ofiles[i];
|
||||
}
|
||||
newfdp->fd_nfiles = i;
|
||||
bcopy(fdp->fd_ofiles, newfdp->fd_ofiles, i * sizeof(struct file **));
|
||||
bcopy(fdp->fd_ofileflags, newfdp->fd_ofileflags, i * sizeof(char));
|
||||
|
||||
/*
|
||||
* kq descriptors cannot be copied.
|
||||
*/
|
||||
if (newfdp->fd_knlistsize != -1) {
|
||||
fpp = &newfdp->fd_ofiles[newfdp->fd_lastfile];
|
||||
for (i = newfdp->fd_lastfile; i >= 0; i--, fpp--) {
|
||||
if (*fpp != NULL && (*fpp)->f_type == DTYPE_KQUEUE) {
|
||||
*fpp = NULL;
|
||||
if (i < newfdp->fd_freefile)
|
||||
newfdp->fd_freefile = i;
|
||||
}
|
||||
if (*fpp == NULL && i == newfdp->fd_lastfile && i > 0)
|
||||
newfdp->fd_lastfile--;
|
||||
}
|
||||
newfdp->fd_knlist = NULL;
|
||||
newfdp->fd_knlistsize = -1;
|
||||
newfdp->fd_knhash = NULL;
|
||||
newfdp->fd_knhashmask = 0;
|
||||
}
|
||||
|
||||
fpp = newfdp->fd_ofiles;
|
||||
for (i = newfdp->fd_lastfile; i-- >= 0; fpp++) {
|
||||
if (*fpp != NULL)
|
||||
fhold(*fpp);
|
||||
}
|
||||
if (newfdp->fd_freefile == -1)
|
||||
newfdp->fd_freefile = i;
|
||||
newfdp->fd_cmask = fdp->fd_cmask;
|
||||
FILEDESC_UNLOCK(newfdp);
|
||||
return (newfdp);
|
||||
}
|
||||
|
||||
@ -1434,7 +1492,7 @@ fdfree(td)
|
||||
i = 0;
|
||||
fpp = fdp->fd_ofiles;
|
||||
for (i = 0, fpp = fdp->fd_ofiles;
|
||||
i < fdp->fd_lastfile;
|
||||
i <= fdp->fd_lastfile;
|
||||
i++, fpp++) {
|
||||
if (*fpp == NULL ||
|
||||
(*fpp)->f_type != DTYPE_VNODE)
|
||||
@ -1518,6 +1576,8 @@ fdfree(td)
|
||||
|
||||
if (fdp->fd_nfiles > NDFILE)
|
||||
FREE(fdp->fd_ofiles, M_FILEDESC);
|
||||
if (NDSLOTS(fdp->fd_nfiles) > NDSLOTS(NDFILE))
|
||||
FREE(fdp->fd_map, M_FILEDESC);
|
||||
if (fdp->fd_cdir)
|
||||
vrele(fdp->fd_cdir);
|
||||
if (fdp->fd_rdir)
|
||||
@ -1591,15 +1651,12 @@ setugidsafety(td)
|
||||
fp = fdp->fd_ofiles[i];
|
||||
fdp->fd_ofiles[i] = NULL;
|
||||
fdp->fd_ofileflags[i] = 0;
|
||||
if (i < fdp->fd_freefile)
|
||||
fdp->fd_freefile = i;
|
||||
fdunused(fdp, i);
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
(void) closef(fp, td);
|
||||
FILEDESC_LOCK(fdp);
|
||||
}
|
||||
}
|
||||
while (fdp->fd_lastfile > 0 && fdp->fd_ofiles[fdp->fd_lastfile] == NULL)
|
||||
fdp->fd_lastfile--;
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
}
|
||||
|
||||
@ -1641,15 +1698,12 @@ fdcloseexec(td)
|
||||
fp = fdp->fd_ofiles[i];
|
||||
fdp->fd_ofiles[i] = NULL;
|
||||
fdp->fd_ofileflags[i] = 0;
|
||||
if (i < fdp->fd_freefile)
|
||||
fdp->fd_freefile = i;
|
||||
fdunused(fdp, i);
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
(void) closef(fp, td);
|
||||
FILEDESC_LOCK(fdp);
|
||||
}
|
||||
}
|
||||
while (fdp->fd_lastfile > 0 && fdp->fd_ofiles[fdp->fd_lastfile] == NULL)
|
||||
fdp->fd_lastfile--;
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
}
|
||||
|
||||
@ -1698,6 +1752,7 @@ fdcheckstd(td)
|
||||
FILEDESC_LOCK(fdp);
|
||||
if (fdp->fd_ofiles[fd] == fp) {
|
||||
fdp->fd_ofiles[fd] = NULL;
|
||||
fdunused(fdp, fd);
|
||||
extraref = 1;
|
||||
}
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
@ -2163,10 +2218,10 @@ dupfdopen(td, fdp, indx, dfd, mode, error)
|
||||
fp = fdp->fd_ofiles[indx];
|
||||
fdp->fd_ofiles[indx] = wfp;
|
||||
fdp->fd_ofileflags[indx] = fdp->fd_ofileflags[dfd];
|
||||
if (fp == NULL)
|
||||
fdused(fdp, indx);
|
||||
fhold_locked(wfp);
|
||||
FILE_UNLOCK(wfp);
|
||||
if (indx > fdp->fd_lastfile)
|
||||
fdp->fd_lastfile = indx;
|
||||
if (fp != NULL)
|
||||
FILE_LOCK(fp);
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
@ -2187,21 +2242,9 @@ dupfdopen(td, fdp, indx, dfd, mode, error)
|
||||
fdp->fd_ofiles[dfd] = NULL;
|
||||
fdp->fd_ofileflags[indx] = fdp->fd_ofileflags[dfd];
|
||||
fdp->fd_ofileflags[dfd] = 0;
|
||||
|
||||
/*
|
||||
* Complete the clean up of the filedesc structure by
|
||||
* recomputing the various hints.
|
||||
*/
|
||||
if (indx > fdp->fd_lastfile) {
|
||||
fdp->fd_lastfile = indx;
|
||||
} else {
|
||||
while (fdp->fd_lastfile > 0 &&
|
||||
fdp->fd_ofiles[fdp->fd_lastfile] == NULL) {
|
||||
fdp->fd_lastfile--;
|
||||
}
|
||||
if (dfd < fdp->fd_freefile)
|
||||
fdp->fd_freefile = dfd;
|
||||
}
|
||||
fdunused(fdp, dfd);
|
||||
if (fp == NULL)
|
||||
fdused(fdp, indx);
|
||||
if (fp != NULL)
|
||||
FILE_LOCK(fp);
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
@ -2221,7 +2264,6 @@ dupfdopen(td, fdp, indx, dfd, mode, error)
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
|
||||
struct filedesc_to_leader *
|
||||
filedesc_to_leader_alloc(struct filedesc_to_leader *old,
|
||||
struct filedesc *fdp,
|
||||
@ -2248,7 +2290,7 @@ filedesc_to_leader_alloc(struct filedesc_to_leader *old,
|
||||
fdtol->fdl_next = fdtol;
|
||||
fdtol->fdl_prev = fdtol;
|
||||
}
|
||||
return fdtol;
|
||||
return (fdtol);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -258,6 +258,7 @@ pipe(td, uap)
|
||||
FILEDESC_LOCK(fdp);
|
||||
if (fdp->fd_ofiles[td->td_retval[0]] == rf) {
|
||||
fdp->fd_ofiles[td->td_retval[0]] = NULL;
|
||||
fdunused(fdp, td->td_retval[0]);
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
fdrop(rf, td);
|
||||
} else {
|
||||
|
@ -125,6 +125,7 @@ socket(td, uap)
|
||||
if (error) {
|
||||
if (fdp->fd_ofiles[fd] == fp) {
|
||||
fdp->fd_ofiles[fd] = NULL;
|
||||
fdunused(fdp, fd);
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
fdrop(fp, td);
|
||||
} else {
|
||||
@ -391,6 +392,7 @@ accept1(td, uap, compat)
|
||||
FILEDESC_LOCK(fdp);
|
||||
if (fdp->fd_ofiles[fd] == nfp) {
|
||||
fdp->fd_ofiles[fd] = NULL;
|
||||
fdunused(fdp, fd);
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
fdrop(nfp, td);
|
||||
} else {
|
||||
@ -585,6 +587,7 @@ socketpair(td, uap)
|
||||
FILEDESC_LOCK(fdp);
|
||||
if (fdp->fd_ofiles[sv[1]] == fp2) {
|
||||
fdp->fd_ofiles[sv[1]] = NULL;
|
||||
fdunused(fdp, sv[1]);
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
fdrop(fp2, td);
|
||||
} else {
|
||||
@ -595,6 +598,7 @@ socketpair(td, uap)
|
||||
FILEDESC_LOCK(fdp);
|
||||
if (fdp->fd_ofiles[sv[0]] == fp1) {
|
||||
fdp->fd_ofiles[sv[0]] = NULL;
|
||||
fdunused(fdp, sv[0]);
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
fdrop(fp1, td);
|
||||
} else {
|
||||
|
@ -1047,7 +1047,7 @@ unp_externalize(control, controlp)
|
||||
fdp = (int *)
|
||||
CMSG_DATA(mtod(*controlp, struct cmsghdr *));
|
||||
for (i = 0; i < newfds; i++) {
|
||||
if (fdalloc(td, 0, &f))
|
||||
if (fdalloc(td, &f))
|
||||
panic("unp_externalize fdalloc failed");
|
||||
fp = *rp++;
|
||||
td->td_proc->p_fd->fd_ofiles[f] = fp;
|
||||
|
@ -998,6 +998,7 @@ kern_open(struct thread *td, char *path, enum uio_seg pathseg, int flags,
|
||||
FILEDESC_LOCK(fdp);
|
||||
if (fdp->fd_ofiles[indx] == fp) {
|
||||
fdp->fd_ofiles[indx] = NULL;
|
||||
fdunused(fdp, indx);
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
fdrop(fp, td);
|
||||
} else {
|
||||
@ -1093,6 +1094,7 @@ kern_open(struct thread *td, char *path, enum uio_seg pathseg, int flags,
|
||||
FILEDESC_LOCK(fdp);
|
||||
if (fdp->fd_ofiles[indx] == fp) {
|
||||
fdp->fd_ofiles[indx] = NULL;
|
||||
fdunused(fdp, indx);
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
fdrop(fp, td);
|
||||
} else {
|
||||
@ -3982,6 +3984,7 @@ fhopen(td, uap)
|
||||
FILEDESC_LOCK(fdp);
|
||||
if (fdp->fd_ofiles[indx] == fp) {
|
||||
fdp->fd_ofiles[indx] = NULL;
|
||||
fdunused(fdp, indx);
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
fdrop(fp, td);
|
||||
} else {
|
||||
|
@ -998,6 +998,7 @@ kern_open(struct thread *td, char *path, enum uio_seg pathseg, int flags,
|
||||
FILEDESC_LOCK(fdp);
|
||||
if (fdp->fd_ofiles[indx] == fp) {
|
||||
fdp->fd_ofiles[indx] = NULL;
|
||||
fdunused(fdp, indx);
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
fdrop(fp, td);
|
||||
} else {
|
||||
@ -1093,6 +1094,7 @@ kern_open(struct thread *td, char *path, enum uio_seg pathseg, int flags,
|
||||
FILEDESC_LOCK(fdp);
|
||||
if (fdp->fd_ofiles[indx] == fp) {
|
||||
fdp->fd_ofiles[indx] = NULL;
|
||||
fdunused(fdp, indx);
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
fdrop(fp, td);
|
||||
} else {
|
||||
@ -3982,6 +3984,7 @@ fhopen(td, uap)
|
||||
FILEDESC_LOCK(fdp);
|
||||
if (fdp->fd_ofiles[indx] == fp) {
|
||||
fdp->fd_ofiles[indx] = NULL;
|
||||
fdunused(fdp, indx);
|
||||
FILEDESC_UNLOCK(fdp);
|
||||
fdrop(fp, td);
|
||||
} else {
|
||||
|
@ -39,6 +39,7 @@
|
||||
|
||||
#include <sys/_lock.h>
|
||||
#include <sys/_mutex.h>
|
||||
#include <sys/limits.h> /* XXX for CHAR_BIT */
|
||||
#include <sys/queue.h>
|
||||
|
||||
/*
|
||||
@ -50,13 +51,15 @@
|
||||
* the historical limit of 20 open files (and the usage of descriptors by
|
||||
* shells). If these descriptors are exhausted, a larger descriptor table
|
||||
* may be allocated, up to a process' resource limit; the internal arrays
|
||||
* are then unused. The initial expansion is set to NDEXTENT; each time
|
||||
* it runs out, it is doubled until the resource limit is reached. NDEXTENT
|
||||
* should be selected to be the biggest multiple of OFILESIZE (see below)
|
||||
* that will fit in a power-of-two sized piece of memory.
|
||||
* are then unused.
|
||||
*/
|
||||
#define NDFILE 20
|
||||
#define NDEXTENT 50 /* 250 bytes in 256-byte alloc. */
|
||||
#define NDSLOTTYPE unsigned long
|
||||
#define NDSLOTSIZE sizeof(NDSLOTTYPE)
|
||||
#define NDENTRIES (NDSLOTSIZE * CHAR_BIT)
|
||||
#define NDSLOT(x) ((x) / NDENTRIES)
|
||||
#define NDBIT(x) ((NDSLOTTYPE)1 << ((x) % NDENTRIES))
|
||||
#define NDSLOTS(x) (((x) + NDENTRIES - 1) / NDENTRIES)
|
||||
|
||||
struct filedesc {
|
||||
struct file **fd_ofiles; /* file structures for open files */
|
||||
@ -65,6 +68,7 @@ struct filedesc {
|
||||
struct vnode *fd_rdir; /* root directory */
|
||||
struct vnode *fd_jdir; /* jail root directory */
|
||||
int fd_nfiles; /* number of open files allocated */
|
||||
NDSLOTTYPE *fd_map; /* bitmap of free fds */
|
||||
int fd_lastfile; /* high-water mark of fd_ofiles */
|
||||
int fd_freefile; /* approx. next free file */
|
||||
u_short fd_cmask; /* mask for file creation */
|
||||
@ -91,6 +95,7 @@ struct filedesc0 {
|
||||
*/
|
||||
struct file *fd_dfiles[NDFILE];
|
||||
char fd_dfileflags[NDFILE];
|
||||
NDSLOTTYPE fd_dmap[NDSLOTS(NDFILE)];
|
||||
};
|
||||
|
||||
|
||||
@ -140,7 +145,9 @@ int closef(struct file *fp, struct thread *p);
|
||||
int dupfdopen(struct thread *td, struct filedesc *fdp, int indx, int dfd,
|
||||
int mode, int error);
|
||||
int falloc(struct thread *p, struct file **resultfp, int *resultfd);
|
||||
int fdalloc(struct thread *p, int want, int *result);
|
||||
void fdused(struct filedesc *fdp, int fd);
|
||||
void fdunused(struct filedesc *fdp, int fd);
|
||||
int fdalloc(struct thread *p, int *result);
|
||||
int fdavail(struct thread *td, int n);
|
||||
void fdcloseexec(struct thread *td);
|
||||
int fdcheckstd(struct thread *td);
|
||||
|
Loading…
Reference in New Issue
Block a user