On 16/02/2016 08:38, Roy Marples wrote: > On Tuesday 16 February 2016 13:37:28 you wrote: >>> Oh, I see. We shouldn't drop any events of link state changes. >> >> i don't see any point in keeping a huge series of link changes >> if they can be collapsed into an equivalent sequence. with VMs >> and other user-controlled systems it's possible to flood such a >> link state checking engine. > > The queue can be kept quite short because we don't care about any prior > entries each time state changes to LINK_STATE_DOWN. > Also, we don't care about any prior entries (other than LINK_STATE_DOWN) when > setting LINK_STATE_UNKNOWN. > Finally we set LINK_STATE_UP. > > So the queue should only ever hold a maximum of 3 items. Could even work it > like so
I found the time to work on this, here is the patch I've been running for an hour or so upping and downing interfaces. OK to commit? Roy
Index: sys/net/if.c =================================================================== RCS file: /cvsroot/src/sys/net/if.c,v retrieving revision 1.324 diff -u -r1.324 if.c --- sys/net/if.c 15 Feb 2016 08:08:04 -0000 1.324 +++ sys/net/if.c 16 Feb 2016 13:22:43 -0000 @@ -603,7 +603,6 @@ ifp->if_broadcastaddr = 0; /* reliably crash if used uninitialized */ ifp->if_link_state = LINK_STATE_UNKNOWN; - ifp->if_old_link_state = LINK_STATE_UNKNOWN; ifp->if_capenable = 0; ifp->if_csum_flags_tx = 0; @@ -1562,41 +1561,73 @@ } } +/* Priority list for link state changes */ +static const int +if_link_state_prio[LINK_STATE_MAX] = { + 0, /* LINK_STATE_UNKNOWN */ + -1, /* LINK_STATE_DOWN */ + 1, /* LINK_STATE_UP */ +}; + /* * Handle a change in the interface link state. - * XXX: We should listen to the routing socket in-kernel rather - * than calling in6_if_link_* functions directly from here. */ void if_link_state_change(struct ifnet *ifp, int link_state) { - int s; + int s, i; + + KASSERT(link_state >= 0 && link_state < LINK_STATE_MAX); s = splnet(); - if (ifp->if_link_state == link_state) { - splx(s); - return; + if (ifp->if_link_state == link_state && ifp->if_link_queue_len == 0) + goto out; + + for (i = 0; i < ifp->if_link_queue_len; i++) { + if (if_link_state_prio[link_state] <= + if_link_state_prio[ifp->if_link_queue[i]]) + { + ifp->if_link_queue[i] = link_state; + ifp->if_link_queue_len = i + 1; + /* Because we are trimming the queue, + * there is no need to call softint_schedule again. */ + goto out; + } } - ifp->if_link_state = link_state; + KASSERT(ifp->if_link_queue_len + 1 < __arraycount(ifp->if_link_queue)); + ifp->if_link_queue[ifp->if_link_queue_len++] = link_state; softint_schedule(ifp->if_link_si); +out: splx(s); } - static void if_link_state_change_si(void *arg) { struct ifnet *ifp = arg; - int s; + int s, i; int link_state, old_link_state; struct domain *dp; s = splnet(); - link_state = ifp->if_link_state; - old_link_state = ifp->if_old_link_state; - ifp->if_old_link_state = ifp->if_link_state; + KASSERT(ifp->if_link_queue_len >= 0); /* unlikely */ + if (ifp->if_link_queue_len == 0) + goto out; + + /* Pop the link state change from the queue */ + link_state = ifp->if_link_queue[0]; + ifp->if_link_queue_len--; + for (i = 0; i < ifp->if_link_queue_len; i++) + ifp->if_link_queue[i] = ifp->if_link_queue[i + 1]; + + /* Ensure link state is actually changing */ + if (ifp->if_link_state == link_state) + goto out; + + old_link_state = ifp->if_link_state; + ifp->if_link_state = link_state; #ifdef DEBUG log(LOG_DEBUG, "%s: link state %s (was %s)\n", ifp->if_xname, @@ -1640,6 +1671,7 @@ dp->dom_if_link_state_change(ifp, link_state); } +out: splx(s); } Index: sys/net/if.h =================================================================== RCS file: /cvsroot/src/sys/net/if.h,v retrieving revision 1.197 diff -u -r1.197 if.h --- sys/net/if.h 16 Feb 2016 01:31:26 -0000 1.197 +++ sys/net/if.h 16 Feb 2016 13:22:43 -0000 @@ -191,10 +191,12 @@ /* * Values for if_link_state. + * When changing these values, consider if_link_state_prio in if.c. */ #define LINK_STATE_UNKNOWN 0 /* link invalid/unknown */ #define LINK_STATE_DOWN 1 /* link is down */ #define LINK_STATE_UP 2 /* link is up */ +#define LINK_STATE_MAX 3 /* size of link states */ /* * Structure defining a queue for a network interface. @@ -351,7 +353,8 @@ struct krwlock *if_afdata_lock; struct if_percpuq *if_percpuq; /* We should remove it in the future */ void *if_link_si; /* softint to handle link state changes */ - int if_old_link_state; /* previous link state */ + int if_link_queue[LINK_STATE_MAX]; /* link state change queue */ + int if_link_queue_len; /* length of link state change queue */ #endif } ifnet_t;