diff options
author | glebius <glebius@FreeBSD.org> | 2005-04-20 09:30:54 +0000 |
---|---|---|
committer | glebius <glebius@FreeBSD.org> | 2005-04-20 09:30:54 +0000 |
commit | 5f725a70e06f9948ad77ff806c8a2a993fdefec5 (patch) | |
tree | 214713abcf1d2a3f43290a8d18676a6fd08d0c93 | |
parent | 0d333ce191c966fb85a3dce5a0d73019b9539c71 (diff) | |
download | FreeBSD-src-5f725a70e06f9948ad77ff806c8a2a993fdefec5.zip FreeBSD-src-5f725a70e06f9948ad77ff806c8a2a993fdefec5.tar.gz |
Do not call all link state callbacks directly, but schedule
a taskqueue(9) task. This fixes LORs and adds possibility
to serve such events pseudorecursively, when link state
change of interface causes subsequent change on other
interfaces.
Sponsored by: Rambler
Reviewed by: sam, brooks, mux
-rw-r--r-- | sys/net/if.c | 26 | ||||
-rw-r--r-- | sys/net/if_var.h | 1 |
2 files changed, 24 insertions, 3 deletions
diff --git a/sys/net/if.c b/sys/net/if.c index 192a038..60d26ad 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -112,6 +112,7 @@ static void link_rtrequest(int, struct rtentry *, struct rt_addrinfo *); static int if_rtdel(struct radix_node *, void *); static int ifhwioctl(u_long, struct ifnet *, caddr_t, struct thread *); static void if_start_deferred(void *context, int pending); +static void do_link_state_change(void *, int); #ifdef INET6 /* * XXX: declare here to avoid to include many inet6 related files.. @@ -385,6 +386,7 @@ if_attach(struct ifnet *ifp) struct ifaddr *ifa; TASK_INIT(&ifp->if_starttask, 0, if_start_deferred, ifp); + TASK_INIT(&ifp->if_linktask, 0, do_link_state_change, ifp); IF_AFDATA_LOCK_INIT(ifp); ifp->if_afdata_initialized = 0; IFNET_WLOCK(); @@ -542,6 +544,11 @@ if_detach(struct ifnet *ifp) struct ifnet *iter; int found; + /* + * Remove/wait for pending events. + */ + taskqueue_drain(taskqueue_swi, &ifp->if_linktask); + EVENTHANDLER_INVOKE(ifnet_departure_event, ifp); #ifdef DEV_CARP /* Maybe hook to the generalized departure handler above?!? */ @@ -988,19 +995,30 @@ if_route(struct ifnet *ifp, int flag, int fam) void (*vlan_link_state_p)(struct ifnet *, int); /* XXX: private from if_vlan */ /* - * Handle a change in the interface link state. + * Handle a change in the interface link state. To avoid LORs + * between driver lock and upper layer locks, as well as possible + * recursions, we post event to taskqueue, and all job + * is done in static do_link_state_change(). */ void if_link_state_change(struct ifnet *ifp, int link_state) { - int link; - /* Return if state hasn't changed. */ if (ifp->if_link_state == link_state) return; ifp->if_link_state = link_state; + taskqueue_enqueue(taskqueue_swi, &ifp->if_linktask); +} + +static void +do_link_state_change(void *arg, int pending) +{ + struct ifnet *ifp = (struct ifnet *)arg; + int link_state = ifp->if_link_state; + int link; + /* Notify that the link state has changed. */ rt_ifmsg(ifp); if (link_state == LINK_STATE_UP) @@ -1020,6 +1038,8 @@ if_link_state_change(struct ifnet *ifp, int link_state) if (ifp->if_carp) carp_carpdev_state(ifp->if_carp); #endif + if (pending > 1) + if_printf(ifp, "%d link states coalesced\n", pending); if (log_link_state_change) log(LOG_NOTICE, "%s: link state changed to %s\n", ifp->if_xname, (link_state == LINK_STATE_UP) ? "UP" : "DOWN" ); diff --git a/sys/net/if_var.h b/sys/net/if_var.h index 78eea2c..a1ff0fb 100644 --- a/sys/net/if_var.h +++ b/sys/net/if_var.h @@ -194,6 +194,7 @@ struct ifnet { int if_afdata_initialized; struct mtx if_afdata_mtx; struct task if_starttask; /* task for IFF_NEEDSGIANT */ + struct task if_linktask; /* task for link change events */ }; typedef void if_init_f_t(void *); |