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 -0000 1.406
> > +++ net/route.c 21 Apr 2022 13:31:52 -0000
> > @@ -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(&rttimer_queue_pool, sizeof(struct rttimer_queue), 0,
> > IPL_MPFLOOR, 0, "rttmrq", NULL);
> >
> > + mtx_init(&rttimer_mtx, IPL_MPFLOOR);
> > LIST_INIT(&rttimer_queue_head);
> > timeout_set_proc(&rt_timer_timeout, rt_timer_timer, &rt_timer_timeout);
> > timeout_add_sec(&rt_timer_timeout, 1);
> > @@ -1408,7 +1410,10 @@ rt_timer_queue_create(int timeout)
> > rtq->rtq_timeout = timeout;
> > rtq->rtq_count = 0;
> > TAILQ_INIT(&rtq->rtq_head);
> > +
> > + mtx_enter(&rttimer_mtx);
> > LIST_INSERT_HEAD(&rttimer_queue_head, rtq, rtq_link);
> > + mtx_leave(&rttimer_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(&rttimer_mtx);
> > rtq->rtq_timeout = timeout;
> > + mtx_leave(&rttimer_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(&rttlist);
> > + mtx_enter(&rttimer_mtx);
> > while ((r = TAILQ_FIRST(&rtq->rtq_head)) != NULL) {
> > LIST_REMOVE(r, rtt_link);
> > TAILQ_REMOVE(&rtq->rtq_head, r, rtt_next);
> > + TAILQ_INSERT_TAIL(&rttlist, r, rtt_next);
> > + KASSERT(rtq->rtq_count > 0);
> > + rtq->rtq_count--;
> > + }
> > + LIST_REMOVE(rtq, rtq_link);
> > + mtx_leave(&rttimer_mtx);
> > +
> > + while ((r = TAILQ_FIRST(&rttlist)) != NULL) {
> > + TAILQ_REMOVE(&rttlist, r, rtt_next);
> > RTTIMER_CALLOUT(r);
> > pool_put(&rttimer_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(&rttimer_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(&rttlist);
> > + mtx_enter(&rttimer_mtx);
> > while ((r = LIST_FIRST(&rt->rt_timer)) != NULL) {
> > LIST_REMOVE(r, rtt_link);
> > TAILQ_REMOVE(&r->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(&rttlist, r, rtt_next);
> > + KASSERT(r->rtt_queue->rtq_count > 0);
> > + r->rtt_queue->rtq_count--;
> > + }
> > + mtx_leave(&rttimer_mtx);
> > +
> > + while ((r = TAILQ_FIRST(&rttlist)) != NULL) {
> > + TAILQ_REMOVE(&rttlist, r, rtt_next);
> > pool_put(&rttimer_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(&rttimer_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(&rttimer_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 +1512,19 @@ rt_timer_add(struct rtentry *rt, void (*
> > if (r->rtt_func == func) {
> > LIST_REMOVE(r, rtt_link);
> > TAILQ_REMOVE(&r->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(&rttimer_pool, r);
> > + KASSERT(r->rtt_queue->rtq_count > 0);
> > + r->rtt_queue->rtq_count--;
> > break; /* only one per list, so we can quit... */
> > }
> > }
> >
> > - r = pool_get(&rttimer_pool, PR_NOWAIT | PR_ZERO);
> > - if (r == NULL)
> > - return (ENOBUFS);
> > + LIST_INSERT_HEAD(&rt->rt_timer, rnew, rtt_link);
> > + TAILQ_INSERT_TAIL(&queue->rtq_head, rnew, rtt_next);
> > + rnew->rtt_queue->rtq_count++;
> > + mtx_leave(&rttimer_mtx);
> >
> > - r->rtt_rt = rt;
> > - r->rtt_time = current_time;
> > - r->rtt_func = func;
> > - r->rtt_queue = queue;
> > - r->rtt_tableid = rtableid;
> > - LIST_INSERT_HEAD(&rt->rt_timer, r, rtt_link);
> > - TAILQ_INSERT_TAIL(&queue->rtq_head, r, rtt_next);
> > - r->rtt_queue->rtq_count++;
> > + if (r != NULL)
> > + pool_put(&rttimer_pool, r);
> >
> > return (0);
> > }
> > @@ -1512,23 +1535,30 @@ rt_timer_timer(void *arg)
> > struct timeout *to = (struct timeout *)arg;
> > struct rttimer_queue *rtq;
> > struct rttimer *r;
> > + TAILQ_HEAD(, rttimer) rttlist;
> > time_t current_time;
> >
> > current_time = getuptime();
> > + TAILQ_INIT(&rttlist);
> >
> > NET_LOCK();
> > + mtx_enter(&rttimer_mtx);
> > LIST_FOREACH(rtq, &rttimer_queue_head, rtq_link) {
> > while ((r = TAILQ_FIRST(&rtq->rtq_head)) != NULL &&
> > (r->rtt_time + rtq->rtq_timeout) < current_time) {
> > LIST_REMOVE(r, rtt_link);
> > TAILQ_REMOVE(&rtq->rtq_head, r, rtt_next);
> > - RTTIMER_CALLOUT(r);
> > - pool_put(&rttimer_pool, r);
> > - if (rtq->rtq_count > 0)
> > - rtq->rtq_count--;
> > - else
> > - printf("rt_timer_timer: rtq_count reached 0\n");
> > + TAILQ_INSERT_TAIL(&rttlist, r, rtt_next);
> > + KASSERT(rtq->rtq_count > 0);
> > + rtq->rtq_count--;
> > }
> > + }
> > + mtx_leave(&rttimer_mtx);
> > +
> > + while ((r = TAILQ_FIRST(&rttlist)) != NULL) {
> > + TAILQ_REMOVE(&rttlist, r, rtt_next);
> > + RTTIMER_CALLOUT(r);
> > + pool_put(&rttimer_pool, r);
> > }
> > NET_UNLOCK();
> >
> > Index: net/route.h
> > ===================================================================
> > RCS file: /data/mirror/openbsd/cvs/src/sys/net/route.h,v
> > retrieving revision 1.190
> > diff -u -p -r1.190 route.h
> > --- net/route.h 20 Apr 2022 17:58:22 -0000 1.190
> > +++ net/route.h 21 Apr 2022 13:26:18 -0000
> > @@ -36,6 +36,12 @@
> > #define _NET_ROUTE_H_
> >
> > /*
> > + * Locks used to protect struct members in this file:
> > + * I immutable after creation
> > + * T rttimer_mtx route timer lists
> > + */
> > +
> > +/*
> > * Kernel resident routing tables.
> > *
> > * The routing tables are initialized when interface addresses
> > @@ -400,21 +406,21 @@ rtstat_inc(enum rtstat_counters c)
> > * These allow functions to be called for routes at specific times.
> > */
> > struct rttimer {
> > - TAILQ_ENTRY(rttimer) rtt_next; /* entry on timer queue */
> > - LIST_ENTRY(rttimer) rtt_link; /* multiple timers per rtentry */
> > - struct rttimer_queue *rtt_queue;/* back pointer to queue */
> > - struct rtentry *rtt_rt; /* Back pointer to the route */
> > - void (*rtt_func)(struct rtentry *,
> > - struct rttimer *);
> > - time_t rtt_time; /* When this timer was registered */
> > - u_int rtt_tableid; /* routing table id of rtt_rt */
> > + TAILQ_ENTRY(rttimer) rtt_next; /* [T] entry on timer queue */
> > + LIST_ENTRY(rttimer) rtt_link; /* [T] timers per rtentry */
> > + struct rttimer_queue *rtt_queue; /* [T] back pointer to queue */
> > + struct rtentry *rtt_rt; /* [I] back pointer to route */
> > + void (*rtt_func) /* [I] callback */
> > + (struct rtentry *, struct rttimer *);
> > + time_t rtt_time; /* [I] when timer registered */
> > + u_int rtt_tableid; /* [I] rtable id of rtt_rt */
> > };
> >
> > struct rttimer_queue {
> > - TAILQ_HEAD(, rttimer) rtq_head;
> > - LIST_ENTRY(rttimer_queue) rtq_link;
> > - unsigned long rtq_count;
> > - int rtq_timeout;
> > + TAILQ_HEAD(, rttimer) rtq_head; /* [T] */
> > + LIST_ENTRY(rttimer_queue) rtq_link; /* [T] */
> > + unsigned long rtq_count; /* [T] */
> > + int rtq_timeout; /* [T] */
> > };
> >
> > const char *rtlabel_id2name(u_int16_t);
>
--
:wq Claudio