Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F171312963
D5829.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
5 KB
Referenced Files
None
Subscribers
None
D5829.id.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D5829: Defer vlan register/deregister operations in mlxen.
Attached
Detach File
Event Timeline
Log In to Comment