Skip to content

Commit 9daae9b

Browse files
Gal Pressmandavem330
authored andcommitted
net: Call add/kill vid ndo on vlan filter feature toggling
NETIF_F_HW_VLAN_[CS]TAG_FILTER features require more than just a bit flip in dev->features in order to keep the driver in a consistent state. These features notify the driver of each added/removed vlan, but toggling of vlan-filter does not notify the driver accordingly for each of the existing vlans. This patch implements a similar solution to NETIF_F_RX_UDP_TUNNEL_PORT behavior (which notifies the driver about UDP ports in the same manner that vids are reported). Each toggling of the features propagates to the 8021q module, which iterates over the vlans and call add/kill ndo accordingly. Signed-off-by: Gal Pressman <[email protected]> Reviewed-by: Tariq Toukan <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 004c3cf commit 9daae9b

File tree

6 files changed

+148
-25
lines changed

6 files changed

+148
-25
lines changed

include/linux/if_vlan.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,30 @@ static inline bool is_vlan_dev(const struct net_device *dev)
8383
#define skb_vlan_tag_get_id(__skb) ((__skb)->vlan_tci & VLAN_VID_MASK)
8484
#define skb_vlan_tag_get_prio(__skb) ((__skb)->vlan_tci & VLAN_PRIO_MASK)
8585

86+
static inline int vlan_get_rx_ctag_filter_info(struct net_device *dev)
87+
{
88+
ASSERT_RTNL();
89+
return notifier_to_errno(call_netdevice_notifiers(NETDEV_CVLAN_FILTER_PUSH_INFO, dev));
90+
}
91+
92+
static inline void vlan_drop_rx_ctag_filter_info(struct net_device *dev)
93+
{
94+
ASSERT_RTNL();
95+
call_netdevice_notifiers(NETDEV_CVLAN_FILTER_DROP_INFO, dev);
96+
}
97+
98+
static inline int vlan_get_rx_stag_filter_info(struct net_device *dev)
99+
{
100+
ASSERT_RTNL();
101+
return notifier_to_errno(call_netdevice_notifiers(NETDEV_SVLAN_FILTER_PUSH_INFO, dev));
102+
}
103+
104+
static inline void vlan_drop_rx_stag_filter_info(struct net_device *dev)
105+
{
106+
ASSERT_RTNL();
107+
call_netdevice_notifiers(NETDEV_SVLAN_FILTER_DROP_INFO, dev);
108+
}
109+
86110
/**
87111
* struct vlan_pcpu_stats - VLAN percpu rx/tx stats
88112
* @rx_packets: number of received packets

include/linux/netdevice.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2349,6 +2349,10 @@ enum netdev_cmd {
23492349
NETDEV_UDP_TUNNEL_PUSH_INFO,
23502350
NETDEV_UDP_TUNNEL_DROP_INFO,
23512351
NETDEV_CHANGE_TX_QUEUE_LEN,
2352+
NETDEV_CVLAN_FILTER_PUSH_INFO,
2353+
NETDEV_CVLAN_FILTER_DROP_INFO,
2354+
NETDEV_SVLAN_FILTER_PUSH_INFO,
2355+
NETDEV_SVLAN_FILTER_DROP_INFO,
23522356
};
23532357
const char *netdev_cmd_to_name(enum netdev_cmd cmd);
23542358

net/8021q/vlan.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
360360
struct vlan_dev_priv *vlan;
361361
bool last = false;
362362
LIST_HEAD(list);
363+
int err;
363364

364365
if (is_vlan_dev(dev)) {
365366
int err = __vlan_device_event(dev, event);
@@ -489,6 +490,26 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
489490
vlan_group_for_each_dev(grp, i, vlandev)
490491
call_netdevice_notifiers(event, vlandev);
491492
break;
493+
494+
case NETDEV_CVLAN_FILTER_PUSH_INFO:
495+
err = vlan_filter_push_vids(vlan_info, htons(ETH_P_8021Q));
496+
if (err)
497+
return notifier_from_errno(err);
498+
break;
499+
500+
case NETDEV_CVLAN_FILTER_DROP_INFO:
501+
vlan_filter_drop_vids(vlan_info, htons(ETH_P_8021Q));
502+
break;
503+
504+
case NETDEV_SVLAN_FILTER_PUSH_INFO:
505+
err = vlan_filter_push_vids(vlan_info, htons(ETH_P_8021AD));
506+
if (err)
507+
return notifier_from_errno(err);
508+
break;
509+
510+
case NETDEV_SVLAN_FILTER_DROP_INFO:
511+
vlan_filter_drop_vids(vlan_info, htons(ETH_P_8021AD));
512+
break;
492513
}
493514

494515
out:

net/8021q/vlan.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ static inline struct net_device *vlan_find_dev(struct net_device *real_dev,
9797
if (((dev) = __vlan_group_get_device((grp), (i) / VLAN_N_VID, \
9898
(i) % VLAN_N_VID)))
9999

100+
int vlan_filter_push_vids(struct vlan_info *vlan_info, __be16 proto);
101+
void vlan_filter_drop_vids(struct vlan_info *vlan_info, __be16 proto);
102+
100103
/* found in vlan_dev.c */
101104
void vlan_dev_set_ingress_priority(const struct net_device *dev,
102105
u32 skb_prio, u16 vlan_prio);

net/8021q/vlan_core.c

Lines changed: 76 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -165,13 +165,12 @@ struct vlan_vid_info {
165165
int refcount;
166166
};
167167

168-
static bool vlan_hw_filter_capable(const struct net_device *dev,
169-
const struct vlan_vid_info *vid_info)
168+
bool vlan_hw_filter_capable(const struct net_device *dev, __be16 proto)
170169
{
171-
if (vid_info->proto == htons(ETH_P_8021Q) &&
170+
if (proto == htons(ETH_P_8021Q) &&
172171
dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)
173172
return true;
174-
if (vid_info->proto == htons(ETH_P_8021AD) &&
173+
if (proto == htons(ETH_P_8021AD) &&
175174
dev->features & NETIF_F_HW_VLAN_STAG_FILTER)
176175
return true;
177176
return false;
@@ -202,28 +201,86 @@ static struct vlan_vid_info *vlan_vid_info_alloc(__be16 proto, u16 vid)
202201
return vid_info;
203202
}
204203

204+
static int vlan_add_rx_filter_info(struct net_device *dev, __be16 proto, u16 vid)
205+
{
206+
if (!vlan_hw_filter_capable(dev, proto))
207+
return 0;
208+
209+
if (netif_device_present(dev))
210+
return dev->netdev_ops->ndo_vlan_rx_add_vid(dev, proto, vid);
211+
else
212+
return -ENODEV;
213+
}
214+
215+
static int vlan_kill_rx_filter_info(struct net_device *dev, __be16 proto, u16 vid)
216+
{
217+
if (!vlan_hw_filter_capable(dev, proto))
218+
return 0;
219+
220+
if (netif_device_present(dev))
221+
return dev->netdev_ops->ndo_vlan_rx_kill_vid(dev, proto, vid);
222+
else
223+
return -ENODEV;
224+
}
225+
226+
int vlan_filter_push_vids(struct vlan_info *vlan_info, __be16 proto)
227+
{
228+
struct net_device *real_dev = vlan_info->real_dev;
229+
struct vlan_vid_info *vlan_vid_info;
230+
int err;
231+
232+
list_for_each_entry(vlan_vid_info, &vlan_info->vid_list, list) {
233+
if (vlan_vid_info->proto == proto) {
234+
err = vlan_add_rx_filter_info(real_dev, proto,
235+
vlan_vid_info->vid);
236+
if (err)
237+
goto unwind;
238+
}
239+
}
240+
241+
return 0;
242+
243+
unwind:
244+
list_for_each_entry_continue_reverse(vlan_vid_info,
245+
&vlan_info->vid_list, list) {
246+
if (vlan_vid_info->proto == proto)
247+
vlan_kill_rx_filter_info(real_dev, proto,
248+
vlan_vid_info->vid);
249+
}
250+
251+
return err;
252+
}
253+
EXPORT_SYMBOL(vlan_filter_push_vids);
254+
255+
void vlan_filter_drop_vids(struct vlan_info *vlan_info, __be16 proto)
256+
{
257+
struct vlan_vid_info *vlan_vid_info;
258+
259+
list_for_each_entry(vlan_vid_info, &vlan_info->vid_list, list)
260+
if (vlan_vid_info->proto == proto)
261+
vlan_kill_rx_filter_info(vlan_info->real_dev,
262+
vlan_vid_info->proto,
263+
vlan_vid_info->vid);
264+
}
265+
EXPORT_SYMBOL(vlan_filter_drop_vids);
266+
205267
static int __vlan_vid_add(struct vlan_info *vlan_info, __be16 proto, u16 vid,
206268
struct vlan_vid_info **pvid_info)
207269
{
208270
struct net_device *dev = vlan_info->real_dev;
209-
const struct net_device_ops *ops = dev->netdev_ops;
210271
struct vlan_vid_info *vid_info;
211272
int err;
212273

213274
vid_info = vlan_vid_info_alloc(proto, vid);
214275
if (!vid_info)
215276
return -ENOMEM;
216277

217-
if (vlan_hw_filter_capable(dev, vid_info)) {
218-
if (netif_device_present(dev))
219-
err = ops->ndo_vlan_rx_add_vid(dev, proto, vid);
220-
else
221-
err = -ENODEV;
222-
if (err) {
223-
kfree(vid_info);
224-
return err;
225-
}
278+
err = vlan_add_rx_filter_info(dev, proto, vid);
279+
if (err) {
280+
kfree(vid_info);
281+
return err;
226282
}
283+
227284
list_add(&vid_info->list, &vlan_info->vid_list);
228285
vlan_info->nr_vids++;
229286
*pvid_info = vid_info;
@@ -270,21 +327,15 @@ static void __vlan_vid_del(struct vlan_info *vlan_info,
270327
struct vlan_vid_info *vid_info)
271328
{
272329
struct net_device *dev = vlan_info->real_dev;
273-
const struct net_device_ops *ops = dev->netdev_ops;
274330
__be16 proto = vid_info->proto;
275331
u16 vid = vid_info->vid;
276332
int err;
277333

278-
if (vlan_hw_filter_capable(dev, vid_info)) {
279-
if (netif_device_present(dev))
280-
err = ops->ndo_vlan_rx_kill_vid(dev, proto, vid);
281-
else
282-
err = -ENODEV;
283-
if (err) {
284-
pr_warn("failed to kill vid %04x/%d for device %s\n",
285-
proto, vid, dev->name);
286-
}
287-
}
334+
err = vlan_kill_rx_filter_info(dev, proto, vid);
335+
if (err)
336+
pr_warn("failed to kill vid %04x/%d for device %s\n",
337+
proto, vid, dev->name);
338+
288339
list_del(&vid_info->list);
289340
kfree(vid_info);
290341
vlan_info->nr_vids--;

net/core/dev.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1584,6 +1584,8 @@ const char *netdev_cmd_to_name(enum netdev_cmd cmd)
15841584
N(RESEND_IGMP) N(PRECHANGEMTU) N(CHANGEINFODATA) N(BONDING_INFO)
15851585
N(PRECHANGEUPPER) N(CHANGELOWERSTATE) N(UDP_TUNNEL_PUSH_INFO)
15861586
N(UDP_TUNNEL_DROP_INFO) N(CHANGE_TX_QUEUE_LEN)
1587+
N(CVLAN_FILTER_PUSH_INFO) N(CVLAN_FILTER_DROP_INFO)
1588+
N(SVLAN_FILTER_PUSH_INFO) N(SVLAN_FILTER_DROP_INFO)
15871589
};
15881590
#undef N
15891591
return "UNKNOWN_NETDEV_EVENT";
@@ -7665,6 +7667,24 @@ int __netdev_update_features(struct net_device *dev)
76657667
}
76667668
}
76677669

7670+
if (diff & NETIF_F_HW_VLAN_CTAG_FILTER) {
7671+
if (features & NETIF_F_HW_VLAN_CTAG_FILTER) {
7672+
dev->features = features;
7673+
err |= vlan_get_rx_ctag_filter_info(dev);
7674+
} else {
7675+
vlan_drop_rx_ctag_filter_info(dev);
7676+
}
7677+
}
7678+
7679+
if (diff & NETIF_F_HW_VLAN_STAG_FILTER) {
7680+
if (features & NETIF_F_HW_VLAN_STAG_FILTER) {
7681+
dev->features = features;
7682+
err |= vlan_get_rx_stag_filter_info(dev);
7683+
} else {
7684+
vlan_drop_rx_stag_filter_info(dev);
7685+
}
7686+
}
7687+
76687688
dev->features = features;
76697689
}
76707690

0 commit comments

Comments
 (0)