On Tue, Dec 15, 2020 at 05:23:45PM +0000, Visa Hankala wrote:
> On Tue, Dec 15, 2020 at 07:46:01AM -0300, Martin Pieuchot wrote:
> > @@ -636,43 +651,59 @@ dopselect(struct proc *p, int nd, fd_set
> >     if (sigmask)
> >             dosigsuspend(p, *sigmask &~ sigcantmask);
> >  
> > -retry:
> > -   ncoll = nselcoll;
> > -   atomic_setbits_int(&p->p_flag, P_SELECT);
> > -   error = selscan(p, pibits[0], pobits[0], nd, ni, retval);
> > -   if (error || *retval)
> > +   /* Register kqueue events */
> > +   if ((error = pselregister(p, pibits[0], nd, ni, &nevents) != 0))
> >             goto done;
> 
> The above has parentheses wrong and returns 1 (EPERM) as error.
> The lines should read:
> 
>       if ((error = pselregister(p, pibits[0], nd, ni, &nevents)) != 0)
>               goto done;
> 
> 
> In addition to the above, I noticed that select(2) behaves differently
> than before when a file descriptor that is being monitored is closed by
> another thread. The old implementation returns EBADF. The new code keeps
> on waiting on the underlying object.
> 
> The diff below makes kqueue clear kqpoll's fd event registration on
> fd close. However, it does not make select(2) return an error, the fd
> just will not cause a wakeup any longer. I think I have an idea on how
> to correct that but I need to consider it some more.

The following diff adds the ability to return EBADF on fd close. The
idea is to set the error pending on the kqueue instance and deliver it
as the return value of kqueue_scan().

The error condition could also be reported via a kevent struct that
kqueue_scan() returns: set EV_ERROR in kev->flags and error code
in kev->data. However, doing so is somewhat contrived. knote_remove()
would have to rewire the knote, or allocate a new one, with a suitable
f_event code and activate it. In addition, the code would have to use
a dedicated knlist for these knotes, or play some tricks with kq_knlist
or kq_knhash, so that the knotes are still reachable from the kqueue
through some knlist.

The diff assumes eager kqpoll purging after poll(2) or select(2).
Lazy purging needs an additional check that skips raising EBADF if
the knote is not part of current scan.

Index: kern/kern_event.c
===================================================================
RCS file: src/sys/kern/kern_event.c,v
retrieving revision 1.150
diff -u -p -r1.150 kern_event.c
--- kern/kern_event.c   16 Dec 2020 15:07:30 -0000      1.150
+++ kern/kern_event.c   16 Dec 2020 16:27:12 -0000
@@ -95,6 +95,8 @@ void  knote_enqueue(struct knote *kn);
 void   knote_dequeue(struct knote *kn);
 int    knote_acquire(struct knote *kn);
 void   knote_release(struct knote *kn);
+void   knote_activate(struct knote *);
+void   knote_remove(struct proc *p, struct knlist *list, int purge);
 
 void   filt_kqdetach(struct knote *kn);
 int    filt_kqueue(struct knote *kn, long hint);
@@ -504,8 +506,14 @@ kqpoll_init(void)
        struct proc *p = curproc;
        struct filedesc *fdp;
 
-       if (p->p_kq != NULL)
+       if (p->p_kq != NULL) {
+               /*
+                * Clear any pending error that was raised after
+                * previous scan.
+                */
+               p->p_kq->kq_error = 0;
                return;
+       }
 
        p->p_kq = kqueue_alloc(p->p_fd);
        p->p_kq_serial = arc4random();
@@ -962,6 +970,15 @@ retry:
        }
 
        s = splhigh();
+
+       if (kq->kq_error != 0) {
+               /* Deliver the pending error. */
+               error = kq->kq_error;
+               kq->kq_error = 0;
+               splx(s);
+               goto done;
+       }
+
        if (kq->kq_count == 0) {
                /*
                 * Successive loops are only necessary if there are more
@@ -1173,10 +1190,10 @@ kqueue_purge(struct proc *p, struct kque
        KERNEL_ASSERT_LOCKED();
 
        for (i = 0; i < kq->kq_knlistsize; i++)
-               knote_remove(p, &kq->kq_knlist[i]);
+               knote_remove(p, &kq->kq_knlist[i], 1);
        if (kq->kq_knhashmask != 0) {
                for (i = 0; i < kq->kq_knhashmask + 1; i++)
-                       knote_remove(p, &kq->kq_knhash[i]);
+                       knote_remove(p, &kq->kq_knhash[i], 1);
        }
 }
 
@@ -1356,9 +1373,10 @@ knote(struct klist *list, long hint)
  * remove all knotes from a specified knlist
  */
 void
-knote_remove(struct proc *p, struct knlist *list)
+knote_remove(struct proc *p, struct knlist *list, int purge)
 {
        struct knote *kn;
+       struct kqueue *kq;
        int s;
 
        while ((kn = SLIST_FIRST(list)) != NULL) {
@@ -1369,6 +1387,21 @@ knote_remove(struct proc *p, struct knli
                }
                splx(s);
                kn->kn_fop->f_detach(kn);
+
+               /*
+                * Notify poll(2) and select(2) when a monitored
+                * file descriptor is closed.
+                */
+               if (!purge && (kn->kn_flags & __EV_POLL) != 0) {
+                       kq = kn->kn_kq;
+                       s = splhigh();
+                       if (kq->kq_error == 0) {
+                               kq->kq_error = EBADF;
+                               kqueue_wakeup(kq);
+                       }
+                       splx(s);
+               }
+
                knote_drop(kn, p);
        }
 }
@@ -1399,7 +1432,7 @@ knote_fdclose(struct proc *p, int fd)
                        continue;
 
                list = &kq->kq_knlist[fd];
-               knote_remove(p, list);
+               knote_remove(p, list, 0);
        }
        KERNEL_UNLOCK();
 }
Index: sys/event.h
===================================================================
RCS file: src/sys/sys/event.h,v
retrieving revision 1.49
diff -u -p -r1.49 event.h
--- sys/event.h 9 Dec 2020 18:58:19 -0000       1.49
+++ sys/event.h 16 Dec 2020 16:27:12 -0000
@@ -218,8 +218,6 @@ extern const struct filterops dead_filto
 extern void    kqpoll_init(void);
 extern void    kqpoll_exit(void);
 extern void    knote(struct klist *list, long hint);
-extern void    knote_activate(struct knote *);
-extern void    knote_remove(struct proc *p, struct knlist *list);
 extern void    knote_fdclose(struct proc *p, int fd);
 extern void    knote_processexit(struct proc *);
 extern int     kqueue_register(struct kqueue *kq,
Index: sys/eventvar.h
===================================================================
RCS file: src/sys/sys/eventvar.h,v
retrieving revision 1.9
diff -u -p -r1.9 eventvar.h
--- sys/eventvar.h      7 Apr 2020 13:27:52 -0000       1.9
+++ sys/eventvar.h      16 Dec 2020 16:27:12 -0000
@@ -59,6 +59,7 @@ struct kqueue {
 #define KQ_SEL         0x01
 #define KQ_SLEEP       0x02
 #define KQ_DYING       0x04
+       int             kq_error;               /* pending error */
 };
 
 #endif /* !_SYS_EVENTVAR_H_ */

Reply via email to