Index: sys/compat/linuxkpi/common/include/linux/file.h =================================================================== --- sys/compat/linuxkpi/common/include/linux/file.h +++ sys/compat/linuxkpi/common/include/linux/file.h @@ -71,13 +71,21 @@ static inline void fput(struct linux_file *filp) { - if (refcount_release(filp->_file == NULL ? - &filp->f_count : &filp->_file->f_count)) { + volatile u_long *count; + u_long old; + + count = &filp->f_count; + if (filp->_file != NULL) + count = &filp->_file->f_count; + atomic_thread_fence_rel(); + old = atomic_fetchadd_long(count, -1); + KASSERT(old > 0, ("%s: bad count %ld in fput(%p)\n", + __func__, old, filp)); + if (__predict_false(old == 1)) linux_file_free(filp); - } } -static inline unsigned int +static inline unsigned long file_count(struct linux_file *filp) { return (filp->_file == NULL ? @@ -108,6 +116,7 @@ fd_install(unsigned int fd, struct linux_file *filp) { struct file *file; + u_long count; if (fget_unlocked(curthread->td_proc->p_fd, fd, &cap_no_rights, &file, NULL) != 0) { @@ -117,8 +126,8 @@ finit(file, filp->f_mode, DTYPE_DEV, filp, &linuxfileops); /* transfer reference count from "filp" to "file" */ - while (refcount_release(&filp->f_count) == 0) - refcount_acquire(&file->f_count); + count = atomic_swap_long(&filp->f_count, 0); + atomic_add_long(&file->f_count, count); } /* drop the extra reference */ Index: sys/compat/linuxkpi/common/include/linux/fs.h =================================================================== --- sys/compat/linuxkpi/common/include/linux/fs.h +++ sys/compat/linuxkpi/common/include/linux/fs.h @@ -92,7 +92,7 @@ struct sigio *f_sigio; struct vnode *f_vnode; #define f_inode f_vnode - volatile u_int f_count; + volatile u_long f_count; /* anonymous shmem object */ vm_object_t f_shmem; @@ -250,8 +250,12 @@ static inline struct linux_file * get_file(struct linux_file *f) { + volatile u_long *count; - refcount_acquire(f->_file == NULL ? &f->f_count : &f->_file->f_count); + count = &f->f_count; + if (f->_file != NULL) + count = &f->_file->f_count; + atomic_add_long(count, 1); return (f); } Index: sys/compat/linuxkpi/common/src/linux_compat.c =================================================================== --- sys/compat/linuxkpi/common/src/linux_compat.c +++ sys/compat/linuxkpi/common/src/linux_compat.c @@ -1496,7 +1496,7 @@ filp = (struct linux_file *)file->f_data; KASSERT(file_count(filp) == 0, - ("File refcount(%d) is not zero", file_count(filp))); + ("File refcount(%ld) is not zero", file_count(filp))); if (td == NULL) td = curthread; Index: sys/kern/kern_descrip.c =================================================================== --- sys/kern/kern_descrip.c +++ sys/kern/kern_descrip.c @@ -922,10 +922,6 @@ goto unlock; } - oldfde = &fdp->fd_ofiles[old]; - if (!fhold(oldfde->fde_file)) - goto unlock; - /* * If the caller specified a file descriptor, make sure the file * table is large enough to hold it, and grab it. Otherwise, just @@ -934,17 +930,13 @@ switch (mode) { case FDDUP_NORMAL: case FDDUP_FCNTL: - if ((error = fdalloc(td, new, &new)) != 0) { - fdrop(oldfde->fde_file, td); + if ((error = fdalloc(td, new, &new)) != 0) goto unlock; - } break; case FDDUP_MUSTREPLACE: /* Target file descriptor must exist. */ - if (fget_locked(fdp, new) == NULL) { - fdrop(oldfde->fde_file, td); + if (fget_locked(fdp, new) == NULL) goto unlock; - } break; case FDDUP_FIXED: if (new >= fdp->fd_nfiles) { @@ -961,7 +953,6 @@ error = racct_set_unlocked(p, RACCT_NOFILE, new + 1); if (error != 0) { error = EMFILE; - fdrop(oldfde->fde_file, td); goto unlock; } } @@ -977,6 +968,8 @@ KASSERT(old != new, ("new fd is same as old")); + oldfde = &fdp->fd_ofiles[old]; + fhold(oldfde->fde_file); newfde = &fdp->fd_ofiles[new]; delfp = newfde->fde_file; @@ -1932,7 +1925,7 @@ } fp = uma_zalloc(file_zone, M_WAITOK); bzero(fp, sizeof(*fp)); - refcount_init(&fp->f_count, 1); + fp->f_count = 1; fp->f_cred = crhold(td->td_ucred); fp->f_ops = &badfileops; *resultfp = fp; @@ -1977,8 +1970,7 @@ MPASS(fd != NULL); - if (!fhold(fp)) - return (EBADF); + fhold(fp); FILEDESC_XLOCK(fdp); if ((error = fdalloc(td, 0, fd))) { FILEDESC_XUNLOCK(fdp); @@ -2125,8 +2117,7 @@ for (i = 0; i <= fdp->fd_lastfile; ++i) { ofde = &fdp->fd_ofiles[i]; if (ofde->fde_file == NULL || - (ofde->fde_file->f_ops->fo_flags & DFLAG_PASSABLE) == 0 || - !fhold(ofde->fde_file)) { + (ofde->fde_file->f_ops->fo_flags & DFLAG_PASSABLE) == 0) { if (newfdp->fd_freefile == -1) newfdp->fd_freefile = i; continue; @@ -2134,6 +2125,7 @@ nfde = &newfdp->fd_ofiles[i]; *nfde = *ofde; filecaps_copy(&ofde->fde_caps, &nfde->fde_caps, true); + fhold(nfde->fde_file); fdused_init(newfdp, i); newfdp->fd_lastfile = i; } @@ -2186,13 +2178,10 @@ error = EINVAL; goto bad; } - if (!fhold(nfde->fde_file)) { - error = EBADF; - goto bad; - } nfde = &newfdp->fd_ofiles[i]; *nfde = *ofde; filecaps_copy(&ofde->fde_caps, &nfde->fde_caps, true); + fhold(nfde->fde_file); fdused_init(newfdp, i); newfdp->fd_lastfile = i; } @@ -2234,9 +2223,9 @@ (p->p_leader->p_flag & P_ADVLOCK) != 0) { for (i = 0; i <= fdp->fd_lastfile; i++) { fp = fdp->fd_ofiles[i].fde_file; - if (fp == NULL || fp->f_type != DTYPE_VNODE || - !fhold(fp)) + if (fp == NULL || fp->f_type != DTYPE_VNODE) continue; + fhold(fp); FILEDESC_XUNLOCK(fdp); lf.l_whence = SEEK_SET; lf.l_start = 0; @@ -2677,8 +2666,8 @@ get_locked: FILEDESC_SLOCK(fdp); error = fget_cap_locked(fdp, fd, needrightsp, fpp, havecapsp); - if (error == 0 && !fhold(*fpp)) - error = EBADF; + if (error == 0) + fhold(*fpp); FILEDESC_SUNLOCK(fdp); #endif return (error); @@ -2693,7 +2682,7 @@ #endif const struct fdescenttbl *fdt; struct file *fp; - u_int count; + u_long count; #ifdef CAPABILITIES seqc_t seq; cap_rights_t haverights; @@ -2740,14 +2729,11 @@ fdt = (struct fdescenttbl *)atomic_load_ptr(&fdp->fd_files); continue; } - if (__predict_false(count + 1 < count)) - return (EBADF); - /* * Use an acquire barrier to force re-reading of fdt so it is * refreshed for verification. */ - if (__predict_false(atomic_fcmpset_acq_int(&fp->f_count, + if (__predict_false(atomic_fcmpset_acq_long(&fp->f_count, &count, count + 1) == 0)) goto retry; fdt = fdp->fd_files; @@ -3018,7 +3004,7 @@ int error; if (fp->f_count != 0) - panic("fdrop: count %d", fp->f_count); + panic("fdrop: count %lu", fp->f_count); error = fo_close(fp, td); atomic_subtract_int(&openfiles, 1); crfree(fp->f_cred); @@ -3133,11 +3119,7 @@ FILEDESC_XUNLOCK(fdp); return (EACCES); } - if (!fhold(fp)) { - fdunused(fdp, indx); - FILEDESC_XUNLOCK(fdp); - return (EBADF); - } + fhold(fp); newfde = &fdp->fd_ofiles[indx]; oldfde = &fdp->fd_ofiles[dfd]; ioctls = filecaps_copy_prep(&oldfde->fde_caps); @@ -4040,7 +4022,7 @@ "GCFl", "Count", "MCount", XPTRWIDTH, "Vnode", "FPID", "FCmd"); p = file_to_first_proc(fp); - db_printf("%*p %6s %*p %08x %04x %5d %6d %*p %5d %s\n", XPTRWIDTH, + db_printf("%*p %6s %*p %08x %04x %5lu %6d %*p %5d %s\n", XPTRWIDTH, fp, file_type_to_name(fp->f_type), XPTRWIDTH, fp->f_data, fp->f_flag, 0, fp->f_count, 0, XPTRWIDTH, fp->f_vnode, p != NULL ? p->p_pid : -1, p != NULL ? p->p_comm : "-"); Index: sys/kern/sys_generic.c =================================================================== --- sys/kern/sys_generic.c +++ sys/kern/sys_generic.c @@ -754,11 +754,7 @@ fp = NULL; /* fhold() was not called yet */ goto out; } - if (!fhold(fp)) { - error = EBADF; - fp = NULL; - goto out; - } + fhold(fp); if (locked == LA_SLOCKED) { FILEDESC_SUNLOCK(fdp); locked = LA_UNLOCKED; Index: sys/kern/uipc_usrreq.c =================================================================== --- sys/kern/uipc_usrreq.c +++ sys/kern/uipc_usrreq.c @@ -2152,7 +2152,7 @@ struct timespec *ts; void *data; socklen_t clen, datalen; - int i, j, error, *fdp, oldfds; + int i, error, *fdp, oldfds; u_int newlen; UNP_LINK_UNLOCK_ASSERT(); @@ -2235,19 +2235,6 @@ goto out; } fdp = data; - for (i = 0; i < oldfds; i++, fdp++) { - if (!fhold(fdesc->fd_ofiles[*fdp].fde_file)) { - fdp = data; - for (j = 0; j < i; j++, fdp++) { - fdrop(fdesc->fd_ofiles[*fdp]. - fde_file, td); - } - FILEDESC_SUNLOCK(fdesc); - error = EBADF; - goto out; - } - } - fdp = data; fdep = (struct filedescent **) CMSG_DATA(mtod(*controlp, struct cmsghdr *)); fdev = malloc(sizeof(*fdev) * oldfds, M_FILECAPS, @@ -2452,6 +2439,7 @@ unp->unp_file = fp; unp->unp_msgcount++; } + fhold(fp); unp_rights++; UNP_LINK_WUNLOCK(); } @@ -2670,10 +2658,10 @@ unp->unp_gcflag &= ~UNPGC_DEAD; f = unp->unp_file; if (unp->unp_msgcount == 0 || f == NULL || - f->f_count != unp->unp_msgcount || - !fhold(f)) + f->f_count != unp->unp_msgcount) continue; unref[total++] = f; + fhold(f); KASSERT(total <= unp_unreachable, ("%s: incorrect unreachable count.", __func__)); } Index: sys/sys/file.h =================================================================== --- sys/sys/file.h +++ sys/sys/file.h @@ -182,7 +182,7 @@ short f_type; /* descriptor type */ short f_vnread_flags; /* (f) Sleep lock for f_offset */ volatile u_int f_flag; /* see fcntl.h */ - volatile u_int f_count; /* reference count */ + volatile u_long f_count; /* reference count */ /* * DTYPE_VNODE specific fields. */ @@ -200,10 +200,6 @@ * DFLAG_SEEKABLE specific fields */ off_t f_offset; - /* - * Mandatory Access control information. - */ - void *f_label; /* Place-holder for MAC label. */ }; #define f_cdevpriv f_vnun.fvn_cdevpriv @@ -282,22 +278,27 @@ int fgetvp_write(struct thread *td, int fd, cap_rights_t *rightsp, struct vnode **vpp); -static __inline int -_fnoop(void) +static __inline void +fhold(struct file *fp) { - return (0); + atomic_add_long(&fp->f_count, 1); } -static __inline __result_use_check bool -fhold(struct file *fp) +static __inline int +fdrop(struct file *fp, struct thread *td) { - return (refcount_acquire_checked(&fp->f_count)); + long old; + + atomic_thread_fence_rel(); + old = atomic_fetchadd_long(&fp->f_count, -1); + KASSERT(old > 0, ("%s: bad count %ld in fdrop(%p)\n", + __func__, old, fp)); + if (__predict_false(old == 1)) + return (_fdrop(fp, td)); + return (0); } -#define fdrop(fp, td) \ - (refcount_release(&(fp)->f_count) ? _fdrop((fp), (td)) : _fnoop()) - static __inline fo_rdwr_t fo_read; static __inline fo_rdwr_t fo_write; static __inline fo_truncate_t fo_truncate;