Page MenuHomeFreeBSD

D5829.id.diff
No OneTemporary

D5829.id.diff

Index: sys/ofed/drivers/net/mlx4/en_netdev.c
===================================================================
--- sys/ofed/drivers/net/mlx4/en_netdev.c
+++ sys/ofed/drivers/net/mlx4/en_netdev.c
@@ -451,6 +451,107 @@
}
+/*
+ * When VLANs are added/removed, the VLAN ioctl path acquires a non-sleepable
+ * rmlock. As part of this ioctl path, a hook for the underlying device driver
+ * is called so the card can possibly register the vlan with the controller.
+ * The mlx4 hook acquires a per-port state Linux mutex, which is a sxlock on
+ * FreeBSD. This can sleep, and if it does it will cause a panic. This avoids
+ * that panic by utilizing mlx4's per-port workqueue (which is backed by a
+ * FreeBSD taskqueue) to defer all add/remove operations.
+ * Note: In the time between a remove ioctl and when the remove actually
+ * happens, the tagged packets will still be seen by bpf listeners.
+ */
+static void mlx4_en_add_kill_vlans(struct mlx4_en_priv *priv)
+{
+ u16 vid;
+ long *pending_adds = &priv->pending_vlan_adds[0];
+ long *pending_kills = &priv->pending_vlan_kills[0];
+ while (1) {
+ spin_lock(&priv->pending_vlan_lock);
+ vid = find_first_bit(pending_adds, MLX4_VLAN_PENDING_SIZE);
+ if (vid >= MLX4_VLAN_PENDING_SIZE) {
+ spin_unlock(&priv->pending_vlan_lock);
+ break;
+ }
+ clear_bit(vid, pending_adds);
+ spin_unlock(&priv->pending_vlan_lock);
+ mlx4_en_vlan_rx_add_vid(priv, priv->dev, vid);
+ }
+ while (1) {
+ spin_lock(&priv->pending_vlan_lock);
+ vid = find_first_bit(pending_kills, MLX4_VLAN_PENDING_SIZE);
+ if (vid >= MLX4_VLAN_PENDING_SIZE) {
+ spin_unlock(&priv->pending_vlan_lock);
+ break;
+ }
+ clear_bit(vid, pending_kills);
+ spin_unlock(&priv->pending_vlan_lock);
+ mlx4_en_vlan_rx_kill_vid(priv, priv->dev, vid);
+ }
+}
+
+static void mlx4_en_vlan_vid_fn(struct work_struct *work)
+{
+ struct mlx4_en_priv *priv;
+ priv = container_of(work, struct mlx4_en_priv, vid_task);
+ mlx4_en_add_kill_vlans(priv);
+}
+
+static void mlx4_en_vlan_add_vid(void *arg, struct net_device *dev, u16 vid)
+{
+ struct mlx4_en_priv *priv = netdev_priv(dev);
+ /*
+ * Since the vlan_config handler is registered for both ports it will
+ * fire twice. This check ensures that we only actually do the add for
+ * the port that triggered the event.
+ */
+ if (arg != priv)
+ return;
+ spin_lock(&priv->pending_vlan_lock);
+ long *pending_adds = &priv->pending_vlan_adds[0];
+ long *pending_kills = &priv->pending_vlan_kills[0];
+ if (test_bit(vid, pending_kills)) {
+ /*
+ * If the vlan is going to be killed and re-added, there's
+ * nothing to do.
+ */
+ clear_bit(vid, pending_kills);
+ spin_unlock(&priv->pending_vlan_lock);
+ return;
+ }
+ set_bit(vid, pending_adds);
+ spin_unlock(&priv->pending_vlan_lock);
+ queue_work(priv->mdev->workqueue, &priv->vid_task);
+}
+
+static void mlx4_en_vlan_kill_vid(void *arg, struct net_device *dev, u16 vid)
+{
+ struct mlx4_en_priv *priv = netdev_priv(dev);
+ /*
+ * Since the vlan_unconfig handler is registered for both ports it will
+ * fire twice. This check ensures that we only actually do the kill for
+ * the port that triggered the event.
+ */
+ if (arg != priv)
+ return;
+ spin_lock(&priv->pending_vlan_lock);
+ long *pending_adds = &priv->pending_vlan_adds[0];
+ long *pending_kills = &priv->pending_vlan_kills[0];
+ if (test_bit(vid, pending_adds)) {
+ /*
+ * If the vlan is going to be added and re-killed, there's
+ * nothing to do.
+ */
+ clear_bit(vid, pending_adds);
+ spin_unlock(&priv->pending_vlan_lock);
+ return;
+ }
+ set_bit(vid, pending_kills);
+ spin_unlock(&priv->pending_vlan_lock);
+ queue_work(priv->mdev->workqueue, &priv->vid_task);
+}
+
static int mlx4_en_uc_steer_add(struct mlx4_en_priv *priv,
unsigned char *mac, int *qpn, u64 *reg_id)
{
@@ -2082,6 +2183,11 @@
spin_lock_init(&priv->filters_lock);
#endif
+ INIT_WORK(&priv->vid_task, mlx4_en_vlan_vid_fn);
+ spin_lock_init(&priv->pending_vlan_lock);
+ memset(&priv->pending_vlan_adds, 0, sizeof(priv->pending_vlan_adds));
+ memset(&priv->pending_vlan_kills, 0, sizeof(priv->pending_vlan_kills));
+
priv->msg_enable = MLX4_EN_MSG_LEVEL;
priv->dev = dev;
priv->mdev = mdev;
@@ -2193,9 +2299,9 @@
/* Register for VLAN events */
priv->vlan_attach = EVENTHANDLER_REGISTER(vlan_config,
- mlx4_en_vlan_rx_add_vid, priv, EVENTHANDLER_PRI_FIRST);
+ mlx4_en_vlan_add_vid, priv, EVENTHANDLER_PRI_FIRST);
priv->vlan_detach = EVENTHANDLER_REGISTER(vlan_unconfig,
- mlx4_en_vlan_rx_kill_vid, priv, EVENTHANDLER_PRI_FIRST);
+ mlx4_en_vlan_kill_vid, priv, EVENTHANDLER_PRI_FIRST);
mdev->pndev[priv->port] = dev;
Index: sys/ofed/drivers/net/mlx4/mlx4_en.h
===================================================================
--- sys/ofed/drivers/net/mlx4/mlx4_en.h
+++ sys/ofed/drivers/net/mlx4/mlx4_en.h
@@ -480,6 +480,7 @@
#endif
+#define MLX4_VLAN_PENDING_SIZE (NBLONG * BITS_TO_LONGS(VLAN_N_VID))
enum {
MLX4_EN_FLAG_PROMISC = (1 << 0),
@@ -570,6 +571,7 @@
struct work_struct linkstate_task;
struct delayed_work stats_task;
struct delayed_work service_task;
+ struct work_struct vid_task;
struct mlx4_en_perf_stats pstats;
struct mlx4_en_pkt_stats pkstats;
struct mlx4_en_pkt_stats pkstats_last;
@@ -583,6 +585,9 @@
u64 broadcast_id;
struct mlx4_en_stat_out_mbox hw_stats;
int vids[128];
+ long pending_vlan_adds[BITS_TO_LONGS(VLAN_N_VID)];
+ long pending_vlan_kills[BITS_TO_LONGS(VLAN_N_VID)];
+ spinlock_t pending_vlan_lock;
bool wol;
struct device *ddev;
struct dentry *dev_root;

File Metadata

Mime Type
text/plain
Expires
Fri, Sep 11, 8:20 AM (6 h, 49 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
38592045
Default Alt Text
D5829.id.diff (5 KB)

Event Timeline