mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-16 04:21:09 -04:00
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:
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user