Improve carp(4) locking:
- Use the carp_sx to serialize not only CARP ioctls, but also carp_attach() and carp_detach(). - Use cif_mtx to lock only access to those the linked list. - These locking changes allow us to do some memory allocations with M_WAITOK and also properly call callout_drain() in carp_destroy(). - In carp_attach() assert that ifaddr isn't attached. We always come here with a pristine address from in[6]_control(). Reviewed by: oleg Sponsored by: Nginx, Inc.
This commit is contained in:
parent
270538b2b6
commit
9c2cd1aa84
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=281839
@ -256,7 +256,7 @@ SYSCTL_VNET_PCPUSTAT(_net_inet_carp, OID_AUTO, stats, struct carpstats,
|
||||
#define CIF_LOCK(cif) mtx_lock(&(cif)->cif_mtx)
|
||||
#define CIF_UNLOCK(cif) mtx_unlock(&(cif)->cif_mtx)
|
||||
#define CIF_FREE(cif) do { \
|
||||
CIF_LOCK_ASSERT(cif); \
|
||||
CIF_LOCK(cif); \
|
||||
if (TAILQ_EMPTY(&(cif)->cif_vrs)) \
|
||||
carp_free_if(cif); \
|
||||
else \
|
||||
@ -296,7 +296,6 @@ SYSCTL_VNET_PCPUSTAT(_net_inet_carp, OID_AUTO, stats, struct carpstats,
|
||||
static void carp_input_c(struct mbuf *, struct carp_header *, sa_family_t);
|
||||
static struct carp_softc
|
||||
*carp_alloc(struct ifnet *);
|
||||
static void carp_detach_locked(struct ifaddr *);
|
||||
static void carp_destroy(struct carp_softc *);
|
||||
static struct carp_if
|
||||
*carp_alloc_if(struct ifnet *);
|
||||
@ -1250,8 +1249,6 @@ carp_multicast_setup(struct carp_if *cif, sa_family_t sa)
|
||||
struct ifnet *ifp = cif->cif_ifp;
|
||||
int error = 0;
|
||||
|
||||
CIF_LOCK_ASSERT(cif);
|
||||
|
||||
switch (sa) {
|
||||
#ifdef INET
|
||||
case AF_INET:
|
||||
@ -1264,9 +1261,7 @@ carp_multicast_setup(struct carp_if *cif, sa_family_t sa)
|
||||
|
||||
imo->imo_membership = (struct in_multi **)malloc(
|
||||
(sizeof(struct in_multi *) * IP_MIN_MEMBERSHIPS), M_CARP,
|
||||
M_NOWAIT);
|
||||
if (imo->imo_membership == NULL)
|
||||
return (ENOMEM);
|
||||
M_WAITOK);
|
||||
imo->imo_mfilters = NULL;
|
||||
imo->imo_max_memberships = IP_MIN_MEMBERSHIPS;
|
||||
imo->imo_multicast_vif = -1;
|
||||
@ -1296,9 +1291,7 @@ carp_multicast_setup(struct carp_if *cif, sa_family_t sa)
|
||||
|
||||
im6o->im6o_membership = (struct in6_multi **)malloc(
|
||||
(sizeof(struct in6_multi *) * IPV6_MIN_MEMBERSHIPS), M_CARP,
|
||||
M_ZERO | M_NOWAIT);
|
||||
if (im6o->im6o_membership == NULL)
|
||||
return (ENOMEM);
|
||||
M_ZERO | M_WAITOK);
|
||||
im6o->im6o_mfilters = NULL;
|
||||
im6o->im6o_max_memberships = IPV6_MIN_MEMBERSHIPS;
|
||||
im6o->im6o_multicast_hlim = CARP_DFLTTL;
|
||||
@ -1355,7 +1348,8 @@ static void
|
||||
carp_multicast_cleanup(struct carp_if *cif, sa_family_t sa)
|
||||
{
|
||||
|
||||
CIF_LOCK_ASSERT(cif);
|
||||
sx_assert(&carp_sx, SA_XLOCKED);
|
||||
|
||||
switch (sa) {
|
||||
#ifdef INET
|
||||
case AF_INET:
|
||||
@ -1504,22 +1498,18 @@ carp_alloc(struct ifnet *ifp)
|
||||
return (sc);
|
||||
}
|
||||
|
||||
static int
|
||||
static void
|
||||
carp_grow_ifas(struct carp_softc *sc)
|
||||
{
|
||||
struct ifaddr **new;
|
||||
|
||||
CARP_LOCK_ASSERT(sc);
|
||||
|
||||
new = malloc(sc->sc_ifasiz * 2, M_CARP, M_NOWAIT|M_ZERO);
|
||||
if (new == NULL)
|
||||
return (ENOMEM);
|
||||
new = malloc(sc->sc_ifasiz * 2, M_CARP, M_WAITOK | M_ZERO);
|
||||
CARP_LOCK(sc);
|
||||
bcopy(sc->sc_ifas, new, sc->sc_ifasiz);
|
||||
free(sc->sc_ifas, M_CARP);
|
||||
sc->sc_ifas = new;
|
||||
sc->sc_ifasiz *= 2;
|
||||
|
||||
return (0);
|
||||
CARP_UNLOCK(sc);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1528,17 +1518,20 @@ carp_destroy(struct carp_softc *sc)
|
||||
struct ifnet *ifp = sc->sc_carpdev;
|
||||
struct carp_if *cif = ifp->if_carp;
|
||||
|
||||
CIF_LOCK_ASSERT(cif);
|
||||
sx_assert(&carp_sx, SA_XLOCKED);
|
||||
|
||||
if (sc->sc_suppress)
|
||||
carp_demote_adj(-V_carp_ifdown_adj, "vhid removed");
|
||||
CARP_UNLOCK(sc);
|
||||
|
||||
CIF_LOCK(cif);
|
||||
TAILQ_REMOVE(&cif->cif_vrs, sc, sc_list);
|
||||
CIF_UNLOCK(cif);
|
||||
|
||||
mtx_lock(&carp_mtx);
|
||||
LIST_REMOVE(sc, sc_next);
|
||||
mtx_unlock(&carp_mtx);
|
||||
|
||||
CARP_LOCK(sc);
|
||||
if (sc->sc_suppress)
|
||||
carp_demote_adj(-V_carp_ifdown_adj, "vhid removed");
|
||||
callout_drain(&sc->sc_ad_tmo);
|
||||
#ifdef INET
|
||||
callout_drain(&sc->sc_md_tmo);
|
||||
@ -1807,8 +1800,7 @@ carp_attach(struct ifaddr *ifa, int vhid)
|
||||
struct carp_softc *sc;
|
||||
int index, error;
|
||||
|
||||
if (ifp->if_carp == NULL)
|
||||
return (ENOPROTOOPT);
|
||||
KASSERT(ifa->ifa_carp == NULL, ("%s: ifa %p attached", __func__, ifa));
|
||||
|
||||
switch (ifa->ifa_addr->sa_family) {
|
||||
#ifdef INET
|
||||
@ -1822,40 +1814,32 @@ carp_attach(struct ifaddr *ifa, int vhid)
|
||||
return (EPROTOTYPE);
|
||||
}
|
||||
|
||||
sx_xlock(&carp_sx);
|
||||
if (ifp->if_carp == NULL) {
|
||||
sx_xunlock(&carp_sx);
|
||||
return (ENOPROTOOPT);
|
||||
}
|
||||
|
||||
CIF_LOCK(cif);
|
||||
IFNET_FOREACH_CARP(ifp, sc)
|
||||
if (sc->sc_vhid == vhid)
|
||||
break;
|
||||
CIF_UNLOCK(cif);
|
||||
if (sc == NULL) {
|
||||
CIF_UNLOCK(cif);
|
||||
sx_xunlock(&carp_sx);
|
||||
return (ENOENT);
|
||||
}
|
||||
|
||||
if (ifa->ifa_carp) {
|
||||
if (ifa->ifa_carp->sc_vhid != vhid)
|
||||
carp_detach_locked(ifa);
|
||||
else {
|
||||
CIF_UNLOCK(cif);
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
error = carp_multicast_setup(cif, ifa->ifa_addr->sa_family);
|
||||
if (error) {
|
||||
CIF_FREE(cif);
|
||||
sx_xunlock(&carp_sx);
|
||||
return (error);
|
||||
}
|
||||
|
||||
CARP_LOCK(sc);
|
||||
index = sc->sc_naddrs + sc->sc_naddrs6 + 1;
|
||||
if (index > sc->sc_ifasiz / sizeof(struct ifaddr *))
|
||||
if ((error = carp_grow_ifas(sc)) != 0) {
|
||||
carp_multicast_cleanup(cif,
|
||||
ifa->ifa_addr->sa_family);
|
||||
CARP_UNLOCK(sc);
|
||||
CIF_FREE(cif);
|
||||
return (error);
|
||||
}
|
||||
carp_grow_ifas(sc);
|
||||
|
||||
switch (ifa->ifa_addr->sa_family) {
|
||||
#ifdef INET
|
||||
@ -1873,31 +1857,21 @@ carp_attach(struct ifaddr *ifa, int vhid)
|
||||
}
|
||||
|
||||
ifa_ref(ifa);
|
||||
|
||||
CARP_LOCK(sc);
|
||||
sc->sc_ifas[index - 1] = ifa;
|
||||
ifa->ifa_carp = sc;
|
||||
|
||||
carp_hmac_prepare(sc);
|
||||
carp_sc_state(sc);
|
||||
|
||||
CARP_UNLOCK(sc);
|
||||
CIF_UNLOCK(cif);
|
||||
|
||||
sx_xunlock(&carp_sx);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
carp_detach(struct ifaddr *ifa)
|
||||
{
|
||||
struct ifnet *ifp = ifa->ifa_ifp;
|
||||
struct carp_if *cif = ifp->if_carp;
|
||||
|
||||
CIF_LOCK(cif);
|
||||
carp_detach_locked(ifa);
|
||||
CIF_FREE(cif);
|
||||
}
|
||||
|
||||
static void
|
||||
carp_detach_locked(struct ifaddr *ifa)
|
||||
{
|
||||
struct ifnet *ifp = ifa->ifa_ifp;
|
||||
struct carp_if *cif = ifp->if_carp;
|
||||
@ -1906,9 +1880,9 @@ carp_detach_locked(struct ifaddr *ifa)
|
||||
|
||||
KASSERT(sc != NULL, ("%s: %p not attached", __func__, ifa));
|
||||
|
||||
CIF_LOCK_ASSERT(cif);
|
||||
CARP_LOCK(sc);
|
||||
sx_xlock(&carp_sx);
|
||||
|
||||
CARP_LOCK(sc);
|
||||
/* Shift array. */
|
||||
index = sc->sc_naddrs + sc->sc_naddrs6;
|
||||
for (i = 0; i < index; i++)
|
||||
@ -1943,11 +1917,14 @@ carp_detach_locked(struct ifaddr *ifa)
|
||||
carp_hmac_prepare(sc);
|
||||
carp_sc_state(sc);
|
||||
|
||||
if (sc->sc_naddrs == 0 && sc->sc_naddrs6 == 0) {
|
||||
CARP_UNLOCK(sc);
|
||||
if (sc->sc_naddrs == 0 && sc->sc_naddrs6 == 0)
|
||||
carp_destroy(sc);
|
||||
} else
|
||||
else
|
||||
CARP_UNLOCK(sc);
|
||||
|
||||
CIF_FREE(cif);
|
||||
|
||||
sx_xunlock(&carp_sx);
|
||||
}
|
||||
|
||||
static void
|
||||
|
Loading…
Reference in New Issue
Block a user