Now that we have a_fflag in struct vop_poll_args we can handle
things like POLLOUT on a read-only fd more sensibly.  Previously,
any poll events could be used regardless of the actual file flags
of the descriptor.

Rather than call soo_poll() I've created a real fifo_poll() that
checks the socket status.  With this change, POLLOUT on a read-only
fd will be ignored.

This is similar to what Linux does, though Linux will return POLLHUP
when the writer closes even when there are no valid events specified.

I have updated regress tests too...

 - todd

Index: sys/miscfs/fifofs/fifo_vnops.c
===================================================================
RCS file: /cvs/src/sys/miscfs/fifofs/fifo_vnops.c,v
retrieving revision 1.45
diff -u -p -u -r1.45 fifo_vnops.c
--- sys/miscfs/fifofs/fifo_vnops.c      12 Feb 2015 14:31:02 -0000      1.45
+++ sys/miscfs/fifofs/fifo_vnops.c      4 May 2015 20:41:22 -0000
@@ -38,6 +38,7 @@
 #include <sys/namei.h>
 #include <sys/vnode.h>
 #include <sys/lock.h>
+#include <sys/protosw.h>
 #include <sys/socket.h>
 #include <sys/socketvar.h>
 #include <sys/stat.h>
@@ -289,23 +290,46 @@ int
 fifo_poll(void *v)
 {
        struct vop_poll_args *ap = v;
-       struct file filetmp;
-       const int events = ap->a_events;
+       struct socket *rso = ap->a_vp->v_fifoinfo->fi_readsock;
+       struct socket *wso = ap->a_vp->v_fifoinfo->fi_writesock;
+       int events = 0;
        int revents = 0;
+       int s;
 
-       if (events & (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)) {
-               filetmp.f_data = ap->a_vp->v_fifoinfo->fi_readsock;
-               if (filetmp.f_data)
-                       revents |= soo_poll(&filetmp, events, ap->a_p);
+       if (ap->a_fflag & FREAD)
+               events |= ap->a_events & (POLLIN | POLLPRI | POLLRDNORM | 
POLLRDBAND);
+       if (ap->a_fflag & FWRITE)
+               events |= ap->a_events & (POLLOUT | POLLWRNORM | POLLWRBAND);
+       if (events == 0)
+               return (0);
+
+       s = splsoftnet();
+       if (events & (POLLIN | POLLRDNORM)) {
+               if (soreadable(rso))
+                       revents |= events & (POLLIN | POLLRDNORM);
+       }
+       /* NOTE: POLLHUP and POLLOUT/POLLWRNORM are mutually exclusive */
+       if (rso->so_state & SS_ISDISCONNECTED) {
+               revents |= POLLHUP;
+       } else if (events & (POLLOUT | POLLWRNORM)) {
+               if (sowriteable(wso))
+                       revents |= events & (POLLOUT | POLLWRNORM);
        }
-       /* POLLHUP and POLLOUT/POLLWRNORM/POLLWRBAND are mutually exclusive */
-       if (!(revents & POLLHUP)) {
-               if (events & (POLLOUT | POLLWRNORM | POLLWRBAND)) {
-                       filetmp.f_data = ap->a_vp->v_fifoinfo->fi_writesock;
-                       if (filetmp.f_data)
-                               revents |= soo_poll(&filetmp, events, ap->a_p);
+       if (events & (POLLPRI | POLLRDBAND)) {
+               if (rso->so_oobmark || (rso->so_state & SS_RCVATMARK))
+                       revents |= events & (POLLPRI | POLLRDBAND);
+       }
+       if (revents == 0) {
+               if (events & (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)) {
+                       selrecord(ap->a_p, &rso->so_rcv.sb_sel);
+                       rso->so_rcv.sb_flagsintr |= SB_SEL;
+               }
+               if (events & (POLLOUT | POLLWRNORM)) {
+                       selrecord(ap->a_p, &wso->so_snd.sb_sel);
+                       wso->so_snd.sb_flagsintr |= SB_SEL;
                }
        }
+       splx(s);
        return (revents);
 }
 

Reply via email to