Re: router timer mutex

2022-04-28 Thread Claudio Jeker
On Thu, Apr 28, 2022 at 07:24:22PM +0200, Alexander Bluhm wrote:
> I still need an ok for this diff.  It is the final step before we
> can run IP forwaring in parallel.

Fine with me. If it holds you back put it in OK claudio@
I will rip the rttimer code appart in the next days and make that API a
lot simpler.
 
> bluhm
> 
> On Thu, Apr 21, 2022 at 05:44:17PM +0200, Alexander Bluhm wrote:
> > On Wed, Apr 20, 2022 at 08:12:51PM +0200, Alexander Bluhm wrote:
> > > mvs@ reminded me of a crash I have seen in December.  Route timers
> > > are not MP safe, but I think this can be fixed with a mutex.  The
> > > idea is to protect the global lists with a mutex and move the rttimer
> > > into a temporary list.  Then the callback and pool put can be called
> > > later without mutex.
> > 
> > I have a tiny update to the diff.
> > 
> > - Global locks are documented with capital letter, so use [T].
> > 
> > - rt_timer_add() grabbed the mutex twice, first remove then add.
> >   Better exchange in one critical section.  pool_get before and
> >   pool_put after.
> > 
> > ok?
> > 
> > Index: net/route.c
> > ===
> > RCS file: /data/mirror/openbsd/cvs/src/sys/net/route.c,v
> > retrieving revision 1.406
> > diff -u -p -r1.406 route.c
> > --- net/route.c 20 Apr 2022 17:58:22 -  1.406
> > +++ net/route.c 21 Apr 2022 13:31:52 -
> > @@ -1361,7 +1361,8 @@ rt_ifa_purge_walker(struct rtentry *rt, 
> >   * for multiple queues for efficiency's sake...
> >   */
> >  
> > -LIST_HEAD(, rttimer_queue) rttimer_queue_head;
> > +struct mutex   rttimer_mtx;
> > +LIST_HEAD(, rttimer_queue) rttimer_queue_head; /* [T] */
> >  
> >  #define RTTIMER_CALLOUT(r) {   \
> > if (r->rtt_func != NULL) {  \
> > @@ -1393,6 +1394,7 @@ rt_timer_init(void)
> > pool_init(_queue_pool, sizeof(struct rttimer_queue), 0,
> > IPL_MPFLOOR, 0, "rttmrq", NULL);
> >  
> > +   mtx_init(_mtx, IPL_MPFLOOR);
> > LIST_INIT(_queue_head);
> > timeout_set_proc(_timer_timeout, rt_timer_timer, _timer_timeout);
> > timeout_add_sec(_timer_timeout, 1);
> > @@ -1408,7 +1410,10 @@ rt_timer_queue_create(int timeout)
> > rtq->rtq_timeout = timeout;
> > rtq->rtq_count = 0;
> > TAILQ_INIT(>rtq_head);
> > +
> > +   mtx_enter(_mtx);
> > LIST_INSERT_HEAD(_queue_head, rtq, rtq_link);
> > +   mtx_leave(_mtx);
> >  
> > return (rtq);
> >  }
> > @@ -1416,28 +1421,36 @@ rt_timer_queue_create(int timeout)
> >  void
> >  rt_timer_queue_change(struct rttimer_queue *rtq, int timeout)
> >  {
> > +   mtx_enter(_mtx);
> > rtq->rtq_timeout = timeout;
> > +   mtx_leave(_mtx);
> >  }
> >  
> >  void
> >  rt_timer_queue_destroy(struct rttimer_queue *rtq)
> >  {
> > -   struct rttimer  *r;
> > +   struct rttimer  *r;
> > +   TAILQ_HEAD(, rttimer)rttlist;
> >  
> > NET_ASSERT_LOCKED();
> >  
> > +   TAILQ_INIT();
> > +   mtx_enter(_mtx);
> > while ((r = TAILQ_FIRST(>rtq_head)) != NULL) {
> > LIST_REMOVE(r, rtt_link);
> > TAILQ_REMOVE(>rtq_head, r, rtt_next);
> > +   TAILQ_INSERT_TAIL(, r, rtt_next);
> > +   KASSERT(rtq->rtq_count > 0);
> > +   rtq->rtq_count--;
> > +   }
> > +   LIST_REMOVE(rtq, rtq_link);
> > +   mtx_leave(_mtx);
> > +
> > +   while ((r = TAILQ_FIRST()) != NULL) {
> > +   TAILQ_REMOVE(, r, rtt_next);
> > RTTIMER_CALLOUT(r);
> > pool_put(_pool, r);
> > -   if (rtq->rtq_count > 0)
> > -   rtq->rtq_count--;
> > -   else
> > -   printf("rt_timer_queue_destroy: rtq_count reached 0\n");
> > }
> > -
> > -   LIST_REMOVE(rtq, rtq_link);
> > pool_put(_queue_pool, rtq);
> >  }
> >  
> > @@ -1450,15 +1463,22 @@ rt_timer_queue_count(struct rttimer_queu
> >  void
> >  rt_timer_remove_all(struct rtentry *rt)
> >  {
> > -   struct rttimer  *r;
> > +   struct rttimer  *r;
> > +   TAILQ_HEAD(, rttimer)rttlist;
> >  
> > +   TAILQ_INIT();
> > +   mtx_enter(_mtx);
> > while ((r = LIST_FIRST(>rt_timer)) != NULL) {
> > LIST_REMOVE(r, rtt_link);
> > TAILQ_REMOVE(>rtt_queue->rtq_head, r, rtt_next);
> > -   if (r->rtt_queue->rtq_count > 0)
> > -   r->rtt_queue->rtq_count--;
> > -   else
> > -   printf("rt_timer_remove_all: rtq_count reached 0\n");
> > +   TAILQ_INSERT_TAIL(, r, rtt_next);
> > +   KASSERT(r->rtt_queue->rtq_count > 0);
> > +   r->rtt_queue->rtq_count--;
> > +   }
> > +   mtx_leave(_mtx);
> > +
> > +   while ((r = TAILQ_FIRST()) != NULL) {
> > +   TAILQ_REMOVE(, r, rtt_next);
> > pool_put(_pool, r);
> > }
> >  }
> > @@ -1467,12 +1487,23 @@ int
> >  rt_timer_add(struct rtentry *rt, void (*func)(struct rtentry *,
> >  struct rttimer *), struct rttimer_queue *queue, u_int rtableid)
> >  

Re: router timer mutex

2022-04-28 Thread Alexander Bluhm
I still need an ok for this diff.  It is the final step before we
can run IP forwaring in parallel.

bluhm

On Thu, Apr 21, 2022 at 05:44:17PM +0200, Alexander Bluhm wrote:
> On Wed, Apr 20, 2022 at 08:12:51PM +0200, Alexander Bluhm wrote:
> > mvs@ reminded me of a crash I have seen in December.  Route timers
> > are not MP safe, but I think this can be fixed with a mutex.  The
> > idea is to protect the global lists with a mutex and move the rttimer
> > into a temporary list.  Then the callback and pool put can be called
> > later without mutex.
> 
> I have a tiny update to the diff.
> 
> - Global locks are documented with capital letter, so use [T].
> 
> - rt_timer_add() grabbed the mutex twice, first remove then add.
>   Better exchange in one critical section.  pool_get before and
>   pool_put after.
> 
> ok?
> 
> Index: net/route.c
> ===
> RCS file: /data/mirror/openbsd/cvs/src/sys/net/route.c,v
> retrieving revision 1.406
> diff -u -p -r1.406 route.c
> --- net/route.c   20 Apr 2022 17:58:22 -  1.406
> +++ net/route.c   21 Apr 2022 13:31:52 -
> @@ -1361,7 +1361,8 @@ rt_ifa_purge_walker(struct rtentry *rt, 
>   * for multiple queues for efficiency's sake...
>   */
>  
> -LIST_HEAD(, rttimer_queue)   rttimer_queue_head;
> +struct mutex rttimer_mtx;
> +LIST_HEAD(, rttimer_queue)   rttimer_queue_head; /* [T] */
>  
>  #define RTTIMER_CALLOUT(r)   {   \
>   if (r->rtt_func != NULL) {  \
> @@ -1393,6 +1394,7 @@ rt_timer_init(void)
>   pool_init(_queue_pool, sizeof(struct rttimer_queue), 0,
>   IPL_MPFLOOR, 0, "rttmrq", NULL);
>  
> + mtx_init(_mtx, IPL_MPFLOOR);
>   LIST_INIT(_queue_head);
>   timeout_set_proc(_timer_timeout, rt_timer_timer, _timer_timeout);
>   timeout_add_sec(_timer_timeout, 1);
> @@ -1408,7 +1410,10 @@ rt_timer_queue_create(int timeout)
>   rtq->rtq_timeout = timeout;
>   rtq->rtq_count = 0;
>   TAILQ_INIT(>rtq_head);
> +
> + mtx_enter(_mtx);
>   LIST_INSERT_HEAD(_queue_head, rtq, rtq_link);
> + mtx_leave(_mtx);
>  
>   return (rtq);
>  }
> @@ -1416,28 +1421,36 @@ rt_timer_queue_create(int timeout)
>  void
>  rt_timer_queue_change(struct rttimer_queue *rtq, int timeout)
>  {
> + mtx_enter(_mtx);
>   rtq->rtq_timeout = timeout;
> + mtx_leave(_mtx);
>  }
>  
>  void
>  rt_timer_queue_destroy(struct rttimer_queue *rtq)
>  {
> - struct rttimer  *r;
> + struct rttimer  *r;
> + TAILQ_HEAD(, rttimer)rttlist;
>  
>   NET_ASSERT_LOCKED();
>  
> + TAILQ_INIT();
> + mtx_enter(_mtx);
>   while ((r = TAILQ_FIRST(>rtq_head)) != NULL) {
>   LIST_REMOVE(r, rtt_link);
>   TAILQ_REMOVE(>rtq_head, r, rtt_next);
> + TAILQ_INSERT_TAIL(, r, rtt_next);
> + KASSERT(rtq->rtq_count > 0);
> + rtq->rtq_count--;
> + }
> + LIST_REMOVE(rtq, rtq_link);
> + mtx_leave(_mtx);
> +
> + while ((r = TAILQ_FIRST()) != NULL) {
> + TAILQ_REMOVE(, r, rtt_next);
>   RTTIMER_CALLOUT(r);
>   pool_put(_pool, r);
> - if (rtq->rtq_count > 0)
> - rtq->rtq_count--;
> - else
> - printf("rt_timer_queue_destroy: rtq_count reached 0\n");
>   }
> -
> - LIST_REMOVE(rtq, rtq_link);
>   pool_put(_queue_pool, rtq);
>  }
>  
> @@ -1450,15 +1463,22 @@ rt_timer_queue_count(struct rttimer_queu
>  void
>  rt_timer_remove_all(struct rtentry *rt)
>  {
> - struct rttimer  *r;
> + struct rttimer  *r;
> + TAILQ_HEAD(, rttimer)rttlist;
>  
> + TAILQ_INIT();
> + mtx_enter(_mtx);
>   while ((r = LIST_FIRST(>rt_timer)) != NULL) {
>   LIST_REMOVE(r, rtt_link);
>   TAILQ_REMOVE(>rtt_queue->rtq_head, r, rtt_next);
> - if (r->rtt_queue->rtq_count > 0)
> - r->rtt_queue->rtq_count--;
> - else
> - printf("rt_timer_remove_all: rtq_count reached 0\n");
> + TAILQ_INSERT_TAIL(, r, rtt_next);
> + KASSERT(r->rtt_queue->rtq_count > 0);
> + r->rtt_queue->rtq_count--;
> + }
> + mtx_leave(_mtx);
> +
> + while ((r = TAILQ_FIRST()) != NULL) {
> + TAILQ_REMOVE(, r, rtt_next);
>   pool_put(_pool, r);
>   }
>  }
> @@ -1467,12 +1487,23 @@ int
>  rt_timer_add(struct rtentry *rt, void (*func)(struct rtentry *,
>  struct rttimer *), struct rttimer_queue *queue, u_int rtableid)
>  {
> - struct rttimer  *r;
> + struct rttimer  *r, *rnew;
>   time_t   current_time;
>  
> + rnew = pool_get(_pool, PR_NOWAIT | PR_ZERO);
> + if (rnew == NULL)
> + return (ENOBUFS);
> +
>   current_time = getuptime();
> - rt->rt_expire = current_time + queue->rtq_timeout;
>  
> + rnew->rtt_rt 

Re: router timer mutex

2022-04-21 Thread Alexander Bluhm
On Wed, Apr 20, 2022 at 08:12:51PM +0200, Alexander Bluhm wrote:
> mvs@ reminded me of a crash I have seen in December.  Route timers
> are not MP safe, but I think this can be fixed with a mutex.  The
> idea is to protect the global lists with a mutex and move the rttimer
> into a temporary list.  Then the callback and pool put can be called
> later without mutex.

I have a tiny update to the diff.

- Global locks are documented with capital letter, so use [T].

- rt_timer_add() grabbed the mutex twice, first remove then add.
  Better exchange in one critical section.  pool_get before and
  pool_put after.

ok?

Index: net/route.c
===
RCS file: /data/mirror/openbsd/cvs/src/sys/net/route.c,v
retrieving revision 1.406
diff -u -p -r1.406 route.c
--- net/route.c 20 Apr 2022 17:58:22 -  1.406
+++ net/route.c 21 Apr 2022 13:31:52 -
@@ -1361,7 +1361,8 @@ rt_ifa_purge_walker(struct rtentry *rt, 
  * for multiple queues for efficiency's sake...
  */
 
-LIST_HEAD(, rttimer_queue) rttimer_queue_head;
+struct mutex   rttimer_mtx;
+LIST_HEAD(, rttimer_queue) rttimer_queue_head; /* [T] */
 
 #define RTTIMER_CALLOUT(r) {   \
if (r->rtt_func != NULL) {  \
@@ -1393,6 +1394,7 @@ rt_timer_init(void)
pool_init(_queue_pool, sizeof(struct rttimer_queue), 0,
IPL_MPFLOOR, 0, "rttmrq", NULL);
 
+   mtx_init(_mtx, IPL_MPFLOOR);
LIST_INIT(_queue_head);
timeout_set_proc(_timer_timeout, rt_timer_timer, _timer_timeout);
timeout_add_sec(_timer_timeout, 1);
@@ -1408,7 +1410,10 @@ rt_timer_queue_create(int timeout)
rtq->rtq_timeout = timeout;
rtq->rtq_count = 0;
TAILQ_INIT(>rtq_head);
+
+   mtx_enter(_mtx);
LIST_INSERT_HEAD(_queue_head, rtq, rtq_link);
+   mtx_leave(_mtx);
 
return (rtq);
 }
@@ -1416,28 +1421,36 @@ rt_timer_queue_create(int timeout)
 void
 rt_timer_queue_change(struct rttimer_queue *rtq, int timeout)
 {
+   mtx_enter(_mtx);
rtq->rtq_timeout = timeout;
+   mtx_leave(_mtx);
 }
 
 void
 rt_timer_queue_destroy(struct rttimer_queue *rtq)
 {
-   struct rttimer  *r;
+   struct rttimer  *r;
+   TAILQ_HEAD(, rttimer)rttlist;
 
NET_ASSERT_LOCKED();
 
+   TAILQ_INIT();
+   mtx_enter(_mtx);
while ((r = TAILQ_FIRST(>rtq_head)) != NULL) {
LIST_REMOVE(r, rtt_link);
TAILQ_REMOVE(>rtq_head, r, rtt_next);
+   TAILQ_INSERT_TAIL(, r, rtt_next);
+   KASSERT(rtq->rtq_count > 0);
+   rtq->rtq_count--;
+   }
+   LIST_REMOVE(rtq, rtq_link);
+   mtx_leave(_mtx);
+
+   while ((r = TAILQ_FIRST()) != NULL) {
+   TAILQ_REMOVE(, r, rtt_next);
RTTIMER_CALLOUT(r);
pool_put(_pool, r);
-   if (rtq->rtq_count > 0)
-   rtq->rtq_count--;
-   else
-   printf("rt_timer_queue_destroy: rtq_count reached 0\n");
}
-
-   LIST_REMOVE(rtq, rtq_link);
pool_put(_queue_pool, rtq);
 }
 
@@ -1450,15 +1463,22 @@ rt_timer_queue_count(struct rttimer_queu
 void
 rt_timer_remove_all(struct rtentry *rt)
 {
-   struct rttimer  *r;
+   struct rttimer  *r;
+   TAILQ_HEAD(, rttimer)rttlist;
 
+   TAILQ_INIT();
+   mtx_enter(_mtx);
while ((r = LIST_FIRST(>rt_timer)) != NULL) {
LIST_REMOVE(r, rtt_link);
TAILQ_REMOVE(>rtt_queue->rtq_head, r, rtt_next);
-   if (r->rtt_queue->rtq_count > 0)
-   r->rtt_queue->rtq_count--;
-   else
-   printf("rt_timer_remove_all: rtq_count reached 0\n");
+   TAILQ_INSERT_TAIL(, r, rtt_next);
+   KASSERT(r->rtt_queue->rtq_count > 0);
+   r->rtt_queue->rtq_count--;
+   }
+   mtx_leave(_mtx);
+
+   while ((r = TAILQ_FIRST()) != NULL) {
+   TAILQ_REMOVE(, r, rtt_next);
pool_put(_pool, r);
}
 }
@@ -1467,12 +1487,23 @@ int
 rt_timer_add(struct rtentry *rt, void (*func)(struct rtentry *,
 struct rttimer *), struct rttimer_queue *queue, u_int rtableid)
 {
-   struct rttimer  *r;
+   struct rttimer  *r, *rnew;
time_t   current_time;
 
+   rnew = pool_get(_pool, PR_NOWAIT | PR_ZERO);
+   if (rnew == NULL)
+   return (ENOBUFS);
+
current_time = getuptime();
-   rt->rt_expire = current_time + queue->rtq_timeout;
 
+   rnew->rtt_rt = rt;
+   rnew->rtt_time = current_time;
+   rnew->rtt_func = func;
+   rnew->rtt_queue = queue;
+   rnew->rtt_tableid = rtableid;
+
+   mtx_enter(_mtx);
+   rt->rt_expire = current_time + queue->rtq_timeout;
/*
 * If there's already a timer with this action, destroy it 

Re: router timer mutex

2022-04-21 Thread Claudio Jeker
On Wed, Apr 20, 2022 at 08:12:51PM +0200, Alexander Bluhm wrote:
> Hi,
> 
> mvs@ reminded me of a crash I have seen in December.  Route timers
> are not MP safe, but I think this can be fixed with a mutex.  The
> idea is to protect the global lists with a mutex and move the rttimer
> into a temporary list.  Then the callback and pool put can be called
> later without mutex.
> 
> It survived a full regress with witness.
> 
> Hrvoje: Can you put this on your test machine together with parallel
> IP forwarding?
> 
> ok?

I would have prefer if the code was refactored first. The removal of a
rt_timer is copied over in 4 or 5 places with almost no difference.
In the rt_timer_queue_destroy() case you can use TAILQ_CONCAT and kill one
loop. Also I would use TAILQ_HEAD_INITIALIZER() instead of TAILQ_INIT().

To be honest most of this code should be directly replaced with the
timeout API. I bet the timeout API is more efficent.
 
> Index: net/route.c
> ===
> RCS file: /cvs/src/sys/net/route.c,v
> retrieving revision 1.406
> diff -u -p -r1.406 route.c
> --- net/route.c   20 Apr 2022 17:58:22 -  1.406
> +++ net/route.c   20 Apr 2022 18:00:39 -
> @@ -1361,7 +1361,8 @@ rt_ifa_purge_walker(struct rtentry *rt, 
>   * for multiple queues for efficiency's sake...
>   */
>  
> -LIST_HEAD(, rttimer_queue)   rttimer_queue_head;
> +struct mutex rttimer_mtx;
> +LIST_HEAD(, rttimer_queue)   rttimer_queue_head; /* [t] */
>  
>  #define RTTIMER_CALLOUT(r)   {   \
>   if (r->rtt_func != NULL) {  \
> @@ -1393,6 +1394,7 @@ rt_timer_init(void)
>   pool_init(_queue_pool, sizeof(struct rttimer_queue), 0,
>   IPL_MPFLOOR, 0, "rttmrq", NULL);
>  
> + mtx_init(_mtx, IPL_MPFLOOR);
>   LIST_INIT(_queue_head);
>   timeout_set_proc(_timer_timeout, rt_timer_timer, _timer_timeout);
>   timeout_add_sec(_timer_timeout, 1);
> @@ -1408,7 +1410,10 @@ rt_timer_queue_create(int timeout)
>   rtq->rtq_timeout = timeout;
>   rtq->rtq_count = 0;
>   TAILQ_INIT(>rtq_head);
> +
> + mtx_enter(_mtx);
>   LIST_INSERT_HEAD(_queue_head, rtq, rtq_link);
> + mtx_leave(_mtx);
>  
>   return (rtq);
>  }
> @@ -1416,28 +1421,36 @@ rt_timer_queue_create(int timeout)
>  void
>  rt_timer_queue_change(struct rttimer_queue *rtq, int timeout)
>  {
> + mtx_enter(_mtx);
>   rtq->rtq_timeout = timeout;
> + mtx_leave(_mtx);
>  }
>  
>  void
>  rt_timer_queue_destroy(struct rttimer_queue *rtq)
>  {
> - struct rttimer  *r;
> + struct rttimer  *r;
> + TAILQ_HEAD(, rttimer)rttlist;
>  
>   NET_ASSERT_LOCKED();
>  
> + TAILQ_INIT();
> + mtx_enter(_mtx);
>   while ((r = TAILQ_FIRST(>rtq_head)) != NULL) {
>   LIST_REMOVE(r, rtt_link);
>   TAILQ_REMOVE(>rtq_head, r, rtt_next);
> + TAILQ_INSERT_TAIL(, r, rtt_next);
> + KASSERT(rtq->rtq_count > 0);
> + rtq->rtq_count--;
> + }
> + LIST_REMOVE(rtq, rtq_link);
> + mtx_leave(_mtx);
> +
> + while ((r = TAILQ_FIRST()) != NULL) {
> + TAILQ_REMOVE(, r, rtt_next);
>   RTTIMER_CALLOUT(r);
>   pool_put(_pool, r);
> - if (rtq->rtq_count > 0)
> - rtq->rtq_count--;
> - else
> - printf("rt_timer_queue_destroy: rtq_count reached 0\n");
>   }
> -
> - LIST_REMOVE(rtq, rtq_link);
>   pool_put(_queue_pool, rtq);
>  }
>  
> @@ -1450,15 +1463,22 @@ rt_timer_queue_count(struct rttimer_queu
>  void
>  rt_timer_remove_all(struct rtentry *rt)
>  {
> - struct rttimer  *r;
> + struct rttimer  *r;
> + TAILQ_HEAD(, rttimer)rttlist;
>  
> + TAILQ_INIT();
> + mtx_enter(_mtx);
>   while ((r = LIST_FIRST(>rt_timer)) != NULL) {
>   LIST_REMOVE(r, rtt_link);
>   TAILQ_REMOVE(>rtt_queue->rtq_head, r, rtt_next);
> - if (r->rtt_queue->rtq_count > 0)
> - r->rtt_queue->rtq_count--;
> - else
> - printf("rt_timer_remove_all: rtq_count reached 0\n");
> + TAILQ_INSERT_TAIL(, r, rtt_next);
> + KASSERT(r->rtt_queue->rtq_count > 0);
> + r->rtt_queue->rtq_count--;
> + }
> + mtx_leave(_mtx);
> +
> + while ((r = TAILQ_FIRST()) != NULL) {
> + TAILQ_REMOVE(, r, rtt_next);
>   pool_put(_pool, r);
>   }
>  }
> @@ -1471,8 +1491,9 @@ rt_timer_add(struct rtentry *rt, void (*
>   time_t   current_time;
>  
>   current_time = getuptime();
> - rt->rt_expire = current_time + queue->rtq_timeout;
>  
> + mtx_enter(_mtx);
> + rt->rt_expire = current_time + queue->rtq_timeout;
>   /*
>* If there's already a timer with this action, destroy it before
>* we add a new one.
> @@ -1481,27 +1502,31 

Re: router timer mutex

2022-04-21 Thread Hrvoje Popovski
On 20.4.2022. 20:12, Alexander Bluhm wrote:
> Hi,
> 
> mvs@ reminded me of a crash I have seen in December.  Route timers
> are not MP safe, but I think this can be fixed with a mutex.  The
> idea is to protect the global lists with a mutex and move the rttimer
> into a temporary list.  Then the callback and pool put can be called
> later without mutex.
> 
> It survived a full regress with witness.
> 
> Hrvoje: Can you put this on your test machine together with parallel
> IP forwarding?
> 
> ok?


No problem,

I'm running this and parallel forwarding diff in lab and production.



router timer mutex

2022-04-20 Thread Alexander Bluhm
Hi,

mvs@ reminded me of a crash I have seen in December.  Route timers
are not MP safe, but I think this can be fixed with a mutex.  The
idea is to protect the global lists with a mutex and move the rttimer
into a temporary list.  Then the callback and pool put can be called
later without mutex.

It survived a full regress with witness.

Hrvoje: Can you put this on your test machine together with parallel
IP forwarding?

ok?

bluhm

Index: net/route.c
===
RCS file: /cvs/src/sys/net/route.c,v
retrieving revision 1.406
diff -u -p -r1.406 route.c
--- net/route.c 20 Apr 2022 17:58:22 -  1.406
+++ net/route.c 20 Apr 2022 18:00:39 -
@@ -1361,7 +1361,8 @@ rt_ifa_purge_walker(struct rtentry *rt, 
  * for multiple queues for efficiency's sake...
  */
 
-LIST_HEAD(, rttimer_queue) rttimer_queue_head;
+struct mutex   rttimer_mtx;
+LIST_HEAD(, rttimer_queue) rttimer_queue_head; /* [t] */
 
 #define RTTIMER_CALLOUT(r) {   \
if (r->rtt_func != NULL) {  \
@@ -1393,6 +1394,7 @@ rt_timer_init(void)
pool_init(_queue_pool, sizeof(struct rttimer_queue), 0,
IPL_MPFLOOR, 0, "rttmrq", NULL);
 
+   mtx_init(_mtx, IPL_MPFLOOR);
LIST_INIT(_queue_head);
timeout_set_proc(_timer_timeout, rt_timer_timer, _timer_timeout);
timeout_add_sec(_timer_timeout, 1);
@@ -1408,7 +1410,10 @@ rt_timer_queue_create(int timeout)
rtq->rtq_timeout = timeout;
rtq->rtq_count = 0;
TAILQ_INIT(>rtq_head);
+
+   mtx_enter(_mtx);
LIST_INSERT_HEAD(_queue_head, rtq, rtq_link);
+   mtx_leave(_mtx);
 
return (rtq);
 }
@@ -1416,28 +1421,36 @@ rt_timer_queue_create(int timeout)
 void
 rt_timer_queue_change(struct rttimer_queue *rtq, int timeout)
 {
+   mtx_enter(_mtx);
rtq->rtq_timeout = timeout;
+   mtx_leave(_mtx);
 }
 
 void
 rt_timer_queue_destroy(struct rttimer_queue *rtq)
 {
-   struct rttimer  *r;
+   struct rttimer  *r;
+   TAILQ_HEAD(, rttimer)rttlist;
 
NET_ASSERT_LOCKED();
 
+   TAILQ_INIT();
+   mtx_enter(_mtx);
while ((r = TAILQ_FIRST(>rtq_head)) != NULL) {
LIST_REMOVE(r, rtt_link);
TAILQ_REMOVE(>rtq_head, r, rtt_next);
+   TAILQ_INSERT_TAIL(, r, rtt_next);
+   KASSERT(rtq->rtq_count > 0);
+   rtq->rtq_count--;
+   }
+   LIST_REMOVE(rtq, rtq_link);
+   mtx_leave(_mtx);
+
+   while ((r = TAILQ_FIRST()) != NULL) {
+   TAILQ_REMOVE(, r, rtt_next);
RTTIMER_CALLOUT(r);
pool_put(_pool, r);
-   if (rtq->rtq_count > 0)
-   rtq->rtq_count--;
-   else
-   printf("rt_timer_queue_destroy: rtq_count reached 0\n");
}
-
-   LIST_REMOVE(rtq, rtq_link);
pool_put(_queue_pool, rtq);
 }
 
@@ -1450,15 +1463,22 @@ rt_timer_queue_count(struct rttimer_queu
 void
 rt_timer_remove_all(struct rtentry *rt)
 {
-   struct rttimer  *r;
+   struct rttimer  *r;
+   TAILQ_HEAD(, rttimer)rttlist;
 
+   TAILQ_INIT();
+   mtx_enter(_mtx);
while ((r = LIST_FIRST(>rt_timer)) != NULL) {
LIST_REMOVE(r, rtt_link);
TAILQ_REMOVE(>rtt_queue->rtq_head, r, rtt_next);
-   if (r->rtt_queue->rtq_count > 0)
-   r->rtt_queue->rtq_count--;
-   else
-   printf("rt_timer_remove_all: rtq_count reached 0\n");
+   TAILQ_INSERT_TAIL(, r, rtt_next);
+   KASSERT(r->rtt_queue->rtq_count > 0);
+   r->rtt_queue->rtq_count--;
+   }
+   mtx_leave(_mtx);
+
+   while ((r = TAILQ_FIRST()) != NULL) {
+   TAILQ_REMOVE(, r, rtt_next);
pool_put(_pool, r);
}
 }
@@ -1471,8 +1491,9 @@ rt_timer_add(struct rtentry *rt, void (*
time_t   current_time;
 
current_time = getuptime();
-   rt->rt_expire = current_time + queue->rtq_timeout;
 
+   mtx_enter(_mtx);
+   rt->rt_expire = current_time + queue->rtq_timeout;
/*
 * If there's already a timer with this action, destroy it before
 * we add a new one.
@@ -1481,27 +1502,31 @@ rt_timer_add(struct rtentry *rt, void (*
if (r->rtt_func == func) {
LIST_REMOVE(r, rtt_link);
TAILQ_REMOVE(>rtt_queue->rtq_head, r, rtt_next);
-   if (r->rtt_queue->rtq_count > 0)
-   r->rtt_queue->rtq_count--;
-   else
-   printf("rt_timer_add: rtq_count reached 0\n");
-   pool_put(_pool, r);
+   KASSERT(r->rtt_queue->rtq_count > 0);
+   r->rtt_queue->rtq_count--;