Changeset View
Changeset View
Standalone View
Standalone View
sys/net/route/nhop_ctl.c
Show First 20 Lines • Show All 101 Lines • ▼ Show 20 Lines | _Static_assert(sizeof(struct nhop_object) <= 128, | ||||
"nhop_object: size exceeds 128 bytes"); | "nhop_object: size exceeds 128 bytes"); | ||||
static uma_zone_t nhops_zone; /* Global zone for each and every nexthop */ | static uma_zone_t nhops_zone; /* Global zone for each and every nexthop */ | ||||
#define NHOP_OBJECT_ALIGNED_SIZE roundup2(sizeof(struct nhop_object), \ | #define NHOP_OBJECT_ALIGNED_SIZE roundup2(sizeof(struct nhop_object), \ | ||||
2 * CACHE_LINE_SIZE) | 2 * CACHE_LINE_SIZE) | ||||
#define NHOP_PRIV_ALIGNED_SIZE roundup2(sizeof(struct nhop_priv), \ | #define NHOP_PRIV_ALIGNED_SIZE roundup2(sizeof(struct nhop_priv), \ | ||||
2 * CACHE_LINE_SIZE) | 2 * CACHE_LINE_SIZE) | ||||
static uma_zone_t nh_prepend_zone; /* Global zone for all nhop prepend data */ | |||||
struct nhop_prepend { | |||||
char prepend[L2_PREPEND_LEN_MAX]; | |||||
struct epoch_context epoch_ctx; | |||||
}; | |||||
#define NHOP_PREPEND_ALIGNED_SIZE roundup2(sizeof(struct nhop_prepend), \ | |||||
CACHE_LINE_SIZE) | |||||
static bool nhop_update_prepend_locked(struct nhop_priv *nh_priv, void *prepend, | |||||
size_t len); | |||||
void | void | ||||
nhops_init(void) | nhops_init(void) | ||||
{ | { | ||||
nhops_zone = uma_zcreate("routing nhops", | nhops_zone = uma_zcreate("routing nhops", | ||||
NHOP_OBJECT_ALIGNED_SIZE + NHOP_PRIV_ALIGNED_SIZE, | NHOP_OBJECT_ALIGNED_SIZE + NHOP_PRIV_ALIGNED_SIZE, | ||||
NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); | NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); | ||||
nh_prepend_zone = uma_zcreate("nhop prepend", NHOP_PREPEND_ALIGNED_SIZE, | |||||
NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0); | |||||
} | } | ||||
/* | /* | ||||
* Fetches the interface of source address used by the route. | * Fetches the interface of source address used by the route. | ||||
* In all cases except interface-address-route it would be the | * In all cases except interface-address-route it would be the | ||||
* same as the transmit interfaces. | * same as the transmit interfaces. | ||||
* However, for the interface address this function will return | * However, for the interface address this function will return | ||||
* this interface ifp instead of loopback. This is needed to support | * this interface ifp instead of loopback. This is needed to support | ||||
▲ Show 20 Lines • Show All 447 Lines • ▼ Show 20 Lines | finalize_nhop(struct nh_control *ctl, struct rt_addrinfo *info, | ||||
refcount_init(&nh_priv->nh_refcnt, 1); | refcount_init(&nh_priv->nh_refcnt, 1); | ||||
/* Please see nhop_free() comments on the initial value */ | /* Please see nhop_free() comments on the initial value */ | ||||
refcount_init(&nh_priv->nh_linked, 2); | refcount_init(&nh_priv->nh_linked, 2); | ||||
nh_priv->nh_fibnum = ctl->ctl_rh->rib_fibnum; | nh_priv->nh_fibnum = ctl->ctl_rh->rib_fibnum; | ||||
if (nhop_need_neigh(nh)) | |||||
nh_priv->nh_priv_flags |= NHF_PRIV_NEIGH; | |||||
#if DEBUG_MAX_LEVEL >= LOG_DEBUG | #if DEBUG_MAX_LEVEL >= LOG_DEBUG | ||||
char nhbuf[48]; | char nhbuf[48]; | ||||
FIB_NH_LOG(LOG_DEBUG, nh, "finalized: %s", nhop_print_buf(nh, nhbuf, sizeof(nhbuf))); | FIB_NH_LOG(LOG_DEBUG, nh, "finalized: %s", nhop_print_buf(nh, nhbuf, sizeof(nhbuf))); | ||||
#endif | #endif | ||||
if (link_nhop(ctl, nh_priv) == 0) { | if (link_nhop(ctl, nh_priv) == 0) { | ||||
/* | /* | ||||
* Adding nexthop to the datastructures | * Adding nexthop to the datastructures | ||||
* failed. Call destructor w/o waiting for | * failed. Call destructor w/o waiting for | ||||
* the epoch end, as nexthop is not used | * the epoch end, as nexthop is not used | ||||
* and return. | * and return. | ||||
*/ | */ | ||||
char nhbuf[48]; | char nhbuf[48]; | ||||
FIB_NH_LOG(LOG_WARNING, nh, "failed to link %s", | FIB_NH_LOG(LOG_WARNING, nh, "failed to link %s", | ||||
nhop_print_buf(nh, nhbuf, sizeof(nhbuf))); | nhop_print_buf(nh, nhbuf, sizeof(nhbuf))); | ||||
destroy_nhop(nh_priv); | destroy_nhop(nh_priv); | ||||
return (ENOBUFS); | return (ENOBUFS); | ||||
} | } | ||||
if (nh_priv->nh_priv_flags & NHF_PRIV_NEIGH) | |||||
nhop_link_neigh(nh_priv); | |||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
destroy_nhop(struct nhop_priv *nh_priv) | destroy_nhop(struct nhop_priv *nh_priv) | ||||
{ | { | ||||
struct nhop_object *nh; | struct nhop_object *nh; | ||||
nh = nh_priv->nh; | nh = nh_priv->nh; | ||||
#if DEBUG_MAX_LEVEL >= LOG_DEBUG | #if DEBUG_MAX_LEVEL >= LOG_DEBUG | ||||
char nhbuf[48]; | char nhbuf[48]; | ||||
FIB_NH_LOG(LOG_DEBUG, nh, "deleting %s", nhop_print_buf(nh, nhbuf, sizeof(nhbuf))); | FIB_NH_LOG(LOG_DEBUG, nh, "deleting %s", nhop_print_buf(nh, nhbuf, sizeof(nhbuf))); | ||||
#endif | #endif | ||||
if_rele(nh->nh_ifp); | if_rele(nh->nh_ifp); | ||||
if_rele(nh->nh_aifp); | if_rele(nh->nh_aifp); | ||||
ifa_free(nh->nh_ifa); | ifa_free(nh->nh_ifa); | ||||
counter_u64_free(nh->nh_pksent); | counter_u64_free(nh->nh_pksent); | ||||
if (nh->nh_prepend_raw != NULL) { | |||||
struct nhop_prepend *np; | |||||
np = (struct nhop_prepend *)NH_L2_PREPEND_GET_PTR(nh->nh_prepend_raw); | |||||
nhop_free_prepend(np); | |||||
} | |||||
uma_zfree(nhops_zone, nh); | uma_zfree(nhops_zone, nh); | ||||
} | } | ||||
/* | /* | ||||
* Epoch callback indicating nhop is safe to destroy | * Epoch callback indicating nhop is safe to destroy | ||||
*/ | */ | ||||
static void | static void | ||||
destroy_nhop_epoch(epoch_context_t ctx) | destroy_nhop_epoch(epoch_context_t ctx) | ||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | nhop_free(struct nhop_object *nh) | ||||
* this epoch. Hence, nexthop can be safely unlinked. | * this epoch. Hence, nexthop can be safely unlinked. | ||||
* | * | ||||
* 2) nh_linked value is 1. In that case, nhops_destroy_rib() | * 2) nh_linked value is 1. In that case, nhops_destroy_rib() | ||||
* has been called and nhop unlink can be skipped. | * has been called and nhop unlink can be skipped. | ||||
*/ | */ | ||||
NET_EPOCH_ENTER(et); | NET_EPOCH_ENTER(et); | ||||
if (refcount_release_if_not_last(&nh_priv->nh_linked)) { | if (refcount_release_if_not_last(&nh_priv->nh_linked)) { | ||||
/* Stop receiving updates for neighbor prepends */ | |||||
if (nh_priv->nh_priv_flags & NHF_PRIV_NEIGH) | |||||
nhop_unlink_neighbor(nh_priv); | |||||
ctl = nh_priv->nh_control; | ctl = nh_priv->nh_control; | ||||
if (unlink_nhop(ctl, nh_priv) == NULL) { | if (unlink_nhop(ctl, nh_priv) == NULL) { | ||||
/* Do not try to reclaim */ | /* Do not try to reclaim */ | ||||
char nhbuf[48]; | char nhbuf[48]; | ||||
FIB_NH_LOG(LOG_WARNING, nh, "failed to unlink %s", | FIB_NH_LOG(LOG_WARNING, nh, "failed to unlink %s", | ||||
nhop_print_buf(nh, nhbuf, sizeof(nhbuf))); | nhop_print_buf(nh, nhbuf, sizeof(nhbuf))); | ||||
NET_EPOCH_EXIT(et); | NET_EPOCH_EXIT(et); | ||||
return; | return; | ||||
▲ Show 20 Lines • Show All 208 Lines • ▼ Show 20 Lines | dump_nhop_entry(struct rib_head *rh, struct nhop_object *nh, struct sysctl_req *w) | ||||
pnhe->nh_fib = rh->rib_fibnum; | pnhe->nh_fib = rh->rib_fibnum; | ||||
pnhe->ifindex = nh->nh_ifp->if_index; | pnhe->ifindex = nh->nh_ifp->if_index; | ||||
pnhe->aifindex = nh->nh_aifp->if_index; | pnhe->aifindex = nh->nh_aifp->if_index; | ||||
pnhe->nh_family = nh->nh_priv->nh_upper_family; | pnhe->nh_family = nh->nh_priv->nh_upper_family; | ||||
pnhe->nh_type = nh->nh_priv->nh_type; | pnhe->nh_type = nh->nh_priv->nh_type; | ||||
pnhe->nh_mtu = nh->nh_mtu; | pnhe->nh_mtu = nh->nh_mtu; | ||||
pnhe->nh_flags = nh->nh_flags; | pnhe->nh_flags = nh->nh_flags; | ||||
memcpy(pnhe->nh_prepend, nh->nh_prepend, sizeof(nh->nh_prepend)); | if (nh->nh_prepend_raw != NULL) { | ||||
pnhe->prepend_len = nh->nh_prepend_len; | void *ptr = nh->nh_prepend_raw; | ||||
pnhe->prepend_len = NH_L2_PREPEND_GET_LEN(ptr); | |||||
memcpy(pnhe->nh_prepend, NH_L2_PREPEND_GET_PTR(ptr), pnhe->prepend_len); | |||||
} | |||||
pnhe->nh_refcount = nh->nh_priv->nh_refcnt; | pnhe->nh_refcount = nh->nh_priv->nh_refcnt; | ||||
pnhe->nh_pksent = counter_u64_fetch(nh->nh_pksent); | pnhe->nh_pksent = counter_u64_fetch(nh->nh_pksent); | ||||
/* sockaddr container */ | /* sockaddr container */ | ||||
addrs_len = sizeof(struct nhop_addrs); | addrs_len = sizeof(struct nhop_addrs); | ||||
arpc.na.gw_sa_off = addrs_len; | arpc.na.gw_sa_off = addrs_len; | ||||
gw_sa = (struct sockaddr *)&nh->gw4_sa; | gw_sa = (struct sockaddr *)&nh->gw4_sa; | ||||
addrs_len += gw_sa->sa_len; | addrs_len += gw_sa->sa_len; | ||||
▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | if (error != 0) { | ||||
NHOPS_RUNLOCK(ctl); | NHOPS_RUNLOCK(ctl); | ||||
return (error); | return (error); | ||||
} | } | ||||
} CHT_SLIST_FOREACH_END; | } CHT_SLIST_FOREACH_END; | ||||
NHOPS_RUNLOCK(ctl); | NHOPS_RUNLOCK(ctl); | ||||
return (0); | return (0); | ||||
} | } | ||||
void * | |||||
nhop_alloc_prepend(size_t size) | |||||
{ | |||||
if (size > L2_PREPEND_LEN_MAX) | |||||
return (NULL); | |||||
void *prepend = uma_zalloc(nh_prepend_zone, M_NOWAIT | M_ZERO); | |||||
return (prepend); | |||||
} | |||||
void | |||||
nhop_free_prepend(void *prepend) | |||||
{ | |||||
uma_zfree(nh_prepend_zone, prepend); | |||||
} | |||||
static void | |||||
destroy_nhop_prepend_epoch(epoch_context_t ctx) | |||||
{ | |||||
struct nhop_prepend *prepend; | |||||
prepend = __containerof(ctx, struct nhop_prepend, epoch_ctx); | |||||
nhop_free_prepend(prepend); | |||||
} | |||||
static bool | |||||
nhop_update_prepend_locked(struct nhop_priv *nh_priv, void *prepend, size_t len) | |||||
{ | |||||
void *ptr = NULL, *old_ptr = NULL; | |||||
bool result = false; | |||||
if (prepend != NH_L2_PREPEND_GET_PTR(prepend)) { | |||||
//KASSERT(); | |||||
/* XXX: check alignment */ | |||||
prepend = NULL; | |||||
} | |||||
if (prepend != NULL) | |||||
ptr = NH_L2_COMPILE_PREPEND_PTR(prepend, len); | |||||
if (is_nhop_linked(nh_priv)) { | |||||
old_ptr = nh_priv->nh->nh_prepend_raw; | |||||
nh_priv->nh->nh_prepend_raw = ptr; | |||||
result = true; | |||||
} | |||||
if (old_ptr != NULL) { | |||||
struct nhop_prepend *np = NH_L2_PREPEND_GET_PTR(old_ptr); | |||||
epoch_call(net_epoch_preempt, destroy_nhop_prepend_epoch, | |||||
&np->epoch_ctx); | |||||
} | |||||
return (result); | |||||
} | |||||
bool | |||||
nhop_update_prepend(struct nhop_object *nh, void *prepend, size_t len) | |||||
{ | |||||
struct nhop_priv *nh_priv = nh->nh_priv; | |||||
struct nh_control *ctl; | |||||
bool result; | |||||
ctl = nh_priv->nh_control; | |||||
NHOPS_WLOCK(ctl); | |||||
result = nhop_update_prepend_locked(nh_priv, prepend, len); | |||||
NHOPS_WUNLOCK(ctl); | |||||
return (result); | |||||
} | |||||