diff options
author | rwatson <rwatson@FreeBSD.org> | 2004-07-27 23:20:45 +0000 |
---|---|---|
committer | rwatson <rwatson@FreeBSD.org> | 2004-07-27 23:20:45 +0000 |
commit | b463bc6c336f88c5c53b54a13c72ffd11be29e4e (patch) | |
tree | 093e047a9b2b8e29f6989662ecc21cb54dc302a4 /sys/net | |
parent | 858cb94e23c67389d48ae321fdd98dc21b3655e1 (diff) | |
download | FreeBSD-src-b463bc6c336f88c5c53b54a13c72ffd11be29e4e.zip FreeBSD-src-b463bc6c336f88c5c53b54a13c72ffd11be29e4e.tar.gz |
Add a new network interface flag, IFF_NEEDSGIANT, which will allow
device drivers to declare that the ifp->if_start() method implemented
by the driver requires Giant in order to operate correctly.
Add a 'struct task' to 'struct ifnet' that can be used to execute a
deferred ifp->if_start() in the event that if_start needs to be called
in a Giant-free environment. To do this, introduce if_start(), a
wrapper function for ifp->if_start(). If the interface can run MPSAFE,
it directly dispatches into the interface start routine. If it can't
run MPSAFE, we're running with debug.mpsafenet != 0, and Giant isn't
currently held, the task is queued to execute in a swi holding Giant
via if_start_deferred().
Modify if_handoff() to use if_start() instead of direct dispatch.
Modify 802.11 to use if_start() instead of direct dispatch.
This is intended to provide increased compatibility for non-MPSAFE
network device drivers in the presence of Giant-free operation via
asynchronous dispatch. However, this commit does not mark any network
interfaces as IFF_NEEDSGIANT.
Diffstat (limited to 'sys/net')
-rw-r--r-- | sys/net/if.c | 49 | ||||
-rw-r--r-- | sys/net/if.h | 1 | ||||
-rw-r--r-- | sys/net/if_ethersubr.c | 2 | ||||
-rw-r--r-- | sys/net/if_var.h | 8 |
4 files changed, 58 insertions, 2 deletions
diff --git a/sys/net/if.c b/sys/net/if.c index 1d76fc9..0944d44 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -50,6 +50,7 @@ #include <sys/sockio.h> #include <sys/syslog.h> #include <sys/sysctl.h> +#include <sys/taskqueue.h> #include <sys/domain.h> #include <sys/jail.h> #include <machine/stdarg.h> @@ -92,6 +93,7 @@ static void if_unroute(struct ifnet *, int flag, int fam); 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); #ifdef INET6 /* * XXX: declare here to avoid to include many inet6 related files.. @@ -365,6 +367,7 @@ if_attach(struct ifnet *ifp) struct sockaddr_dl *sdl; struct ifaddr *ifa; + TASK_INIT(&ifp->if_starttask, 0, if_start_deferred, ifp); IF_AFDATA_LOCK_INIT(ifp); ifp->if_afdata_initialized = 0; IFNET_WLOCK(); @@ -983,6 +986,10 @@ if_qflush(struct ifaltq *ifq) * Handle interface watchdog timer routines. Called * from softclock, we decrement timers (if set) and * call the appropriate interface routine on expiration. + * + * XXXRW: Note that because timeouts run with Giant, if_watchdog() is called + * holding Giant. If we switch to an MPSAFE callout, we likely need to grab + * Giant before entering if_watchdog() on an IFF_NEEDSGIANT interface. */ static void if_slowtimo(void *arg) @@ -1839,5 +1846,47 @@ if_printf(struct ifnet *ifp, const char * fmt, ...) return (retval); } +/* + * When an interface is marked IFF_NEEDSGIANT, its if_start() routine cannot + * be called without Giant. However, we often can't acquire the Giant lock + * at those points; instead, we run it via a task queue that holds Giant via + * if_start_deferred. + * + * XXXRW: We need to make sure that the ifnet isn't fully detached until any + * outstanding if_start_deferred() tasks that will run after the free. This + * probably means waiting in if_detach(). + */ +void +if_start(struct ifnet *ifp) +{ + + NET_ASSERT_GIANT(); + + if ((ifp->if_flags & IFF_NEEDSGIANT) != 0 && debug_mpsafenet != 0) { + if (mtx_owned(&Giant)) + (*(ifp)->if_start)(ifp); + else + taskqueue_enqueue(taskqueue_swi_giant, + &ifp->if_starttask); + } else + (*(ifp)->if_start)(ifp); +} + +static void +if_start_deferred(void *context, int pending) +{ + struct ifnet *ifp; + + /* + * This code must be entered with Giant, and should never run if + * we're not running with debug.mpsafenet. + */ + KASSERT(debug_mpsafenet != 0, ("if_start_deferred: debug.mpsafenet")); + GIANT_REQUIRED; + + ifp = (struct ifnet *)context; + (ifp->if_start)(ifp); +} + SYSCTL_NODE(_net, PF_LINK, link, CTLFLAG_RW, 0, "Link layers"); SYSCTL_NODE(_net_link, 0, generic, CTLFLAG_RW, 0, "Generic link-management"); diff --git a/sys/net/if.h b/sys/net/if.h index 1dab8c0..084fc9d 100644 --- a/sys/net/if.h +++ b/sys/net/if.h @@ -126,6 +126,7 @@ struct if_data { #define IFF_PPROMISC 0x20000 /* user-requested promisc mode */ #define IFF_MONITOR 0x40000 /* user-requested monitor mode */ #define IFF_STATICARP 0x80000 /* static ARP */ +#define IFF_NEEDSGIANT 0x100000 /* hold Giant over if_start calls */ /* flags set internally only: */ #define IFF_CANTCHANGE \ diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index fb10e01..080dcfb 100644 --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -888,6 +888,8 @@ ether_ifattach(struct ifnet *ifp, const u_int8_t *llc) break; if (i != ifp->if_addrlen) if_printf(ifp, "Ethernet address: %6D\n", llc, ":"); + if (debug_mpsafenet && (ifp->if_flags & IFF_NEEDSGIANT) != 0) + if_printf(ifp, "if_start running deferred for Giant\n"); } /* diff --git a/sys/net/if_var.h b/sys/net/if_var.h index a01ed03..0675bc9 100644 --- a/sys/net/if_var.h +++ b/sys/net/if_var.h @@ -79,6 +79,7 @@ struct ether_header; #include <sys/lock.h> /* XXX */ #include <sys/mutex.h> /* XXX */ #include <sys/event.h> /* XXX */ +#include <sys/_task.h> #define IF_DUNIT_NONE -1 @@ -191,6 +192,7 @@ struct ifnet { void *if_afdata[AF_MAX]; int if_afdata_initialized; struct mtx if_afdata_mtx; + struct task if_starttask; /* task for IFF_NEEDSGIANT */ }; typedef void if_init_f_t(void *); @@ -329,6 +331,8 @@ EVENTHANDLER_DECLARE(ifnet_departure_event, ifnet_departure_event_handler_t); #define IF_HANDOFF_ADJ(ifq, m, ifp, adj) \ if_handoff((struct ifqueue *)ifq, m, ifp, adj) +void if_start(struct ifnet *); + static __inline int if_handoff(struct ifqueue *ifq, struct mbuf *m, struct ifnet *ifp, int adjust) { @@ -350,7 +354,7 @@ if_handoff(struct ifqueue *ifq, struct mbuf *m, struct ifnet *ifp, int adjust) _IF_ENQUEUE(ifq, m); IF_UNLOCK(ifq); if (ifp != NULL && !active) - (*ifp->if_start)(ifp); + if_start(ifp); return (1); } #if 1 /* ALTQ */ @@ -474,7 +478,7 @@ do { \ if (mflags & M_MCAST) \ (ifp)->if_omcasts++; \ if (((ifp)->if_flags & IFF_OACTIVE) == 0) \ - (*(ifp)->if_start)(ifp); \ + if_start(ifp); \ } \ } while (0) |