Commit 8f0545d2 authored by Jakub Kicinski's avatar Jakub Kicinski Committed by David S. Miller
Browse files

be2net: convert to new udp_tunnel_nic infra



Convert be2net to new udp_tunnel_nic infra. NIC only takes one VxLAN
port. Remove the port tracking using a list. The warning in
be_work_del_vxlan_port() looked suspicious - like the driver expected
ports to be removed in order of addition.

be2net unregisters ports when going down and re-registers them (for
skyhawk) when coming up, but it never checks if the device is up
in the add_port / del_port callbacks. Make it use
UDP_TUNNEL_NIC_INFO_OPEN_ONLY. Sadly this driver calls its own
open/close functions directly so the udp_tunnel_nic_reset_ntf()
workaround is needed.

Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 641ca085
Loading
Loading
Loading
Loading
+0 −5
Original line number Diff line number Diff line
@@ -654,8 +654,6 @@ struct be_adapter {
	u8 hba_port_num;
	u16 pvid;
	__be16 vxlan_port;		/* offloaded vxlan port num */
	int vxlan_port_count;		/* active vxlan port count */
	struct list_head vxlan_port_list;	/* vxlan port list */
	struct phy_info phy;
	u8 wol_cap;
	bool wol_en;
@@ -679,9 +677,6 @@ struct be_adapter {
struct be_cmd_work {
	struct work_struct work;
	struct be_adapter *adapter;
	union {
		__be16 vxlan_port;
	} info;
};

#define be_physfn(adapter)		(!adapter->virtfn)
+38 −160
Original line number Diff line number Diff line
@@ -3829,8 +3829,8 @@ static int be_open(struct net_device *netdev)
		be_link_status_update(adapter, link_status);

	netif_tx_start_all_queues(netdev);
	if (skyhawk_chip(adapter))
		udp_tunnel_get_rx_info(netdev);

	udp_tunnel_nic_reset_ntf(netdev);

	return 0;
err:
@@ -3967,18 +3967,23 @@ static void be_cancel_err_detection(struct be_adapter *adapter)
	}
}

static int be_enable_vxlan_offloads(struct be_adapter *adapter)
/* VxLAN offload Notes:
 *
 * The stack defines tunnel offload flags (hw_enc_features) for IP and doesn't
 * distinguish various types of transports (VxLAN, GRE, NVGRE ..). So, offload
 * is expected to work across all types of IP tunnels once exported. Skyhawk
 * supports offloads for either VxLAN or NVGRE, exclusively. So we export VxLAN
 * offloads in hw_enc_features only when a VxLAN port is added. If other (non
 * VxLAN) tunnels are configured while VxLAN offloads are enabled, offloads for
 * those other tunnels are unexported on the fly through ndo_features_check().
 */
static int be_vxlan_set_port(struct net_device *netdev, unsigned int table,
			     unsigned int entry, struct udp_tunnel_info *ti)
{
	struct net_device *netdev = adapter->netdev;
	struct be_adapter *adapter = netdev_priv(netdev);
	struct device *dev = &adapter->pdev->dev;
	struct be_vxlan_port *vxlan_port;
	__be16 port;
	int status;

	vxlan_port = list_first_entry(&adapter->vxlan_port_list,
				      struct be_vxlan_port, list);
	port = vxlan_port->port;

	status = be_cmd_manage_iface(adapter, adapter->if_handle,
				     OP_CONVERT_NORMAL_TO_TUNNEL);
	if (status) {
@@ -3987,25 +3992,26 @@ static int be_enable_vxlan_offloads(struct be_adapter *adapter)
	}
	adapter->flags |= BE_FLAGS_VXLAN_OFFLOADS;

	status = be_cmd_set_vxlan_port(adapter, port);
	status = be_cmd_set_vxlan_port(adapter, ti->port);
	if (status) {
		dev_warn(dev, "Failed to add VxLAN port\n");
		return status;
	}
	adapter->vxlan_port = port;
	adapter->vxlan_port = ti->port;

	netdev->hw_enc_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
				   NETIF_F_TSO | NETIF_F_TSO6 |
				   NETIF_F_GSO_UDP_TUNNEL;

	dev_info(dev, "Enabled VxLAN offloads for UDP port %d\n",
		 be16_to_cpu(port));
		 be16_to_cpu(ti->port));
	return 0;
}

static void be_disable_vxlan_offloads(struct be_adapter *adapter)
static int be_vxlan_unset_port(struct net_device *netdev, unsigned int table,
			       unsigned int entry, struct udp_tunnel_info *ti)
{
	struct net_device *netdev = adapter->netdev;
	struct be_adapter *adapter = netdev_priv(netdev);

	if (adapter->flags & BE_FLAGS_VXLAN_OFFLOADS)
		be_cmd_manage_iface(adapter, adapter->if_handle,
@@ -4018,8 +4024,19 @@ static void be_disable_vxlan_offloads(struct be_adapter *adapter)
	adapter->vxlan_port = 0;

	netdev->hw_enc_features = 0;
	return 0;
}

static const struct udp_tunnel_nic_info be_udp_tunnels = {
	.set_port	= be_vxlan_set_port,
	.unset_port	= be_vxlan_unset_port,
	.flags		= UDP_TUNNEL_NIC_INFO_MAY_SLEEP |
			  UDP_TUNNEL_NIC_INFO_OPEN_ONLY,
	.tables		= {
		{ .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_VXLAN, },
	},
};

static void be_calculate_vf_res(struct be_adapter *adapter, u16 num_vfs,
				struct be_resources *vft_res)
{
@@ -4135,7 +4152,7 @@ static int be_clear(struct be_adapter *adapter)
					&vft_res);
	}

	be_disable_vxlan_offloads(adapter);
	be_vxlan_unset_port(adapter->netdev, 0, 0, NULL);

	be_if_destroy(adapter);

@@ -5053,147 +5070,6 @@ static struct be_cmd_work *be_alloc_work(struct be_adapter *adapter,
	return work;
}

/* VxLAN offload Notes:
 *
 * The stack defines tunnel offload flags (hw_enc_features) for IP and doesn't
 * distinguish various types of transports (VxLAN, GRE, NVGRE ..). So, offload
 * is expected to work across all types of IP tunnels once exported. Skyhawk
 * supports offloads for either VxLAN or NVGRE, exclusively. So we export VxLAN
 * offloads in hw_enc_features only when a VxLAN port is added. If other (non
 * VxLAN) tunnels are configured while VxLAN offloads are enabled, offloads for
 * those other tunnels are unexported on the fly through ndo_features_check().
 *
 * Skyhawk supports VxLAN offloads only for one UDP dport. So, if the stack
 * adds more than one port, disable offloads and re-enable them again when
 * there's only one port left. We maintain a list of ports for this purpose.
 */
static void be_work_add_vxlan_port(struct work_struct *work)
{
	struct be_cmd_work *cmd_work =
				container_of(work, struct be_cmd_work, work);
	struct be_adapter *adapter = cmd_work->adapter;
	struct device *dev = &adapter->pdev->dev;
	__be16 port = cmd_work->info.vxlan_port;
	struct be_vxlan_port *vxlan_port;
	int status;

	/* Bump up the alias count if it is an existing port */
	list_for_each_entry(vxlan_port, &adapter->vxlan_port_list, list) {
		if (vxlan_port->port == port) {
			vxlan_port->port_aliases++;
			goto done;
		}
	}

	/* Add a new port to our list. We don't need a lock here since port
	 * add/delete are done only in the context of a single-threaded work
	 * queue (be_wq).
	 */
	vxlan_port = kzalloc(sizeof(*vxlan_port), GFP_KERNEL);
	if (!vxlan_port)
		goto done;

	vxlan_port->port = port;
	INIT_LIST_HEAD(&vxlan_port->list);
	list_add_tail(&vxlan_port->list, &adapter->vxlan_port_list);
	adapter->vxlan_port_count++;

	if (adapter->flags & BE_FLAGS_VXLAN_OFFLOADS) {
		dev_info(dev,
			 "Only one UDP port supported for VxLAN offloads\n");
		dev_info(dev, "Disabling VxLAN offloads\n");
		goto err;
	}

	if (adapter->vxlan_port_count > 1)
		goto done;

	status = be_enable_vxlan_offloads(adapter);
	if (!status)
		goto done;

err:
	be_disable_vxlan_offloads(adapter);
done:
	kfree(cmd_work);
	return;
}

static void be_work_del_vxlan_port(struct work_struct *work)
{
	struct be_cmd_work *cmd_work =
				container_of(work, struct be_cmd_work, work);
	struct be_adapter *adapter = cmd_work->adapter;
	__be16 port = cmd_work->info.vxlan_port;
	struct be_vxlan_port *vxlan_port;

	/* Nothing to be done if a port alias is being deleted */
	list_for_each_entry(vxlan_port, &adapter->vxlan_port_list, list) {
		if (vxlan_port->port == port) {
			if (vxlan_port->port_aliases) {
				vxlan_port->port_aliases--;
				goto done;
			}
			break;
		}
	}

	/* No port aliases left; delete the port from the list */
	list_del(&vxlan_port->list);
	adapter->vxlan_port_count--;

	/* Disable VxLAN offload if this is the offloaded port */
	if (adapter->vxlan_port == vxlan_port->port) {
		WARN_ON(adapter->vxlan_port_count);
		be_disable_vxlan_offloads(adapter);
		dev_info(&adapter->pdev->dev,
			 "Disabled VxLAN offloads for UDP port %d\n",
			 be16_to_cpu(port));
		goto out;
	}

	/* If only 1 port is left, re-enable VxLAN offload */
	if (adapter->vxlan_port_count == 1)
		be_enable_vxlan_offloads(adapter);

out:
	kfree(vxlan_port);
done:
	kfree(cmd_work);
}

static void be_cfg_vxlan_port(struct net_device *netdev,
			      struct udp_tunnel_info *ti,
			      void (*func)(struct work_struct *))
{
	struct be_adapter *adapter = netdev_priv(netdev);
	struct be_cmd_work *cmd_work;

	if (ti->type != UDP_TUNNEL_TYPE_VXLAN)
		return;

	if (lancer_chip(adapter) || BEx_chip(adapter) || be_is_mc(adapter))
		return;

	cmd_work = be_alloc_work(adapter, func);
	if (cmd_work) {
		cmd_work->info.vxlan_port = ti->port;
		queue_work(be_wq, &cmd_work->work);
	}
}

static void be_del_vxlan_port(struct net_device *netdev,
			      struct udp_tunnel_info *ti)
{
	be_cfg_vxlan_port(netdev, ti, be_work_del_vxlan_port);
}

static void be_add_vxlan_port(struct net_device *netdev,
			      struct udp_tunnel_info *ti)
{
	be_cfg_vxlan_port(netdev, ti, be_work_add_vxlan_port);
}

static netdev_features_t be_features_check(struct sk_buff *skb,
					   struct net_device *dev,
					   netdev_features_t features)
@@ -5309,8 +5185,8 @@ static const struct net_device_ops be_netdev_ops = {
#endif
	.ndo_bridge_setlink	= be_ndo_bridge_setlink,
	.ndo_bridge_getlink	= be_ndo_bridge_getlink,
	.ndo_udp_tunnel_add	= be_add_vxlan_port,
	.ndo_udp_tunnel_del	= be_del_vxlan_port,
	.ndo_udp_tunnel_add	= udp_tunnel_nic_add_port,
	.ndo_udp_tunnel_del	= udp_tunnel_nic_del_port,
	.ndo_features_check	= be_features_check,
	.ndo_get_phys_port_id   = be_get_phys_port_id,
};
@@ -5342,6 +5218,9 @@ static void be_netdev_init(struct net_device *netdev)

	netdev->ethtool_ops = &be_ethtool_ops;

	if (!lancer_chip(adapter) && !BEx_chip(adapter) && !be_is_mc(adapter))
		netdev->udp_tunnel_nic_info = &be_udp_tunnels;

	/* MTU range: 256 - 9000 */
	netdev->min_mtu = BE_MIN_MTU;
	netdev->max_mtu = BE_MAX_MTU;
@@ -5819,7 +5698,6 @@ static int be_drv_init(struct be_adapter *adapter)
	/* Must be a power of 2 or else MODULO will BUG_ON */
	adapter->be_get_temp_freq = 64;

	INIT_LIST_HEAD(&adapter->vxlan_port_list);
	return 0;

free_rx_filter: