- boot-time size the ipv4 flowtable and the maximum number of flows

- increase flow cleaning frequency and decrease flow caching time
  when near the flow limit
- stop allocating new flows when within 3% of maxflows don't start
  allocating again until below 12.5%

MFC after:	7 days
This commit is contained in:
Kip Macy 2010-03-22 23:04:12 +00:00
parent 1a23373ceb
commit 3059584e2a
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=205488
2 changed files with 96 additions and 26 deletions

View File

@ -155,30 +155,33 @@ struct flowtable_stats {
uint64_t ft_frees;
uint64_t ft_hits;
uint64_t ft_lookups;
} __aligned(128);
} __aligned(CACHE_LINE_SIZE);
struct flowtable {
struct flowtable_stats ft_stats[MAXCPU];
int ft_size;
int ft_lock_count;
uint32_t ft_flags;
uint32_t ft_udp_idle;
uint32_t ft_fin_wait_idle;
uint32_t ft_syn_idle;
uint32_t ft_tcp_idle;
char *ft_name;
fl_lock_t *ft_lock;
fl_lock_t *ft_unlock;
fl_rtalloc_t *ft_rtalloc;
/*
* XXX need to pad out
*/
struct mtx *ft_locks;
union flentryp ft_table;
bitstr_t *ft_masks[MAXCPU];
bitstr_t *ft_tmpmask;
struct flowtable *ft_next;
} __aligned(128);
uint32_t ft_count __aligned(CACHE_LINE_SIZE);
uint32_t ft_udp_idle __aligned(CACHE_LINE_SIZE);
uint32_t ft_fin_wait_idle;
uint32_t ft_syn_idle;
uint32_t ft_tcp_idle;
boolean_t ft_full;
} __aligned(CACHE_LINE_SIZE);
static struct proc *flowcleanerproc;
static VNET_DEFINE(struct flowtable *, flow_list_head);
@ -191,9 +194,11 @@ static VNET_DEFINE(uma_zone_t, flow_ipv6_zone);
#define V_flow_ipv4_zone VNET(flow_ipv4_zone)
#define V_flow_ipv6_zone VNET(flow_ipv6_zone)
static struct cv flowclean_cv;
static struct mtx flowclean_lock;
static uint32_t flowclean_cycles;
static uint32_t flowclean_freq;
#ifdef FLOWTABLE_DEBUG
#define FLDPRINTF(ft, flags, fmt, ...) \
@ -230,7 +235,7 @@ static VNET_DEFINE(int, flowtable_syn_expire) = SYN_IDLE;
static VNET_DEFINE(int, flowtable_udp_expire) = UDP_IDLE;
static VNET_DEFINE(int, flowtable_fin_wait_expire) = FIN_WAIT_IDLE;
static VNET_DEFINE(int, flowtable_tcp_expire) = TCP_IDLE;
static VNET_DEFINE(int, flowtable_nmbflows) = 4096;
static VNET_DEFINE(int, flowtable_nmbflows);
static VNET_DEFINE(int, flowtable_ready) = 0;
#define V_flowtable_enable VNET(flowtable_enable)
@ -905,6 +910,61 @@ flowtable_set_hashkey(struct flentry *fle, uint32_t *key)
hashkey[i] = key[i];
}
static struct flentry *
flow_alloc(struct flowtable *ft)
{
struct flentry *newfle;
uma_zone_t zone;
newfle = NULL;
zone = (ft->ft_flags & FL_IPV6) ? V_flow_ipv6_zone : V_flow_ipv4_zone;
newfle = uma_zalloc(zone, M_NOWAIT | M_ZERO);
if (newfle != NULL)
atomic_add_int(&ft->ft_count, 1);
return (newfle);
}
static void
flow_free(struct flentry *fle, struct flowtable *ft)
{
uma_zone_t zone;
zone = (ft->ft_flags & FL_IPV6) ? V_flow_ipv6_zone : V_flow_ipv4_zone;
atomic_add_int(&ft->ft_count, -1);
uma_zfree(zone, fle);
}
static int
flow_full(struct flowtable *ft)
{
boolean_t full;
uint32_t count;
full = ft->ft_full;
count = ft->ft_count;
if (full && (count < (V_flowtable_nmbflows - (V_flowtable_nmbflows >> 3))))
ft->ft_full = FALSE;
else if (!full && (count > (V_flowtable_nmbflows - (V_flowtable_nmbflows >> 5))))
ft->ft_full = TRUE;
if (full && !ft->ft_full) {
flowclean_freq = 4*hz;
if ((ft->ft_flags & FL_HASH_ALL) == 0)
ft->ft_udp_idle = ft->ft_fin_wait_idle =
ft->ft_syn_idle = ft->ft_tcp_idle = 5;
cv_broadcast(&flowclean_cv);
} else if (!full && ft->ft_full) {
flowclean_freq = 20*hz;
if ((ft->ft_flags & FL_HASH_ALL) == 0)
ft->ft_udp_idle = ft->ft_fin_wait_idle =
ft->ft_syn_idle = ft->ft_tcp_idle = 30;
}
return (ft->ft_full);
}
static int
flowtable_insert(struct flowtable *ft, uint32_t hash, uint32_t *key,
uint32_t fibnum, struct route *ro, uint16_t flags)
@ -912,12 +972,10 @@ flowtable_insert(struct flowtable *ft, uint32_t hash, uint32_t *key,
struct flentry *fle, *fletail, *newfle, **flep;
struct flowtable_stats *fs = &ft->ft_stats[curcpu];
int depth;
uma_zone_t flezone;
bitstr_t *mask;
uint8_t proto;
flezone = (flags & FL_IPV6) ? V_flow_ipv6_zone : V_flow_ipv4_zone;
newfle = uma_zalloc(flezone, M_NOWAIT | M_ZERO);
newfle = flow_alloc(ft);
if (newfle == NULL)
return (ENOMEM);
@ -948,9 +1006,8 @@ flowtable_insert(struct flowtable *ft, uint32_t hash, uint32_t *key,
* or we lost a race to insert
*/
FL_ENTRY_UNLOCK(ft, hash);
uma_zfree((newfle->f_flags & FL_IPV6) ?
V_flow_ipv6_zone : V_flow_ipv4_zone, newfle);
flow_free(newfle, ft);
if (flags & FL_OVERWRITE)
goto skip;
return (EEXIST);
@ -1147,7 +1204,7 @@ flowtable_lookup(struct flowtable *ft, struct sockaddr_storage *ssa,
}
FL_ENTRY_UNLOCK(ft, hash);
uncached:
if (flags & FL_NOAUTO)
if (flags & FL_NOAUTO || flow_full(ft))
return (NULL);
fs->ft_misses++;
@ -1325,7 +1382,7 @@ flowtable_alloc(char *name, int nentry, int flags)
*
*/
static void
fle_free(struct flentry *fle)
fle_free(struct flentry *fle, struct flowtable *ft)
{
struct rtentry *rt;
struct llentry *lle;
@ -1334,8 +1391,7 @@ fle_free(struct flentry *fle)
lle = __DEVOLATILE(struct llentry *, fle->f_lle);
RTFREE(rt);
LLE_FREE(lle);
uma_zfree((fle->f_flags & FL_IPV6) ?
V_flow_ipv6_zone : V_flow_ipv4_zone, fle);
flow_free(fle, ft);
}
static void
@ -1426,7 +1482,7 @@ flowtable_free_stale(struct flowtable *ft, struct rtentry *rt)
flefreehead = fle->f_next;
count++;
fs->ft_frees++;
fle_free(fle);
fle_free(fle, ft);
}
if (V_flowtable_debug && count)
log(LOG_DEBUG, "freed %d flow entries\n", count);
@ -1518,7 +1574,7 @@ flowtable_cleaner(void)
*/
mtx_lock(&flowclean_lock);
cv_broadcast(&flowclean_cv);
cv_timedwait(&flowclean_cv, &flowclean_lock, 10*hz);
cv_timedwait(&flowclean_cv, &flowclean_lock, flowclean_freq);
mtx_unlock(&flowclean_lock);
}
}
@ -1548,6 +1604,7 @@ static void
flowtable_init_vnet(const void *unused __unused)
{
V_flowtable_nmbflows = 1024 + maxusers * 64 * mp_ncpus;
V_flow_ipv4_zone = uma_zcreate("ip4flow", sizeof(struct flentry_v4),
NULL, NULL, NULL, NULL, 64, UMA_ZONE_MAXBUCKET);
V_flow_ipv6_zone = uma_zcreate("ip6flow", sizeof(struct flentry_v6),
@ -1556,7 +1613,7 @@ flowtable_init_vnet(const void *unused __unused)
uma_zone_set_max(V_flow_ipv6_zone, V_flowtable_nmbflows);
V_flowtable_ready = 1;
}
VNET_SYSINIT(flowtable_init_vnet, SI_SUB_KTHREAD_INIT, SI_ORDER_MIDDLE,
VNET_SYSINIT(flowtable_init_vnet, SI_SUB_SMP, SI_ORDER_ANY,
flowtable_init_vnet, NULL);
static void
@ -1567,8 +1624,9 @@ flowtable_init(const void *unused __unused)
mtx_init(&flowclean_lock, "flowclean lock", NULL, MTX_DEF);
EVENTHANDLER_REGISTER(ifnet_departure_event, flowtable_flush, NULL,
EVENTHANDLER_PRI_ANY);
flowclean_freq = 20*hz;
}
SYSINIT(flowtable_init, SI_SUB_KTHREAD_INIT, SI_ORDER_ANY,
SYSINIT(flowtable_init, SI_SUB_SMP, SI_ORDER_MIDDLE,
flowtable_init, NULL);

View File

@ -327,8 +327,20 @@ ip_init(void)
"error %d\n", __func__, i);
#ifdef FLOWTABLE
TUNABLE_INT_FETCH("net.inet.ip.output_flowtable_size",
&V_ip_output_flowtable_size);
if (TUNABLE_INT_FETCH("net.inet.ip.output_flowtable_size",
&V_ip_output_flowtable_size)) {
if (V_ip_output_flowtable_size < 256)
V_ip_output_flowtable_size = 256;
if (!powerof2(V_ip_output_flowtable_size)) {
printf("flowtable must be power of 2 size\n");
V_ip_output_flowtable_size = 2048;
}
} else {
/*
* round up to the next power of 2
*/
V_ip_output_flowtable_size = 1 << fls((1024 + maxusers * 64)-1);
}
V_ip_ft = flowtable_alloc("ipv4", V_ip_output_flowtable_size, FL_PCPU);
#endif