summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorglebius <glebius@FreeBSD.org>2005-04-20 09:30:54 +0000
committerglebius <glebius@FreeBSD.org>2005-04-20 09:30:54 +0000
commit5f725a70e06f9948ad77ff806c8a2a993fdefec5 (patch)
tree214713abcf1d2a3f43290a8d18676a6fd08d0c93 /sys
parent0d333ce191c966fb85a3dce5a0d73019b9539c71 (diff)
downloadFreeBSD-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
Diffstat (limited to 'sys')
-rw-r--r--sys/net/if.c26
-rw-r--r--sys/net/if_var.h1
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 *);
OpenPOWER on IntegriCloud