[PATCH 3.16 114/294] dst: Increase alignment of metrics to allow extra flag on pointers

From: Ben Hutchings
Date: Mon Nov 06 2017 - 19:19:54 EST


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);