Re: [PATCH net-next v2 1/1] geneve: add rtnl changelink support
On Thu, Jul 20, 2017 at 10:42 AM, Girish Moodalbailwrote: > Hello Pravin, > > >>> +/* Quiesces the geneve device data path for both TX and RX. */ >>> +static inline void geneve_quiesce(struct geneve_dev *geneve, >>> + struct geneve_sock **gs4, >>> + struct geneve_sock **gs6) >>> +{ >>> + *gs4 = rtnl_dereference(geneve->sock4); >>> + rcu_assign_pointer(geneve->sock4, NULL); >>> + >>> +#if IS_ENABLED(CONFIG_IPV6) >>> + *gs6 = rtnl_dereference(geneve->sock6); >>> + rcu_assign_pointer(geneve->sock6, NULL); >>> +#else >>> + *gs6 = NULL; >>> +#endif >>> + synchronize_net(); >>> +} >>> + >>> +/* Resumes the geneve device data path for both TX and RX. */ >>> +static inline void geneve_unquiesce(struct geneve_dev *geneve, >>> + struct geneve_sock *gs4, >>> + struct geneve_sock __maybe_unused >>> *gs6) >>> +{ >>> + rcu_assign_pointer(geneve->sock4, gs4); >>> +#if IS_ENABLED(CONFIG_IPV6) >>> + rcu_assign_pointer(geneve->sock6, gs6); >>> +#endif >>> + synchronize_net(); >>> +} >>> + >>> +static int geneve_changelink(struct net_device *dev, struct nlattr >>> *tb[], >>> +struct nlattr *data[], >>> +struct netlink_ext_ack *extack) >>> +{ >>> + struct geneve_dev *geneve = netdev_priv(dev); >>> + struct geneve_sock *gs4, *gs6; >>> + struct ip_tunnel_info info; >>> + bool metadata; >>> + bool use_udp6_rx_checksums; >>> + int err; >>> + >>> + /* If the geneve device is configured for metadata (or externally >>> +* controlled, for example, OVS), then nothing can be changed. >>> +*/ >>> + if (geneve->collect_md) >>> + return -EOPNOTSUPP; >>> + >>> + /* Start with the existing info. */ >>> + memcpy(, >info, sizeof(info)); >>> + metadata = geneve->collect_md; >>> + use_udp6_rx_checksums = geneve->use_udp6_rx_checksums; >>> + err = geneve_nl2info(dev, tb, data, , , >>> +_udp6_rx_checksums, true); >>> + if (err) >>> + return err; >>> + >>> + if (!geneve_dst_addr_equal(>info, )) >>> + dst_cache_reset(_cache); >>> + >>> + geneve_quiesce(geneve, , ); >>> + geneve->info = info; >>> + geneve->collect_md = metadata; >>> + geneve->use_udp6_rx_checksums = use_udp6_rx_checksums; >>> + geneve_unquiesce(geneve, gs4, gs6); >>> + >> >> This is nice trick. But it adds check for the socket in datapath. did >> you explore updating entire device state in single atomic transaction? > > > I did explore, however what I have now seemed like a more concise method to > perform changelink operation atomically w.r.t the datapath. > > That said, there is one thing I could do. Today we already check for the > socket in datapath like below: > > (a) ipv4 datapath today: > > > geneve_xmit_skb(...) > | > +->geneve_get_v4_rt() > | > +---> if (!rcu_dereference(geneve->sock4)) > return ERR_PTR(-EIO); > > (b) ipv4 datapath with my current patch: > > > geneve_xmit_skb(...) > | > +->if (!rcu_dereference(geneve->sock4)) > | return -EIO; > | > +->geneve_get_v4_rt() > | > +---> if (!rcu_dereference(geneve->sock4)) > return ERR_PTR(-EIO); > > Perhaps, I could piggyback on the already existing check for NULL socket in > geneve_get_v4_rt() and avoid the additional check I have added in datapath. > (The situation is same for IPv6) > Sounds good to me. But I would like to see comment in code explaining it bit since this is not standard RCU usage pattern.
Re: [PATCH net-next v2 1/1] geneve: add rtnl changelink support
Hello Pravin, +/* Quiesces the geneve device data path for both TX and RX. */ +static inline void geneve_quiesce(struct geneve_dev *geneve, + struct geneve_sock **gs4, + struct geneve_sock **gs6) +{ + *gs4 = rtnl_dereference(geneve->sock4); + rcu_assign_pointer(geneve->sock4, NULL); + +#if IS_ENABLED(CONFIG_IPV6) + *gs6 = rtnl_dereference(geneve->sock6); + rcu_assign_pointer(geneve->sock6, NULL); +#else + *gs6 = NULL; +#endif + synchronize_net(); +} + +/* Resumes the geneve device data path for both TX and RX. */ +static inline void geneve_unquiesce(struct geneve_dev *geneve, + struct geneve_sock *gs4, + struct geneve_sock __maybe_unused *gs6) +{ + rcu_assign_pointer(geneve->sock4, gs4); +#if IS_ENABLED(CONFIG_IPV6) + rcu_assign_pointer(geneve->sock6, gs6); +#endif + synchronize_net(); +} + +static int geneve_changelink(struct net_device *dev, struct nlattr *tb[], +struct nlattr *data[], +struct netlink_ext_ack *extack) +{ + struct geneve_dev *geneve = netdev_priv(dev); + struct geneve_sock *gs4, *gs6; + struct ip_tunnel_info info; + bool metadata; + bool use_udp6_rx_checksums; + int err; + + /* If the geneve device is configured for metadata (or externally +* controlled, for example, OVS), then nothing can be changed. +*/ + if (geneve->collect_md) + return -EOPNOTSUPP; + + /* Start with the existing info. */ + memcpy(, >info, sizeof(info)); + metadata = geneve->collect_md; + use_udp6_rx_checksums = geneve->use_udp6_rx_checksums; + err = geneve_nl2info(dev, tb, data, , , +_udp6_rx_checksums, true); + if (err) + return err; + + if (!geneve_dst_addr_equal(>info, )) + dst_cache_reset(_cache); + + geneve_quiesce(geneve, , ); + geneve->info = info; + geneve->collect_md = metadata; + geneve->use_udp6_rx_checksums = use_udp6_rx_checksums; + geneve_unquiesce(geneve, gs4, gs6); + This is nice trick. But it adds check for the socket in datapath. did you explore updating entire device state in single atomic transaction? I did explore, however what I have now seemed like a more concise method to perform changelink operation atomically w.r.t the datapath. That said, there is one thing I could do. Today we already check for the socket in datapath like below: (a) ipv4 datapath today: geneve_xmit_skb(...) | +->geneve_get_v4_rt() | +---> if (!rcu_dereference(geneve->sock4)) return ERR_PTR(-EIO); (b) ipv4 datapath with my current patch: geneve_xmit_skb(...) | +->if (!rcu_dereference(geneve->sock4)) | return -EIO; | +->geneve_get_v4_rt() | +---> if (!rcu_dereference(geneve->sock4)) return ERR_PTR(-EIO); Perhaps, I could piggyback on the already existing check for NULL socket in geneve_get_v4_rt() and avoid the additional check I have added in datapath. (The situation is same for IPv6) regards, ~Girish
Re: [PATCH net-next v2 1/1] geneve: add rtnl changelink support
On Tue, Jul 18, 2017 at 4:33 PM, Girish Moodalbailwrote: > This patch adds changelink rtnl operation support for geneve devices > and the code changes involve: > > - add geneve_quiesce() which quiesces the geneve device data path > for both TX and RX. This lets us perform the changelink operation > atomically w.r.t data path. Also add geneve_unquiesce() to > reverse the operation of geneve_quiesce(). > > - refactor geneve_newlink into geneve_nl2info to be used by both > geneve_newlink and geneve_changelink > > - geneve_nl2info takes a changelink boolean argument to isolate > changelink checks. > > - Allow changing only a few attributes (ttl, tos, and remote tunnel > endpoint IP address (within the same address family)): > - return -EOPNOTSUPP for attributes that cannot be changed for > now. Incremental patches can make the non-supported one > available in the future if needed. > > Signed-off-by: Girish Moodalbail > --- > v0 -> v1: >- added geneve_quiesce() and geneve_unquiesce() functions to > perform the changelink operation atomically w.r.t data path > --- > drivers/net/geneve.c | 192 > +-- > 1 file changed, 157 insertions(+), 35 deletions(-) > > diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c > index de8156c..829f541 100644 > --- a/drivers/net/geneve.c > +++ b/drivers/net/geneve.c ... ... > +/* Quiesces the geneve device data path for both TX and RX. */ > +static inline void geneve_quiesce(struct geneve_dev *geneve, > + struct geneve_sock **gs4, > + struct geneve_sock **gs6) > +{ > + *gs4 = rtnl_dereference(geneve->sock4); > + rcu_assign_pointer(geneve->sock4, NULL); > + > +#if IS_ENABLED(CONFIG_IPV6) > + *gs6 = rtnl_dereference(geneve->sock6); > + rcu_assign_pointer(geneve->sock6, NULL); > +#else > + *gs6 = NULL; > +#endif > + synchronize_net(); > +} > + > +/* Resumes the geneve device data path for both TX and RX. */ > +static inline void geneve_unquiesce(struct geneve_dev *geneve, > + struct geneve_sock *gs4, > + struct geneve_sock __maybe_unused *gs6) > +{ > + rcu_assign_pointer(geneve->sock4, gs4); > +#if IS_ENABLED(CONFIG_IPV6) > + rcu_assign_pointer(geneve->sock6, gs6); > +#endif > + synchronize_net(); > +} > + > +static int geneve_changelink(struct net_device *dev, struct nlattr *tb[], > +struct nlattr *data[], > +struct netlink_ext_ack *extack) > +{ > + struct geneve_dev *geneve = netdev_priv(dev); > + struct geneve_sock *gs4, *gs6; > + struct ip_tunnel_info info; > + bool metadata; > + bool use_udp6_rx_checksums; > + int err; > + > + /* If the geneve device is configured for metadata (or externally > +* controlled, for example, OVS), then nothing can be changed. > +*/ > + if (geneve->collect_md) > + return -EOPNOTSUPP; > + > + /* Start with the existing info. */ > + memcpy(, >info, sizeof(info)); > + metadata = geneve->collect_md; > + use_udp6_rx_checksums = geneve->use_udp6_rx_checksums; > + err = geneve_nl2info(dev, tb, data, , , > +_udp6_rx_checksums, true); > + if (err) > + return err; > + > + if (!geneve_dst_addr_equal(>info, )) > + dst_cache_reset(_cache); > + > + geneve_quiesce(geneve, , ); > + geneve->info = info; > + geneve->collect_md = metadata; > + geneve->use_udp6_rx_checksums = use_udp6_rx_checksums; > + geneve_unquiesce(geneve, gs4, gs6); > + This is nice trick. But it adds check for the socket in datapath. did you explore updating entire device state in single atomic transaction? > + return 0; > +} > + > static void geneve_dellink(struct net_device *dev, struct list_head *head) > { > struct geneve_dev *geneve = netdev_priv(dev); > @@ -1375,6 +1496,7 @@ static int geneve_fill_info(struct sk_buff *skb, const > struct net_device *dev) > .setup = geneve_setup, > .validate = geneve_validate, > .newlink= geneve_newlink, > + .changelink = geneve_changelink, > .dellink= geneve_dellink, > .get_size = geneve_get_size, > .fill_info = geneve_fill_info, > -- > 1.8.3.1 >
Re: [PATCH net-next v2 1/1] geneve: add rtnl changelink support
On 7/19/17 4:51 PM, David Miller wrote: From: Girish MoodalbailDate: Tue, 18 Jul 2017 16:33:06 -0700 +static inline bool geneve_dst_addr_equal(struct ip_tunnel_info *a, ... +static inline void geneve_quiesce(struct geneve_dev *geneve, ... +static inline void geneve_unquiesce(struct geneve_dev *geneve, Please no inline functions in foo.c files, let the compiler decide. Sure thing. Will do. regards, ~Girish Thanks.
Re: [PATCH net-next v2 1/1] geneve: add rtnl changelink support
From: Girish MoodalbailDate: Tue, 18 Jul 2017 16:33:06 -0700 > +static inline bool geneve_dst_addr_equal(struct ip_tunnel_info *a, ... > +static inline void geneve_quiesce(struct geneve_dev *geneve, ... > +static inline void geneve_unquiesce(struct geneve_dev *geneve, Please no inline functions in foo.c files, let the compiler decide. Thanks.
[PATCH net-next v2 1/1] geneve: add rtnl changelink support
This patch adds changelink rtnl operation support for geneve devices and the code changes involve: - add geneve_quiesce() which quiesces the geneve device data path for both TX and RX. This lets us perform the changelink operation atomically w.r.t data path. Also add geneve_unquiesce() to reverse the operation of geneve_quiesce(). - refactor geneve_newlink into geneve_nl2info to be used by both geneve_newlink and geneve_changelink - geneve_nl2info takes a changelink boolean argument to isolate changelink checks. - Allow changing only a few attributes (ttl, tos, and remote tunnel endpoint IP address (within the same address family)): - return -EOPNOTSUPP for attributes that cannot be changed for now. Incremental patches can make the non-supported one available in the future if needed. Signed-off-by: Girish Moodalbail--- v0 -> v1: - added geneve_quiesce() and geneve_unquiesce() functions to perform the changelink operation atomically w.r.t data path --- drivers/net/geneve.c | 192 +-- 1 file changed, 157 insertions(+), 35 deletions(-) diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index de8156c..829f541 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -827,6 +827,9 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, __be16 df; int err; + if (!gs4) + return -EIO; + rt = geneve_get_v4_rt(skb, dev, , info); if (IS_ERR(rt)) return PTR_ERR(rt); @@ -866,6 +869,9 @@ static int geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, __be16 sport; int err; + if (!gs6) + return -EIO; + dst = geneve_get_v6_dst(skb, dev, , info); if (IS_ERR(dst)) return PTR_ERR(dst); @@ -1140,6 +1146,15 @@ static bool is_tnl_info_zero(const struct ip_tunnel_info *info) return true; } +static inline bool geneve_dst_addr_equal(struct ip_tunnel_info *a, +struct ip_tunnel_info *b) +{ + if (ip_tunnel_info_af(a) == AF_INET) + return a->key.u.ipv4.dst == b->key.u.ipv4.dst; + else + return ipv6_addr_equal(>key.u.ipv6.dst, >key.u.ipv6.dst); +} + static int geneve_configure(struct net *net, struct net_device *dev, const struct ip_tunnel_info *info, bool metadata, bool ipv6_rx_csum) @@ -1197,24 +1212,22 @@ static void init_tnl_info(struct ip_tunnel_info *info, __u16 dst_port) info->key.tp_dst = htons(dst_port); } -static int geneve_newlink(struct net *net, struct net_device *dev, - struct nlattr *tb[], struct nlattr *data[], - struct netlink_ext_ack *extack) +static int geneve_nl2info(struct net_device *dev, struct nlattr *tb[], + struct nlattr *data[], struct ip_tunnel_info *info, + bool *metadata, bool *use_udp6_rx_checksums, + bool changelink) { - bool use_udp6_rx_checksums = false; - struct ip_tunnel_info info; - bool metadata = false; - - init_tnl_info(, GENEVE_UDP_PORT); - if (data[IFLA_GENEVE_REMOTE] && data[IFLA_GENEVE_REMOTE6]) return -EINVAL; if (data[IFLA_GENEVE_REMOTE]) { - info.key.u.ipv4.dst = + if (changelink && (ip_tunnel_info_af(info) == AF_INET6)) + return -EOPNOTSUPP; + + info->key.u.ipv4.dst = nla_get_in_addr(data[IFLA_GENEVE_REMOTE]); - if (IN_MULTICAST(ntohl(info.key.u.ipv4.dst))) { + if (IN_MULTICAST(ntohl(info->key.u.ipv4.dst))) { netdev_dbg(dev, "multicast remote is unsupported\n"); return -EINVAL; } @@ -1222,21 +1235,24 @@ static int geneve_newlink(struct net *net, struct net_device *dev, if (data[IFLA_GENEVE_REMOTE6]) { #if IS_ENABLED(CONFIG_IPV6) - info.mode = IP_TUNNEL_INFO_IPV6; - info.key.u.ipv6.dst = + if (changelink && (ip_tunnel_info_af(info) == AF_INET)) + return -EOPNOTSUPP; + + info->mode = IP_TUNNEL_INFO_IPV6; + info->key.u.ipv6.dst = nla_get_in6_addr(data[IFLA_GENEVE_REMOTE6]); - if (ipv6_addr_type() & + if (ipv6_addr_type(>key.u.ipv6.dst) & IPV6_ADDR_LINKLOCAL) { netdev_dbg(dev, "link-local remote is unsupported\n"); return -EINVAL; } - if (ipv6_addr_is_multicast()) { + if (ipv6_addr_is_multicast(>key.u.ipv6.dst)) { netdev_dbg(dev, "multicast remote is