Index: src/sys/kern/kern_event.c =================================================================== --- src/sys/kern/kern_event.c (revision 163) +++ src/sys/kern/kern_event.c (working copy) @@ -45,8 +45,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -57,6 +59,7 @@ #include #include #include +#include #include @@ -132,6 +135,9 @@ static int filt_timerattach(struct knote *kn); static void filt_timerdetach(struct knote *kn); static int filt_timer(struct knote *kn, long hint); +static int filt_nvnodeattach(struct knote *kn); +static void filt_nvnodedetach(struct knote *kn); +static int filt_nvnode(struct knote *kn, long hint); static struct filterops file_filtops = { 1, filt_fileattach, NULL, NULL }; @@ -142,6 +148,8 @@ { 0, filt_procattach, filt_procdetach, filt_proc }; static struct filterops timer_filtops = { 0, filt_timerattach, filt_timerdetach, filt_timer }; +static struct filterops nvnode_filtops = + { 0, filt_nvnodeattach, filt_nvnodedetach, filt_nvnode }; static uma_zone_t knote_zone; static int kq_ncallouts = 0; @@ -228,6 +236,7 @@ { &timer_filtops }, /* EVFILT_TIMER */ { &file_filtops }, /* EVFILT_NETDEV */ { &fs_filtops }, /* EVFILT_FS */ + { &nvnode_filtops }, /* EVFILT_NAMEDVNODE */ }; /* @@ -478,7 +487,91 @@ return (kn->kn_data != 0); } +static int maxwatchedvnodes = 8192; + +SYSCTL_INT(_kern, OID_AUTO, maxwatchedvnodes, CTLFLAG_RW, + &maxwatchedvnodes, 0, "Maximum number of watched vnodes by kqueue per " + "user"); + /* + * Attaches a knote to a vnode found from a path. + * Note: The vnode does not get vput(). + */ +static int +filt_nvnodeattach(struct knote *kn) +{ + struct vnode *vp; + struct nameidata nd; + struct uidinfo *uip; + int error, vfslocked; + + error = 0; + + NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_SYSSPACE, + (char *)kn->kn_id, curthread); + + if ((error = namei(&nd)) != 0) + return (error); + + vfslocked = NDHASGIANT(&nd); + vp = nd.ni_vp; + kn->kn_hook = vp; + + if (vp->v_pollinfo == NULL) + v_addpollinfo(vp); + if (vp->v_pollinfo == NULL) { + error = ENOMEM; + goto done; + } + + uip = curthread->td_ucred->cr_uidinfo; + UIDINFO_LOCK(uip); + if (uip->ui_watchedvnodes >= maxwatchedvnodes) { + UIDINFO_UNLOCK(uip); + error = EMFILE; + goto done; + } + uip->ui_watchedvnodes++; + UIDINFO_UNLOCK(uip); + + knlist_add(&vp->v_pollinfo->vpi_selinfo.si_note, kn, 0); +done: + NDFREE(&nd, NDF_ONLY_PNBUF); + VOP_UNLOCK(vp, 0, curthread); + VFS_UNLOCK_GIANT(vfslocked); + + return (error); +} + +static void +filt_nvnodedetach(struct knote *kn) +{ + struct uidinfo *uip; + struct vnode *vp; + + vp = (struct vnode *)kn->kn_hook; + KASSERT(vp->v_pollinfo != NULL, ("Missing v_pollinfo")); + uip = curthread->td_ucred->cr_uidinfo; + UIDINFO_LOCK(uip); + uip->ui_watchedvnodes--; + UIDINFO_UNLOCK(uip); + knlist_remove(&vp->v_pollinfo->vpi_selinfo.si_note, kn, 0); + vrele(vp); +} + +static int +filt_nvnode(struct knote *kn, long hint) +{ + if (kn->kn_sfflags & hint) + kn->kn_fflags |= hint; + if (hint & NOTE_REVOKE) { + kn->kn_flags |= EV_EOF; + return (1); + } + return (kn->kn_fflags != 0); +} + +/* * MPSAFE */ int Index: src/sys/sys/resourcevar.h =================================================================== --- src/sys/sys/resourcevar.h (revision 163) +++ src/sys/sys/resourcevar.h (working copy) @@ -99,6 +99,7 @@ long ui_proccnt; /* (b) number of processes */ uid_t ui_uid; /* (a) uid */ u_int ui_ref; /* (b) reference count */ + long ui_watchedvnodes; /* (b) number of watched vnodes */ struct mtx *ui_mtxp; /* protect all counts/limits */ }; Index: src/sys/sys/event.h =================================================================== --- src/sys/sys/event.h (revision 163) +++ src/sys/sys/event.h (working copy) @@ -38,8 +38,9 @@ #define EVFILT_TIMER (-7) /* timers */ #define EVFILT_NETDEV (-8) /* network devices */ #define EVFILT_FS (-9) /* filesystem events */ +#define EVFILT_NAMEDVNODE (-10) /* vnodes from paths */ -#define EVFILT_SYSCOUNT 9 +#define EVFILT_SYSCOUNT 10 #define EV_SET(kevp_, a, b, c, d, e, f) do { \ struct kevent *kevp = (kevp_); \