Re: Replace ttkqflush() with klist_invalidate()

2020-02-04 Thread Visa Hankala
On Tue, Feb 04, 2020 at 01:04:24PM +0100, Martin Pieuchot wrote:
> On 02/02/20(Sun) 09:58, Visa Hankala wrote:
> > tty(4) uses custom code for revoking knotes. It should be changed to use
> > klist_invalidate() to handle the revocation in one place. The following
> > diff does that.
> 
> Great, your diff is ok mpi@
> 
> > In addition, the patch makes the code store the tty context pointer
> > in kn_hook directly. This simplifies the code.
> 
> Does that mean we'll need some reference counting when getting kqueue
> out of the KERNEL_LOCK()?  Who is the owner of `kn_hook'?

I think the proper way is to invalidate knotes before the associated
event source object or kqueue is destroyed. That way there is no need
for reference counting. klist_invalidate() does the job when the object
is destroyed kind of involuntarily, like when a device is detached.
Typically, knotes are freed by knote_fdclose() when the file descriptor
is closed, which happens before the object is freed. kqueue_close() 
takes care of freeing kqueue's registered knotes when the kqueue itself
is being freed.

I would say the main owner of kn_hook and the knote itself is the object
that owns the kn_selnext list. klist_invalidate() dissolves that
ownership (well, any knotes on the list get freed either when the EOF
event is delivered to userspace or when the kqueue is destroyed).

> > Unlike ttkqflush(), klist_invalidate() can sleep. However, that should
> > not be a problem because the callers of ttyfree() call vdevgone() that
> > can sleep as well.
> 
> It took me some time to audit com_detach() because ttyfree() isn't the
> last call.  Most of the *_detach() functions have similar problems: they
> assume the execution of the code is atomic and free data structures
> before unregistering handlers.
> 
> Diff below improves that, ok?

Right, at least the soft interrupt handler dereferences sc_tty. The
code assumes that softintr_disestablish() has barrier-like behaviour,
which should be the case in this context on most architectures because
of the kernel lock.

OK visa@

> Index: dev/ic/com.c
> ===
> RCS file: /cvs/src/sys/dev/ic/com.c,v
> retrieving revision 1.170
> diff -u -p -r1.170 com.c
> --- dev/ic/com.c  19 Jul 2019 00:17:15 -  1.170
> +++ dev/ic/com.c  4 Feb 2020 11:27:06 -
> @@ -186,14 +186,14 @@ com_detach(struct device *self, int flag
>   mn |= 0x80;
>   vdevgone(maj, mn, mn, VCHR);
>  
> + timeout_del(>sc_dtr_tmo);
> + timeout_del(>sc_diag_tmo);
> + softintr_disestablish(sc->sc_si);
> +
>   /* Detach and free the tty. */
>   if (sc->sc_tty) {
>   ttyfree(sc->sc_tty);
>   }
> -
> - timeout_del(>sc_dtr_tmo);
> - timeout_del(>sc_diag_tmo);
> - softintr_disestablish(sc->sc_si);
>  
>   return (0);
>  }
> 



Re: Replace ttkqflush() with klist_invalidate()

2020-02-04 Thread Martin Pieuchot
On 02/02/20(Sun) 09:58, Visa Hankala wrote:
> tty(4) uses custom code for revoking knotes. It should be changed to use
> klist_invalidate() to handle the revocation in one place. The following
> diff does that.

Great, your diff is ok mpi@

> In addition, the patch makes the code store the tty context pointer
> in kn_hook directly. This simplifies the code.

Does that mean we'll need some reference counting when getting kqueue
out of the KERNEL_LOCK()?  Who is the owner of `kn_hook'?

> Unlike ttkqflush(), klist_invalidate() can sleep. However, that should
> not be a problem because the callers of ttyfree() call vdevgone() that
> can sleep as well.

It took me some time to audit com_detach() because ttyfree() isn't the
last call.  Most of the *_detach() functions have similar problems: they
assume the execution of the code is atomic and free data structures
before unregistering handlers.

Diff below improves that, ok?

Index: dev/ic/com.c
===
RCS file: /cvs/src/sys/dev/ic/com.c,v
retrieving revision 1.170
diff -u -p -r1.170 com.c
--- dev/ic/com.c19 Jul 2019 00:17:15 -  1.170
+++ dev/ic/com.c4 Feb 2020 11:27:06 -
@@ -186,14 +186,14 @@ com_detach(struct device *self, int flag
mn |= 0x80;
vdevgone(maj, mn, mn, VCHR);
 
+   timeout_del(>sc_dtr_tmo);
+   timeout_del(>sc_diag_tmo);
+   softintr_disestablish(sc->sc_si);
+
/* Detach and free the tty. */
if (sc->sc_tty) {
ttyfree(sc->sc_tty);
}
-
-   timeout_del(>sc_dtr_tmo);
-   timeout_del(>sc_diag_tmo);
-   softintr_disestablish(sc->sc_si);
 
return (0);
 }



Replace ttkqflush() with klist_invalidate()

2020-02-02 Thread Visa Hankala
tty(4) uses custom code for revoking knotes. It should be changed to use
klist_invalidate() to handle the revocation in one place. The following
diff does that.

In addition, the patch makes the code store the tty context pointer
in kn_hook directly. This simplifies the code.

Unlike ttkqflush(), klist_invalidate() can sleep. However, that should
not be a problem because the callers of ttyfree() call vdevgone() that
can sleep as well.

It would be good to have this tested in various setups. Originally,
ttkqflush() was added to prevent crashing when a ucom(4) device is
detached while still in use by a kqueue-enabled program such as cu(1).

OK?

Index: kern/tty.c
===
RCS file: src/sys/kern/tty.c,v
retrieving revision 1.151
diff -u -p -r1.151 tty.c
--- kern/tty.c  9 Jan 2020 14:35:20 -   1.151
+++ kern/tty.c  2 Feb 2020 09:38:17 -
@@ -75,7 +75,6 @@ static void ttyblock(struct tty *);
 void ttyunblock(struct tty *);
 static void ttyecho(int, struct tty *);
 static void ttyrubo(struct tty *, int);
-void   ttkqflush(struct klist *klist);
 intfilt_ttyread(struct knote *kn, long hint);
 void   filt_ttyrdetach(struct knote *kn);
 intfilt_ttywrite(struct knote *kn, long hint);
@@ -1124,7 +1123,7 @@ ttkqfilter(dev_t dev, struct knote *kn)
return (EINVAL);
}
 
-   kn->kn_hook = (caddr_t)((u_long)dev);
+   kn->kn_hook = tp;
 
s = spltty();
SLIST_INSERT_HEAD(klist, kn, kn_selnext);
@@ -1134,29 +1133,11 @@ ttkqfilter(dev_t dev, struct knote *kn)
 }
 
 void
-ttkqflush(struct klist *klist)
-{
-   struct knote *kn, *kn1;
-
-   SLIST_FOREACH_SAFE(kn, klist, kn_selnext, kn1) {
-   SLIST_REMOVE(klist, kn, knote, kn_selnext);
-   kn->kn_hook = (caddr_t)((u_long)NODEV);
-   kn->kn_flags |= EV_EOF;
-   knote_activate(kn);
-   }
-}
-
-void
 filt_ttyrdetach(struct knote *kn)
 {
-   dev_t dev = (dev_t)((u_long)kn->kn_hook);
-   struct tty *tp;
+   struct tty *tp = kn->kn_hook;
int s;
 
-   if (dev == NODEV)
-   return;
-   tp = (*cdevsw[major(dev)].d_tty)(dev);
-
s = spltty();
SLIST_REMOVE(>t_rsel.si_note, kn, knote, kn_selnext);
splx(s);
@@ -1165,16 +1146,9 @@ filt_ttyrdetach(struct knote *kn)
 int
 filt_ttyread(struct knote *kn, long hint)
 {
-   dev_t dev = (dev_t)((u_long)kn->kn_hook);
-   struct tty *tp;
+   struct tty *tp = kn->kn_hook;
int s;
 
-   if (dev == NODEV) {
-   kn->kn_flags |= EV_EOF;
-   return (1);
-   }
-   tp = (*cdevsw[major(dev)].d_tty)(dev);
-
s = spltty();
kn->kn_data = ttnread(tp);
splx(s);
@@ -1188,14 +1162,9 @@ filt_ttyread(struct knote *kn, long hint
 void
 filt_ttywdetach(struct knote *kn)
 {
-   dev_t dev = (dev_t)((u_long)kn->kn_hook);
-   struct tty *tp;
+   struct tty *tp = kn->kn_hook;
int s;
 
-   if (dev == NODEV)
-   return;
-   tp = (*cdevsw[major(dev)].d_tty)(dev);
-
s = spltty();
SLIST_REMOVE(>t_wsel.si_note, kn, knote, kn_selnext);
splx(s);
@@ -1204,16 +1173,9 @@ filt_ttywdetach(struct knote *kn)
 int
 filt_ttywrite(struct knote *kn, long hint)
 {
-   dev_t dev = (dev_t)((u_long)kn->kn_hook);
-   struct tty *tp;
+   struct tty *tp = kn->kn_hook;
int canwrite, s;
 
-   if (dev == NODEV) {
-   kn->kn_flags |= EV_EOF;
-   return (1);
-   }
-   tp = (*cdevsw[major(dev)].d_tty)(dev);
-
s = spltty();
kn->kn_data = tp->t_outq.c_cn - tp->t_outq.c_cc;
canwrite = (tp->t_outq.c_cc <= tp->t_lowat);
@@ -2378,6 +2340,7 @@ ttymalloc(int baud)
 void
 ttyfree(struct tty *tp)
 {
+   int s;
 
--tty_count;
 #ifdef DIAGNOSTIC
@@ -2386,8 +2349,10 @@ ttyfree(struct tty *tp)
 #endif
TAILQ_REMOVE(, tp, tty_link);
 
-   ttkqflush(>t_rsel.si_note);
-   ttkqflush(>t_wsel.si_note);
+   s = spltty();
+   klist_invalidate(>t_rsel.si_note);
+   klist_invalidate(>t_wsel.si_note);
+   splx(s);
 
clfree(>t_rawq);
clfree(>t_canq);