Per-net and on-demand link indices (and related)
Subsystems (4)
cover Per-net and on-demand link indices (and related)
Hi!
This set tries to summarize the recent discussion of making ifindices friendly to checkpoint-restore and consists of:
1. Prepare hash function to non-unique ifindices
2. Allow for specifying the desired ifindex on net link creation
3. Make ifindex generation per-net
4. Simplify loopback device ifindex access
Thanks,
Pavel
PATCH 1/5 net: Don't use ifindices in hash fns
Eric noticed, that when there will be devices with equal indices, some hash functions that use them will become less effective as they could.
Fix this in advance by taking the net_device address into calculations instead of the device index. Since the net_device is always aligned in memory, shift the pointer to eliminate always zero bits (like we do it in net_hash_mix).
This is true for arp and ndisc hash fns. The netlabel, can and llc ones are also ifindex-based, but that three are init_net-only, thus will not be affected.
--- include/linux/netdevice.h | 6 ++++++ include/net/arp.h | 2 +- include/net/ndisc.h | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-)
include/linux/netdevice.h +6 -0
@@ -1330,6 +1330,12 @@ struct net_device {#define NETDEV_ALIGN 32 +static inline unsigned int netdev_hash_mix (const struct net_device *dev) +{ +return (unsigned int )(((unsigned long )dev) >> +max (L1_CACHE_BYTES ,NETDEV_ALIGN )); +} +static inline int netdev_get_prio_tc_map (const struct net_device *dev,u32 prio) {
include/net/arp.h +1 -1
@@ -10,7 +10,7 @@ extern struct neigh_table arp_tbl;static inline u32 arp_hashfn (u32 key,const struct net_device *dev,u32 hash_rnd) { -u32 val = key ^ dev->ifindex; +u32 val = key ^netdev_hash_mix (dev);return val * hash_rnd; }
include/net/ndisc.h +1 -1
@@ -134,7 +134,7 @@ static inline u32 ndisc_hashfn(const void *pkey, const struct net_device *dev, _ {const u32 *p32 = pkey; -return (((p32[0 ] ^ dev->ifindex) * hash_rnd[0 ]) + +return (((p32[0 ] ^netdev_hash_mix (dev)) * hash_rnd[0 ]) + (p32[1 ] * hash_rnd[1 ]) + (p32[2 ] * hash_rnd[2 ]) + (p32[3 ] * hash_rnd[3 ]));
Signature · 1 line
1.7.6.5
Re: [PATCH 1/5] net: Don't use ifindices in hash fns
Quoted 25 lines · Pavel Emelyanov
Eric noticed, that when there will be devices with equal indices, some hash functions that use them will become less effective as they could.
Fix this in advance by taking the
net_deviceaddress into calculations instead of the device index. Since thenet_deviceis always aligned in memory, shift the pointer to eliminate always zero bits (like we do it innet_hash_mix).This is true for arp and ndisc hash fns. The netlabel, can and llc ones are also ifindex-based, but that three are init_net-only, thus will not be affected.
Signed-off-by Pavel Emelyanov <[email protected]>--- include/linux/netdevice.h | 6 ++++++ include/net/arp.h | 2 +- include/net/ndisc.h | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-)include/linux/netdevice.h +0
-0
include/linux/netdevice.h struct net_device { · line 1338
Quoted 9 lines
include/linux/netdevice.h +6
-0@@ -1330,6 +1330,12 @@ struct net_device {#define NETDEV_ALIGN 32 +static inline unsigned int netdev_hash_mix (const struct net_device *dev) +{ +return (unsigned int )(((unsigned long )dev) >> +max (L1_CACHE_BYTES ,NETDEV_ALIGN )); +} +
I guess you didnt test this patch very well ...
This returns 0 as is
I would define a generic pointer hash mix instead of a 'net_device thing'
static inline u32 ptr_hash_mix(void *ptr)
{
#if BITS_PER_LONG==32 return (u32)(unsigned long)ptr; #else return (u32)((unsigned long)ptr >> L1_CACHE_SHIFT);
#endif
}
Re: [PATCH 1/5] net: Don't use ifindices in hash fns
Quoted 29 lines · Eric Dumazet
Quoted 28 lines · Pavel Emelyanov
Eric noticed, that when there will be devices with equal indices, some hash functions that use them will become less effective as they could.
Fix this in advance by taking the
net_deviceaddress into calculations instead of the device index. Since thenet_deviceis always aligned in memory, shift the pointer to eliminate always zero bits (like we do it innet_hash_mix).This is true for arp and ndisc hash fns. The netlabel, can and llc ones are also ifindex-based, but that three are init_net-only, thus will not be affected.
Signed-off-by Pavel Emelyanov <[email protected]>--- include/linux/netdevice.h | 6 ++++++ include/net/arp.h | 2 +- include/net/ndisc.h | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-)include/linux/netdevice.h +0
-0@@ -1330,6 +1330,12 @@ struct net_device {#define NETDEV_ALIGN 32
Quoted 8 lines
include/linux/netdevice.h struct net_device { · line 1338
Quoted 6 lines
include/linux/netdevice.h +6
-0+static inline unsigned int netdev_hash_mix (const struct net_device *dev) +{ +return (unsigned int )(((unsigned long )dev) >> +max (L1_CACHE_BYTES ,NETDEV_ALIGN )); +} +I guess you didnt test this patch very well ...
Damn :( You're right.
Quoted 1 line
This returns 0 as is
Well, on 64-bit no, but what it does is also not what it was supposed to.
Quoted 11 lines
I would define a generic pointer hash mix instead of a '
net_devicething'static inline u32
ptr_hash_mix(void *ptr)
{#if BITS_PER_LONG==32 return (u32)(unsigned long)ptr; #else return (u32)((unsigned long)ptr >> L1_CACHE_SHIFT);#endif
}
OK. It will also obsolete the net_hash_mix then. Any suggestions where to put the new one?
Thanks,
Pavel
Re: [PATCH 1/5] net: Don't use ifindices in hash fns
Quoted 6 lines · Pavel Emelyanov
Damn :( You're right.
Quoted 1 line
This returns 0 as is
Well, on 64-bit no, but what it does is also not what it was supposed to.
Here, my L1_CACHE_BYTES is 64, but whatever
Quoted 12 lines
Quoted 11 lines
I would define a generic pointer hash mix instead of a '
net_devicething'static inline u32
ptr_hash_mix(void *ptr)
{#if BITS_PER_LONG==32 return (u32)(unsigned long)ptr; #else return (u32)((unsigned long)ptr >> L1_CACHE_SHIFT);#endif
}
Quoted 2 lines
OK. It will also obsolete the
net_hash_mixthen. Any suggestions where to put the new one?
Not obsolete net_hash_mix(), since this one can return 0 if CONFIG_NET_NS is not set.
static inline unsigned int net_hash_mix(struct net *net) {
#ifdef CONFIG_NET_NS return ptr_hash_mix(net); #else return 0;
#endif
}
I dont know, you could add this to include/linux/hash.h
PATCH 2/5 net: Allow to create links with given ifindex
In reply to Pavel EmelyanovCurrently the RTM_NEWLINK results in -EOPNOTSUPP if the ifinfomsg->ifi_index is not zero. I propose to allow requesting ifindices on link creation. This is required by the checkpoint-restore to correctly restore a net namespace (i.e. -- a container).
--- net/core/dev.c | 7 ++++++- net/core/rtnetlink.c | 12 +++++++----- 2 files changed, 13 insertions(+), 6 deletions(-)
net/core/dev.c +6 -1
@@ -5579,7 +5579,12 @@ int register_netdevice(struct net_device *dev) } } - dev->ifindex =dev_new_index (net); + ret = -EBUSY ; +if (!dev->ifindex) + dev->ifindex =dev_new_index (net); +else if (__dev_get_by_index (net, dev->ifindex)) + goto err_uninit; +if (dev->iflink ==-1 ) dev->iflink = dev->ifindex;
net/core/rtnetlink.c +7 -5
@@ -1812,8 +1812,6 @@ replay:return -ENODEV ; } -if (ifm->ifi_index) -return -EOPNOTSUPP ;if (tb[IFLA_MAP ] || tb[IFLA_MASTER ] || tb[IFLA_PROTINFO ])return -EOPNOTSUPP ;
@@ -1839,10 +1837,14 @@ replay:return PTR_ERR (dest_net); dev =rtnl_create_link (net, dest_net, ifname, ops, tb); - -if (IS_ERR (dev)) +if (IS_ERR (dev)) { err =PTR_ERR (dev); -else if (ops->newlink) + goto out; + } + + dev->ifindex = ifm->ifi_index; + +if (ops->newlink) err = ops->newlink (net, dev, tb, data);else err =register_netdevice (dev);
Signature · 1 line
1.7.6.5
PATCH 3/5 veth: Allow to create peer link with given ifindex
In reply to Pavel EmelyanovThe ifinfomsg is in there (thanks kaber@ for foreseeing this long time ago), so take the given ifidex and register netdev with it.
--- drivers/net/veth.c | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
drivers/net/veth.c +3 -0
@@ -348,6 +348,9 @@ static int veth_newlink(struct net *src_net, struct net_device *dev,if (tbp[IFLA_ADDRESS ] ==NULL )eth_hw_addr_random (peer); +if (ifmp) + peer->ifindex = ifmp->ifi_index; + err =register_netdevice (peer);put_net (net); net =NULL ;
Signature · 1 line
1.7.6.5
PATCH 4/5 net: Make ifindex generation per-net namespace
In reply to Pavel EmelyanovStrictly speaking this is only _really_ required for checkpoint-restore to make loopback device always have the same index.
This change appears to be safe wrt "ifindex should be unique per-system" concept, as all the ifindex usage is either already made per net namespace of is explicitly limited with init_net only.
There are two cool side effects of this. The first one -- ifindices of devices in container are always small, regardless of how many containers we've started (and re-started) so far. The second one is -- we can speed up the loopback ifidex access as shown in the next patch.
--- include/net/net_namespace.h | 1 + net/core/dev.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-)
include/net/net_namespace.h +1 -0
@@ -62,6 +62,7 @@ struct net {struct sock *rtnl;/* rtnetlink socket */ struct sock *genl_sock; +int ifindex;struct list_head dev_base_head;struct hlist_head *dev_name_head;struct hlist_head *dev_index_head;
net/core/dev.c +2 -2
@@ -5221,12 +5221,12 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)*/ static int dev_new_index (struct net *net) { -static int ifindex; +int ifindex = net->ifindex;for (;;) {if (++ifindex <=0 ) ifindex =1 ;if (!__dev_get_by_index (net, ifindex)) -return ifindex; +return net->ifindex = ifindex; } }
Signature · 1 line
1.7.6.5
PATCH 5/5 net: Loopback ifindex is constant now
In reply to Pavel EmelyanovAs pointed out, there are places, that access net->loopback_dev->ifindex and after ifindex generation is made per-net this value becomes constant equals 1. So go ahead and introduce the LOOPBACK_IFINDEX constant and use it where appropriate.
--- drivers/net/loopback.c | 1 + include/net/net_namespace.h | 6 ++++++ net/decnet/dn_route.c | 6 +++--- net/ipv4/fib_frontend.c | 2 +- net/ipv4/ipmr.c | 2 +- net/ipv4/netfilter/ipt_rpfilter.c | 2 +- net/ipv4/route.c | 6 +++--- net/ipv6/route.c | 2 +- 8 files changed, 17 insertions(+), 10 deletions(-)
drivers/net/loopback.c +1 -0
@@ -197,6 +197,7 @@ static __net_init int loopback_net_init(struct net *net)if (err) goto out_free_netdev; +BUG_ON (dev->ifindex !=LOOPBACK_IFINDEX ); net->loopback_dev = dev;return 0 ;
include/net/net_namespace.h +6 -0
@@ -105,6 +105,12 @@ struct net {struct sock *diag_nlsk; }; +/* +* ifindex generation is per-net namespace, thus any loopback +* device should get ifindex 1 +*/ + +#define LOOPBACK_IFINDEX 1#include <linux/seq_file_net.h>
net/decnet/dn_route.c +3 -3
@@ -961,7 +961,7 @@ static int dn_route_output_slow(struct dst_entry **pprt, const struct flowidn *o .saddr = oldflp->saddr, .flowidn_scope = RT_SCOPE_UNIVERSE, .flowidn_mark = oldflp->flowidn_mark, - .flowidn_iif = init_net.loopback_dev->ifindex, + .flowidn_iif = LOOPBACK_IFINDEX, .flowidn_oif = oldflp->flowidn_oif, };struct dn_route *rt =NULL ;
@@ -979,7 +979,7 @@ static int dn_route_output_slow(struct dst_entry **pprt, const struct flowidn *o"dn_route_output_slow: dst=%04x src=%04x mark=%d" " iif=%d oif=%d\n" ,le16_to_cpu (oldflp->daddr),le16_to_cpu (oldflp->saddr), - oldflp->flowidn_mark, init_net.loopback_dev->ifindex, + oldflp->flowidn_mark,LOOPBACK_IFINDEX , oldflp->flowidn_oif);/* If we have an output interface, verify its a DECnet device */
@@ -1042,7 +1042,7 @@ source_ok:if (!fld.daddr) goto out; } - fld.flowidn_oif = init_net.loopback_dev->ifindex; + fld.flowidn_oif =LOOPBACK_IFINDEX ; res.type =RTN_LOCAL ; goto make_route; }
net/ipv4/fib_frontend.c +1 -1
@@ -218,7 +218,7 @@ __be32 fib_compute_spec_dst(struct sk_buff *skb) scope =RT_SCOPE_UNIVERSE ;if (!ipv4_is_zeronet (ip_hdr (skb)->saddr)) { fl4.flowi4_oif =0 ; - fl4.flowi4_iif = net->loopback_dev->ifindex; + fl4.flowi4_iif =LOOPBACK_IFINDEX ; fl4.daddr =ip_hdr (skb)->saddr; fl4.saddr =0 ; fl4.flowi4_tos =RT_TOS (ip_hdr (skb)->tos);
net/ipv4/ipmr.c +1 -1
@@ -1798,7 +1798,7 @@ static struct mr_table *ipmr_rt_fib_lookup(struct net *net, struct sk_buff *skb) .flowi4_oif = (rt_is_output_route(rt) ? skb->dev->ifindex : 0), .flowi4_iif = (rt_is_output_route (rt) ? - net->loopback_dev->ifindex : +LOOPBACK_IFINDEX : skb->dev->ifindex), .flowi4_mark = skb->mark, };
net/ipv4/netfilter/ipt_rpfilter.c +1 -1
@@ -85,7 +85,7 @@ static bool rpfilter_mt(const struct sk_buff *skb, struct xt_action_param *par)return ipv4_is_local_multicast (iph->daddr) ^ invert; flow.flowi4_iif =0 ; }else { - flow.flowi4_iif =dev_net (par->in)->loopback_dev->ifindex; + flow.flowi4_iif =LOOPBACK_IFINDEX ; } flow.daddr = iph->saddr;
net/ipv4/route.c +3 -3
@@ -1619,7 +1619,7 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,if (res.type ==RTN_LOCAL ) { err =fib_validate_source (skb, saddr, daddr, tos, - net->loopback_dev->ifindex, +LOOPBACK_IFINDEX , dev, in_dev, &itag);if (err <0 ) goto martian_source_keep_err;
@@ -1895,7 +1895,7 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4) orig_oif = fl4->flowi4_oif; - fl4->flowi4_iif = net->loopback_dev->ifindex; + fl4->flowi4_iif =LOOPBACK_IFINDEX ; fl4->flowi4_tos = tos &IPTOS_RT_MASK ; fl4->flowi4_scope = ((tos &RTO_ONLINK ) ?RT_SCOPE_LINK :RT_SCOPE_UNIVERSE );
@@ -1984,7 +1984,7 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)if (!fl4->daddr) fl4->daddr = fl4->saddr =htonl (INADDR_LOOPBACK ); dev_out = net->loopback_dev; - fl4->flowi4_oif = net->loopback_dev->ifindex; + fl4->flowi4_oif =LOOPBACK_IFINDEX ; res.type =RTN_LOCAL ; flags |=RTCF_LOCAL ; goto make_route;
net/ipv6/route.c +1 -1
@@ -965,7 +965,7 @@ struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk, {int flags =0 ; - fl6->flowi6_iif = net->loopback_dev->ifindex; + fl6->flowi6_iif =LOOPBACK_IFINDEX ;if ((sk && sk->sk_bound_dev_if) ||rt6_need_strict (&fl6->daddr)) flags |=RT6_LOOKUP_F_IFACE ;
Signature · 1 line
1.7.6.5
Re: [PATCH 5/5] net: Loopback ifindex is constant now
Quoted 6 lines · Pavel Emelyanov
As pointed out, there are places, that access
net->loopback_dev->ifindexand after ifindex generation is made per-net this value becomes constant equals 1. So go ahead and introduce theLOOPBACK_IFINDEXconstant and use it where appropriate.Signed-off-by Pavel Emelyanov <[email protected]>
What guarantee do we have that loopback is the first device per net ?
You should add this to the changelog because its not that obvious.
Re: [PATCH 5/5] net: Loopback ifindex is constant now
Quoted 9 lines · Eric Dumazet
Quoted 6 lines · Pavel Emelyanov
As pointed out, there are places, that access
net->loopback_dev->ifindexand after ifindex generation is made per-net this value becomes constant equals 1. So go ahead and introduce theLOOPBACK_IFINDEXconstant and use it where appropriate.Signed-off-by Pavel Emelyanov <[email protected]>What guarantee do we have that loopback is the first device per net ?
In net_dev_init():
/* The loopback device is special if any other network devices
* is present in a network namespace the loopback device must
* be present. Since we now dynamically allocate and free the
* loopback device ensure this invariant is maintained by
* keeping the loopback device as the first device on the
* list of network devices. Ensuring the loopback devices
* is the first device that appears and the last network device
* that disappears.
*/
if (register_pernet_device(&loopback_net_ops))
goto out;
Quoted 1 line
You should add this to the changelog because its not that obvious.
OK.
Thanks,
Pavel