8021q: delete cleared egress QoS mappings
vlan_dev_set_egress_priority() currently keeps cleared egress
priority mappings in the hash as tombstones. Repeated set/clear cycles
with distinct skb priorities therefore accumulate mapping nodes until
device teardown and leak memory.
Delete mappings when vlan_prio is cleared instead of keeping tombstones.
Now that the egress mapping lists are RCU protected, the node can be
unlinked safely and freed after a grace period.
Fixes: 1da177e4c3 ("Linux-2.6.12-rc2")
Cc: stable@kernel.org
Reported-by: Yifan Wu <yifanwucs@gmail.com>
Reported-by: Juefei Pu <tomapufckgml@gmail.com>
Reported-by: Xin Liu <bird@lzu.edu.cn>
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/ecfa6f6ce2467a42647ff4c5221238ae85b79a59.1776647968.git.yuantan098@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
master
parent
fc69decc81
commit
7dddc74af3
|
|
@ -172,26 +172,34 @@ int vlan_dev_set_egress_priority(const struct net_device *dev,
|
||||||
u32 skb_prio, u16 vlan_prio)
|
u32 skb_prio, u16 vlan_prio)
|
||||||
{
|
{
|
||||||
struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
|
struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
|
||||||
|
struct vlan_priority_tci_mapping __rcu **mpp;
|
||||||
struct vlan_priority_tci_mapping *mp;
|
struct vlan_priority_tci_mapping *mp;
|
||||||
struct vlan_priority_tci_mapping *np;
|
struct vlan_priority_tci_mapping *np;
|
||||||
u32 bucket = skb_prio & 0xF;
|
u32 bucket = skb_prio & 0xF;
|
||||||
u32 vlan_qos = (vlan_prio << VLAN_PRIO_SHIFT) & VLAN_PRIO_MASK;
|
u32 vlan_qos = (vlan_prio << VLAN_PRIO_SHIFT) & VLAN_PRIO_MASK;
|
||||||
|
|
||||||
/* See if a priority mapping exists.. */
|
/* See if a priority mapping exists.. */
|
||||||
mp = rtnl_dereference(vlan->egress_priority_map[bucket]);
|
mpp = &vlan->egress_priority_map[bucket];
|
||||||
|
mp = rtnl_dereference(*mpp);
|
||||||
while (mp) {
|
while (mp) {
|
||||||
if (mp->priority == skb_prio) {
|
if (mp->priority == skb_prio) {
|
||||||
if (mp->vlan_qos && !vlan_qos)
|
if (!vlan_qos) {
|
||||||
|
rcu_assign_pointer(*mpp, rtnl_dereference(mp->next));
|
||||||
vlan->nr_egress_mappings--;
|
vlan->nr_egress_mappings--;
|
||||||
else if (!mp->vlan_qos && vlan_qos)
|
kfree_rcu(mp, rcu);
|
||||||
vlan->nr_egress_mappings++;
|
} else {
|
||||||
WRITE_ONCE(mp->vlan_qos, vlan_qos);
|
WRITE_ONCE(mp->vlan_qos, vlan_qos);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
mp = rtnl_dereference(mp->next);
|
mpp = &mp->next;
|
||||||
|
mp = rtnl_dereference(*mpp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create a new mapping then. */
|
/* Create a new mapping then. */
|
||||||
|
if (!vlan_qos)
|
||||||
|
return 0;
|
||||||
|
|
||||||
np = kmalloc_obj(struct vlan_priority_tci_mapping);
|
np = kmalloc_obj(struct vlan_priority_tci_mapping);
|
||||||
if (!np)
|
if (!np)
|
||||||
return -ENOBUFS;
|
return -ENOBUFS;
|
||||||
|
|
|
||||||
|
|
@ -263,10 +263,6 @@ static int vlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
|
||||||
for (pm = rcu_dereference_rtnl(vlan->egress_priority_map[i]); pm;
|
for (pm = rcu_dereference_rtnl(vlan->egress_priority_map[i]); pm;
|
||||||
pm = rcu_dereference_rtnl(pm->next)) {
|
pm = rcu_dereference_rtnl(pm->next)) {
|
||||||
u16 vlan_qos = READ_ONCE(pm->vlan_qos);
|
u16 vlan_qos = READ_ONCE(pm->vlan_qos);
|
||||||
|
|
||||||
if (!vlan_qos)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
m.from = pm->priority;
|
m.from = pm->priority;
|
||||||
m.to = (vlan_qos >> 13) & 0x7;
|
m.to = (vlan_qos >> 13) & 0x7;
|
||||||
if (nla_put(skb, IFLA_VLAN_QOS_MAPPING,
|
if (nla_put(skb, IFLA_VLAN_QOS_MAPPING,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue