3.16.50-rc1 review patch. If anyone has any objections, please let me know. ------------------ From: Ben Hutchings <ben@xxxxxxxxxxxxxxx> For the backport of "ipv4: add reference counting to metrics", we will need a third flag on metrics pointers. This was not needed upstream as the DST_METRICS_FORCE_OVERWRITE flag has been eliminated there. In order to use three flag bits we need to increase the alignment of metrics from 4 to 8 bytes. Signed-off-by: Ben Hutchings <ben@xxxxxxxxxxxxxxx> --- --- a/include/net/dst.h +++ b/include/net/dst.h @@ -105,12 +105,15 @@ struct dst_entry { }; }; +void *dst_alloc_metrics(gfp_t flags); +void dst_free_metrics(void *metrics); u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old); extern const u32 dst_default_metrics[]; #define DST_METRICS_READ_ONLY 0x1UL #define DST_METRICS_FORCE_OVERWRITE 0x2UL -#define DST_METRICS_FLAGS 0x3UL +#define DST_METRICS_FLAGS 0x7UL +#define DST_METRICS_ALIGNMENT 0x8UL #define __DST_METRICS_PTR(Y) \ ((u32 *)((Y) & ~DST_METRICS_FLAGS)) #define DST_METRICS_PTR(X) __DST_METRICS_PTR((X)->_metrics) --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -155,7 +155,7 @@ static struct dst_ops fake_dst_ops = { * ipt_REJECT needs it. Future netfilter modules might * require us to fill additional fields. */ -static const u32 br_dst_default_metrics[RTAX_MAX] = { +static const u32 br_dst_default_metrics[RTAX_MAX] __aligned(DST_METRICS_ALIGNMENT) = { [RTAX_MTU - 1] = 1500, }; --- a/net/core/dst.c +++ b/net/core/dst.c @@ -149,7 +149,7 @@ int dst_discard_sk(struct sock *sk, stru } EXPORT_SYMBOL(dst_discard_sk); -const u32 dst_default_metrics[RTAX_MAX + 1] = { +const u32 dst_default_metrics[RTAX_MAX + 1] __aligned(DST_METRICS_ALIGNMENT) = { /* This initializer is needed to force linker to place this variable * into const section. Otherwise it might end into bss section. * We really want to avoid false sharing on this variable, and catch @@ -292,9 +292,23 @@ void dst_release(struct dst_entry *dst) } EXPORT_SYMBOL(dst_release); +static struct kmem_cache *metrics_cache; + +void *dst_alloc_metrics(gfp_t flags) +{ + return kmem_cache_alloc(metrics_cache, flags); +} +EXPORT_SYMBOL(dst_alloc_metrics); + +void dst_free_metrics(void *metrics) +{ + kmem_cache_free(metrics_cache, metrics); +} +EXPORT_SYMBOL(dst_free_metrics); + u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old) { - u32 *p = kmalloc(sizeof(u32) * RTAX_MAX, GFP_ATOMIC); + u32 *p = dst_alloc_metrics(GFP_ATOMIC); if (p) { u32 *old_p = __DST_METRICS_PTR(old); @@ -306,7 +320,7 @@ u32 *dst_cow_metrics_generic(struct dst_ prev = cmpxchg(&dst->_metrics, old, new); if (prev != old) { - kfree(p); + dst_free_metrics(p); p = __DST_METRICS_PTR(prev); if (prev & DST_METRICS_READ_ONLY) p = NULL; @@ -324,7 +338,7 @@ void __dst_destroy_metrics_generic(struc new = ((unsigned long) dst_default_metrics) | DST_METRICS_READ_ONLY; prev = cmpxchg(&dst->_metrics, old, new); if (prev == old) - kfree(__DST_METRICS_PTR(old)); + dst_free_metrics(__DST_METRICS_PTR(old)); } EXPORT_SYMBOL(__dst_destroy_metrics_generic); @@ -419,4 +433,8 @@ static struct notifier_block dst_dev_not void __init dst_init(void) { register_netdevice_notifier(&dst_dev_notifier); + metrics_cache = kmem_cache_create("dst_metrics", + sizeof(u32) * RTAX_MAX, + DST_METRICS_ALIGNMENT, + SLAB_PANIC, NULL); } --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -2502,7 +2502,7 @@ static void mod_cur_headers(struct pktge #ifdef CONFIG_XFRM -static u32 pktgen_dst_metrics[RTAX_MAX + 1] = { +static u32 pktgen_dst_metrics[RTAX_MAX + 1] __aligned(DST_METRICS_ALIGNMENT) = { [RTAX_HOPLIMIT] = 0x5, /* Set a static hoplimit */ }; --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -213,7 +213,7 @@ static void free_fib_info_rcu(struct rcu release_net(fi->fib_net); if (fi->fib_metrics != (u32 *) dst_default_metrics) - kfree(fi->fib_metrics); + dst_free_metrics(fi->fib_metrics); kfree(fi); } @@ -823,7 +823,7 @@ struct fib_info *fib_create_info(struct goto failure; fib_info_cnt++; if (cfg->fc_mx) { - fi->fib_metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL); + fi->fib_metrics = dst_alloc_metrics(GFP_KERNEL | __GFP_ZERO); if (!fi->fib_metrics) goto failure; } else --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -247,7 +247,7 @@ static struct dst_ops ip6_dst_blackhole_ .neigh_lookup = ip6_neigh_lookup, }; -static const u32 ip6_template_metrics[RTAX_MAX] = { +static const u32 ip6_template_metrics[RTAX_MAX] __aligned(DST_METRICS_ALIGNMENT) = { [RTAX_HOPLIMIT - 1] = 0, }; --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -643,7 +643,7 @@ static int fib6_commit_metrics(struct ds if (dst->flags & DST_HOST) { mp = dst_metrics_write_ptr(dst); } else { - mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_ATOMIC); + mp = dst_alloc_metrics(GFP_ATOMIC | __GFP_ZERO); if (!mp) return -ENOMEM; dst_init_metrics(dst, mp, 0);