Commit 56eda01d authored by David S. Miller's avatar David S. Miller

Merge branch 'udp-tunnel-offloads-toggle'

Sabrina Dubroca says:

====================
Allow to switch off UDP-based tunnel offloads per netdevice

This patchset adds a new netdevice feature to toggle RX offloads of
UDP-based tunnel via ethtool. This is useful if the offload is causing
issues, for example if the hardware is buggy.

The feature is added to all devices providing the ->ndo_udp_tunnel_add
op, and enabled by default to preserve current behavior.

When the administrator disables this feature on a device, all
currently offloaded ports are cleared from the device.  When the
feature is turned on, the stack notifies the device about all current
ports.

v2:
 - rename feature bit to NETIF_F_RX_UDP_TUNNEL_PORT
 - rename ethtool feature to rx-udp_tunnel-port-offload
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 5b861f6b 04584957
...@@ -1016,16 +1016,22 @@ static struct device_type geneve_type = { ...@@ -1016,16 +1016,22 @@ static struct device_type geneve_type = {
* supply the listening GENEVE udp ports. Callers are expected * supply the listening GENEVE udp ports. Callers are expected
* to implement the ndo_udp_tunnel_add. * to implement the ndo_udp_tunnel_add.
*/ */
static void geneve_push_rx_ports(struct net_device *dev) static void geneve_offload_rx_ports(struct net_device *dev, bool push)
{ {
struct net *net = dev_net(dev); struct net *net = dev_net(dev);
struct geneve_net *gn = net_generic(net, geneve_net_id); struct geneve_net *gn = net_generic(net, geneve_net_id);
struct geneve_sock *gs; struct geneve_sock *gs;
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(gs, &gn->sock_list, list) list_for_each_entry_rcu(gs, &gn->sock_list, list) {
if (push) {
udp_tunnel_push_rx_port(dev, gs->sock, udp_tunnel_push_rx_port(dev, gs->sock,
UDP_TUNNEL_TYPE_GENEVE); UDP_TUNNEL_TYPE_GENEVE);
} else {
udp_tunnel_drop_rx_port(dev, gs->sock,
UDP_TUNNEL_TYPE_GENEVE);
}
}
rcu_read_unlock(); rcu_read_unlock();
} }
...@@ -1560,8 +1566,14 @@ static int geneve_netdevice_event(struct notifier_block *unused, ...@@ -1560,8 +1566,14 @@ static int geneve_netdevice_event(struct notifier_block *unused,
{ {
struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct net_device *dev = netdev_notifier_info_to_dev(ptr);
if (event == NETDEV_UDP_TUNNEL_PUSH_INFO) if (event == NETDEV_UDP_TUNNEL_PUSH_INFO ||
geneve_push_rx_ports(dev); event == NETDEV_UDP_TUNNEL_DROP_INFO) {
geneve_offload_rx_ports(dev, event == NETDEV_UDP_TUNNEL_PUSH_INFO);
} else if (event == NETDEV_UNREGISTER) {
geneve_offload_rx_ports(dev, false);
} else if (event == NETDEV_REGISTER) {
geneve_offload_rx_ports(dev, true);
}
return NOTIFY_DONE; return NOTIFY_DONE;
} }
......
...@@ -2608,7 +2608,7 @@ static struct device_type vxlan_type = { ...@@ -2608,7 +2608,7 @@ static struct device_type vxlan_type = {
* supply the listening VXLAN udp ports. Callers are expected * supply the listening VXLAN udp ports. Callers are expected
* to implement the ndo_udp_tunnel_add. * to implement the ndo_udp_tunnel_add.
*/ */
static void vxlan_push_rx_ports(struct net_device *dev) static void vxlan_offload_rx_ports(struct net_device *dev, bool push)
{ {
struct vxlan_sock *vs; struct vxlan_sock *vs;
struct net *net = dev_net(dev); struct net *net = dev_net(dev);
...@@ -2617,11 +2617,19 @@ static void vxlan_push_rx_ports(struct net_device *dev) ...@@ -2617,11 +2617,19 @@ static void vxlan_push_rx_ports(struct net_device *dev)
spin_lock(&vn->sock_lock); spin_lock(&vn->sock_lock);
for (i = 0; i < PORT_HASH_SIZE; ++i) { for (i = 0; i < PORT_HASH_SIZE; ++i) {
hlist_for_each_entry_rcu(vs, &vn->sock_list[i], hlist) hlist_for_each_entry_rcu(vs, &vn->sock_list[i], hlist) {
udp_tunnel_push_rx_port(dev, vs->sock, unsigned short type;
(vs->flags & VXLAN_F_GPE) ?
UDP_TUNNEL_TYPE_VXLAN_GPE : if (vs->flags & VXLAN_F_GPE)
UDP_TUNNEL_TYPE_VXLAN); type = UDP_TUNNEL_TYPE_VXLAN_GPE;
else
type = UDP_TUNNEL_TYPE_VXLAN;
if (push)
udp_tunnel_push_rx_port(dev, vs->sock, type);
else
udp_tunnel_drop_rx_port(dev, vs->sock, type);
}
} }
spin_unlock(&vn->sock_lock); spin_unlock(&vn->sock_lock);
} }
...@@ -3630,10 +3638,15 @@ static int vxlan_netdevice_event(struct notifier_block *unused, ...@@ -3630,10 +3638,15 @@ static int vxlan_netdevice_event(struct notifier_block *unused,
struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id); struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id);
if (event == NETDEV_UNREGISTER) if (event == NETDEV_UNREGISTER) {
vxlan_offload_rx_ports(dev, false);
vxlan_handle_lowerdev_unregister(vn, dev); vxlan_handle_lowerdev_unregister(vn, dev);
else if (event == NETDEV_UDP_TUNNEL_PUSH_INFO) } else if (event == NETDEV_REGISTER) {
vxlan_push_rx_ports(dev); vxlan_offload_rx_ports(dev, true);
} else if (event == NETDEV_UDP_TUNNEL_PUSH_INFO ||
event == NETDEV_UDP_TUNNEL_DROP_INFO) {
vxlan_offload_rx_ports(dev, event == NETDEV_UDP_TUNNEL_PUSH_INFO);
}
return NOTIFY_DONE; return NOTIFY_DONE;
} }
......
...@@ -75,6 +75,7 @@ enum { ...@@ -75,6 +75,7 @@ enum {
NETIF_F_HW_TC_BIT, /* Offload TC infrastructure */ NETIF_F_HW_TC_BIT, /* Offload TC infrastructure */
NETIF_F_HW_ESP_BIT, /* Hardware ESP transformation offload */ NETIF_F_HW_ESP_BIT, /* Hardware ESP transformation offload */
NETIF_F_HW_ESP_TX_CSUM_BIT, /* ESP with TX checksum offload */ NETIF_F_HW_ESP_TX_CSUM_BIT, /* ESP with TX checksum offload */
NETIF_F_RX_UDP_TUNNEL_PORT_BIT, /* Offload of RX port for UDP tunnels */
/* /*
* Add your fresh new feature above and remember to update * Add your fresh new feature above and remember to update
...@@ -138,6 +139,7 @@ enum { ...@@ -138,6 +139,7 @@ enum {
#define NETIF_F_HW_TC __NETIF_F(HW_TC) #define NETIF_F_HW_TC __NETIF_F(HW_TC)
#define NETIF_F_HW_ESP __NETIF_F(HW_ESP) #define NETIF_F_HW_ESP __NETIF_F(HW_ESP)
#define NETIF_F_HW_ESP_TX_CSUM __NETIF_F(HW_ESP_TX_CSUM) #define NETIF_F_HW_ESP_TX_CSUM __NETIF_F(HW_ESP_TX_CSUM)
#define NETIF_F_RX_UDP_TUNNEL_PORT __NETIF_F(RX_UDP_TUNNEL_PORT)
#define for_each_netdev_feature(mask_addr, bit) \ #define for_each_netdev_feature(mask_addr, bit) \
for_each_set_bit(bit, (unsigned long *)mask_addr, NETDEV_FEATURE_COUNT) for_each_set_bit(bit, (unsigned long *)mask_addr, NETDEV_FEATURE_COUNT)
......
...@@ -2317,6 +2317,7 @@ struct netdev_lag_lower_state_info { ...@@ -2317,6 +2317,7 @@ struct netdev_lag_lower_state_info {
#define NETDEV_PRECHANGEUPPER 0x001A #define NETDEV_PRECHANGEUPPER 0x001A
#define NETDEV_CHANGELOWERSTATE 0x001B #define NETDEV_CHANGELOWERSTATE 0x001B
#define NETDEV_UDP_TUNNEL_PUSH_INFO 0x001C #define NETDEV_UDP_TUNNEL_PUSH_INFO 0x001C
#define NETDEV_UDP_TUNNEL_DROP_INFO 0x001D
#define NETDEV_CHANGE_TX_QUEUE_LEN 0x001E #define NETDEV_CHANGE_TX_QUEUE_LEN 0x001E
int register_netdevice_notifier(struct notifier_block *nb); int register_netdevice_notifier(struct notifier_block *nb);
......
...@@ -115,6 +115,8 @@ struct udp_tunnel_info { ...@@ -115,6 +115,8 @@ struct udp_tunnel_info {
/* Notify network devices of offloadable types */ /* Notify network devices of offloadable types */
void udp_tunnel_push_rx_port(struct net_device *dev, struct socket *sock, void udp_tunnel_push_rx_port(struct net_device *dev, struct socket *sock,
unsigned short type); unsigned short type);
void udp_tunnel_drop_rx_port(struct net_device *dev, struct socket *sock,
unsigned short type);
void udp_tunnel_notify_add_rx_port(struct socket *sock, unsigned short type); void udp_tunnel_notify_add_rx_port(struct socket *sock, unsigned short type);
void udp_tunnel_notify_del_rx_port(struct socket *sock, unsigned short type); void udp_tunnel_notify_del_rx_port(struct socket *sock, unsigned short type);
...@@ -124,6 +126,12 @@ static inline void udp_tunnel_get_rx_info(struct net_device *dev) ...@@ -124,6 +126,12 @@ static inline void udp_tunnel_get_rx_info(struct net_device *dev)
call_netdevice_notifiers(NETDEV_UDP_TUNNEL_PUSH_INFO, dev); call_netdevice_notifiers(NETDEV_UDP_TUNNEL_PUSH_INFO, dev);
} }
static inline void udp_tunnel_drop_rx_info(struct net_device *dev)
{
ASSERT_RTNL();
call_netdevice_notifiers(NETDEV_UDP_TUNNEL_DROP_INFO, dev);
}
/* Transmit the skb using UDP encapsulation. */ /* Transmit the skb using UDP encapsulation. */
void udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb, void udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
__be32 src, __be32 dst, __u8 tos, __u8 ttl, __be32 src, __be32 dst, __u8 tos, __u8 ttl,
......
...@@ -144,6 +144,7 @@ ...@@ -144,6 +144,7 @@
#include <linux/netfilter_ingress.h> #include <linux/netfilter_ingress.h>
#include <linux/crash_dump.h> #include <linux/crash_dump.h>
#include <linux/sctp.h> #include <linux/sctp.h>
#include <net/udp_tunnel.h>
#include "net-sysfs.h" #include "net-sysfs.h"
...@@ -7327,8 +7328,27 @@ int __netdev_update_features(struct net_device *dev) ...@@ -7327,8 +7328,27 @@ int __netdev_update_features(struct net_device *dev)
netdev_for_each_lower_dev(dev, lower, iter) netdev_for_each_lower_dev(dev, lower, iter)
netdev_sync_lower_features(dev, lower, features); netdev_sync_lower_features(dev, lower, features);
if (!err) if (!err) {
netdev_features_t diff = features ^ dev->features;
if (diff & NETIF_F_RX_UDP_TUNNEL_PORT) {
/* udp_tunnel_{get,drop}_rx_info both need
* NETIF_F_RX_UDP_TUNNEL_PORT enabled on the
* device, or they won't do anything.
* Thus we need to update dev->features
* *before* calling udp_tunnel_get_rx_info,
* but *after* calling udp_tunnel_drop_rx_info.
*/
if (features & NETIF_F_RX_UDP_TUNNEL_PORT) {
dev->features = features;
udp_tunnel_get_rx_info(dev);
} else {
udp_tunnel_drop_rx_info(dev);
}
}
dev->features = features; dev->features = features;
}
return err < 0 ? 0 : 1; return err < 0 ? 0 : 1;
} }
...@@ -7530,6 +7550,12 @@ int register_netdevice(struct net_device *dev) ...@@ -7530,6 +7550,12 @@ int register_netdevice(struct net_device *dev)
*/ */
dev->hw_features |= NETIF_F_SOFT_FEATURES; dev->hw_features |= NETIF_F_SOFT_FEATURES;
dev->features |= NETIF_F_SOFT_FEATURES; dev->features |= NETIF_F_SOFT_FEATURES;
if (dev->netdev_ops->ndo_udp_tunnel_add) {
dev->features |= NETIF_F_RX_UDP_TUNNEL_PORT;
dev->hw_features |= NETIF_F_RX_UDP_TUNNEL_PORT;
}
dev->wanted_features = dev->features & dev->hw_features; dev->wanted_features = dev->features & dev->hw_features;
if (!(dev->flags & IFF_LOOPBACK)) if (!(dev->flags & IFF_LOOPBACK))
......
...@@ -105,6 +105,7 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] ...@@ -105,6 +105,7 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN]
[NETIF_F_HW_TC_BIT] = "hw-tc-offload", [NETIF_F_HW_TC_BIT] = "hw-tc-offload",
[NETIF_F_HW_ESP_BIT] = "esp-hw-offload", [NETIF_F_HW_ESP_BIT] = "esp-hw-offload",
[NETIF_F_HW_ESP_TX_CSUM_BIT] = "esp-tx-csum-hw-offload", [NETIF_F_HW_ESP_TX_CSUM_BIT] = "esp-tx-csum-hw-offload",
[NETIF_F_RX_UDP_TUNNEL_PORT_BIT] = "rx-udp_tunnel-port-offload",
}; };
static const char static const char
......
...@@ -82,7 +82,8 @@ void udp_tunnel_push_rx_port(struct net_device *dev, struct socket *sock, ...@@ -82,7 +82,8 @@ void udp_tunnel_push_rx_port(struct net_device *dev, struct socket *sock,
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
struct udp_tunnel_info ti; struct udp_tunnel_info ti;
if (!dev->netdev_ops->ndo_udp_tunnel_add) if (!dev->netdev_ops->ndo_udp_tunnel_add ||
!(dev->features & NETIF_F_RX_UDP_TUNNEL_PORT))
return; return;
ti.type = type; ti.type = type;
...@@ -93,6 +94,24 @@ void udp_tunnel_push_rx_port(struct net_device *dev, struct socket *sock, ...@@ -93,6 +94,24 @@ void udp_tunnel_push_rx_port(struct net_device *dev, struct socket *sock,
} }
EXPORT_SYMBOL_GPL(udp_tunnel_push_rx_port); EXPORT_SYMBOL_GPL(udp_tunnel_push_rx_port);
void udp_tunnel_drop_rx_port(struct net_device *dev, struct socket *sock,
unsigned short type)
{
struct sock *sk = sock->sk;
struct udp_tunnel_info ti;
if (!dev->netdev_ops->ndo_udp_tunnel_del ||
!(dev->features & NETIF_F_RX_UDP_TUNNEL_PORT))
return;
ti.type = type;
ti.sa_family = sk->sk_family;
ti.port = inet_sk(sk)->inet_sport;
dev->netdev_ops->ndo_udp_tunnel_del(dev, &ti);
}
EXPORT_SYMBOL_GPL(udp_tunnel_drop_rx_port);
/* Notify netdevs that UDP port started listening */ /* Notify netdevs that UDP port started listening */
void udp_tunnel_notify_add_rx_port(struct socket *sock, unsigned short type) void udp_tunnel_notify_add_rx_port(struct socket *sock, unsigned short type)
{ {
...@@ -109,6 +128,8 @@ void udp_tunnel_notify_add_rx_port(struct socket *sock, unsigned short type) ...@@ -109,6 +128,8 @@ void udp_tunnel_notify_add_rx_port(struct socket *sock, unsigned short type)
for_each_netdev_rcu(net, dev) { for_each_netdev_rcu(net, dev) {
if (!dev->netdev_ops->ndo_udp_tunnel_add) if (!dev->netdev_ops->ndo_udp_tunnel_add)
continue; continue;
if (!(dev->features & NETIF_F_RX_UDP_TUNNEL_PORT))
continue;
dev->netdev_ops->ndo_udp_tunnel_add(dev, &ti); dev->netdev_ops->ndo_udp_tunnel_add(dev, &ti);
} }
rcu_read_unlock(); rcu_read_unlock();
...@@ -131,6 +152,8 @@ void udp_tunnel_notify_del_rx_port(struct socket *sock, unsigned short type) ...@@ -131,6 +152,8 @@ void udp_tunnel_notify_del_rx_port(struct socket *sock, unsigned short type)
for_each_netdev_rcu(net, dev) { for_each_netdev_rcu(net, dev) {
if (!dev->netdev_ops->ndo_udp_tunnel_del) if (!dev->netdev_ops->ndo_udp_tunnel_del)
continue; continue;
if (!(dev->features & NETIF_F_RX_UDP_TUNNEL_PORT))
continue;
dev->netdev_ops->ndo_udp_tunnel_del(dev, &ti); dev->netdev_ops->ndo_udp_tunnel_del(dev, &ti);
} }
rcu_read_unlock(); rcu_read_unlock();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment