defer routing table updates on link state changes (again)
hi, since mpi's if_index diff is now in, this should probably go in as well. it has received some testing in the meantime. original description: 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. 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 *); intif_group_egress_build(void); +void if_link_state_change_task(void *, void *); + intifai_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
Re: defer routing table updates on link state changes (again)
On Sat, Oct 19, 2013 at 01:26:39PM +0200, Mike Belopuhov wrote: hi, since mpi's if_index diff is now in, this should probably go in as well. it has received some testing in the meantime. original description: 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. 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? OK claudio@ 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 -- :wq Claudio