Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a02cc9d3 authored by Michal Schmidt's avatar Michal Schmidt Committed by David S. Miller
Browse files

bnx2x: allow adding VLANs while interface is down



Since implementing VLAN filtering in commit 05cc5a39
("bnx2x: add vlan filtering offload") bnx2x refuses to add a VLAN while
the interface is down:

  # ip link add link enp3s0f0 enp3s0f0_10 type vlan id 10
  RTNETLINK answers: Bad address

and in dmesg (with bnx2x.debug=0x20):
  bnx2x: [bnx2x_vlan_rx_add_vid:12941(enp3s0f0)]Ignoring VLAN
  configuration the interface is down

Other drivers have no problem with this.
Fix this peculiar behavior in the following way:
 - Accept requests to add/kill VID regardless of the device state.
   Maintain the requested list of VIDs in the bp->vlan_reg list.
 - If the device is up, try to configure the VID list into the hardware.
   If we run out of VLAN credits or encounter a failure configuring an
   entry, fall back to accepting all VLANs.
   If we successfully configure all entries from the list, turn the
   fallback off.
 - Use the same code for reconfiguring VLANs during NIC load.

Signed-off-by: default avatarMichal Schmidt <mschmidt@redhat.com>
Acked-by: default avatarYuval Mintz <Yuval.Mintz@qlogic.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 4ef36e15
Loading
Loading
Loading
Loading
+62 −89
Original line number Diff line number Diff line
@@ -12895,146 +12895,119 @@ static int __bnx2x_vlan_configure_vid(struct bnx2x *bp, u16 vid, bool add)
	return rc;
}

int bnx2x_vlan_reconfigure_vid(struct bnx2x *bp)
static int bnx2x_vlan_configure_vid_list(struct bnx2x *bp)
{
	struct bnx2x_vlan_entry *vlan;
	int rc = 0;

	if (!bp->vlan_cnt) {
		DP(NETIF_MSG_IFUP, "No need to re-configure vlan filters\n");
		return 0;
	}

	/* Configure all non-configured entries */
	list_for_each_entry(vlan, &bp->vlan_reg, link) {
		/* Prepare for cleanup in case of errors */
		if (rc) {
			vlan->hw = false;
			continue;
		}

		if (!vlan->hw)
		if (vlan->hw)
			continue;

		DP(NETIF_MSG_IFUP, "Re-configuring vlan 0x%04x\n", vlan->vid);
		if (bp->vlan_cnt >= bp->vlan_credit)
			return -ENOBUFS;

		rc = __bnx2x_vlan_configure_vid(bp, vlan->vid, true);
		if (rc) {
			BNX2X_ERR("Unable to configure VLAN %d\n", vlan->vid);
			vlan->hw = false;
			rc = -EINVAL;
			continue;
			BNX2X_ERR("Unable to config VLAN %d\n", vlan->vid);
			return rc;
		}

		DP(NETIF_MSG_IFUP, "HW configured for VLAN %d\n", vlan->vid);
		vlan->hw = true;
		bp->vlan_cnt++;
	}

	return rc;
	return 0;
}

static int bnx2x_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
static void bnx2x_vlan_configure(struct bnx2x *bp, bool set_rx_mode)
{
	bool need_accept_any_vlan;

	need_accept_any_vlan = !!bnx2x_vlan_configure_vid_list(bp);

	if (bp->accept_any_vlan != need_accept_any_vlan) {
		bp->accept_any_vlan = need_accept_any_vlan;
		DP(NETIF_MSG_IFUP, "Accept all VLAN %s\n",
		   bp->accept_any_vlan ? "raised" : "cleared");
		if (set_rx_mode) {
			if (IS_PF(bp))
				bnx2x_set_rx_mode_inner(bp);
			else
				bnx2x_vfpf_storm_rx_mode(bp);
		}
	}
}

int bnx2x_vlan_reconfigure_vid(struct bnx2x *bp)
{
	struct bnx2x *bp = netdev_priv(dev);
	struct bnx2x_vlan_entry *vlan;
	bool hw = false;
	int rc = 0;

	if (!netif_running(bp->dev)) {
		DP(NETIF_MSG_IFUP,
		   "Ignoring VLAN configuration the interface is down\n");
		return -EFAULT;
	/* The hw forgot all entries after reload */
	list_for_each_entry(vlan, &bp->vlan_reg, link)
		vlan->hw = false;
	bp->vlan_cnt = 0;

	/* Don't set rx mode here. Our caller will do it. */
	bnx2x_vlan_configure(bp, false);

	return 0;
}

static int bnx2x_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
{
	struct bnx2x *bp = netdev_priv(dev);
	struct bnx2x_vlan_entry *vlan;

	DP(NETIF_MSG_IFUP, "Adding VLAN %d\n", vid);

	vlan = kmalloc(sizeof(*vlan), GFP_KERNEL);
	if (!vlan)
		return -ENOMEM;

	bp->vlan_cnt++;
	if (bp->vlan_cnt > bp->vlan_credit && !bp->accept_any_vlan) {
		DP(NETIF_MSG_IFUP, "Accept all VLAN raised\n");
		bp->accept_any_vlan = true;
		if (IS_PF(bp))
			bnx2x_set_rx_mode_inner(bp);
		else
			bnx2x_vfpf_storm_rx_mode(bp);
	} else if (bp->vlan_cnt <= bp->vlan_credit) {
		rc = __bnx2x_vlan_configure_vid(bp, vid, true);
		hw = true;
	}

	vlan->vid = vid;
	vlan->hw = hw;

	if (!rc) {
		list_add(&vlan->link, &bp->vlan_reg);
	} else {
		bp->vlan_cnt--;
		kfree(vlan);
	}
	vlan->hw = false;
	list_add_tail(&vlan->link, &bp->vlan_reg);

	DP(NETIF_MSG_IFUP, "Adding VLAN result %d\n", rc);
	if (netif_running(dev))
		bnx2x_vlan_configure(bp, true);

	return rc;
	return 0;
}

static int bnx2x_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
{
	struct bnx2x *bp = netdev_priv(dev);
	struct bnx2x_vlan_entry *vlan;
	bool found = false;
	int rc = 0;

	if (!netif_running(bp->dev)) {
		DP(NETIF_MSG_IFUP,
		   "Ignoring VLAN configuration the interface is down\n");
		return -EFAULT;
	}

	DP(NETIF_MSG_IFUP, "Removing VLAN %d\n", vid);

	if (!bp->vlan_cnt) {
		BNX2X_ERR("Unable to kill VLAN %d\n", vid);
		return -EINVAL;
	}

	list_for_each_entry(vlan, &bp->vlan_reg, link)
		if (vlan->vid == vid)
		if (vlan->vid == vid) {
			found = true;
			break;
		}

	if (vlan->vid != vid) {
	if (!found) {
		BNX2X_ERR("Unable to kill VLAN %d - not found\n", vid);
		return -EINVAL;
	}

	if (vlan->hw)
	if (netif_running(dev) && vlan->hw) {
		rc = __bnx2x_vlan_configure_vid(bp, vid, false);
		DP(NETIF_MSG_IFUP, "HW deconfigured for VLAN %d\n", vid);
		bp->vlan_cnt--;
	}

	list_del(&vlan->link);
	kfree(vlan);

	bp->vlan_cnt--;

	if (bp->vlan_cnt <= bp->vlan_credit && bp->accept_any_vlan) {
		/* Configure all non-configured entries */
		list_for_each_entry(vlan, &bp->vlan_reg, link) {
			if (vlan->hw)
				continue;

			rc = __bnx2x_vlan_configure_vid(bp, vlan->vid, true);
			if (rc) {
				BNX2X_ERR("Unable to config VLAN %d\n",
					  vlan->vid);
				continue;
			}
			DP(NETIF_MSG_IFUP, "HW configured for VLAN %d\n",
			   vlan->vid);
			vlan->hw = true;
		}
		DP(NETIF_MSG_IFUP, "Accept all VLAN Removed\n");
		bp->accept_any_vlan = false;
		if (IS_PF(bp))
			bnx2x_set_rx_mode_inner(bp);
		else
			bnx2x_vfpf_storm_rx_mode(bp);
	}
	if (netif_running(dev))
		bnx2x_vlan_configure(bp, true);

	DP(NETIF_MSG_IFUP, "Removing VLAN result %d\n", rc);