I've ported Niels Provos's file descriptor allocation code to FreeBSD in case anyone wants to try it out & run some benchmarks. If the performance boost turns out to be worth the added complexity, I might clean it up a bit and commit it. See http://mail-index.netbsd.org/tech-perform/2003/10/28/0001.html and Banga & Mogul's USENIX paper (linked to from the other URL) for the details. http://perforce.freebsd.org/chv.cgi?CH=43066 --- //depot/user/tjr/freebsd-tjr/src/sys/kern/init_main.c 2003/10/05 17:21:48 +++ //depot/user/tjr/freebsd-tjr/src/sys/kern/init_main.c 2003/11/26 19:42:24 _at__at_ -415,6 +415,8 _at__at_ 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_himap = fdp->fd_dhimap; + fdp->fd_fd.fd_lomap = fdp->fd_dlomap; /* Create the limits structures. */ p->p_limit = &limit0; --- //depot/user/tjr/freebsd-tjr/src/sys/kern/kern_descrip.c 2003/10/27 00:31:20 +++ //depot/user/tjr/freebsd-tjr/src/sys/kern/kern_descrip.c 2003/11/26 19:42:24 _at__at_ -96,6 +96,7 _at__at_ static int do_dup(struct thread *td, enum dup_type type, int old, int new, register_t *retval); +static __inline int find_next_zero(uint32_t *, int, u_int); /* * Descriptor management. _at__at_ -105,6 +106,62 _at__at_ struct sx filelist_lock; /* sx to protect filelist */ struct mtx sigio_lock; /* mtx to protect pointers to sigio */ +static __inline int +find_next_zero(uint32_t *bitmap, int want, u_int bits) +{ + int i, off, maxoff; + uint32_t sub; + + if (want > bits) + return -1; + + off = want >> NDENTRYSHIFT; + i = want & NDENTRYMASK; + if (i) { + sub = bitmap[off] | ((u_int)~0 >> (NDENTRIES - i)); + if (sub != ~0) + goto found; + off++; + } + + maxoff = NDLOSLOTS(bits); + while (off < maxoff) { + if ((sub = bitmap[off]) != ~0) + goto found; + off++; + } + + return (-1); + +found: + return (off << NDENTRYSHIFT) + ffs(~sub) - 1; +} + +int +fd_find_last_set(struct filedesc *fd, int last) +{ + int off, i; + struct file **ofiles = fd->fd_ofiles; + uint32_t *bitmap = fd->fd_lomap; + + off = (last - 1) >> NDENTRYSHIFT; + + while (!bitmap[off] && off >= 0) + off--; + + if (off < 0) + return (0); + + i = ((off + 1) << NDENTRYSHIFT) - 1; + if (i >= last) + i = last - 1; + + while (i > 0 && ofiles[i] == NULL) + i--; + + return (i); +} + /* * System calls on descriptors. */ _at__at_ -505,13 +562,8 _at__at_ * 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--; - } + if (fdp->fd_ofiles[new] == NULL) + fd_unused(fdp, new); FILEDESC_UNLOCK(fdp); fdrop(fp, td); return (EBADF); _at__at_ -545,8 +597,7 _at__at_ */ fdp->fd_ofiles[new] = fp; fdp->fd_ofileflags[new] = fdp->fd_ofileflags[old] &~ UF_EXCLOSE; - if (new > fdp->fd_lastfile) - fdp->fd_lastfile = new; + fd_used(fdp, new); FILEDESC_UNLOCK(fdp); *retval = new; _at__at_ -836,6 +887,7 _at__at_ #endif fdp->fd_ofiles[fd] = NULL; fdp->fd_ofileflags[fd] = 0; + fd_unused(fdp, fd); if (td->td_proc->p_fdtol != NULL) { /* * Ask fdfree() to sleep to ensure that all relevant _at__at_ -849,10 +901,6 _at__at_ * 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); _at__at_ -1052,9 +1100,11 _at__at_ struct proc *p = td->td_proc; struct filedesc *fdp = td->td_proc->p_fd; int i; - int lim, last, nfiles; + int lim, last, nfiles, oldnfiles; struct file **newofile, **oldofile; char *newofileflags; + uint32_t *newhimap, *newlomap, *oldhimap, *oldlomap; + u_int off, new; FILEDESC_LOCK_ASSERT(fdp, MA_OWNED); _at__at_ -1066,12 +1116,28 _at__at_ lim = min((int)p->p_rlimit[RLIMIT_NOFILE].rlim_cur, maxfilesperproc); for (;;) { last = min(fdp->fd_nfiles, lim); +again: 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; + 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 again; + } + i += (new << NDENTRYSHIFT); + if (i < last) { + KASSERT(fdp->fd_ofiles[i] == NULL, + ("free descriptor isn't")); + fdp->fd_ofileflags[i] = 0; /* XXX needed? */ + fd_used(fdp, i); if (want <= fdp->fd_freefile) fdp->fd_freefile = i; *result = i; _at__at_ -1082,7 +1148,7 _at__at_ /* * No space in current array. Expand? */ - if (i >= lim) + if (fdp->fd_nfiles >= lim) return (EMFILE); if (fdp->fd_nfiles < NDEXTENT) nfiles = NDEXTENT; _at__at_ -1090,8 +1156,16 _at__at_ nfiles = 2 * fdp->fd_nfiles; while (nfiles < want) nfiles <<= 1; + oldnfiles = fdp->fd_nfiles; FILEDESC_UNLOCK(fdp); newofile = malloc(nfiles * OFILESIZE, M_FILEDESC, M_WAITOK); + if (NDHISLOTS(nfiles) > NDHISLOTS(oldnfiles)) { + newhimap = malloc(NDHISLOTS(nfiles) * sizeof(uint32_t), + M_FILEDESC, M_WAITOK); + newlomap = malloc(NDLOSLOTS(nfiles) * sizeof(uint32_t), + M_FILEDESC, M_WAITOK); + } else + newhimap = newlomap = NULL; /* * Deal with file-table extend race that might have _at__at_ -1101,6 +1175,10 _at__at_ if (fdp->fd_nfiles >= nfiles) { FILEDESC_UNLOCK(fdp); free(newofile, M_FILEDESC); + if (newhimap != NULL) + free(newhimap, M_FILEDESC); + if (newlomap != NULL) + free(newlomap, M_FILEDESC); FILEDESC_LOCK(fdp); continue; } _at__at_ -1122,11 +1200,33 _at__at_ oldofile = NULL; fdp->fd_ofiles = newofile; fdp->fd_ofileflags = newofileflags; + oldlomap = oldhimap = NULL; + if (NDHISLOTS(nfiles) > NDHISLOTS(oldnfiles)) { + memcpy(newhimap, fdp->fd_himap, + (i = NDHISLOTS(oldnfiles) * sizeof(uint32_t))); + memset((char *)newhimap + i, 0, + NDHISLOTS(nfiles) * sizeof(uint32_t) - i); + memcpy(newlomap, fdp->fd_lomap, + (i = NDLOSLOTS(oldnfiles) * sizeof(uint32_t))); + memset((char *)newlomap + i, 0, + NDLOSLOTS(nfiles) * sizeof(uint32_t) - i); + if (NDHISLOTS(oldnfiles) > NDHISLOTS(NDFILE)) { + oldhimap = fdp->fd_himap; + oldlomap = fdp->fd_lomap; + } + fdp->fd_himap = newhimap; + fdp->fd_lomap = newlomap; + } fdp->fd_nfiles = nfiles; fdexpand++; - if (oldofile != NULL) { + if (oldofile != NULL || oldlomap != NULL || oldhimap != NULL) { FILEDESC_UNLOCK(fdp); - free(oldofile, M_FILEDESC); + if (oldofile != NULL) + free(oldofile, M_FILEDESC); + if (oldlomap != NULL) + free(oldlomap, M_FILEDESC); + if (oldhimap != NULL) + free(oldhimap, M_FILEDESC); FILEDESC_LOCK(fdp); } } _at__at_ -1276,6 +1376,8 _at__at_ newfdp->fd_fd.fd_ofileflags = newfdp->fd_dfileflags; newfdp->fd_fd.fd_nfiles = NDFILE; newfdp->fd_fd.fd_knlistsize = -1; + newfdp->fd_fd.fd_himap = newfdp->fd_dhimap; + newfdp->fd_fd.fd_lomap = newfdp->fd_dlomap; return (&newfdp->fd_fd); } _at__at_ -1340,6 +1442,10 _at__at_ newfdp->fd_ofiles = ((struct filedesc0 *) newfdp)->fd_dfiles; newfdp->fd_ofileflags = ((struct filedesc0 *) newfdp)->fd_dfileflags; + newfdp->fd_himap = + ((struct filedesc0 *) newfdp)->fd_dhimap; + newfdp->fd_lomap = + ((struct filedesc0 *) newfdp)->fd_dlomap; i = NDFILE; } else { /* _at__at_ -1354,6 +1460,17 _at__at_ FILEDESC_UNLOCK(fdp); MALLOC(newfdp->fd_ofiles, struct file **, i * OFILESIZE, M_FILEDESC, M_WAITOK); + if (NDHISLOTS(i) <= NDHISLOTS(NDFILE)) { + newfdp->fd_himap = + ((struct filedesc0 *) newfdp)->fd_dhimap; + newfdp->fd_lomap = + ((struct filedesc0 *) newfdp)->fd_dlomap; + } else { + newfdp->fd_himap = malloc(NDHISLOTS(i) * sizeof(uint32_t), + M_FILEDESC, M_WAITOK); + newfdp->fd_lomap = malloc(NDLOSLOTS(i) * sizeof(uint32_t), + M_FILEDESC, M_WAITOK); + } FILEDESC_LOCK(fdp); newfdp->fd_lastfile = fdp->fd_lastfile; newfdp->fd_nfiles = fdp->fd_nfiles; _at__at_ -1377,6 +1494,10 _at__at_ 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)); + if (i < NDENTRIES * NDENTRIES) + i = NDENTRIES * NDENTRIES; /* size of inlined bitmaps */ + memcpy(newfdp->fd_himap, fdp->fd_himap, NDHISLOTS(i)*sizeof(uint32_t)); + memcpy(newfdp->fd_lomap, fdp->fd_lomap, NDLOSLOTS(i)*sizeof(uint32_t)); /* * kq descriptors cannot be copied. _at__at_ -1385,12 +1506,9 _at__at_ fpp = &newfdp->fd_ofiles[newfdp->fd_lastfile]; for (i = newfdp->fd_lastfile; i >= 0; i--, fpp--) { if (*fpp != NULL && (*fpp)->f_type == DTYPE_KQUEUE) { + fd_unused(newfdp, i); *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; _at__at_ -1526,6 +1644,10 _at__at_ if (fdp->fd_nfiles > NDFILE) FREE(fdp->fd_ofiles, M_FILEDESC); + if (NDHISLOTS(fdp->fd_nfiles) > NDHISLOTS(NDFILE)) { + free(fdp->fd_himap, M_FILEDESC); + free(fdp->fd_lomap, M_FILEDESC); + } if (fdp->fd_cdir) vrele(fdp->fd_cdir); if (fdp->fd_rdir) _at__at_ -1603,15 +1725,12 _at__at_ fp = fdp->fd_ofiles[i]; fdp->fd_ofiles[i] = NULL; fdp->fd_ofileflags[i] = 0; - if (i < fdp->fd_freefile) - fdp->fd_freefile = i; + fd_unused(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); } _at__at_ -1657,15 +1776,12 _at__at_ fp = fdp->fd_ofiles[i]; fdp->fd_ofiles[i] = NULL; fdp->fd_ofileflags[i] = 0; - if (i < fdp->fd_freefile) - fdp->fd_freefile = i; + fd_unused(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); } _at__at_ -1714,6 +1830,7 _at__at_ FILEDESC_LOCK(fdp); if (fdp->fd_ofiles[fd] == fp) { fdp->fd_ofiles[fd] = NULL; + fd_unused(fdp, fd); extraref = 1; } FILEDESC_UNLOCK(fdp); _at__at_ -2182,10 +2299,9 _at__at_ #endif fdp->fd_ofiles[indx] = wfp; fdp->fd_ofileflags[indx] = fdp->fd_ofileflags[dfd]; + fd_used(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); _at__at_ -2210,21 +2326,8 _at__at_ 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; - } + fd_unused(fdp, dfd); + fd_used(fdp, indx); if (fp != NULL) FILE_LOCK(fp); FILEDESC_UNLOCK(fdp); --- //depot/user/tjr/freebsd-tjr/src/sys/kern/sys_pipe.c 2003/11/12 14:04:51 +++ //depot/user/tjr/freebsd-tjr/src/sys/kern/sys_pipe.c 2003/11/26 19:42:24 _at__at_ -258,6 +258,7 _at__at_ FILEDESC_LOCK(fdp); if (fdp->fd_ofiles[td->td_retval[0]] == rf) { fdp->fd_ofiles[td->td_retval[0]] = NULL; + fd_unused(fdp, td->td_retval[0]); FILEDESC_UNLOCK(fdp); fdrop(rf, td); } else --- //depot/user/tjr/freebsd-tjr/src/sys/kern/uipc_syscalls.c 2003/11/19 22:23:06 +++ //depot/user/tjr/freebsd-tjr/src/sys/kern/uipc_syscalls.c 2003/11/26 19:42:24 _at__at_ -124,6 +124,7 _at__at_ if (error) { if (fdp->fd_ofiles[fd] == fp) { fdp->fd_ofiles[fd] = NULL; + fd_unused(fdp, fd); FILEDESC_UNLOCK(fdp); fdrop(fp, td); } else _at__at_ -389,6 +390,7 _at__at_ FILEDESC_LOCK(fdp); if (fdp->fd_ofiles[fd] == nfp) { fdp->fd_ofiles[fd] = NULL; + fd_unused(fdp, fd); FILEDESC_UNLOCK(fdp); fdrop(nfp, td); } else { _at__at_ -583,6 +585,7 _at__at_ FILEDESC_LOCK(fdp); if (fdp->fd_ofiles[sv[1]] == fp2) { fdp->fd_ofiles[sv[1]] = NULL; + fd_unused(fdp, sv[1]); FILEDESC_UNLOCK(fdp); fdrop(fp2, td); } else _at__at_ -592,6 +595,7 _at__at_ FILEDESC_LOCK(fdp); if (fdp->fd_ofiles[sv[0]] == fp1) { fdp->fd_ofiles[sv[0]] = NULL; + fd_unused(fdp, sv[0]); FILEDESC_UNLOCK(fdp); fdrop(fp1, td); } else --- //depot/user/tjr/freebsd-tjr/src/sys/kern/vfs_syscalls.c 2003/11/12 14:04:51 +++ //depot/user/tjr/freebsd-tjr/src/sys/kern/vfs_syscalls.c 2003/11/26 19:42:24 _at__at_ -998,6 +998,7 _at__at_ FILEDESC_LOCK(fdp); if (fdp->fd_ofiles[indx] == fp) { fdp->fd_ofiles[indx] = NULL; + fd_unused(fdp, indx); FILEDESC_UNLOCK(fdp); fdrop(fp, td); } else _at__at_ -1090,6 +1091,7 _at__at_ FILEDESC_LOCK(fdp); if (fdp->fd_ofiles[indx] == fp) { fdp->fd_ofiles[indx] = NULL; + fd_unused(fdp, indx); FILEDESC_UNLOCK(fdp); fdrop(fp, td); } else _at__at_ -3978,6 +3980,7 _at__at_ FILEDESC_LOCK(fdp); if (fdp->fd_ofiles[indx] == fp) { fdp->fd_ofiles[indx] = NULL; + fd_unused(fdp, indx); FILEDESC_UNLOCK(fdp); fdrop(fp, td); } else --- //depot/user/tjr/freebsd-tjr/src/sys/sys/filedesc.h 2003/11/12 14:04:51 +++ //depot/user/tjr/freebsd-tjr/src/sys/sys/filedesc.h 2003/11/26 19:42:24 _at__at_ -57,6 +57,11 _at__at_ */ #define NDFILE 20 #define NDEXTENT 50 /* 250 bytes in 256-byte alloc. */ +#define NDENTRIES 32 /* 32 fds per entry */ +#define NDENTRYMASK (NDENTRIES - 1) +#define NDENTRYSHIFT 5 /* bits per entry */ +#define NDLOSLOTS(x) (((x) + NDENTRIES - 1) >> NDENTRYSHIFT) +#define NDHISLOTS(x) ((NDLOSLOTS(x) + NDENTRIES - 1) >> NDENTRYSHIFT) struct filedesc { struct file **fd_ofiles; /* file structures for open files */ _at__at_ -65,6 +70,8 _at__at_ struct vnode *fd_rdir; /* root directory */ struct vnode *fd_jdir; /* jail root directory */ int fd_nfiles; /* number of open files allocated */ + uint32_t *fd_himap; /* each bit points to 32 fds */ + uint32_t *fd_lomap; /* 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 */ _at__at_ -91,6 +98,12 _at__at_ */ struct file *fd_dfiles[NDFILE]; char fd_dfileflags[NDFILE]; + /* + * These arrays are used when the number of open files is + * <= 1024, and are then pointed to by the pointers above. + */ + uint32_t fd_dhimap[NDENTRIES >> NDENTRYSHIFT]; + uint32_t fd_dlomap[NDENTRIES]; }; _at__at_ -143,6 +156,7 _at__at_ 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 fd_find_last_set(struct filedesc *, int); int fdalloc(struct thread *p, int want, int *result); int fdavail(struct thread *td, int n); void fdcloseexec(struct thread *td); _at__at_ -170,6 +184,37 _at__at_ return ((u_int)fd >= (u_int)fdp->fd_nfiles ? NULL : fdp->fd_ofiles[fd]); } +static __inline void +fd_used(struct filedesc *fdp, int fd) +{ + u_int off = fd >> NDENTRYSHIFT; + + fdp->fd_lomap[off] |= 1 << (fd & NDENTRYMASK); + if (fdp->fd_lomap[off] == ~0) + fdp->fd_himap[off >> NDENTRYSHIFT] |= 1 << (off & NDENTRYMASK); + + if (fd > fdp->fd_lastfile) + fdp->fd_lastfile = fd; +} + +static __inline void +fd_unused(struct filedesc *fdp, int fd) +{ + u_int off = fd >> NDENTRYSHIFT; + + if (fd < fdp->fd_freefile) + fdp->fd_freefile = fd; + if (fdp->fd_lomap[off] == ~0) + fdp->fd_himap[off >> NDENTRYSHIFT] &= ~(1 << (off & NDENTRYMASK)); + fdp->fd_lomap[off] &= ~(1 << (fd & NDENTRYMASK)); +#ifdef KASSERT /* XXX */ + KASSERT(fd <= fdp->fd_lastfile, + ("fd_unused: fd_lastfile inconsistent")); +#endif + if (fd == fdp->fd_lastfile) + fdp->fd_lastfile = fd_find_last_set(fdp, fd); +} + extern struct mtx fdesc_mtx; #endif /* _KERNEL */Received on Wed Nov 26 2003 - 22:02:11 UTC
This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:37:31 UTC