hi, in order to make our life a bit easier and prevent rogue accesses to the routing table from the hardware interrupt context violating all kinds of spl assumptions we would like if_link_state_change that is called by network device drivers in their interrupt service routines to defer its work to the process context or thereabouts.
i did some testing here, but wouldn't mind if someone tries this diff in gre/vlan/ospf/anything-weird setups. making sure that hot-plugging/unplugging usb interfaces doesn't produce any undesirable effects would be superb as well. please note that a token (an interface index) is passed to the workq in order to make sure that if the interface would be gone by the time syswq goes around to run the task it would just fall through. ok? diff --git sys/net/if.c sys/net/if.c index 6dafd0d..5b6800a 100644 --- sys/net/if.c +++ sys/net/if.c @@ -79,10 +79,11 @@ #include <sys/protosw.h> #include <sys/kernel.h> #include <sys/ioctl.h> #include <sys/domain.h> #include <sys/sysctl.h> +#include <sys/workq.h> #include <net/if.h> #include <net/if_dl.h> #include <net/if_media.h> #include <net/if_types.h> @@ -151,10 +152,12 @@ int if_clone_list(struct if_clonereq *); struct if_clone *if_clone_lookup(const char *, int *); void if_congestion_clear(void *); int if_group_egress_build(void); +void if_link_state_change_task(void *, void *); + int ifai_cmp(struct ifaddr_item *, struct ifaddr_item *); void ifa_item_insert(struct sockaddr *, struct ifaddr *, struct ifnet *); void ifa_item_remove(struct sockaddr *, struct ifaddr *, struct ifnet *); #ifndef SMALL_KERNEL void ifa_print_rb(void); @@ -1106,21 +1109,39 @@ if_up(struct ifnet *ifp) m_clinitifp(ifp); } /* - * Process a link state change. - * NOTE: must be called at splsoftnet or equivalent. + * Schedule a link state change task. */ void if_link_state_change(struct ifnet *ifp) { - rt_ifmsg(ifp); + /* try to put the routing table update task on syswq */ + workq_add_task(NULL, 0, if_link_state_change_task, + (void *)((unsigned long)ifp->if_index), NULL); +} + +/* + * Process a link state change. + */ +void +if_link_state_change_task(void *arg, void *unused) +{ + unsigned int index = (unsigned long)arg; + struct ifnet *ifp; + int s; + + s = splsoftnet(); + if ((ifp = if_get(index)) != NULL) { + rt_ifmsg(ifp); #ifndef SMALL_KERNEL - rt_if_track(ifp); + rt_if_track(ifp); #endif - dohooks(ifp->if_linkstatehooks, 0); + dohooks(ifp->if_linkstatehooks, 0); + } + splx(s); } /* * Handle interface watchdog timer routines. Called * from softclock, we decrement timers (if set) and