Index: sys/kern/kern_exec.c =================================================================== --- sys/kern/kern_exec.c (revision 195617) +++ sys/kern/kern_exec.c (working copy) @@ -836,7 +836,7 @@ done1: int tvfslocked; tvfslocked = VFS_LOCK_GIANT(tracevp->v_mount); - vrele(tracevp); + ktrvrele(tracevp); VFS_UNLOCK_GIANT(tvfslocked); } if (tracecred != NULL) Index: sys/kern/kern_exit.c =================================================================== --- sys/kern/kern_exit.c (revision 195617) +++ sys/kern/kern_exit.c (working copy) @@ -372,7 +372,7 @@ exit1(struct thread *td, int rv) PROC_UNLOCK(p); if (tracevp != NULL) { locked = VFS_LOCK_GIANT(tracevp->v_mount); - vrele(tracevp); + ktrvrele(tracevp); VFS_UNLOCK_GIANT(locked); } if (tracecred != NULL) Index: sys/kern/kern_fork.c =================================================================== --- sys/kern/kern_fork.c (revision 195617) +++ sys/kern/kern_fork.c (working copy) @@ -652,7 +652,7 @@ again: if (p1->p_traceflag & KTRFAC_INHERIT) { p2->p_traceflag = p1->p_traceflag; if ((p2->p_tracevp = p1->p_tracevp) != NULL) { - VREF(p2->p_tracevp); + ktrvref(p2->p_tracevp); KASSERT(p1->p_tracecred != NULL, ("ktrace vnode with no cred")); p2->p_tracecred = crhold(p1->p_tracecred); Index: sys/kern/kern_ktrace.c =================================================================== --- sys/kern/kern_ktrace.c (revision 195617) +++ sys/kern/kern_ktrace.c (working copy) @@ -100,6 +100,12 @@ struct ktr_request { STAILQ_ENTRY(ktr_request) ktr_list; }; +struct ktr_tracefile { + struct vnode *vp; + u_int refcnt; + LIST_ENTRY(ktr_tracefile) link; +}; + static int data_lengths[] = { 0, /* none */ offsetof(struct ktr_syscall, ktr_args), /* KTR_SYSCALL */ @@ -114,6 +120,7 @@ static int data_lengths[] = { }; static STAILQ_HEAD(, ktr_request) ktr_free; +static LIST_HEAD(, ktr_tracefile) ktr_tracefilelist; static SYSCTL_NODE(_kern, OID_AUTO, ktrace, CTLFLAG_RD, 0, "KTRACE options"); @@ -127,6 +134,7 @@ SYSCTL_UINT(_kern_ktrace, OID_AUTO, geni static int print_message = 1; struct mtx ktrace_mtx; +static struct mtx ktr_tracefilelist_mtx; static struct sx ktrace_sx; static void ktrace_init(void *dummy); @@ -135,6 +143,8 @@ static u_int ktrace_resize_pool(u_int ne static struct ktr_request *ktr_getrequest(int type); static void ktr_submitrequest(struct thread *td, struct ktr_request *req); static void ktr_freerequest(struct ktr_request *req); +static int ktr_vn_open(const char *fname, struct vnode **vpp, + struct thread *td); static void ktr_writerequest(struct thread *td, struct ktr_request *req); static int ktrcanset(struct thread *,struct proc *); static int ktrsetchildren(struct thread *,struct proc *,int,int,struct vnode *); @@ -176,12 +186,15 @@ ktrace_init(void *dummy) int i; mtx_init(&ktrace_mtx, "ktrace", NULL, MTX_DEF | MTX_QUIET); + mtx_init(&ktr_tracefilelist_mtx, "ktrace tracefile list", NULL, + MTX_DEF | MTX_QUIET); sx_init(&ktrace_sx, "ktrace_sx"); STAILQ_INIT(&ktr_free); for (i = 0; i < ktr_requestpool; i++) { req = malloc(sizeof(struct ktr_request), M_KTRACE, M_WAITOK); STAILQ_INSERT_HEAD(&ktr_free, req, ktr_list); } + LIST_INIT(&ktr_tracefilelist); } SYSINIT(ktrace_init, SI_SUB_KTRACE, SI_ORDER_ANY, ktrace_init, NULL); @@ -376,6 +389,59 @@ ktr_freerequest(struct ktr_request *req) mtx_unlock(&ktrace_mtx); } +static int +ktr_vn_open(const char *fname, struct vnode **vpp, struct thread *td) +{ + struct nameidata nd; + struct ktr_tracefile *tf, *tf_new; + struct vnode *vp; + int error, flags, vfslocked; + + tf_new = malloc(sizeof(struct ktr_tracefile), M_KTRACE, M_WAITOK); + + NDINIT(&nd, LOOKUP, NOFOLLOW | MPSAFE, UIO_USERSPACE, fname, td); + flags = FREAD | FWRITE | O_NOFOLLOW; + error = vn_open(&nd, &flags, 0, NULL); + if (error) { + free(tf_new, M_KTRACE); + return (error); + } + + vfslocked = NDHASGIANT(&nd); + NDFREE(&nd, NDF_ONLY_PNBUF); + vp = nd.ni_vp; + VOP_UNLOCK(vp, 0); + + if (vp->v_type != VREG) { + error = EACCES; + goto out; + } + + *vpp = vp; + + mtx_lock(&ktr_tracefilelist_mtx); + LIST_FOREACH(tf, &ktr_tracefilelist, link) { + if (tf->vp == vp) { + /* already open, just increase the reference count */ + tf->refcnt++; + mtx_unlock(&ktr_tracefilelist_mtx); + goto out; + } + } + tf_new->vp = vp; + tf_new->refcnt = 1; + LIST_INSERT_HEAD(&ktr_tracefilelist, tf_new, link); + mtx_unlock(&ktr_tracefilelist_mtx); + VFS_UNLOCK_GIANT(vfslocked); + return (0); + +out: + (void)vn_close(vp, FREAD | FWRITE, td->td_ucred, td); + VFS_UNLOCK_GIANT(vfslocked); + free(tf_new, M_KTRACE); + return (error); +} + void ktrsyscall(code, narg, args) int code, narg; @@ -616,6 +682,51 @@ ktrstruct(name, namelen, data, datalen) req->ktr_header.ktr_len = buflen; ktr_submitrequest(curthread, req); } + +void +ktrvref(struct vnode *vp) +{ + struct ktr_tracefile *tf; + + mtx_lock(&ktr_tracefilelist_mtx); + LIST_FOREACH(tf, &ktr_tracefilelist, link) { + if (tf->vp == vp) { + tf->refcnt++; + mtx_unlock(&ktr_tracefilelist_mtx); + return; + } + } + panic("ktrvref: vnode %p not open", vp); +} + +void +ktrvrele(struct vnode *vp) +{ + struct ktr_tracefile *tf; + int closevp = 0; + + mtx_lock(&ktr_tracefilelist_mtx); + LIST_FOREACH(tf, &ktr_tracefilelist, link) { + if (tf->vp == vp) + goto found; + } + panic("ktrvrele: vnode %p not open", vp); +found: + if (--tf->refcnt == 0) { + LIST_REMOVE(tf, link); + closevp = 1; + } + mtx_unlock(&ktr_tracefilelist_mtx); + + if (closevp) { + free(tf, M_KTRACE); + /* + * The original thread which opened the file may not exist. + * Thus we are using NULL td here. + */ + (void)vn_close(vp, FREAD | FWRITE, NOCRED, NULL); + } +} #endif /* KTRACE */ /* Interface and common routines */ @@ -635,15 +746,14 @@ ktrace(td, uap) register struct ktrace_args *uap; { #ifdef KTRACE - register struct vnode *vp = NULL; - register struct proc *p; + struct vnode *vp = NULL; + struct proc *p; struct pgrp *pg; int facs = uap->facs & ~KTRFAC_ROOT; int ops = KTROP(uap->ops); int descend = uap->ops & KTRFLAG_DESCEND; int nfound, ret = 0; - int flags, error = 0, vfslocked; - struct nameidata nd; + int error = 0, vfslocked; struct ucred *cred; /* @@ -657,25 +767,11 @@ ktrace(td, uap) /* * an operation which requires a file argument. */ - NDINIT(&nd, LOOKUP, NOFOLLOW | MPSAFE, UIO_USERSPACE, - uap->fname, td); - flags = FREAD | FWRITE | O_NOFOLLOW; - error = vn_open(&nd, &flags, 0, NULL); + error = ktr_vn_open(uap->fname, &vp, td); if (error) { ktrace_exit(td); return (error); } - vfslocked = NDHASGIANT(&nd); - NDFREE(&nd, NDF_ONLY_PNBUF); - vp = nd.ni_vp; - VOP_UNLOCK(vp, 0); - if (vp->v_type != VREG) { - (void) vn_close(vp, FREAD|FWRITE, td->td_ucred, td); - VFS_UNLOCK_GIANT(vfslocked); - ktrace_exit(td); - return (EACCES); - } - VFS_UNLOCK_GIANT(vfslocked); } /* * Clear all uses of the tracefile. @@ -706,7 +802,7 @@ ktrace(td, uap) if (vrele_count > 0) { vfslocked = VFS_LOCK_GIANT(vp->v_mount); while (vrele_count-- > 0) - vrele(vp); + ktrvrele(vp); VFS_UNLOCK_GIANT(vfslocked); } goto done; @@ -780,7 +876,7 @@ ktrace(td, uap) done: if (vp != NULL) { vfslocked = VFS_LOCK_GIANT(vp->v_mount); - (void) vn_close(vp, FWRITE, td->td_ucred, td); + ktrvrele(vp); VFS_UNLOCK_GIANT(vfslocked); } ktrace_exit(td); @@ -849,7 +945,7 @@ ktrops(td, p, ops, facs, vp) * if trace file already in use, relinquish below */ tracevp = p->p_tracevp; - VREF(vp); + ktrvref(vp); p->p_tracevp = vp; } if (p->p_tracecred != td->td_ucred) { @@ -876,7 +972,7 @@ ktrops(td, p, ops, facs, vp) int vfslocked; vfslocked = VFS_LOCK_GIANT(tracevp->v_mount); - vrele(tracevp); + ktrvrele(tracevp); VFS_UNLOCK_GIANT(vfslocked); } if (tracecred != NULL) @@ -935,9 +1031,6 @@ ktr_writerequest(struct thread *td, stru /* * We hold the vnode and credential for use in I/O in case ktrace is * disabled on the process as we write out the request. - * - * XXXRW: This is not ideal: we could end up performing a write after - * the vnode has been closed. */ mtx_lock(&ktrace_mtx); vp = td->td_proc->p_tracevp; @@ -953,7 +1046,7 @@ ktr_writerequest(struct thread *td, stru mtx_unlock(&ktrace_mtx); return; } - VREF(vp); + ktrvref(vp); KASSERT(cred != NULL, ("ktr_writerequest: cred == NULL")); crhold(cred); mtx_unlock(&ktrace_mtx); @@ -1000,7 +1093,7 @@ ktr_writerequest(struct thread *td, stru vn_finished_write(mp); crfree(cred); if (!error) { - vrele(vp); + ktrvrele(vp); VFS_UNLOCK_GIANT(vfslocked); return; } @@ -1053,7 +1146,7 @@ ktr_writerequest(struct thread *td, stru */ vfslocked = VFS_LOCK_GIANT(vp->v_mount); while (vrele_count-- > 0) - vrele(vp); + ktrvrele(vp); VFS_UNLOCK_GIANT(vfslocked); } Index: sys/sys/ktrace.h =================================================================== --- sys/sys/ktrace.h (revision 195617) +++ sys/sys/ktrace.h (working copy) @@ -207,6 +207,8 @@ void ktrstruct(const char *, size_t, voi ktrstruct("sockaddr", 8, (s), ((struct sockaddr *)(s))->sa_len) #define ktrstat(s) \ ktrstruct("stat", 4, (s), sizeof(struct stat)) +void ktrvref(struct vnode *); +void ktrvrele(struct vnode *); #else Index: sys/kern/subr_witness.c =================================================================== --- sys/kern/subr_witness.c (revision 195617) +++ sys/kern/subr_witness.c (working copy) @@ -617,6 +617,12 @@ static struct witness_order_list_entry o { "db->db_mtx", &lock_class_sx }, { NULL, NULL }, /* + * ktrace + */ + { "ktrace", &lock_class_mtx_sleep }, + { "ktrace tracefile list", &lock_class_mtx_sleep }, + { NULL, NULL }, + /* * spin locks */ #ifdef SMP