]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
8021q: use RCU for egress QoS mappings
authorLongxuan Yu <ylong030@ucr.edu>
Mon, 20 Apr 2026 03:18:45 +0000 (11:18 +0800)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 23 Apr 2026 10:13:57 +0000 (12:13 +0200)
The TX fast path and reporting paths walk egress QoS mappings without
RTNL. Convert the mapping lists to RCU-protected pointers, use RCU
reader annotations in readers, and defer freeing mapping nodes with an
embedded rcu_head.

This prepares the egress QoS mapping code for safe removal of mapping
nodes in a follow-up change while preserving the current behavior.

Co-developed-by: Yuan Tan <yuantan098@gmail.com>
Signed-off-by: Yuan Tan <yuantan098@gmail.com>
Signed-off-by: Longxuan Yu <ylong030@ucr.edu>
Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
Link: https://patch.msgid.link/9136768189f8c6d3f824f476c62d2fa1111688e8.1776647968.git.yuantan098@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
include/linux/if_vlan.h
net/8021q/vlan_dev.c
net/8021q/vlan_netlink.c
net/8021q/vlanproc.c

index e6272f9c5e42cc8023e8ece45e5c7a6eed5338e0..20cc16ea4e5abca92dd74a9602dc49271c438b60 100644 (file)
@@ -147,11 +147,13 @@ extern __be16 vlan_dev_vlan_proto(const struct net_device *dev);
  *     @priority: skb priority
  *     @vlan_qos: vlan priority: (skb->priority << 13) & 0xE000
  *     @next: pointer to next struct
+ *     @rcu: used for deferred freeing of mapping nodes
  */
 struct vlan_priority_tci_mapping {
        u32                                     priority;
        u16                                     vlan_qos;
-       struct vlan_priority_tci_mapping        *next;
+       struct vlan_priority_tci_mapping __rcu  *next;
+       struct rcu_head                 rcu;
 };
 
 struct proc_dir_entry;
@@ -177,7 +179,7 @@ struct vlan_dev_priv {
        unsigned int                            nr_ingress_mappings;
        u32                                     ingress_priority_map[8];
        unsigned int                            nr_egress_mappings;
-       struct vlan_priority_tci_mapping        *egress_priority_map[16];
+       struct vlan_priority_tci_mapping __rcu  *egress_priority_map[16];
 
        __be16                                  vlan_proto;
        u16                                     vlan_id;
@@ -209,19 +211,24 @@ static inline u16
 vlan_dev_get_egress_qos_mask(struct net_device *dev, u32 skprio)
 {
        struct vlan_priority_tci_mapping *mp;
+       u16 vlan_qos = 0;
 
-       smp_rmb(); /* coupled with smp_wmb() in vlan_dev_set_egress_priority() */
+       rcu_read_lock();
 
-       mp = vlan_dev_priv(dev)->egress_priority_map[(skprio & 0xF)];
+       mp = rcu_dereference(vlan_dev_priv(dev)->egress_priority_map[skprio & 0xF]);
        while (mp) {
                if (mp->priority == skprio) {
-                       return mp->vlan_qos; /* This should already be shifted
-                                             * to mask correctly with the
-                                             * VLAN's TCI */
+                       vlan_qos = READ_ONCE(mp->vlan_qos);
+                       break;
                }
-               mp = mp->next;
+               mp = rcu_dereference(mp->next);
        }
-       return 0;
+       rcu_read_unlock();
+
+       /* This should already be shifted to mask correctly with
+        * the VLAN's TCI.
+        */
+       return vlan_qos;
 }
 
 extern bool vlan_do_receive(struct sk_buff **skb);
index c40f7d5c4fca589bec15deb3e07554028f5bdce1..a5340932b657a6a040005664db8063ffa850976a 100644 (file)
@@ -172,39 +172,34 @@ int vlan_dev_set_egress_priority(const struct net_device *dev,
                                 u32 skb_prio, u16 vlan_prio)
 {
        struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
-       struct vlan_priority_tci_mapping *mp = NULL;
+       struct vlan_priority_tci_mapping *mp;
        struct vlan_priority_tci_mapping *np;
+       u32 bucket = skb_prio & 0xF;
        u32 vlan_qos = (vlan_prio << VLAN_PRIO_SHIFT) & VLAN_PRIO_MASK;
 
        /* See if a priority mapping exists.. */
-       mp = vlan->egress_priority_map[skb_prio & 0xF];
+       mp = rtnl_dereference(vlan->egress_priority_map[bucket]);
        while (mp) {
                if (mp->priority == skb_prio) {
                        if (mp->vlan_qos && !vlan_qos)
                                vlan->nr_egress_mappings--;
                        else if (!mp->vlan_qos && vlan_qos)
                                vlan->nr_egress_mappings++;
-                       mp->vlan_qos = vlan_qos;
+                       WRITE_ONCE(mp->vlan_qos, vlan_qos);
                        return 0;
                }
-               mp = mp->next;
+               mp = rtnl_dereference(mp->next);
        }
 
        /* Create a new mapping then. */
-       mp = vlan->egress_priority_map[skb_prio & 0xF];
        np = kmalloc_obj(struct vlan_priority_tci_mapping);
        if (!np)
                return -ENOBUFS;
 
-       np->next = mp;
        np->priority = skb_prio;
        np->vlan_qos = vlan_qos;
-       /* Before inserting this element in hash table, make sure all its fields
-        * are committed to memory.
-        * coupled with smp_rmb() in vlan_dev_get_egress_qos_mask()
-        */
-       smp_wmb();
-       vlan->egress_priority_map[skb_prio & 0xF] = np;
+       RCU_INIT_POINTER(np->next, rtnl_dereference(vlan->egress_priority_map[bucket]));
+       rcu_assign_pointer(vlan->egress_priority_map[bucket], np);
        if (vlan_qos)
                vlan->nr_egress_mappings++;
        return 0;
@@ -604,11 +599,17 @@ void vlan_dev_free_egress_priority(const struct net_device *dev)
        int i;
 
        for (i = 0; i < ARRAY_SIZE(vlan->egress_priority_map); i++) {
-               while ((pm = vlan->egress_priority_map[i]) != NULL) {
-                       vlan->egress_priority_map[i] = pm->next;
-                       kfree(pm);
+               pm = rtnl_dereference(vlan->egress_priority_map[i]);
+               RCU_INIT_POINTER(vlan->egress_priority_map[i], NULL);
+               while (pm) {
+                       struct vlan_priority_tci_mapping *next;
+
+                       next = rtnl_dereference(pm->next);
+                       kfree_rcu(pm, rcu);
+                       pm = next;
                }
        }
+       vlan->nr_egress_mappings = 0;
 }
 
 static void vlan_dev_uninit(struct net_device *dev)
index a000b1ef05206a08c2e26657852a3b0c384fbe0f..a5b16833e2ceeb204e6602649aa457f2560dbf94 100644 (file)
@@ -260,13 +260,15 @@ static int vlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
                        goto nla_put_failure;
 
                for (i = 0; i < ARRAY_SIZE(vlan->egress_priority_map); i++) {
-                       for (pm = vlan->egress_priority_map[i]; pm;
-                            pm = pm->next) {
-                               if (!pm->vlan_qos)
+                       for (pm = rcu_dereference_rtnl(vlan->egress_priority_map[i]); pm;
+                            pm = rcu_dereference_rtnl(pm->next)) {
+                               u16 vlan_qos = READ_ONCE(pm->vlan_qos);
+
+                               if (!vlan_qos)
                                        continue;
 
                                m.from = pm->priority;
-                               m.to   = (pm->vlan_qos >> 13) & 0x7;
+                               m.to   = (vlan_qos >> 13) & 0x7;
                                if (nla_put(skb, IFLA_VLAN_QOS_MAPPING,
                                            sizeof(m), &m))
                                        goto nla_put_failure;
index fa67374bda494c343bd96b3df167f242918f6de9..0e424e0895b7e860138c89743c2646bcb83750cd 100644 (file)
@@ -262,15 +262,19 @@ static int vlandev_seq_show(struct seq_file *seq, void *offset)
                   vlan->ingress_priority_map[7]);
 
        seq_printf(seq, " EGRESS priority mappings: ");
+       rcu_read_lock();
        for (i = 0; i < 16; i++) {
-               const struct vlan_priority_tci_mapping *mp
-                       = vlan->egress_priority_map[i];
+               const struct vlan_priority_tci_mapping *mp =
+                       rcu_dereference(vlan->egress_priority_map[i]);
                while (mp) {
+                       u16 vlan_qos = READ_ONCE(mp->vlan_qos);
+
                        seq_printf(seq, "%u:%d ",
-                                  mp->priority, ((mp->vlan_qos >> 13) & 0x7));
-                       mp = mp->next;
+                                  mp->priority, ((vlan_qos >> 13) & 0x7));
+                       mp = rcu_dereference(mp->next);
                }
        }
+       rcu_read_unlock();
        seq_puts(seq, "\n");
 
        return 0;