8021q: use RCU for egress QoS mappings

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>
This commit is contained in:
Longxuan Yu
2026-04-20 11:18:45 +08:00
committed by Paolo Abeni
parent 5a5db99c34
commit fc69decc81
4 changed files with 46 additions and 32 deletions

View 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);

View 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)

View 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;

View 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;