From 57b24f4ee66aaaa59b54e9577b93253cf435672f Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Sun, 22 Aug 2010 15:30:08 -0700 Subject: [PATCH] kernel - Fix read event on file for select/poll API * select/poll have always returned an immediate read event on regular files, but kqueue is expected to only return a EVFILT_READ event when not sitting at the file EOF. * The kernel adds a NOTE_OLDAPI flag which filter functions can use to discern between select/poll and kqueue related knotes. * Adjust filesystem filter function to always return an immediate event for reads via select/poll. * Fixes guile, which for some reason beyond our ken select()'s for a read event on a file. Reported-by: Johannes Hofmann --- sys/kern/sys_generic.c | 12 ++++++------ sys/sys/event.h | 4 ++++ sys/vfs/gnu/ext2fs/ext2_vnops.c | 7 +++++-- sys/vfs/hammer/hammer_vnops.c | 11 +++++------ sys/vfs/ufs/ufs_vnops.c | 6 +++++- 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/sys/kern/sys_generic.c b/sys/kern/sys_generic.c index 9a36c331e2..0bab31d779 100644 --- a/sys/kern/sys_generic.c +++ b/sys/kern/sys_generic.c @@ -915,7 +915,7 @@ select_copyin(void *arg, struct kevent *kevp, int maxevents, int *events) */ fdp = skap->read_set; filter = EVFILT_READ; - fflags = 0; + fflags = NOTE_OLDAPI; if (fdp) break; ++skap->active_set; @@ -927,7 +927,7 @@ select_copyin(void *arg, struct kevent *kevp, int maxevents, int *events) */ fdp = skap->write_set; filter = EVFILT_WRITE; - fflags = 0; + fflags = NOTE_OLDAPI; if (fdp) break; ++skap->active_set; @@ -939,7 +939,7 @@ select_copyin(void *arg, struct kevent *kevp, int maxevents, int *events) */ fdp = skap->except_set; filter = EVFILT_EXCEPT; - fflags = NOTE_OOB; + fflags = NOTE_OLDAPI | NOTE_OOB; if (fdp) break; ++skap->active_set; @@ -1253,17 +1253,17 @@ poll_copyin(void *arg, struct kevent *kevp, int maxevents, int *events) kev = &kevp[*events]; if (pfd->events & (POLLIN | POLLRDNORM)) { EV_SET(kev++, pfd->fd, EVFILT_READ, EV_ADD|EV_ENABLE, - 0, 0, (void *)(uintptr_t) + NOTE_OLDAPI, 0, (void *)(uintptr_t) (pkap->lwp->lwp_kqueue_serial + pkap->pfds)); } if (pfd->events & (POLLOUT | POLLWRNORM)) { EV_SET(kev++, pfd->fd, EVFILT_WRITE, EV_ADD|EV_ENABLE, - 0, 0, (void *)(uintptr_t) + NOTE_OLDAPI, 0, (void *)(uintptr_t) (pkap->lwp->lwp_kqueue_serial + pkap->pfds)); } if (pfd->events & (POLLPRI | POLLRDBAND)) { EV_SET(kev++, pfd->fd, EVFILT_EXCEPT, EV_ADD|EV_ENABLE, - NOTE_OOB, 0, + NOTE_OLDAPI | NOTE_OOB, 0, (void *)(uintptr_t) (pkap->lwp->lwp_kqueue_serial + pkap->pfds)); } diff --git a/sys/sys/event.h b/sys/sys/event.h index b5681783b7..ae40ab007f 100644 --- a/sys/sys/event.h +++ b/sys/sys/event.h @@ -157,8 +157,12 @@ MALLOC_DECLARE(M_KQUEUE); /* * Flag indicating hint is a signal. Used by EVFILT_SIGNAL, and also * shared by EVFILT_PROC (all knotes attached to p->p_klist) + * + * NOTE_OLDAPI is used to signal that standard filters are being called + * from the select/poll wrapper. */ #define NOTE_SIGNAL 0x08000000 +#define NOTE_OLDAPI 0x04000000 /* select/poll note */ #define FILTEROP_ISFD 0x0001 /* if ident == filedescriptor */ #define FILTEROP_MPSAFE 0x0002 diff --git a/sys/vfs/gnu/ext2fs/ext2_vnops.c b/sys/vfs/gnu/ext2fs/ext2_vnops.c index 8283e8c735..bf0ec6e84c 100644 --- a/sys/vfs/gnu/ext2fs/ext2_vnops.c +++ b/sys/vfs/gnu/ext2fs/ext2_vnops.c @@ -1982,6 +1982,7 @@ filt_ext2read(struct knote *kn, long hint) { struct vnode *vp = (struct vnode *)kn->kn_hook; struct inode *ip = VTOI(vp); + off_t off; /* * filesystem is gone, so set the EOF flag and schedule @@ -1991,8 +1992,10 @@ filt_ext2read(struct knote *kn, long hint) kn->kn_flags |= (EV_EOF | EV_ONESHOT); return (1); } - - kn->kn_data = ip->i_size - kn->kn_fp->f_offset; + off = ip->i_size - kn->kn_fp->f_offset; + kn->kn_data = (off < INTPTR_MAX) ? off : INTPTR_MAX; + if (kn->kn_sfflags & NOTE_OLDAPI) + return(1); return (kn->kn_data != 0); } diff --git a/sys/vfs/hammer/hammer_vnops.c b/sys/vfs/hammer/hammer_vnops.c index d02b004cfa..05c4084e89 100644 --- a/sys/vfs/hammer/hammer_vnops.c +++ b/sys/vfs/hammer/hammer_vnops.c @@ -3384,10 +3384,7 @@ hammer_vop_kqfilter(struct vop_kqfilter_args *ap) kn->kn_hook = (caddr_t)vp; - /* XXX: kq token actually protects the list */ - lwkt_gettoken(&vp->v_token); knote_insert(&vp->v_pollinfo.vpi_kqinfo.ki_note, kn); - lwkt_reltoken(&vp->v_token); return(0); } @@ -3397,9 +3394,7 @@ filt_hammerdetach(struct knote *kn) { struct vnode *vp = (void *)kn->kn_hook; - lwkt_gettoken(&vp->v_token); knote_remove(&vp->v_pollinfo.vpi_kqinfo.ki_note, kn); - lwkt_reltoken(&vp->v_token); } static int @@ -3407,12 +3402,16 @@ filt_hammerread(struct knote *kn, long hint) { struct vnode *vp = (void *)kn->kn_hook; hammer_inode_t ip = VTOI(vp); + off_t off; if (hint == NOTE_REVOKE) { kn->kn_flags |= (EV_EOF | EV_ONESHOT); return(1); } - kn->kn_data = ip->ino_data.size - kn->kn_fp->f_offset; + off = ip->ino_data.size - kn->kn_fp->f_offset; + kn->kn_data = (off < INTPTR_MAX) ? off : INTPTR_MAX; + if (kn->kn_sfflags & NOTE_OLDAPI) + return(1); return (kn->kn_data != 0); } diff --git a/sys/vfs/ufs/ufs_vnops.c b/sys/vfs/ufs/ufs_vnops.c index 0640f4a83c..65fdd6d1ec 100644 --- a/sys/vfs/ufs/ufs_vnops.c +++ b/sys/vfs/ufs/ufs_vnops.c @@ -2174,6 +2174,7 @@ filt_ufsread(struct knote *kn, long hint) { struct vnode *vp = (struct vnode *)kn->kn_hook; struct inode *ip = VTOI(vp); + off_t off; /* * filesystem is gone, so set the EOF flag and schedule @@ -2184,7 +2185,10 @@ filt_ufsread(struct knote *kn, long hint) return (1); } - kn->kn_data = ip->i_size - kn->kn_fp->f_offset; + off = ip->i_size - kn->kn_fp->f_offset; + kn->kn_data = (off < INTPTR_MAX) ? off : INTPTR_MAX; + if (kn->kn_sfflags & NOTE_OLDAPI) + return(1); return (kn->kn_data != 0); } -- 2.41.0