Re: Lock operations for knote lists

2020-12-15 Thread Visa Hankala
On Tue, Dec 15, 2020 at 09:58:16AM -0300, Martin Pieuchot wrote:
> On 11/12/20(Fri) 17:37, Visa Hankala wrote:
> > Index: kern/kern_event.c
> > ===
> > RCS file: src/sys/kern/kern_event.c,v
> > retrieving revision 1.147
> > diff -u -p -r1.147 kern_event.c
> > --- kern/kern_event.c   9 Dec 2020 18:58:19 -   1.147
> > +++ kern/kern_event.c   11 Dec 2020 17:05:09 -
> > @@ -1539,9 +1576,14 @@ klist_invalidate(struct klist *list)
> > NET_ASSERT_UNLOCKED();
> >  
> > s = splhigh();
> > +   ls = klist_lock(list);
> 
> Isn't splhigh() redundant with klist_lock() now?  If a subsystem
> provides its own lock/unlock routine shouldn't it ensure that the
> necessary SPL is used?  Or is this protecting something else?  Or is
> it just paranoia and we should try to remove it in a later step?

splhigh() is needed by knote_acquire(). The code will change when
a mutex is added to it.

> > while ((kn = SLIST_FIRST(>kl_list)) != NULL) {
> > -   if (!knote_acquire(kn))
> > +   if (!knote_acquire(kn, list, ls)) {
> > +   /* knote_acquire() has unlocked list. */
> > +   ls = klist_lock(list);
> > continue;
> > +   }
> > +   klist_unlock(list, ls);
> > splx(s);
> > kn->kn_fop->f_detach(kn);
> > if (kn->kn_fop->f_flags & FILTEROP_ISFD) {
> 



Re: Lock operations for knote lists

2020-12-15 Thread Martin Pieuchot
On 11/12/20(Fri) 17:37, Visa Hankala wrote:
> This patch extends struct klist with a callback descriptor and
> an argument. The main purpose of this is to let the kqueue subsystem
> assert when a klist should be locked, and operate the klist lock
> in klist_invalidate().

Lovely!

> Access to a knote list of a kqueue-monitored object has to be
> serialized somehow. Because the object often has a lock for protecting
> its state, and because the object often acquires this lock at the latest
> in its f_event callback functions, I would like to use the same lock
> also for the knote lists. Uses of NOTE_SUBMIT already show a pattern
> arising.
> 
> There could be an embedded lock in klist. However, such a lock would be
> redundant in many cases. The code could not rely on a single lock type
> (mutex, rwlock, something else) because the needs of monitored objects
> vary. In addition, an embedded lock would introduce new lock order
> constraints. Note that this patch does not rule out use of dedicated
> klist locks.

Indeed, I'm currently dealing with such different type of lock issue in
UVM :/

> The patch introduces a way to associate lock operations with a klist.
> The caller can provide a custom implementation, or use a ready-made
> interface with a mutex or rwlock.
> 
> For compatibility with old code, the new code falls back to using the
> kernel lock if no specific klist initialization has been done. The
> existing code already relies on implicit initialization of klist.
> 
> Unfortunately, the size of struct klist will grow threefold.

The growth is unavoidable, it could have been of the size of a lock...

> As the patch gives the code the ability to operate the klist lock,
> the klist API could provide variants of insert and remove actions that
> handle locking internally, for convenience. However, that I would leave
> for another patch because I would prefer to rename the current
> klist_insert() to klist_insert_locked(), and klist_remove() to
> klist_remove_locked().
> 
> The patch additionally provides three examples of usage: audio, pipes,
> and sockets. Each of these examples is logically a separate changeset.

One question below.

> Please test and review.

This is in the middle of a build on sparc64, so far so good.

> Index: kern/kern_event.c
> ===
> RCS file: src/sys/kern/kern_event.c,v
> retrieving revision 1.147
> diff -u -p -r1.147 kern_event.c
> --- kern/kern_event.c 9 Dec 2020 18:58:19 -   1.147
> +++ kern/kern_event.c 11 Dec 2020 17:05:09 -
> @@ -1539,9 +1576,14 @@ klist_invalidate(struct klist *list)
>   NET_ASSERT_UNLOCKED();
>  
>   s = splhigh();
> + ls = klist_lock(list);

Isn't splhigh() redundant with klist_lock() now?  If a subsystem
provides its own lock/unlock routine shouldn't it ensure that the
necessary SPL is used?  Or is this protecting something else?  Or is
it just paranoia and we should try to remove it in a later step?

>   while ((kn = SLIST_FIRST(>kl_list)) != NULL) {
> - if (!knote_acquire(kn))
> + if (!knote_acquire(kn, list, ls)) {
> + /* knote_acquire() has unlocked list. */
> + ls = klist_lock(list);
>   continue;
> + }
> + klist_unlock(list, ls);
>   splx(s);
>   kn->kn_fop->f_detach(kn);
>   if (kn->kn_fop->f_flags & FILTEROP_ISFD) {



Re: Lock operations for knote lists

2020-12-11 Thread David Gwynne
On Fri, Dec 11, 2020 at 05:37:57PM +, Visa Hankala wrote:
> This patch extends struct klist with a callback descriptor and
> an argument. The main purpose of this is to let the kqueue subsystem
> assert when a klist should be locked, and operate the klist lock
> in klist_invalidate().

i've always felt that the klist has always had an obvious need for
locking, but it's excellent that you've actually gone and done more than
feel things about it.

> Access to a knote list of a kqueue-monitored object has to be
> serialized somehow. Because the object often has a lock for protecting
> its state, and because the object often acquires this lock at the latest
> in its f_event callback functions, I would like to use the same lock
> also for the knote lists. Uses of NOTE_SUBMIT already show a pattern
> arising.

That makes sense. If it helps, even if it just helps justify it,
this is the same approach used in kstat, for much the same reason.

> There could be an embedded lock in klist. However, such a lock would be
> redundant in many cases. The code could not rely on a single lock type
> (mutex, rwlock, something else) because the needs of monitored objects
> vary. In addition, an embedded lock would introduce new lock order
> constraints. Note that this patch does not rule out use of dedicated
> klist locks.
> 
> The patch introduces a way to associate lock operations with a klist.
> The caller can provide a custom implementation, or use a ready-made
> interface with a mutex or rwlock.
> 
> For compatibility with old code, the new code falls back to using the
> kernel lock if no specific klist initialization has been done. The
> existing code already relies on implicit initialization of klist.

I was going to ask if you could provide a struct klistops around
KERNEL_LOCK as the default, but that would involve a lot more churn to
explicitly init all the klist structs.

> Unfortunately, the size of struct klist will grow threefold.

I think we'll live.

> As the patch gives the code the ability to operate the klist lock,
> the klist API could provide variants of insert and remove actions that
> handle locking internally, for convenience. However, that I would leave
> for another patch because I would prefer to rename the current
> klist_insert() to klist_insert_locked(), and klist_remove() to
> klist_remove_locked().
> 
> The patch additionally provides three examples of usage: audio, pipes,
> and sockets. Each of these examples is logically a separate changeset.
> 
> 
> Please test and review.

I'll try to have a closer look soon.

> Index: dev/audio.c
> ===
> RCS file: src/sys/dev/audio.c,v
> retrieving revision 1.191
> diff -u -p -r1.191 audio.c
> --- dev/audio.c   19 May 2020 06:32:24 -  1.191
> +++ dev/audio.c   11 Dec 2020 17:05:09 -
> @@ -305,11 +305,12 @@ audio_buf_wakeup(void *addr)
>  int
>  audio_buf_init(struct audio_softc *sc, struct audio_buf *buf, int dir)
>  {
> + klist_init_mutex(>sel.si_note, _lock);
>   buf->softintr = softintr_establish(IPL_SOFTAUDIO,
>   audio_buf_wakeup, buf);
>   if (buf->softintr == NULL) {
>   printf("%s: can't establish softintr\n", DEVNAME(sc));
> - return ENOMEM;
> + goto bad;
>   }
>   if (sc->ops->round_buffersize) {
>   buf->datalen = sc->ops->round_buffersize(sc->arg,
> @@ -323,9 +324,12 @@ audio_buf_init(struct audio_softc *sc, s
>   buf->data = malloc(buf->datalen, M_DEVBUF, M_WAITOK);
>   if (buf->data == NULL) {
>   softintr_disestablish(buf->softintr);
> - return ENOMEM;
> + goto bad;
>   }
>   return 0;
> +bad:
> + klist_free(>sel.si_note);
> + return ENOMEM;
>  }
>  
>  void
> @@ -336,6 +340,7 @@ audio_buf_done(struct audio_softc *sc, s
>   else
>   free(buf->data, M_DEVBUF, buf->datalen);
>   softintr_disestablish(buf->softintr);
> + klist_free(>sel.si_note);
>  }
>  
>  /*
> @@ -1256,6 +1261,7 @@ audio_attach(struct device *parent, stru
>   return;
>   }
>  
> + klist_init_mutex(>mix_sel.si_note, _lock);
>   sc->mix_softintr = softintr_establish(IPL_SOFTAUDIO,
>   audio_mixer_wakeup, sc);
>   if (sc->mix_softintr == NULL) {
> @@ -1451,6 +1457,7 @@ audio_detach(struct device *self, int fl
>  
>   /* free resources */
>   softintr_disestablish(sc->mix_softintr);
> + klist_free(>mix_sel.si_note);
>   free(sc->mix_evbuf, M_DEVBUF, sc->mix_nent * sizeof(struct mixer_ev));
>   free(sc->mix_ents, M_DEVBUF, sc->mix_nent * sizeof(struct mixer_ctrl));
>   audio_buf_done(sc, >play);
> Index: kern/kern_event.c
> ===
> RCS file: src/sys/kern/kern_event.c,v
> retrieving revision 1.147
> diff -u -p -r1.147 kern_event.c
> --- kern/kern_event.c 9 Dec 2020 18:58:19 -   1.147
> +++ 

Lock operations for knote lists

2020-12-11 Thread Visa Hankala
This patch extends struct klist with a callback descriptor and
an argument. The main purpose of this is to let the kqueue subsystem
assert when a klist should be locked, and operate the klist lock
in klist_invalidate().

Access to a knote list of a kqueue-monitored object has to be
serialized somehow. Because the object often has a lock for protecting
its state, and because the object often acquires this lock at the latest
in its f_event callback functions, I would like to use the same lock
also for the knote lists. Uses of NOTE_SUBMIT already show a pattern
arising.

There could be an embedded lock in klist. However, such a lock would be
redundant in many cases. The code could not rely on a single lock type
(mutex, rwlock, something else) because the needs of monitored objects
vary. In addition, an embedded lock would introduce new lock order
constraints. Note that this patch does not rule out use of dedicated
klist locks.

The patch introduces a way to associate lock operations with a klist.
The caller can provide a custom implementation, or use a ready-made
interface with a mutex or rwlock.

For compatibility with old code, the new code falls back to using the
kernel lock if no specific klist initialization has been done. The
existing code already relies on implicit initialization of klist.

Unfortunately, the size of struct klist will grow threefold.

As the patch gives the code the ability to operate the klist lock,
the klist API could provide variants of insert and remove actions that
handle locking internally, for convenience. However, that I would leave
for another patch because I would prefer to rename the current
klist_insert() to klist_insert_locked(), and klist_remove() to
klist_remove_locked().

The patch additionally provides three examples of usage: audio, pipes,
and sockets. Each of these examples is logically a separate changeset.


Please test and review.


Index: dev/audio.c
===
RCS file: src/sys/dev/audio.c,v
retrieving revision 1.191
diff -u -p -r1.191 audio.c
--- dev/audio.c 19 May 2020 06:32:24 -  1.191
+++ dev/audio.c 11 Dec 2020 17:05:09 -
@@ -305,11 +305,12 @@ audio_buf_wakeup(void *addr)
 int
 audio_buf_init(struct audio_softc *sc, struct audio_buf *buf, int dir)
 {
+   klist_init_mutex(>sel.si_note, _lock);
buf->softintr = softintr_establish(IPL_SOFTAUDIO,
audio_buf_wakeup, buf);
if (buf->softintr == NULL) {
printf("%s: can't establish softintr\n", DEVNAME(sc));
-   return ENOMEM;
+   goto bad;
}
if (sc->ops->round_buffersize) {
buf->datalen = sc->ops->round_buffersize(sc->arg,
@@ -323,9 +324,12 @@ audio_buf_init(struct audio_softc *sc, s
buf->data = malloc(buf->datalen, M_DEVBUF, M_WAITOK);
if (buf->data == NULL) {
softintr_disestablish(buf->softintr);
-   return ENOMEM;
+   goto bad;
}
return 0;
+bad:
+   klist_free(>sel.si_note);
+   return ENOMEM;
 }
 
 void
@@ -336,6 +340,7 @@ audio_buf_done(struct audio_softc *sc, s
else
free(buf->data, M_DEVBUF, buf->datalen);
softintr_disestablish(buf->softintr);
+   klist_free(>sel.si_note);
 }
 
 /*
@@ -1256,6 +1261,7 @@ audio_attach(struct device *parent, stru
return;
}
 
+   klist_init_mutex(>mix_sel.si_note, _lock);
sc->mix_softintr = softintr_establish(IPL_SOFTAUDIO,
audio_mixer_wakeup, sc);
if (sc->mix_softintr == NULL) {
@@ -1451,6 +1457,7 @@ audio_detach(struct device *self, int fl
 
/* free resources */
softintr_disestablish(sc->mix_softintr);
+   klist_free(>mix_sel.si_note);
free(sc->mix_evbuf, M_DEVBUF, sc->mix_nent * sizeof(struct mixer_ev));
free(sc->mix_ents, M_DEVBUF, sc->mix_nent * sizeof(struct mixer_ctrl));
audio_buf_done(sc, >play);
Index: kern/kern_event.c
===
RCS file: src/sys/kern/kern_event.c,v
retrieving revision 1.147
diff -u -p -r1.147 kern_event.c
--- kern/kern_event.c   9 Dec 2020 18:58:19 -   1.147
+++ kern/kern_event.c   11 Dec 2020 17:05:09 -
@@ -57,6 +57,17 @@
 #include 
 #include 
 
+#ifdef DIAGNOSTIC
+#define KLIST_ASSERT_LOCKED(kl) do {   \
+   if ((kl)->kl_ops != NULL)   \
+   (kl)->kl_ops->klo_assertlk((kl)->kl_arg);   \
+   else\
+   KERNEL_ASSERT_LOCKED(); \
+} while (0)
+#else
+#define KLIST_ASSERT_LOCKED(kl)((void)(kl))
+#endif
+
 struct kqueue *kqueue_alloc(struct filedesc *);
 void   kqueue_terminate(struct proc *p, struct kqueue *);
 void   kqueue_free(struct kqueue *);
@@ -79,6 +90,8 @@ void