diff options
author | rwatson <rwatson@FreeBSD.org> | 2009-08-23 20:40:19 +0000 |
---|---|---|
committer | rwatson <rwatson@FreeBSD.org> | 2009-08-23 20:40:19 +0000 |
commit | ef8d755d4df716bf13f8a1833f7dd1db0b78c569 (patch) | |
tree | 83b839696bd918acacee8b38993abd3c6857a5fb /sys/net/if_var.h | |
parent | 3d8e6186e2a82569f4f5214f8c8af700e5e36c13 (diff) | |
download | FreeBSD-src-ef8d755d4df716bf13f8a1833f7dd1db0b78c569.zip FreeBSD-src-ef8d755d4df716bf13f8a1833f7dd1db0b78c569.tar.gz |
Rework global locks for interface list and index management, correcting
several critical bugs, including race conditions and lock order issues:
Replace the single rwlock, ifnet_lock, with two locks, an rwlock and an
sxlock. Either can be held to stablize the lists and indexes, but both
are required to write. This allows the list to be held stable in both
network interrupt contexts and sleepable user threads across sleeping
memory allocations or device driver interactions. As before, writes to
the interface list must occur from sleepable contexts.
Reviewed by: bz, julian
MFC after: 3 days
Diffstat (limited to 'sys/net/if_var.h')
-rw-r--r-- | sys/net/if_var.h | 42 |
1 files changed, 34 insertions, 8 deletions
diff --git a/sys/net/if_var.h b/sys/net/if_var.h index aa83161..0bfd9f1 100644 --- a/sys/net/if_var.h +++ b/sys/net/if_var.h @@ -85,6 +85,7 @@ struct vnet; #include <sys/lock.h> /* XXX */ #include <sys/mutex.h> /* XXX */ #include <sys/rwlock.h> /* XXX */ +#include <sys/sx.h> /* XXX */ #include <sys/event.h> /* XXX */ #include <sys/_task.h> @@ -754,14 +755,39 @@ struct ifmultiaddr { #ifdef _KERNEL -extern struct rwlock ifnet_lock; -#define IFNET_LOCK_INIT() \ - rw_init_flags(&ifnet_lock, "ifnet", RW_RECURSE) -#define IFNET_WLOCK() rw_wlock(&ifnet_lock) -#define IFNET_WUNLOCK() rw_wunlock(&ifnet_lock) -#define IFNET_WLOCK_ASSERT() rw_assert(&ifnet_lock, RA_LOCKED) -#define IFNET_RLOCK() rw_rlock(&ifnet_lock) -#define IFNET_RUNLOCK() rw_runlock(&ifnet_lock) +extern struct rwlock ifnet_rwlock; +extern struct sx ifnet_sxlock; + +#define IFNET_LOCK_INIT() do { \ + rw_init_flags(&ifnet_rwlock, "ifnet_rw", RW_RECURSE); \ + sx_init_flags(&ifnet_sxlock, "ifnet_sx", SX_RECURSE); \ +} while(0) + +#define IFNET_WLOCK() do { \ + sx_xlock(&ifnet_sxlock); \ + rw_wlock(&ifnet_rwlock); \ +} while (0) + +#define IFNET_WUNLOCK() do { \ + rw_wunlock(&ifnet_rwlock); \ + sx_xunlock(&ifnet_sxlock); \ +} while (0) + +/* + * To assert the ifnet lock, you must know not only whether it's for read or + * write, but also whether it was acquired with sleep support or not. + */ +#define IFNET_RLOCK_ASSERT() sx_assert(&ifnet_sxlock, SA_SLOCKED) +#define IFNET_RLOCK_NOSLEEP_ASSERT() rw_assert(&ifnet_rwlock, RA_RLOCKED) +#define IFNET_WLOCK_ASSERT() do { \ + sx_assert(&ifnet_sxlock, SA_XLOCKED); \ + rw_assert(&ifnet_rwlock, RA_WLOCKED); \ +} while (0) + +#define IFNET_RLOCK() sx_slock(&ifnet_sxlock) +#define IFNET_RLOCK_NOSLEEP() rw_rlock(&ifnet_rwlock) +#define IFNET_RUNLOCK() sx_sunlock(&ifnet_sxlock) +#define IFNET_RUNLOCK_NOSLEEP() rw_runlock(&ifnet_rwlock) /* * Look up an ifnet given its index; the _ref variant also acquires a |