c1aa8fda822204de2d33b7358ad73f8d6a10013f
[openwrt/staging/mans0n.git] /
1 From c4bb76a9a0ef87c4cc1f636defed5f12deb9f5a7 Mon Sep 17 00:00:00 2001
2 From: Vladimir Oltean <vladimir.oltean@nxp.com>
3 Date: Wed, 6 Jan 2021 11:51:32 +0200
4 Subject: [PATCH] net: dsa: don't use switchdev_notifier_fdb_info in
5 dsa_switchdev_event_work
6
7 Currently DSA doesn't add FDB entries on the CPU port, because it only
8 does so through switchdev, which is associated with a net_device, and
9 there are none of those for the CPU port.
10
11 But actually FDB addresses on the CPU port have some use cases of their
12 own, if the switchdev operations are initiated from within the DSA
13 layer. There is just one problem with the existing code: it passes a
14 structure in dsa_switchdev_event_work which was retrieved directly from
15 switchdev, so it contains a net_device. We need to generalize the
16 contents to something that covers the CPU port as well: the "ds, port"
17 tuple is fine for that.
18
19 Note that the new procedure for notifying the successful FDB offload is
20 inspired from the rocker model.
21
22 Also, nothing was being done if added_by_user was false. Let's check for
23 that a lot earlier, and don't actually bother to schedule the worker
24 for nothing.
25
26 Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
27 Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
28 Signed-off-by: Jakub Kicinski <kuba@kernel.org>
29 ---
30 net/dsa/dsa_priv.h | 12 +++++
31 net/dsa/slave.c | 106 ++++++++++++++++++++++-----------------------
32 2 files changed, 65 insertions(+), 53 deletions(-)
33
34 --- a/net/dsa/dsa_priv.h
35 +++ b/net/dsa/dsa_priv.h
36 @@ -73,6 +73,18 @@ struct dsa_notifier_mtu_info {
37 int mtu;
38 };
39
40 +struct dsa_switchdev_event_work {
41 + struct dsa_switch *ds;
42 + int port;
43 + struct work_struct work;
44 + unsigned long event;
45 + /* Specific for SWITCHDEV_FDB_ADD_TO_DEVICE and
46 + * SWITCHDEV_FDB_DEL_TO_DEVICE
47 + */
48 + unsigned char addr[ETH_ALEN];
49 + u16 vid;
50 +};
51 +
52 struct dsa_slave_priv {
53 /* Copy of CPU port xmit for faster access in slave transmit hot path */
54 struct sk_buff * (*xmit)(struct sk_buff *skb,
55 --- a/net/dsa/slave.c
56 +++ b/net/dsa/slave.c
57 @@ -2087,76 +2087,66 @@ static int dsa_slave_netdevice_event(str
58 return NOTIFY_DONE;
59 }
60
61 -struct dsa_switchdev_event_work {
62 - struct work_struct work;
63 - struct switchdev_notifier_fdb_info fdb_info;
64 - struct net_device *dev;
65 - unsigned long event;
66 -};
67 +static void
68 +dsa_fdb_offload_notify(struct dsa_switchdev_event_work *switchdev_work)
69 +{
70 + struct dsa_switch *ds = switchdev_work->ds;
71 + struct switchdev_notifier_fdb_info info;
72 + struct dsa_port *dp;
73 +
74 + if (!dsa_is_user_port(ds, switchdev_work->port))
75 + return;
76 +
77 + info.addr = switchdev_work->addr;
78 + info.vid = switchdev_work->vid;
79 + info.offloaded = true;
80 + dp = dsa_to_port(ds, switchdev_work->port);
81 + call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED,
82 + dp->slave, &info.info, NULL);
83 +}
84
85 static void dsa_slave_switchdev_event_work(struct work_struct *work)
86 {
87 struct dsa_switchdev_event_work *switchdev_work =
88 container_of(work, struct dsa_switchdev_event_work, work);
89 - struct net_device *dev = switchdev_work->dev;
90 - struct switchdev_notifier_fdb_info *fdb_info;
91 - struct dsa_port *dp = dsa_slave_to_port(dev);
92 + struct dsa_switch *ds = switchdev_work->ds;
93 + struct dsa_port *dp;
94 int err;
95
96 + dp = dsa_to_port(ds, switchdev_work->port);
97 +
98 rtnl_lock();
99 switch (switchdev_work->event) {
100 case SWITCHDEV_FDB_ADD_TO_DEVICE:
101 - fdb_info = &switchdev_work->fdb_info;
102 - if (!fdb_info->added_by_user)
103 - break;
104 -
105 - err = dsa_port_fdb_add(dp, fdb_info->addr, fdb_info->vid);
106 + err = dsa_port_fdb_add(dp, switchdev_work->addr,
107 + switchdev_work->vid);
108 if (err) {
109 - netdev_err(dev,
110 - "failed to add %pM vid %d to fdb: %d\n",
111 - fdb_info->addr, fdb_info->vid, err);
112 + dev_err(ds->dev,
113 + "port %d failed to add %pM vid %d to fdb: %d\n",
114 + dp->index, switchdev_work->addr,
115 + switchdev_work->vid, err);
116 break;
117 }
118 - fdb_info->offloaded = true;
119 - call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, dev,
120 - &fdb_info->info, NULL);
121 + dsa_fdb_offload_notify(switchdev_work);
122 break;
123
124 case SWITCHDEV_FDB_DEL_TO_DEVICE:
125 - fdb_info = &switchdev_work->fdb_info;
126 - if (!fdb_info->added_by_user)
127 - break;
128 -
129 - err = dsa_port_fdb_del(dp, fdb_info->addr, fdb_info->vid);
130 + err = dsa_port_fdb_del(dp, switchdev_work->addr,
131 + switchdev_work->vid);
132 if (err) {
133 - netdev_err(dev,
134 - "failed to delete %pM vid %d from fdb: %d\n",
135 - fdb_info->addr, fdb_info->vid, err);
136 + dev_err(ds->dev,
137 + "port %d failed to delete %pM vid %d from fdb: %d\n",
138 + dp->index, switchdev_work->addr,
139 + switchdev_work->vid, err);
140 }
141
142 break;
143 }
144 rtnl_unlock();
145
146 - kfree(switchdev_work->fdb_info.addr);
147 kfree(switchdev_work);
148 - dev_put(dev);
149 -}
150 -
151 -static int
152 -dsa_slave_switchdev_fdb_work_init(struct dsa_switchdev_event_work *
153 - switchdev_work,
154 - const struct switchdev_notifier_fdb_info *
155 - fdb_info)
156 -{
157 - memcpy(&switchdev_work->fdb_info, fdb_info,
158 - sizeof(switchdev_work->fdb_info));
159 - switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
160 - if (!switchdev_work->fdb_info.addr)
161 - return -ENOMEM;
162 - ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
163 - fdb_info->addr);
164 - return 0;
165 + if (dsa_is_user_port(ds, dp->index))
166 + dev_put(dp->slave);
167 }
168
169 /* Called under rcu_read_lock() */
170 @@ -2164,7 +2154,9 @@ static int dsa_slave_switchdev_event(str
171 unsigned long event, void *ptr)
172 {
173 struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
174 + const struct switchdev_notifier_fdb_info *fdb_info;
175 struct dsa_switchdev_event_work *switchdev_work;
176 + struct dsa_port *dp;
177 int err;
178
179 if (event == SWITCHDEV_PORT_ATTR_SET) {
180 @@ -2177,20 +2169,32 @@ static int dsa_slave_switchdev_event(str
181 if (!dsa_slave_dev_check(dev))
182 return NOTIFY_DONE;
183
184 + dp = dsa_slave_to_port(dev);
185 +
186 switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
187 if (!switchdev_work)
188 return NOTIFY_BAD;
189
190 INIT_WORK(&switchdev_work->work,
191 dsa_slave_switchdev_event_work);
192 - switchdev_work->dev = dev;
193 + switchdev_work->ds = dp->ds;
194 + switchdev_work->port = dp->index;
195 switchdev_work->event = event;
196
197 switch (event) {
198 case SWITCHDEV_FDB_ADD_TO_DEVICE:
199 case SWITCHDEV_FDB_DEL_TO_DEVICE:
200 - if (dsa_slave_switchdev_fdb_work_init(switchdev_work, ptr))
201 - goto err_fdb_work_init;
202 + fdb_info = ptr;
203 +
204 + if (!fdb_info->added_by_user) {
205 + kfree(switchdev_work);
206 + return NOTIFY_OK;
207 + }
208 +
209 + ether_addr_copy(switchdev_work->addr,
210 + fdb_info->addr);
211 + switchdev_work->vid = fdb_info->vid;
212 +
213 dev_hold(dev);
214 break;
215 default:
216 @@ -2200,10 +2204,6 @@ static int dsa_slave_switchdev_event(str
217
218 dsa_schedule_work(&switchdev_work->work);
219 return NOTIFY_OK;
220 -
221 -err_fdb_work_init:
222 - kfree(switchdev_work);
223 - return NOTIFY_BAD;
224 }
225
226 static int dsa_slave_switchdev_blocking_event(struct notifier_block *unused,