Changeset View
Standalone View
sys/netinet/ip_carp.c
Show First 20 Lines • Show All 137 Lines • ▼ Show 20 Lines | #endif | ||||
TAILQ_HEAD(, carp_softc) cif_vrs; | TAILQ_HEAD(, carp_softc) cif_vrs; | ||||
#ifdef INET | #ifdef INET | ||||
struct ip_moptions cif_imo; | struct ip_moptions cif_imo; | ||||
#endif | #endif | ||||
#ifdef INET6 | #ifdef INET6 | ||||
struct ip6_moptions cif_im6o; | struct ip6_moptions cif_im6o; | ||||
#endif | #endif | ||||
struct ifnet *cif_ifp; | struct ifnet *cif_ifp; | ||||
struct mtx cif_mtx; | struct rwlock cif_mtx; | ||||
ae: Since you changed the lock type and all places from where it is invoked, maybe it will be… | |||||
uint32_t cif_flags; | uint32_t cif_flags; | ||||
#define CIF_PROMISC 0x00000001 | #define CIF_PROMISC 0x00000001 | ||||
}; | }; | ||||
#define CARP_INET 0 | #define CARP_INET 0 | ||||
#define CARP_INET6 1 | #define CARP_INET6 1 | ||||
static int proto_reg[] = {-1, -1}; | static int proto_reg[] = {-1, -1}; | ||||
▲ Show 20 Lines • Show All 89 Lines • ▼ Show 20 Lines | SYSCTL_VNET_PCPUSTAT(_net_inet_carp, OID_AUTO, stats, struct carpstats, | ||||
carpstats, "CARP statistics (struct carpstats, netinet/ip_carp.h)"); | carpstats, "CARP statistics (struct carpstats, netinet/ip_carp.h)"); | ||||
#define CARP_LOCK_INIT(sc) mtx_init(&(sc)->sc_mtx, "carp_softc", \ | #define CARP_LOCK_INIT(sc) mtx_init(&(sc)->sc_mtx, "carp_softc", \ | ||||
NULL, MTX_DEF) | NULL, MTX_DEF) | ||||
#define CARP_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_mtx) | #define CARP_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_mtx) | ||||
#define CARP_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) | #define CARP_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) | ||||
#define CARP_LOCK(sc) mtx_lock(&(sc)->sc_mtx) | #define CARP_LOCK(sc) mtx_lock(&(sc)->sc_mtx) | ||||
#define CARP_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) | #define CARP_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) | ||||
#define CIF_LOCK_INIT(cif) mtx_init(&(cif)->cif_mtx, "carp_if", \ | #define CIF_LOCK_INIT(cif) rw_init(&(cif)->cif_mtx, "carp_if") | ||||
NULL, MTX_DEF) | #define CIF_LOCK_DESTROY(cif) rw_destroy(&(cif)->cif_mtx) | ||||
#define CIF_LOCK_DESTROY(cif) mtx_destroy(&(cif)->cif_mtx) | #define CIF_LOCK_ASSERT(cif) rw_assert(&(cif)->cif_mtx, MA_OWNED) | ||||
#define CIF_LOCK_ASSERT(cif) mtx_assert(&(cif)->cif_mtx, MA_OWNED) | #define CIF_RLOCK(cif) rw_rlock(&(cif)->cif_mtx) | ||||
#define CIF_LOCK(cif) mtx_lock(&(cif)->cif_mtx) | #define CIF_RUNLOCK(cif) rw_runlock(&(cif)->cif_mtx) | ||||
#define CIF_UNLOCK(cif) mtx_unlock(&(cif)->cif_mtx) | #define CIF_WLOCK(cif) rw_wlock(&(cif)->cif_mtx) | ||||
#define CIF_WUNLOCK(cif) rw_wunlock(&(cif)->cif_mtx) | |||||
#define CIF_FREE(cif) do { \ | #define CIF_FREE(cif) do { \ | ||||
CIF_LOCK(cif); \ | CIF_WLOCK(cif); \ | ||||
if (TAILQ_EMPTY(&(cif)->cif_vrs)) \ | if (TAILQ_EMPTY(&(cif)->cif_vrs)) \ | ||||
carp_free_if(cif); \ | carp_free_if(cif); \ | ||||
else \ | else \ | ||||
CIF_UNLOCK(cif); \ | CIF_WUNLOCK(cif); \ | ||||
} while (0) | } while (0) | ||||
#define CARP_LOG(...) do { \ | #define CARP_LOG(...) do { \ | ||||
if (V_carp_log > 0) \ | if (V_carp_log > 0) \ | ||||
log(LOG_INFO, "carp: " __VA_ARGS__); \ | log(LOG_INFO, "carp: " __VA_ARGS__); \ | ||||
} while (0) | } while (0) | ||||
#define CARP_DEBUG(...) do { \ | #define CARP_DEBUG(...) do { \ | ||||
▲ Show 20 Lines • Show All 849 Lines • ▼ Show 20 Lines | carp_iamatch(struct ifaddr *ifa, uint8_t **enaddr) | ||||
if (sc->sc_state == MASTER) { | if (sc->sc_state == MASTER) { | ||||
*enaddr = LLADDR(&sc->sc_addr); | *enaddr = LLADDR(&sc->sc_addr); | ||||
return (1); | return (1); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
#endif | #endif | ||||
Done Inline ActionsDo not leave commented out locking code in the tree please. gnn: Do not leave commented out locking code in the tree please. | |||||
Done Inline ActionsI left it so it could be more easily reviewable but will remove it. eri: I left it so it could be more easily reviewable but will remove it.
| |||||
#ifdef INET6 | #ifdef INET6 | ||||
static void | static void | ||||
carp_send_na(struct carp_softc *sc) | carp_send_na(struct carp_softc *sc) | ||||
{ | { | ||||
Done Inline ActionsDo not leave commented out locking code in the tree please. gnn: Do not leave commented out locking code in the tree please. | |||||
static struct in6_addr mcast = IN6ADDR_LINKLOCAL_ALLNODES_INIT; | static struct in6_addr mcast = IN6ADDR_LINKLOCAL_ALLNODES_INIT; | ||||
struct ifaddr *ifa; | struct ifaddr *ifa; | ||||
struct in6_addr *in6; | struct in6_addr *in6; | ||||
CARP_FOREACH_IFA(sc, ifa) { | CARP_FOREACH_IFA(sc, ifa) { | ||||
if (ifa->ifa_addr->sa_family != AF_INET6) | if (ifa->ifa_addr->sa_family != AF_INET6) | ||||
continue; | continue; | ||||
▲ Show 20 Lines • Show All 66 Lines • ▼ Show 20 Lines | |||||
carp_forus(struct ifnet *ifp, u_char *dhost) | carp_forus(struct ifnet *ifp, u_char *dhost) | ||||
{ | { | ||||
struct carp_softc *sc; | struct carp_softc *sc; | ||||
uint8_t *ena = dhost; | uint8_t *ena = dhost; | ||||
if (ena[0] || ena[1] || ena[2] != 0x5e || ena[3] || ena[4] != 1) | if (ena[0] || ena[1] || ena[2] != 0x5e || ena[3] || ena[4] != 1) | ||||
return (0); | return (0); | ||||
CIF_LOCK(ifp->if_carp); | CIF_RLOCK(ifp->if_carp); | ||||
IFNET_FOREACH_CARP(ifp, sc) { | IFNET_FOREACH_CARP(ifp, sc) { | ||||
CARP_LOCK(sc); | if (sc->sc_state == MASTER && ena[5] == sc->sc_vhid) { | ||||
aeUnsubmitted Not Done Inline ActionsNote, you lost the lock protection here. Without this lock you can miss state changing. This is very unlikely and not so important, but anyway I think you need note this possibility at least in comment. Or resurrect the lock back again. But in the carp_linkstate() you keep the lock in the similar loop. ae: Note, you lost the lock protection here. Without this lock you can miss state changing. This is… | |||||
loosUnsubmitted Not Done Inline ActionsInteresting you have noticed this :-) This change is what actually fix this issue, all the other changes are a nop. carp_forus() is used by bridge and there is a race between the moment that the bridge lock is held and the call of carp_forus(). If the a carp callout triggers, it will try to send a packet to the bridge and it now has to wait for the bridge lock with the carp lock held. The carp_forus() runs now and it is now waiting for the carp lock with the bridge lock held. loos: Interesting you have noticed this :-)
This change is what actually fix this issue, all the… | |||||
if (sc->sc_state == MASTER && !bcmp(dhost, LLADDR(&sc->sc_addr), | CIF_RUNLOCK(ifp->if_carp); | ||||
ETHER_ADDR_LEN)) { | |||||
CARP_UNLOCK(sc); | |||||
CIF_UNLOCK(ifp->if_carp); | |||||
return (1); | return (1); | ||||
} | } | ||||
CARP_UNLOCK(sc); | |||||
} | } | ||||
CIF_UNLOCK(ifp->if_carp); | CIF_RUNLOCK(ifp->if_carp); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* Master down timeout event, executed in callout context. */ | /* Master down timeout event, executed in callout context. */ | ||||
static void | static void | ||||
carp_master_down(void *v) | carp_master_down(void *v) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 344 Lines • ▼ Show 20 Lines | |||||
#ifdef INET | #ifdef INET | ||||
callout_init_mtx(&sc->sc_md_tmo, &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); | callout_init_mtx(&sc->sc_md_tmo, &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); | ||||
#endif | #endif | ||||
#ifdef INET6 | #ifdef INET6 | ||||
callout_init_mtx(&sc->sc_md6_tmo, &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); | callout_init_mtx(&sc->sc_md6_tmo, &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); | ||||
#endif | #endif | ||||
callout_init_mtx(&sc->sc_ad_tmo, &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); | callout_init_mtx(&sc->sc_ad_tmo, &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); | ||||
CIF_LOCK(cif); | CIF_WLOCK(cif); | ||||
TAILQ_INSERT_TAIL(&cif->cif_vrs, sc, sc_list); | TAILQ_INSERT_TAIL(&cif->cif_vrs, sc, sc_list); | ||||
CIF_UNLOCK(cif); | CIF_WUNLOCK(cif); | ||||
mtx_lock(&carp_mtx); | mtx_lock(&carp_mtx); | ||||
LIST_INSERT_HEAD(&carp_list, sc, sc_next); | LIST_INSERT_HEAD(&carp_list, sc, sc_next); | ||||
mtx_unlock(&carp_mtx); | mtx_unlock(&carp_mtx); | ||||
return (sc); | return (sc); | ||||
} | } | ||||
Show All 18 Lines | carp_destroy(struct carp_softc *sc) | ||||
struct carp_if *cif = ifp->if_carp; | struct carp_if *cif = ifp->if_carp; | ||||
sx_assert(&carp_sx, SA_XLOCKED); | sx_assert(&carp_sx, SA_XLOCKED); | ||||
if (sc->sc_suppress) | if (sc->sc_suppress) | ||||
carp_demote_adj(-V_carp_ifdown_adj, "vhid removed"); | carp_demote_adj(-V_carp_ifdown_adj, "vhid removed"); | ||||
CARP_UNLOCK(sc); | CARP_UNLOCK(sc); | ||||
CIF_LOCK(cif); | CIF_WLOCK(cif); | ||||
TAILQ_REMOVE(&cif->cif_vrs, sc, sc_list); | TAILQ_REMOVE(&cif->cif_vrs, sc, sc_list); | ||||
CIF_UNLOCK(cif); | CIF_WUNLOCK(cif); | ||||
mtx_lock(&carp_mtx); | mtx_lock(&carp_mtx); | ||||
LIST_REMOVE(sc, sc_next); | LIST_REMOVE(sc, sc_next); | ||||
mtx_unlock(&carp_mtx); | mtx_unlock(&carp_mtx); | ||||
callout_drain(&sc->sc_ad_tmo); | callout_drain(&sc->sc_ad_tmo); | ||||
#ifdef INET | #ifdef INET | ||||
callout_drain(&sc->sc_md_tmo); | callout_drain(&sc->sc_md_tmo); | ||||
▲ Show 20 Lines • Show All 110 Lines • ▼ Show 20 Lines | if ((error = priv_check(td, PRIV_NETINET_CARP))) | ||||
break; | break; | ||||
if (carpr.carpr_vhid <= 0 || carpr.carpr_vhid > CARP_MAXVHID || | if (carpr.carpr_vhid <= 0 || carpr.carpr_vhid > CARP_MAXVHID || | ||||
carpr.carpr_advbase < 0 || carpr.carpr_advskew < 0) { | carpr.carpr_advbase < 0 || carpr.carpr_advskew < 0) { | ||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
} | } | ||||
if (ifp->if_carp) { | if (ifp->if_carp) { | ||||
CIF_LOCK(ifp->if_carp); | CIF_RLOCK(ifp->if_carp); | ||||
IFNET_FOREACH_CARP(ifp, sc) | IFNET_FOREACH_CARP(ifp, sc) | ||||
if (sc->sc_vhid == carpr.carpr_vhid) | if (sc->sc_vhid == carpr.carpr_vhid) | ||||
break; | break; | ||||
CIF_UNLOCK(ifp->if_carp); | CIF_RUNLOCK(ifp->if_carp); | ||||
} | } | ||||
if (sc == NULL) { | if (sc == NULL) { | ||||
sc = carp_alloc(ifp); | sc = carp_alloc(ifp); | ||||
CARP_LOCK(sc); | CARP_LOCK(sc); | ||||
sc->sc_vhid = carpr.carpr_vhid; | sc->sc_vhid = carpr.carpr_vhid; | ||||
LLADDR(&sc->sc_addr)[0] = 0; | LLADDR(&sc->sc_addr)[0] = 0; | ||||
LLADDR(&sc->sc_addr)[1] = 0; | LLADDR(&sc->sc_addr)[1] = 0; | ||||
LLADDR(&sc->sc_addr)[2] = 0x5e; | LLADDR(&sc->sc_addr)[2] = 0x5e; | ||||
▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | case SIOCGVH: | ||||
} | } | ||||
if (ifp->if_carp == NULL) { | if (ifp->if_carp == NULL) { | ||||
error = ENOENT; | error = ENOENT; | ||||
break; | break; | ||||
} | } | ||||
priveleged = (priv_check(td, PRIV_NETINET_CARP) == 0); | priveleged = (priv_check(td, PRIV_NETINET_CARP) == 0); | ||||
if (carpr.carpr_vhid != 0) { | if (carpr.carpr_vhid != 0) { | ||||
CIF_LOCK(ifp->if_carp); | CIF_RLOCK(ifp->if_carp); | ||||
IFNET_FOREACH_CARP(ifp, sc) | IFNET_FOREACH_CARP(ifp, sc) | ||||
if (sc->sc_vhid == carpr.carpr_vhid) | if (sc->sc_vhid == carpr.carpr_vhid) | ||||
break; | break; | ||||
CIF_UNLOCK(ifp->if_carp); | CIF_RUNLOCK(ifp->if_carp); | ||||
if (sc == NULL) { | if (sc == NULL) { | ||||
error = ENOENT; | error = ENOENT; | ||||
break; | break; | ||||
} | } | ||||
carp_carprcp(&carpr, sc, priveleged); | carp_carprcp(&carpr, sc, priveleged); | ||||
error = copyout(&carpr, ifr->ifr_data, sizeof(carpr)); | error = copyout(&carpr, ifr->ifr_data, sizeof(carpr)); | ||||
} else { | } else { | ||||
int i, count; | int i, count; | ||||
count = 0; | count = 0; | ||||
CIF_LOCK(ifp->if_carp); | CIF_RLOCK(ifp->if_carp); | ||||
IFNET_FOREACH_CARP(ifp, sc) | IFNET_FOREACH_CARP(ifp, sc) | ||||
count++; | count++; | ||||
if (count > carpr.carpr_count) { | if (count > carpr.carpr_count) { | ||||
CIF_UNLOCK(ifp->if_carp); | CIF_RUNLOCK(ifp->if_carp); | ||||
error = EMSGSIZE; | error = EMSGSIZE; | ||||
break; | break; | ||||
} | } | ||||
i = 0; | i = 0; | ||||
IFNET_FOREACH_CARP(ifp, sc) { | IFNET_FOREACH_CARP(ifp, sc) { | ||||
carp_carprcp(&carpr, sc, priveleged); | carp_carprcp(&carpr, sc, priveleged); | ||||
carpr.carpr_count = count; | carpr.carpr_count = count; | ||||
error = copyout(&carpr, ifr->ifr_data + | error = copyout(&carpr, ifr->ifr_data + | ||||
(i * sizeof(carpr)), sizeof(carpr)); | (i * sizeof(carpr)), sizeof(carpr)); | ||||
if (error) { | if (error) { | ||||
CIF_UNLOCK(ifp->if_carp); | CIF_RUNLOCK(ifp->if_carp); | ||||
break; | break; | ||||
} | } | ||||
i++; | i++; | ||||
} | } | ||||
CIF_UNLOCK(ifp->if_carp); | CIF_RUNLOCK(ifp->if_carp); | ||||
} | } | ||||
break; | break; | ||||
} | } | ||||
default: | default: | ||||
error = EINVAL; | error = EINVAL; | ||||
} | } | ||||
sx_xunlock(&carp_sx); | sx_xunlock(&carp_sx); | ||||
Show All 38 Lines | #endif | ||||
} | } | ||||
sx_xlock(&carp_sx); | sx_xlock(&carp_sx); | ||||
if (ifp->if_carp == NULL) { | if (ifp->if_carp == NULL) { | ||||
sx_xunlock(&carp_sx); | sx_xunlock(&carp_sx); | ||||
return (ENOPROTOOPT); | return (ENOPROTOOPT); | ||||
} | } | ||||
CIF_LOCK(cif); | CIF_WLOCK(cif); | ||||
IFNET_FOREACH_CARP(ifp, sc) | IFNET_FOREACH_CARP(ifp, sc) | ||||
if (sc->sc_vhid == vhid) | if (sc->sc_vhid == vhid) | ||||
break; | break; | ||||
CIF_UNLOCK(cif); | CIF_WUNLOCK(cif); | ||||
if (sc == NULL) { | if (sc == NULL) { | ||||
sx_xunlock(&carp_sx); | sx_xunlock(&carp_sx); | ||||
return (ENOENT); | return (ENOENT); | ||||
} | } | ||||
error = carp_multicast_setup(cif, ifa->ifa_addr->sa_family); | error = carp_multicast_setup(cif, ifa->ifa_addr->sa_family); | ||||
if (error) { | if (error) { | ||||
CIF_FREE(cif); | CIF_FREE(cif); | ||||
▲ Show 20 Lines • Show All 113 Lines • ▼ Show 20 Lines | carp_set_state(struct carp_softc *sc, int state, const char *reason) | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
carp_linkstate(struct ifnet *ifp) | carp_linkstate(struct ifnet *ifp) | ||||
{ | { | ||||
struct carp_softc *sc; | struct carp_softc *sc; | ||||
CIF_LOCK(ifp->if_carp); | CIF_RLOCK(ifp->if_carp); | ||||
IFNET_FOREACH_CARP(ifp, sc) { | IFNET_FOREACH_CARP(ifp, sc) { | ||||
CARP_LOCK(sc); | CARP_LOCK(sc); | ||||
carp_sc_state(sc); | carp_sc_state(sc); | ||||
CARP_UNLOCK(sc); | CARP_UNLOCK(sc); | ||||
} | } | ||||
CIF_UNLOCK(ifp->if_carp); | CIF_RUNLOCK(ifp->if_carp); | ||||
} | } | ||||
static void | static void | ||||
carp_sc_state(struct carp_softc *sc) | carp_sc_state(struct carp_softc *sc) | ||||
{ | { | ||||
CARP_LOCK_ASSERT(sc); | CARP_LOCK_ASSERT(sc); | ||||
Show All 18 Lines | if (sc->sc_suppress) | ||||
carp_demote_adj(-V_carp_ifdown_adj, "interface up"); | carp_demote_adj(-V_carp_ifdown_adj, "interface up"); | ||||
sc->sc_suppress = 0; | sc->sc_suppress = 0; | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
carp_demote_adj(int adj, char *reason) | carp_demote_adj(int adj, char *reason) | ||||
{ | { | ||||
if (adj == 0) | |||||
return; | |||||
atomic_add_int(&V_carp_demotion, adj); | atomic_add_int(&V_carp_demotion, adj); | ||||
CARP_LOG("demoted by %d to %d (%s)\n", adj, V_carp_demotion, reason); | CARP_LOG("demoted by %d to %d (%s)\n", adj, V_carp_demotion, reason); | ||||
taskqueue_enqueue(taskqueue_swi, &carp_sendall_task); | taskqueue_enqueue(taskqueue_thread, &carp_sendall_task); | ||||
} | } | ||||
static int | static int | ||||
carp_demote_adj_sysctl(SYSCTL_HANDLER_ARGS) | carp_demote_adj_sysctl(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
int new, error; | int new, error; | ||||
new = V_carp_demotion; | new = V_carp_demotion; | ||||
▲ Show 20 Lines • Show All 158 Lines • Show Last 20 Lines |
Since you changed the lock type and all places from where it is invoked, maybe it will be better to change the name too. cif_lock for example.