Commit | Line | Data |
---|---|---|
9948a064 JP |
1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /* Copyright (c) 2018 Mellanox Technologies. All rights reserved */ | |
a629ef21 | 3 | |
946a11e7 | 4 | #include <linux/if_bridge.h> |
a629ef21 | 5 | #include <linux/list.h> |
ed04458d | 6 | #include <linux/mutex.h> |
4c00dafc | 7 | #include <linux/refcount.h> |
a8e7e6e7 IS |
8 | #include <linux/rtnetlink.h> |
9 | #include <linux/workqueue.h> | |
27cf76fe PM |
10 | #include <net/arp.h> |
11 | #include <net/gre.h> | |
b5de82f3 | 12 | #include <net/lag.h> |
8f08a528 PM |
13 | #include <net/ndisc.h> |
14 | #include <net/ip6_tunnel.h> | |
a629ef21 PM |
15 | |
16 | #include "spectrum.h" | |
27cf76fe | 17 | #include "spectrum_ipip.h" |
946a11e7 PM |
18 | #include "spectrum_span.h" |
19 | #include "spectrum_switchdev.h" | |
a629ef21 | 20 | |
9a9f8d1e | 21 | struct mlxsw_sp_span { |
a8e7e6e7 IS |
22 | struct work_struct work; |
23 | struct mlxsw_sp *mlxsw_sp; | |
08a3641f | 24 | const struct mlxsw_sp_span_trigger_ops **span_trigger_ops_arr; |
34e4ace5 IS |
25 | const struct mlxsw_sp_span_entry_ops **span_entry_ops_arr; |
26 | size_t span_entry_ops_arr_size; | |
ed04458d IS |
27 | struct list_head analyzed_ports_list; |
28 | struct mutex analyzed_ports_lock; /* Protects analyzed_ports_list */ | |
c056618c | 29 | struct list_head trigger_entries_list; |
4039504e IS |
30 | u16 policer_id_base; |
31 | refcount_t policer_id_base_ref_count; | |
eb833eec | 32 | atomic_t active_entries_count; |
9a9f8d1e | 33 | int entries_count; |
4780dbdb | 34 | struct mlxsw_sp_span_entry entries[]; |
9a9f8d1e IS |
35 | }; |
36 | ||
ed04458d IS |
37 | struct mlxsw_sp_span_analyzed_port { |
38 | struct list_head list; /* Member of analyzed_ports_list */ | |
39 | refcount_t ref_count; | |
c934757d | 40 | u16 local_port; |
ed04458d IS |
41 | bool ingress; |
42 | }; | |
43 | ||
c056618c IS |
44 | struct mlxsw_sp_span_trigger_entry { |
45 | struct list_head list; /* Member of trigger_entries_list */ | |
08a3641f IS |
46 | struct mlxsw_sp_span *span; |
47 | const struct mlxsw_sp_span_trigger_ops *ops; | |
c056618c | 48 | refcount_t ref_count; |
c934757d | 49 | u16 local_port; |
c056618c IS |
50 | enum mlxsw_sp_span_trigger trigger; |
51 | struct mlxsw_sp_span_trigger_parms parms; | |
52 | }; | |
53 | ||
08a3641f IS |
54 | enum mlxsw_sp_span_trigger_type { |
55 | MLXSW_SP_SPAN_TRIGGER_TYPE_PORT, | |
ab8c06b7 | 56 | MLXSW_SP_SPAN_TRIGGER_TYPE_GLOBAL, |
08a3641f IS |
57 | }; |
58 | ||
59 | struct mlxsw_sp_span_trigger_ops { | |
60 | int (*bind)(struct mlxsw_sp_span_trigger_entry *trigger_entry); | |
61 | void (*unbind)(struct mlxsw_sp_span_trigger_entry *trigger_entry); | |
62 | bool (*matches)(struct mlxsw_sp_span_trigger_entry *trigger_entry, | |
63 | enum mlxsw_sp_span_trigger trigger, | |
64 | struct mlxsw_sp_port *mlxsw_sp_port); | |
2bafb216 IS |
65 | int (*enable)(struct mlxsw_sp_span_trigger_entry *trigger_entry, |
66 | struct mlxsw_sp_port *mlxsw_sp_port, u8 tc); | |
67 | void (*disable)(struct mlxsw_sp_span_trigger_entry *trigger_entry, | |
68 | struct mlxsw_sp_port *mlxsw_sp_port, u8 tc); | |
08a3641f IS |
69 | }; |
70 | ||
a8e7e6e7 IS |
71 | static void mlxsw_sp_span_respin_work(struct work_struct *work); |
72 | ||
868678c5 DR |
73 | static u64 mlxsw_sp_span_occ_get(void *priv) |
74 | { | |
75 | const struct mlxsw_sp *mlxsw_sp = priv; | |
868678c5 | 76 | |
eb833eec | 77 | return atomic_read(&mlxsw_sp->span->active_entries_count); |
868678c5 DR |
78 | } |
79 | ||
a629ef21 PM |
80 | int mlxsw_sp_span_init(struct mlxsw_sp *mlxsw_sp) |
81 | { | |
868678c5 | 82 | struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); |
9a9f8d1e | 83 | struct mlxsw_sp_span *span; |
08a3641f | 84 | int i, entries_count, err; |
a629ef21 PM |
85 | |
86 | if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_SPAN)) | |
87 | return -EIO; | |
88 | ||
6627b93b IS |
89 | entries_count = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_SPAN); |
90 | span = kzalloc(struct_size(span, entries, entries_count), GFP_KERNEL); | |
9a9f8d1e | 91 | if (!span) |
a629ef21 | 92 | return -ENOMEM; |
4039504e | 93 | refcount_set(&span->policer_id_base_ref_count, 0); |
6627b93b | 94 | span->entries_count = entries_count; |
eb833eec | 95 | atomic_set(&span->active_entries_count, 0); |
ed04458d IS |
96 | mutex_init(&span->analyzed_ports_lock); |
97 | INIT_LIST_HEAD(&span->analyzed_ports_list); | |
c056618c | 98 | INIT_LIST_HEAD(&span->trigger_entries_list); |
a8e7e6e7 | 99 | span->mlxsw_sp = mlxsw_sp; |
9a9f8d1e IS |
100 | mlxsw_sp->span = span; |
101 | ||
ca089223 IS |
102 | for (i = 0; i < mlxsw_sp->span->entries_count; i++) |
103 | mlxsw_sp->span->entries[i].id = i; | |
a629ef21 | 104 | |
08a3641f IS |
105 | err = mlxsw_sp->span_ops->init(mlxsw_sp); |
106 | if (err) | |
107 | goto err_init; | |
108 | ||
868678c5 DR |
109 | devlink_resource_occ_get_register(devlink, MLXSW_SP_RESOURCE_SPAN, |
110 | mlxsw_sp_span_occ_get, mlxsw_sp); | |
a8e7e6e7 | 111 | INIT_WORK(&span->work, mlxsw_sp_span_respin_work); |
868678c5 | 112 | |
a629ef21 | 113 | return 0; |
08a3641f IS |
114 | |
115 | err_init: | |
116 | mutex_destroy(&mlxsw_sp->span->analyzed_ports_lock); | |
117 | kfree(mlxsw_sp->span); | |
118 | return err; | |
a629ef21 PM |
119 | } |
120 | ||
121 | void mlxsw_sp_span_fini(struct mlxsw_sp *mlxsw_sp) | |
122 | { | |
868678c5 | 123 | struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); |
a629ef21 | 124 | |
a8e7e6e7 | 125 | cancel_work_sync(&mlxsw_sp->span->work); |
868678c5 DR |
126 | devlink_resource_occ_get_unregister(devlink, MLXSW_SP_RESOURCE_SPAN); |
127 | ||
c056618c | 128 | WARN_ON_ONCE(!list_empty(&mlxsw_sp->span->trigger_entries_list)); |
ed04458d IS |
129 | WARN_ON_ONCE(!list_empty(&mlxsw_sp->span->analyzed_ports_list)); |
130 | mutex_destroy(&mlxsw_sp->span->analyzed_ports_lock); | |
9a9f8d1e | 131 | kfree(mlxsw_sp->span); |
a629ef21 PM |
132 | } |
133 | ||
fa8c08b8 IS |
134 | static bool mlxsw_sp1_span_cpu_can_handle(const struct net_device *dev) |
135 | { | |
136 | return !dev; | |
137 | } | |
138 | ||
139 | static int mlxsw_sp1_span_entry_cpu_parms(struct mlxsw_sp *mlxsw_sp, | |
140 | const struct net_device *to_dev, | |
141 | struct mlxsw_sp_span_parms *sparmsp) | |
142 | { | |
143 | return -EOPNOTSUPP; | |
144 | } | |
145 | ||
146 | static int | |
147 | mlxsw_sp1_span_entry_cpu_configure(struct mlxsw_sp_span_entry *span_entry, | |
148 | struct mlxsw_sp_span_parms sparms) | |
149 | { | |
150 | return -EOPNOTSUPP; | |
151 | } | |
152 | ||
153 | static void | |
154 | mlxsw_sp1_span_entry_cpu_deconfigure(struct mlxsw_sp_span_entry *span_entry) | |
155 | { | |
156 | } | |
157 | ||
158 | static const | |
159 | struct mlxsw_sp_span_entry_ops mlxsw_sp1_span_entry_ops_cpu = { | |
b6f6881a | 160 | .is_static = true, |
fa8c08b8 IS |
161 | .can_handle = mlxsw_sp1_span_cpu_can_handle, |
162 | .parms_set = mlxsw_sp1_span_entry_cpu_parms, | |
163 | .configure = mlxsw_sp1_span_entry_cpu_configure, | |
164 | .deconfigure = mlxsw_sp1_span_entry_cpu_deconfigure, | |
165 | }; | |
166 | ||
7b2ef81f | 167 | static int |
f4a626e2 IS |
168 | mlxsw_sp_span_entry_phys_parms(struct mlxsw_sp *mlxsw_sp, |
169 | const struct net_device *to_dev, | |
169b5d95 PM |
170 | struct mlxsw_sp_span_parms *sparmsp) |
171 | { | |
172 | sparmsp->dest_port = netdev_priv(to_dev); | |
173 | return 0; | |
174 | } | |
175 | ||
176 | static int | |
177 | mlxsw_sp_span_entry_phys_configure(struct mlxsw_sp_span_entry *span_entry, | |
178 | struct mlxsw_sp_span_parms sparms) | |
7b2ef81f | 179 | { |
169b5d95 PM |
180 | struct mlxsw_sp_port *dest_port = sparms.dest_port; |
181 | struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp; | |
c934757d | 182 | u16 local_port = dest_port->local_port; |
7b2ef81f PM |
183 | char mpat_pl[MLXSW_REG_MPAT_LEN]; |
184 | int pa_id = span_entry->id; | |
185 | ||
186 | /* Create a new port analayzer entry for local_port. */ | |
187 | mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true, | |
188 | MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH); | |
5c7659eb | 189 | mlxsw_reg_mpat_session_id_set(mpat_pl, sparms.session_id); |
4039504e IS |
190 | mlxsw_reg_mpat_pide_set(mpat_pl, sparms.policer_enable); |
191 | mlxsw_reg_mpat_pid_set(mpat_pl, sparms.policer_id); | |
169b5d95 | 192 | |
7b2ef81f PM |
193 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl); |
194 | } | |
195 | ||
196 | static void | |
169b5d95 PM |
197 | mlxsw_sp_span_entry_deconfigure_common(struct mlxsw_sp_span_entry *span_entry, |
198 | enum mlxsw_reg_mpat_span_type span_type) | |
7b2ef81f | 199 | { |
169b5d95 PM |
200 | struct mlxsw_sp_port *dest_port = span_entry->parms.dest_port; |
201 | struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp; | |
c934757d | 202 | u16 local_port = dest_port->local_port; |
7b2ef81f PM |
203 | char mpat_pl[MLXSW_REG_MPAT_LEN]; |
204 | int pa_id = span_entry->id; | |
205 | ||
169b5d95 | 206 | mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, false, span_type); |
5c7659eb | 207 | mlxsw_reg_mpat_session_id_set(mpat_pl, span_entry->parms.session_id); |
7b2ef81f PM |
208 | mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl); |
209 | } | |
210 | ||
169b5d95 PM |
211 | static void |
212 | mlxsw_sp_span_entry_phys_deconfigure(struct mlxsw_sp_span_entry *span_entry) | |
213 | { | |
214 | mlxsw_sp_span_entry_deconfigure_common(span_entry, | |
215 | MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH); | |
216 | } | |
217 | ||
218 | static const | |
219 | struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_phys = { | |
b6f6881a | 220 | .is_static = true, |
169b5d95 | 221 | .can_handle = mlxsw_sp_port_dev_check, |
7f9b099b | 222 | .parms_set = mlxsw_sp_span_entry_phys_parms, |
169b5d95 PM |
223 | .configure = mlxsw_sp_span_entry_phys_configure, |
224 | .deconfigure = mlxsw_sp_span_entry_phys_deconfigure, | |
225 | }; | |
226 | ||
27cf76fe PM |
227 | static int mlxsw_sp_span_dmac(struct neigh_table *tbl, |
228 | const void *pkey, | |
fc74ecbc | 229 | struct net_device *dev, |
27cf76fe PM |
230 | unsigned char dmac[ETH_ALEN]) |
231 | { | |
fc74ecbc | 232 | struct neighbour *neigh = neigh_lookup(tbl, pkey, dev); |
27cf76fe PM |
233 | int err = 0; |
234 | ||
235 | if (!neigh) { | |
fc74ecbc | 236 | neigh = neigh_create(tbl, pkey, dev); |
27cf76fe PM |
237 | if (IS_ERR(neigh)) |
238 | return PTR_ERR(neigh); | |
239 | } | |
240 | ||
241 | neigh_event_send(neigh, NULL); | |
242 | ||
243 | read_lock_bh(&neigh->lock); | |
244 | if ((neigh->nud_state & NUD_VALID) && !neigh->dead) | |
245 | memcpy(dmac, neigh->ha, ETH_ALEN); | |
246 | else | |
247 | err = -ENOENT; | |
248 | read_unlock_bh(&neigh->lock); | |
249 | ||
250 | neigh_release(neigh); | |
251 | return err; | |
252 | } | |
253 | ||
254 | static int | |
255 | mlxsw_sp_span_entry_unoffloadable(struct mlxsw_sp_span_parms *sparmsp) | |
256 | { | |
257 | sparmsp->dest_port = NULL; | |
258 | return 0; | |
259 | } | |
260 | ||
946a11e7 PM |
261 | static struct net_device * |
262 | mlxsw_sp_span_entry_bridge_8021q(const struct net_device *br_dev, | |
263 | unsigned char *dmac, | |
264 | u16 *p_vid) | |
265 | { | |
266 | struct bridge_vlan_info vinfo; | |
267 | struct net_device *edev; | |
03c44132 | 268 | u16 vid = *p_vid; |
946a11e7 | 269 | |
03c44132 | 270 | if (!vid && WARN_ON(br_vlan_get_pvid(br_dev, &vid))) |
946a11e7 | 271 | return NULL; |
42c9135f IS |
272 | if (!vid || br_vlan_get_info(br_dev, vid, &vinfo) || |
273 | !(vinfo.flags & BRIDGE_VLAN_INFO_BRENTRY)) | |
946a11e7 PM |
274 | return NULL; |
275 | ||
03c44132 | 276 | edev = br_fdb_find_port(br_dev, dmac, vid); |
946a11e7 PM |
277 | if (!edev) |
278 | return NULL; | |
279 | ||
03c44132 | 280 | if (br_vlan_get_info(edev, vid, &vinfo)) |
946a11e7 | 281 | return NULL; |
1fc68bb7 PM |
282 | if (vinfo.flags & BRIDGE_VLAN_INFO_UNTAGGED) |
283 | *p_vid = 0; | |
284 | else | |
03c44132 | 285 | *p_vid = vid; |
946a11e7 PM |
286 | return edev; |
287 | } | |
288 | ||
289 | static struct net_device * | |
290 | mlxsw_sp_span_entry_bridge_8021d(const struct net_device *br_dev, | |
291 | unsigned char *dmac) | |
292 | { | |
293 | return br_fdb_find_port(br_dev, dmac, 0); | |
294 | } | |
295 | ||
296 | static struct net_device * | |
297 | mlxsw_sp_span_entry_bridge(const struct net_device *br_dev, | |
298 | unsigned char dmac[ETH_ALEN], | |
299 | u16 *p_vid) | |
300 | { | |
301 | struct mlxsw_sp_bridge_port *bridge_port; | |
302 | enum mlxsw_reg_spms_state spms_state; | |
03c44132 | 303 | struct net_device *dev = NULL; |
946a11e7 | 304 | struct mlxsw_sp_port *port; |
946a11e7 PM |
305 | u8 stp_state; |
306 | ||
307 | if (br_vlan_enabled(br_dev)) | |
308 | dev = mlxsw_sp_span_entry_bridge_8021q(br_dev, dmac, p_vid); | |
03c44132 | 309 | else if (!*p_vid) |
946a11e7 PM |
310 | dev = mlxsw_sp_span_entry_bridge_8021d(br_dev, dmac); |
311 | if (!dev) | |
312 | return NULL; | |
313 | ||
314 | port = mlxsw_sp_port_dev_lower_find(dev); | |
315 | if (!port) | |
316 | return NULL; | |
317 | ||
318 | bridge_port = mlxsw_sp_bridge_port_find(port->mlxsw_sp->bridge, dev); | |
319 | if (!bridge_port) | |
320 | return NULL; | |
321 | ||
322 | stp_state = mlxsw_sp_bridge_port_stp_state(bridge_port); | |
323 | spms_state = mlxsw_sp_stp_spms_state(stp_state); | |
324 | if (spms_state != MLXSW_REG_SPMS_STATE_FORWARDING) | |
325 | return NULL; | |
326 | ||
327 | return dev; | |
328 | } | |
329 | ||
e00698d1 PM |
330 | static struct net_device * |
331 | mlxsw_sp_span_entry_vlan(const struct net_device *vlan_dev, | |
332 | u16 *p_vid) | |
333 | { | |
334 | *p_vid = vlan_dev_vlan_id(vlan_dev); | |
335 | return vlan_dev_real_dev(vlan_dev); | |
336 | } | |
337 | ||
55c0211d PM |
338 | static struct net_device * |
339 | mlxsw_sp_span_entry_lag(struct net_device *lag_dev) | |
340 | { | |
341 | struct net_device *dev; | |
342 | struct list_head *iter; | |
343 | ||
344 | netdev_for_each_lower_dev(lag_dev, dev, iter) | |
b5de82f3 PM |
345 | if (netif_carrier_ok(dev) && |
346 | net_lag_port_dev_txable(dev) && | |
347 | mlxsw_sp_port_dev_check(dev)) | |
55c0211d PM |
348 | return dev; |
349 | ||
350 | return NULL; | |
351 | } | |
352 | ||
99db5229 | 353 | static __maybe_unused int |
fc74ecbc | 354 | mlxsw_sp_span_entry_tunnel_parms_common(struct net_device *edev, |
27cf76fe PM |
355 | union mlxsw_sp_l3addr saddr, |
356 | union mlxsw_sp_l3addr daddr, | |
357 | union mlxsw_sp_l3addr gw, | |
358 | __u8 ttl, | |
359 | struct neigh_table *tbl, | |
360 | struct mlxsw_sp_span_parms *sparmsp) | |
361 | { | |
362 | unsigned char dmac[ETH_ALEN]; | |
946a11e7 | 363 | u16 vid = 0; |
27cf76fe PM |
364 | |
365 | if (mlxsw_sp_l3addr_is_zero(gw)) | |
366 | gw = daddr; | |
367 | ||
fc74ecbc | 368 | if (!edev || mlxsw_sp_span_dmac(tbl, &gw, edev, dmac)) |
946a11e7 PM |
369 | goto unoffloadable; |
370 | ||
fc74ecbc PM |
371 | if (is_vlan_dev(edev)) |
372 | edev = mlxsw_sp_span_entry_vlan(edev, &vid); | |
03c44132 | 373 | |
fc74ecbc PM |
374 | if (netif_is_bridge_master(edev)) { |
375 | edev = mlxsw_sp_span_entry_bridge(edev, dmac, &vid); | |
376 | if (!edev) | |
946a11e7 PM |
377 | goto unoffloadable; |
378 | } | |
379 | ||
fc74ecbc PM |
380 | if (is_vlan_dev(edev)) { |
381 | if (vid || !(edev->flags & IFF_UP)) | |
03c44132 | 382 | goto unoffloadable; |
fc74ecbc | 383 | edev = mlxsw_sp_span_entry_vlan(edev, &vid); |
03c44132 PM |
384 | } |
385 | ||
55c0211d PM |
386 | if (netif_is_lag_master(edev)) { |
387 | if (!(edev->flags & IFF_UP)) | |
388 | goto unoffloadable; | |
389 | edev = mlxsw_sp_span_entry_lag(edev); | |
390 | if (!edev) | |
391 | goto unoffloadable; | |
392 | } | |
393 | ||
fc74ecbc | 394 | if (!mlxsw_sp_port_dev_check(edev)) |
946a11e7 | 395 | goto unoffloadable; |
27cf76fe | 396 | |
fc74ecbc | 397 | sparmsp->dest_port = netdev_priv(edev); |
27cf76fe PM |
398 | sparmsp->ttl = ttl; |
399 | memcpy(sparmsp->dmac, dmac, ETH_ALEN); | |
fc74ecbc | 400 | memcpy(sparmsp->smac, edev->dev_addr, ETH_ALEN); |
27cf76fe PM |
401 | sparmsp->saddr = saddr; |
402 | sparmsp->daddr = daddr; | |
946a11e7 | 403 | sparmsp->vid = vid; |
27cf76fe | 404 | return 0; |
946a11e7 PM |
405 | |
406 | unoffloadable: | |
407 | return mlxsw_sp_span_entry_unoffloadable(sparmsp); | |
27cf76fe PM |
408 | } |
409 | ||
99db5229 | 410 | #if IS_ENABLED(CONFIG_NET_IPGRE) |
36a1c3bd PM |
411 | static struct net_device * |
412 | mlxsw_sp_span_gretap4_route(const struct net_device *to_dev, | |
413 | __be32 *saddrp, __be32 *daddrp) | |
414 | { | |
415 | struct ip_tunnel *tun = netdev_priv(to_dev); | |
416 | struct net_device *dev = NULL; | |
417 | struct ip_tunnel_parm parms; | |
418 | struct rtable *rt = NULL; | |
419 | struct flowi4 fl4; | |
420 | ||
421 | /* We assume "dev" stays valid after rt is put. */ | |
422 | ASSERT_RTNL(); | |
423 | ||
424 | parms = mlxsw_sp_ipip_netdev_parms4(to_dev); | |
425 | ip_tunnel_init_flow(&fl4, parms.iph.protocol, *daddrp, *saddrp, | |
db53cd3d | 426 | 0, 0, dev_net(to_dev), parms.link, tun->fwmark, 0); |
36a1c3bd PM |
427 | |
428 | rt = ip_route_output_key(tun->net, &fl4); | |
429 | if (IS_ERR(rt)) | |
430 | return NULL; | |
431 | ||
432 | if (rt->rt_type != RTN_UNICAST) | |
433 | goto out; | |
434 | ||
435 | dev = rt->dst.dev; | |
436 | *saddrp = fl4.saddr; | |
1550c171 DA |
437 | if (rt->rt_gw_family == AF_INET) |
438 | *daddrp = rt->rt_gw4; | |
0f5f7d7b DA |
439 | /* can not offload if route has an IPv6 gateway */ |
440 | else if (rt->rt_gw_family == AF_INET6) | |
441 | dev = NULL; | |
36a1c3bd PM |
442 | |
443 | out: | |
444 | ip_rt_put(rt); | |
445 | return dev; | |
446 | } | |
447 | ||
27cf76fe | 448 | static int |
f4a626e2 IS |
449 | mlxsw_sp_span_entry_gretap4_parms(struct mlxsw_sp *mlxsw_sp, |
450 | const struct net_device *to_dev, | |
27cf76fe PM |
451 | struct mlxsw_sp_span_parms *sparmsp) |
452 | { | |
453 | struct ip_tunnel_parm tparm = mlxsw_sp_ipip_netdev_parms4(to_dev); | |
454 | union mlxsw_sp_l3addr saddr = { .addr4 = tparm.iph.saddr }; | |
455 | union mlxsw_sp_l3addr daddr = { .addr4 = tparm.iph.daddr }; | |
456 | bool inherit_tos = tparm.iph.tos & 0x1; | |
457 | bool inherit_ttl = !tparm.iph.ttl; | |
458 | union mlxsw_sp_l3addr gw = daddr; | |
459 | struct net_device *l3edev; | |
460 | ||
461 | if (!(to_dev->flags & IFF_UP) || | |
462 | /* Reject tunnels with GRE keys, checksums, etc. */ | |
463 | tparm.i_flags || tparm.o_flags || | |
464 | /* Require a fixed TTL and a TOS copied from the mirrored packet. */ | |
465 | inherit_ttl || !inherit_tos || | |
466 | /* A destination address may not be "any". */ | |
467 | mlxsw_sp_l3addr_is_zero(daddr)) | |
468 | return mlxsw_sp_span_entry_unoffloadable(sparmsp); | |
469 | ||
470 | l3edev = mlxsw_sp_span_gretap4_route(to_dev, &saddr.addr4, &gw.addr4); | |
471 | return mlxsw_sp_span_entry_tunnel_parms_common(l3edev, saddr, daddr, gw, | |
472 | tparm.iph.ttl, | |
473 | &arp_tbl, sparmsp); | |
474 | } | |
475 | ||
476 | static int | |
477 | mlxsw_sp_span_entry_gretap4_configure(struct mlxsw_sp_span_entry *span_entry, | |
478 | struct mlxsw_sp_span_parms sparms) | |
479 | { | |
480 | struct mlxsw_sp_port *dest_port = sparms.dest_port; | |
481 | struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp; | |
c934757d | 482 | u16 local_port = dest_port->local_port; |
27cf76fe PM |
483 | char mpat_pl[MLXSW_REG_MPAT_LEN]; |
484 | int pa_id = span_entry->id; | |
485 | ||
486 | /* Create a new port analayzer entry for local_port. */ | |
487 | mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true, | |
488 | MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3); | |
4039504e IS |
489 | mlxsw_reg_mpat_pide_set(mpat_pl, sparms.policer_enable); |
490 | mlxsw_reg_mpat_pid_set(mpat_pl, sparms.policer_id); | |
946a11e7 | 491 | mlxsw_reg_mpat_eth_rspan_pack(mpat_pl, sparms.vid); |
27cf76fe PM |
492 | mlxsw_reg_mpat_eth_rspan_l2_pack(mpat_pl, |
493 | MLXSW_REG_MPAT_ETH_RSPAN_VERSION_NO_HEADER, | |
946a11e7 | 494 | sparms.dmac, !!sparms.vid); |
27cf76fe PM |
495 | mlxsw_reg_mpat_eth_rspan_l3_ipv4_pack(mpat_pl, |
496 | sparms.ttl, sparms.smac, | |
497 | be32_to_cpu(sparms.saddr.addr4), | |
498 | be32_to_cpu(sparms.daddr.addr4)); | |
499 | ||
500 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl); | |
501 | } | |
502 | ||
503 | static void | |
504 | mlxsw_sp_span_entry_gretap4_deconfigure(struct mlxsw_sp_span_entry *span_entry) | |
505 | { | |
506 | mlxsw_sp_span_entry_deconfigure_common(span_entry, | |
507 | MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3); | |
508 | } | |
509 | ||
510 | static const struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_gretap4 = { | |
0621e6fc | 511 | .can_handle = netif_is_gretap, |
7f9b099b | 512 | .parms_set = mlxsw_sp_span_entry_gretap4_parms, |
27cf76fe PM |
513 | .configure = mlxsw_sp_span_entry_gretap4_configure, |
514 | .deconfigure = mlxsw_sp_span_entry_gretap4_deconfigure, | |
515 | }; | |
99db5229 | 516 | #endif |
27cf76fe | 517 | |
99db5229 | 518 | #if IS_ENABLED(CONFIG_IPV6_GRE) |
8f08a528 PM |
519 | static struct net_device * |
520 | mlxsw_sp_span_gretap6_route(const struct net_device *to_dev, | |
521 | struct in6_addr *saddrp, | |
522 | struct in6_addr *daddrp) | |
523 | { | |
524 | struct ip6_tnl *t = netdev_priv(to_dev); | |
525 | struct flowi6 fl6 = t->fl.u.ip6; | |
526 | struct net_device *dev = NULL; | |
527 | struct dst_entry *dst; | |
528 | struct rt6_info *rt6; | |
529 | ||
530 | /* We assume "dev" stays valid after dst is released. */ | |
531 | ASSERT_RTNL(); | |
532 | ||
533 | fl6.flowi6_mark = t->parms.fwmark; | |
534 | if (!ip6_tnl_xmit_ctl(t, &fl6.saddr, &fl6.daddr)) | |
535 | return NULL; | |
536 | ||
537 | dst = ip6_route_output(t->net, NULL, &fl6); | |
538 | if (!dst || dst->error) | |
539 | goto out; | |
540 | ||
541 | rt6 = container_of(dst, struct rt6_info, dst); | |
542 | ||
543 | dev = dst->dev; | |
544 | *saddrp = fl6.saddr; | |
545 | *daddrp = rt6->rt6i_gateway; | |
546 | ||
547 | out: | |
548 | dst_release(dst); | |
549 | return dev; | |
550 | } | |
551 | ||
552 | static int | |
f4a626e2 IS |
553 | mlxsw_sp_span_entry_gretap6_parms(struct mlxsw_sp *mlxsw_sp, |
554 | const struct net_device *to_dev, | |
8f08a528 PM |
555 | struct mlxsw_sp_span_parms *sparmsp) |
556 | { | |
557 | struct __ip6_tnl_parm tparm = mlxsw_sp_ipip_netdev_parms6(to_dev); | |
558 | bool inherit_tos = tparm.flags & IP6_TNL_F_USE_ORIG_TCLASS; | |
559 | union mlxsw_sp_l3addr saddr = { .addr6 = tparm.laddr }; | |
560 | union mlxsw_sp_l3addr daddr = { .addr6 = tparm.raddr }; | |
561 | bool inherit_ttl = !tparm.hop_limit; | |
562 | union mlxsw_sp_l3addr gw = daddr; | |
563 | struct net_device *l3edev; | |
564 | ||
565 | if (!(to_dev->flags & IFF_UP) || | |
566 | /* Reject tunnels with GRE keys, checksums, etc. */ | |
567 | tparm.i_flags || tparm.o_flags || | |
568 | /* Require a fixed TTL and a TOS copied from the mirrored packet. */ | |
569 | inherit_ttl || !inherit_tos || | |
570 | /* A destination address may not be "any". */ | |
571 | mlxsw_sp_l3addr_is_zero(daddr)) | |
572 | return mlxsw_sp_span_entry_unoffloadable(sparmsp); | |
573 | ||
574 | l3edev = mlxsw_sp_span_gretap6_route(to_dev, &saddr.addr6, &gw.addr6); | |
575 | return mlxsw_sp_span_entry_tunnel_parms_common(l3edev, saddr, daddr, gw, | |
576 | tparm.hop_limit, | |
577 | &nd_tbl, sparmsp); | |
578 | } | |
579 | ||
580 | static int | |
581 | mlxsw_sp_span_entry_gretap6_configure(struct mlxsw_sp_span_entry *span_entry, | |
582 | struct mlxsw_sp_span_parms sparms) | |
583 | { | |
584 | struct mlxsw_sp_port *dest_port = sparms.dest_port; | |
585 | struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp; | |
c934757d | 586 | u16 local_port = dest_port->local_port; |
8f08a528 PM |
587 | char mpat_pl[MLXSW_REG_MPAT_LEN]; |
588 | int pa_id = span_entry->id; | |
589 | ||
590 | /* Create a new port analayzer entry for local_port. */ | |
591 | mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true, | |
592 | MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3); | |
4039504e IS |
593 | mlxsw_reg_mpat_pide_set(mpat_pl, sparms.policer_enable); |
594 | mlxsw_reg_mpat_pid_set(mpat_pl, sparms.policer_id); | |
946a11e7 | 595 | mlxsw_reg_mpat_eth_rspan_pack(mpat_pl, sparms.vid); |
8f08a528 PM |
596 | mlxsw_reg_mpat_eth_rspan_l2_pack(mpat_pl, |
597 | MLXSW_REG_MPAT_ETH_RSPAN_VERSION_NO_HEADER, | |
946a11e7 | 598 | sparms.dmac, !!sparms.vid); |
8f08a528 PM |
599 | mlxsw_reg_mpat_eth_rspan_l3_ipv6_pack(mpat_pl, sparms.ttl, sparms.smac, |
600 | sparms.saddr.addr6, | |
601 | sparms.daddr.addr6); | |
602 | ||
603 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl); | |
604 | } | |
605 | ||
606 | static void | |
607 | mlxsw_sp_span_entry_gretap6_deconfigure(struct mlxsw_sp_span_entry *span_entry) | |
608 | { | |
609 | mlxsw_sp_span_entry_deconfigure_common(span_entry, | |
610 | MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3); | |
611 | } | |
612 | ||
613 | static const | |
614 | struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_gretap6 = { | |
0621e6fc | 615 | .can_handle = netif_is_ip6gretap, |
7f9b099b | 616 | .parms_set = mlxsw_sp_span_entry_gretap6_parms, |
8f08a528 PM |
617 | .configure = mlxsw_sp_span_entry_gretap6_configure, |
618 | .deconfigure = mlxsw_sp_span_entry_gretap6_deconfigure, | |
619 | }; | |
99db5229 | 620 | #endif |
8f08a528 | 621 | |
e00698d1 PM |
622 | static bool |
623 | mlxsw_sp_span_vlan_can_handle(const struct net_device *dev) | |
624 | { | |
625 | return is_vlan_dev(dev) && | |
626 | mlxsw_sp_port_dev_check(vlan_dev_real_dev(dev)); | |
627 | } | |
628 | ||
629 | static int | |
f4a626e2 IS |
630 | mlxsw_sp_span_entry_vlan_parms(struct mlxsw_sp *mlxsw_sp, |
631 | const struct net_device *to_dev, | |
e00698d1 PM |
632 | struct mlxsw_sp_span_parms *sparmsp) |
633 | { | |
634 | struct net_device *real_dev; | |
635 | u16 vid; | |
636 | ||
637 | if (!(to_dev->flags & IFF_UP)) | |
638 | return mlxsw_sp_span_entry_unoffloadable(sparmsp); | |
639 | ||
640 | real_dev = mlxsw_sp_span_entry_vlan(to_dev, &vid); | |
641 | sparmsp->dest_port = netdev_priv(real_dev); | |
642 | sparmsp->vid = vid; | |
643 | return 0; | |
644 | } | |
645 | ||
646 | static int | |
647 | mlxsw_sp_span_entry_vlan_configure(struct mlxsw_sp_span_entry *span_entry, | |
648 | struct mlxsw_sp_span_parms sparms) | |
649 | { | |
650 | struct mlxsw_sp_port *dest_port = sparms.dest_port; | |
651 | struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp; | |
c934757d | 652 | u16 local_port = dest_port->local_port; |
e00698d1 PM |
653 | char mpat_pl[MLXSW_REG_MPAT_LEN]; |
654 | int pa_id = span_entry->id; | |
655 | ||
656 | mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true, | |
657 | MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH); | |
4039504e IS |
658 | mlxsw_reg_mpat_pide_set(mpat_pl, sparms.policer_enable); |
659 | mlxsw_reg_mpat_pid_set(mpat_pl, sparms.policer_id); | |
e00698d1 PM |
660 | mlxsw_reg_mpat_eth_rspan_pack(mpat_pl, sparms.vid); |
661 | ||
662 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl); | |
663 | } | |
664 | ||
665 | static void | |
666 | mlxsw_sp_span_entry_vlan_deconfigure(struct mlxsw_sp_span_entry *span_entry) | |
667 | { | |
668 | mlxsw_sp_span_entry_deconfigure_common(span_entry, | |
669 | MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH); | |
670 | } | |
671 | ||
672 | static const | |
673 | struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_vlan = { | |
674 | .can_handle = mlxsw_sp_span_vlan_can_handle, | |
7f9b099b | 675 | .parms_set = mlxsw_sp_span_entry_vlan_parms, |
e00698d1 PM |
676 | .configure = mlxsw_sp_span_entry_vlan_configure, |
677 | .deconfigure = mlxsw_sp_span_entry_vlan_deconfigure, | |
678 | }; | |
679 | ||
169b5d95 | 680 | static const |
34e4ace5 | 681 | struct mlxsw_sp_span_entry_ops *mlxsw_sp1_span_entry_ops_arr[] = { |
fa8c08b8 | 682 | &mlxsw_sp1_span_entry_ops_cpu, |
34e4ace5 IS |
683 | &mlxsw_sp_span_entry_ops_phys, |
684 | #if IS_ENABLED(CONFIG_NET_IPGRE) | |
685 | &mlxsw_sp_span_entry_ops_gretap4, | |
686 | #endif | |
687 | #if IS_ENABLED(CONFIG_IPV6_GRE) | |
688 | &mlxsw_sp_span_entry_ops_gretap6, | |
689 | #endif | |
690 | &mlxsw_sp_span_entry_ops_vlan, | |
691 | }; | |
692 | ||
fa8c08b8 IS |
693 | static bool mlxsw_sp2_span_cpu_can_handle(const struct net_device *dev) |
694 | { | |
695 | return !dev; | |
696 | } | |
697 | ||
698 | static int mlxsw_sp2_span_entry_cpu_parms(struct mlxsw_sp *mlxsw_sp, | |
699 | const struct net_device *to_dev, | |
700 | struct mlxsw_sp_span_parms *sparmsp) | |
701 | { | |
702 | sparmsp->dest_port = mlxsw_sp->ports[MLXSW_PORT_CPU_PORT]; | |
703 | return 0; | |
704 | } | |
705 | ||
706 | static int | |
707 | mlxsw_sp2_span_entry_cpu_configure(struct mlxsw_sp_span_entry *span_entry, | |
708 | struct mlxsw_sp_span_parms sparms) | |
709 | { | |
710 | /* Mirroring to the CPU port is like mirroring to any other physical | |
711 | * port. Its local port is used instead of that of the physical port. | |
712 | */ | |
713 | return mlxsw_sp_span_entry_phys_configure(span_entry, sparms); | |
714 | } | |
715 | ||
716 | static void | |
717 | mlxsw_sp2_span_entry_cpu_deconfigure(struct mlxsw_sp_span_entry *span_entry) | |
718 | { | |
719 | enum mlxsw_reg_mpat_span_type span_type; | |
720 | ||
721 | span_type = MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH; | |
722 | mlxsw_sp_span_entry_deconfigure_common(span_entry, span_type); | |
723 | } | |
724 | ||
725 | static const | |
726 | struct mlxsw_sp_span_entry_ops mlxsw_sp2_span_entry_ops_cpu = { | |
b6f6881a | 727 | .is_static = true, |
fa8c08b8 IS |
728 | .can_handle = mlxsw_sp2_span_cpu_can_handle, |
729 | .parms_set = mlxsw_sp2_span_entry_cpu_parms, | |
730 | .configure = mlxsw_sp2_span_entry_cpu_configure, | |
731 | .deconfigure = mlxsw_sp2_span_entry_cpu_deconfigure, | |
732 | }; | |
733 | ||
34e4ace5 IS |
734 | static const |
735 | struct mlxsw_sp_span_entry_ops *mlxsw_sp2_span_entry_ops_arr[] = { | |
fa8c08b8 | 736 | &mlxsw_sp2_span_entry_ops_cpu, |
169b5d95 | 737 | &mlxsw_sp_span_entry_ops_phys, |
99db5229 | 738 | #if IS_ENABLED(CONFIG_NET_IPGRE) |
27cf76fe | 739 | &mlxsw_sp_span_entry_ops_gretap4, |
99db5229 PM |
740 | #endif |
741 | #if IS_ENABLED(CONFIG_IPV6_GRE) | |
8f08a528 | 742 | &mlxsw_sp_span_entry_ops_gretap6, |
99db5229 | 743 | #endif |
e00698d1 | 744 | &mlxsw_sp_span_entry_ops_vlan, |
169b5d95 PM |
745 | }; |
746 | ||
747 | static int | |
f4a626e2 IS |
748 | mlxsw_sp_span_entry_nop_parms(struct mlxsw_sp *mlxsw_sp, |
749 | const struct net_device *to_dev, | |
169b5d95 PM |
750 | struct mlxsw_sp_span_parms *sparmsp) |
751 | { | |
27cf76fe | 752 | return mlxsw_sp_span_entry_unoffloadable(sparmsp); |
169b5d95 PM |
753 | } |
754 | ||
755 | static int | |
756 | mlxsw_sp_span_entry_nop_configure(struct mlxsw_sp_span_entry *span_entry, | |
757 | struct mlxsw_sp_span_parms sparms) | |
758 | { | |
759 | return 0; | |
760 | } | |
761 | ||
762 | static void | |
763 | mlxsw_sp_span_entry_nop_deconfigure(struct mlxsw_sp_span_entry *span_entry) | |
764 | { | |
765 | } | |
766 | ||
767 | static const struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_nop = { | |
7f9b099b | 768 | .parms_set = mlxsw_sp_span_entry_nop_parms, |
169b5d95 PM |
769 | .configure = mlxsw_sp_span_entry_nop_configure, |
770 | .deconfigure = mlxsw_sp_span_entry_nop_deconfigure, | |
771 | }; | |
772 | ||
773 | static void | |
774 | mlxsw_sp_span_entry_configure(struct mlxsw_sp *mlxsw_sp, | |
775 | struct mlxsw_sp_span_entry *span_entry, | |
776 | struct mlxsw_sp_span_parms sparms) | |
777 | { | |
8146458f AC |
778 | int err; |
779 | ||
780 | if (!sparms.dest_port) | |
781 | goto set_parms; | |
782 | ||
783 | if (sparms.dest_port->mlxsw_sp != mlxsw_sp) { | |
6edc8bea IS |
784 | dev_err(mlxsw_sp->bus_info->dev, |
785 | "Cannot mirror to a port which belongs to a different mlxsw instance\n"); | |
8146458f AC |
786 | sparms.dest_port = NULL; |
787 | goto set_parms; | |
788 | } | |
789 | ||
790 | err = span_entry->ops->configure(span_entry, sparms); | |
791 | if (err) { | |
6edc8bea | 792 | dev_err(mlxsw_sp->bus_info->dev, "Failed to offload mirror\n"); |
8146458f AC |
793 | sparms.dest_port = NULL; |
794 | goto set_parms; | |
169b5d95 PM |
795 | } |
796 | ||
8146458f | 797 | set_parms: |
169b5d95 PM |
798 | span_entry->parms = sparms; |
799 | } | |
800 | ||
801 | static void | |
802 | mlxsw_sp_span_entry_deconfigure(struct mlxsw_sp_span_entry *span_entry) | |
803 | { | |
804 | if (span_entry->parms.dest_port) | |
805 | span_entry->ops->deconfigure(span_entry); | |
806 | } | |
807 | ||
4039504e IS |
808 | static int mlxsw_sp_span_policer_id_base_set(struct mlxsw_sp_span *span, |
809 | u16 policer_id) | |
810 | { | |
811 | struct mlxsw_sp *mlxsw_sp = span->mlxsw_sp; | |
812 | u16 policer_id_base; | |
813 | int err; | |
814 | ||
815 | /* Policers set on SPAN agents must be in the range of | |
816 | * `policer_id_base .. policer_id_base + max_span_agents - 1`. If the | |
817 | * base is set and the new policer is not within the range, then we | |
818 | * must error out. | |
819 | */ | |
820 | if (refcount_read(&span->policer_id_base_ref_count)) { | |
821 | if (policer_id < span->policer_id_base || | |
822 | policer_id >= span->policer_id_base + span->entries_count) | |
823 | return -EINVAL; | |
824 | ||
825 | refcount_inc(&span->policer_id_base_ref_count); | |
826 | return 0; | |
827 | } | |
828 | ||
829 | /* Base must be even. */ | |
830 | policer_id_base = policer_id % 2 == 0 ? policer_id : policer_id - 1; | |
831 | err = mlxsw_sp->span_ops->policer_id_base_set(mlxsw_sp, | |
832 | policer_id_base); | |
833 | if (err) | |
834 | return err; | |
835 | ||
836 | span->policer_id_base = policer_id_base; | |
837 | refcount_set(&span->policer_id_base_ref_count, 1); | |
838 | ||
839 | return 0; | |
840 | } | |
841 | ||
842 | static void mlxsw_sp_span_policer_id_base_unset(struct mlxsw_sp_span *span) | |
843 | { | |
928345c0 PM |
844 | if (refcount_dec_and_test(&span->policer_id_base_ref_count)) |
845 | span->policer_id_base = 0; | |
4039504e IS |
846 | } |
847 | ||
a629ef21 | 848 | static struct mlxsw_sp_span_entry * |
079c9f39 | 849 | mlxsw_sp_span_entry_create(struct mlxsw_sp *mlxsw_sp, |
169b5d95 PM |
850 | const struct net_device *to_dev, |
851 | const struct mlxsw_sp_span_entry_ops *ops, | |
852 | struct mlxsw_sp_span_parms sparms) | |
a629ef21 | 853 | { |
3546b03f | 854 | struct mlxsw_sp_span_entry *span_entry = NULL; |
a629ef21 | 855 | int i; |
a629ef21 PM |
856 | |
857 | /* find a free entry to use */ | |
9a9f8d1e | 858 | for (i = 0; i < mlxsw_sp->span->entries_count; i++) { |
4c00dafc | 859 | if (!refcount_read(&mlxsw_sp->span->entries[i].ref_count)) { |
9a9f8d1e | 860 | span_entry = &mlxsw_sp->span->entries[i]; |
a629ef21 PM |
861 | break; |
862 | } | |
863 | } | |
3546b03f | 864 | if (!span_entry) |
a629ef21 PM |
865 | return NULL; |
866 | ||
4039504e IS |
867 | if (sparms.policer_enable) { |
868 | int err; | |
869 | ||
870 | err = mlxsw_sp_span_policer_id_base_set(mlxsw_sp->span, | |
871 | sparms.policer_id); | |
872 | if (err) | |
873 | return NULL; | |
874 | } | |
875 | ||
eb833eec | 876 | atomic_inc(&mlxsw_sp->span->active_entries_count); |
169b5d95 | 877 | span_entry->ops = ops; |
4c00dafc | 878 | refcount_set(&span_entry->ref_count, 1); |
079c9f39 | 879 | span_entry->to_dev = to_dev; |
169b5d95 PM |
880 | mlxsw_sp_span_entry_configure(mlxsw_sp, span_entry, sparms); |
881 | ||
a629ef21 PM |
882 | return span_entry; |
883 | } | |
884 | ||
eb833eec IS |
885 | static void mlxsw_sp_span_entry_destroy(struct mlxsw_sp *mlxsw_sp, |
886 | struct mlxsw_sp_span_entry *span_entry) | |
a629ef21 | 887 | { |
169b5d95 | 888 | mlxsw_sp_span_entry_deconfigure(span_entry); |
eb833eec | 889 | atomic_dec(&mlxsw_sp->span->active_entries_count); |
4039504e IS |
890 | if (span_entry->parms.policer_enable) |
891 | mlxsw_sp_span_policer_id_base_unset(mlxsw_sp->span); | |
a629ef21 PM |
892 | } |
893 | ||
894 | struct mlxsw_sp_span_entry * | |
079c9f39 PM |
895 | mlxsw_sp_span_entry_find_by_port(struct mlxsw_sp *mlxsw_sp, |
896 | const struct net_device *to_dev) | |
a629ef21 PM |
897 | { |
898 | int i; | |
899 | ||
9a9f8d1e IS |
900 | for (i = 0; i < mlxsw_sp->span->entries_count; i++) { |
901 | struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i]; | |
a629ef21 | 902 | |
4c00dafc | 903 | if (refcount_read(&curr->ref_count) && curr->to_dev == to_dev) |
a629ef21 PM |
904 | return curr; |
905 | } | |
906 | return NULL; | |
907 | } | |
908 | ||
079c9f39 PM |
909 | void mlxsw_sp_span_entry_invalidate(struct mlxsw_sp *mlxsw_sp, |
910 | struct mlxsw_sp_span_entry *span_entry) | |
911 | { | |
169b5d95 PM |
912 | mlxsw_sp_span_entry_deconfigure(span_entry); |
913 | span_entry->ops = &mlxsw_sp_span_entry_ops_nop; | |
079c9f39 PM |
914 | } |
915 | ||
98977089 PM |
916 | static struct mlxsw_sp_span_entry * |
917 | mlxsw_sp_span_entry_find_by_id(struct mlxsw_sp *mlxsw_sp, int span_id) | |
918 | { | |
919 | int i; | |
920 | ||
9a9f8d1e IS |
921 | for (i = 0; i < mlxsw_sp->span->entries_count; i++) { |
922 | struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i]; | |
98977089 | 923 | |
4c00dafc | 924 | if (refcount_read(&curr->ref_count) && curr->id == span_id) |
98977089 PM |
925 | return curr; |
926 | } | |
927 | return NULL; | |
928 | } | |
929 | ||
4039504e IS |
930 | static struct mlxsw_sp_span_entry * |
931 | mlxsw_sp_span_entry_find_by_parms(struct mlxsw_sp *mlxsw_sp, | |
932 | const struct net_device *to_dev, | |
933 | const struct mlxsw_sp_span_parms *sparms) | |
934 | { | |
935 | int i; | |
936 | ||
937 | for (i = 0; i < mlxsw_sp->span->entries_count; i++) { | |
938 | struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i]; | |
939 | ||
940 | if (refcount_read(&curr->ref_count) && curr->to_dev == to_dev && | |
941 | curr->parms.policer_enable == sparms->policer_enable && | |
5c7659eb IS |
942 | curr->parms.policer_id == sparms->policer_id && |
943 | curr->parms.session_id == sparms->session_id) | |
4039504e IS |
944 | return curr; |
945 | } | |
946 | return NULL; | |
947 | } | |
948 | ||
a629ef21 | 949 | static struct mlxsw_sp_span_entry * |
079c9f39 | 950 | mlxsw_sp_span_entry_get(struct mlxsw_sp *mlxsw_sp, |
169b5d95 PM |
951 | const struct net_device *to_dev, |
952 | const struct mlxsw_sp_span_entry_ops *ops, | |
953 | struct mlxsw_sp_span_parms sparms) | |
a629ef21 PM |
954 | { |
955 | struct mlxsw_sp_span_entry *span_entry; | |
956 | ||
4039504e IS |
957 | span_entry = mlxsw_sp_span_entry_find_by_parms(mlxsw_sp, to_dev, |
958 | &sparms); | |
a629ef21 PM |
959 | if (span_entry) { |
960 | /* Already exists, just take a reference */ | |
4c00dafc | 961 | refcount_inc(&span_entry->ref_count); |
a629ef21 PM |
962 | return span_entry; |
963 | } | |
964 | ||
169b5d95 | 965 | return mlxsw_sp_span_entry_create(mlxsw_sp, to_dev, ops, sparms); |
a629ef21 PM |
966 | } |
967 | ||
968 | static int mlxsw_sp_span_entry_put(struct mlxsw_sp *mlxsw_sp, | |
969 | struct mlxsw_sp_span_entry *span_entry) | |
970 | { | |
4c00dafc | 971 | if (refcount_dec_and_test(&span_entry->ref_count)) |
eb833eec | 972 | mlxsw_sp_span_entry_destroy(mlxsw_sp, span_entry); |
a629ef21 PM |
973 | return 0; |
974 | } | |
975 | ||
22881adf | 976 | static int mlxsw_sp_span_port_buffer_update(struct mlxsw_sp_port *mlxsw_sp_port, bool enable) |
4bafb85a | 977 | { |
22881adf | 978 | struct mlxsw_sp_hdroom hdroom; |
4bafb85a | 979 | |
22881adf PM |
980 | hdroom = *mlxsw_sp_port->hdroom; |
981 | hdroom.int_buf.enable = enable; | |
982 | mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom); | |
983 | ||
984 | return mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom); | |
4bafb85a IS |
985 | } |
986 | ||
31c25b94 | 987 | static int |
532b49e4 | 988 | mlxsw_sp_span_port_buffer_enable(struct mlxsw_sp_port *mlxsw_sp_port) |
a629ef21 | 989 | { |
22881adf | 990 | return mlxsw_sp_span_port_buffer_update(mlxsw_sp_port, true); |
31c25b94 JP |
991 | } |
992 | ||
22881adf | 993 | static void mlxsw_sp_span_port_buffer_disable(struct mlxsw_sp_port *mlxsw_sp_port) |
14366da6 | 994 | { |
22881adf | 995 | mlxsw_sp_span_port_buffer_update(mlxsw_sp_port, false); |
14366da6 IS |
996 | } |
997 | ||
835d6b8c | 998 | static struct mlxsw_sp_span_analyzed_port * |
c934757d | 999 | mlxsw_sp_span_analyzed_port_find(struct mlxsw_sp_span *span, u16 local_port, |
835d6b8c IS |
1000 | bool ingress) |
1001 | { | |
1002 | struct mlxsw_sp_span_analyzed_port *analyzed_port; | |
1003 | ||
1004 | list_for_each_entry(analyzed_port, &span->analyzed_ports_list, list) { | |
1005 | if (analyzed_port->local_port == local_port && | |
1006 | analyzed_port->ingress == ingress) | |
1007 | return analyzed_port; | |
1008 | } | |
1009 | ||
1010 | return NULL; | |
1011 | } | |
1012 | ||
169b5d95 PM |
1013 | static const struct mlxsw_sp_span_entry_ops * |
1014 | mlxsw_sp_span_entry_ops(struct mlxsw_sp *mlxsw_sp, | |
1015 | const struct net_device *to_dev) | |
1016 | { | |
34e4ace5 | 1017 | struct mlxsw_sp_span *span = mlxsw_sp->span; |
169b5d95 PM |
1018 | size_t i; |
1019 | ||
34e4ace5 IS |
1020 | for (i = 0; i < span->span_entry_ops_arr_size; ++i) |
1021 | if (span->span_entry_ops_arr[i]->can_handle(to_dev)) | |
1022 | return span->span_entry_ops_arr[i]; | |
169b5d95 PM |
1023 | |
1024 | return NULL; | |
1025 | } | |
1026 | ||
a8e7e6e7 IS |
1027 | static void mlxsw_sp_span_respin_work(struct work_struct *work) |
1028 | { | |
1029 | struct mlxsw_sp_span *span; | |
1030 | struct mlxsw_sp *mlxsw_sp; | |
1031 | int i, err; | |
1032 | ||
1033 | span = container_of(work, struct mlxsw_sp_span, work); | |
1034 | mlxsw_sp = span->mlxsw_sp; | |
1035 | ||
1036 | rtnl_lock(); | |
1037 | for (i = 0; i < mlxsw_sp->span->entries_count; i++) { | |
1038 | struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i]; | |
1039 | struct mlxsw_sp_span_parms sparms = {NULL}; | |
1040 | ||
4c00dafc | 1041 | if (!refcount_read(&curr->ref_count)) |
a8e7e6e7 IS |
1042 | continue; |
1043 | ||
b6f6881a IS |
1044 | if (curr->ops->is_static) |
1045 | continue; | |
1046 | ||
f4a626e2 | 1047 | err = curr->ops->parms_set(mlxsw_sp, curr->to_dev, &sparms); |
a8e7e6e7 IS |
1048 | if (err) |
1049 | continue; | |
1050 | ||
1051 | if (memcmp(&sparms, &curr->parms, sizeof(sparms))) { | |
1052 | mlxsw_sp_span_entry_deconfigure(curr); | |
1053 | mlxsw_sp_span_entry_configure(mlxsw_sp, curr, sparms); | |
1054 | } | |
1055 | } | |
1056 | rtnl_unlock(); | |
1057 | } | |
622110f2 IS |
1058 | |
1059 | void mlxsw_sp_span_respin(struct mlxsw_sp *mlxsw_sp) | |
1060 | { | |
eb833eec IS |
1061 | if (atomic_read(&mlxsw_sp->span->active_entries_count) == 0) |
1062 | return; | |
622110f2 IS |
1063 | mlxsw_core_schedule_work(&mlxsw_sp->span->work); |
1064 | } | |
46601034 | 1065 | |
a120ecc3 IS |
1066 | int mlxsw_sp_span_agent_get(struct mlxsw_sp *mlxsw_sp, int *p_span_id, |
1067 | const struct mlxsw_sp_span_agent_parms *parms) | |
46601034 | 1068 | { |
a120ecc3 | 1069 | const struct net_device *to_dev = parms->to_dev; |
46601034 IS |
1070 | const struct mlxsw_sp_span_entry_ops *ops; |
1071 | struct mlxsw_sp_span_entry *span_entry; | |
1072 | struct mlxsw_sp_span_parms sparms; | |
1073 | int err; | |
1074 | ||
1075 | ASSERT_RTNL(); | |
1076 | ||
1077 | ops = mlxsw_sp_span_entry_ops(mlxsw_sp, to_dev); | |
1078 | if (!ops) { | |
1079 | dev_err(mlxsw_sp->bus_info->dev, "Cannot mirror to requested destination\n"); | |
1080 | return -EOPNOTSUPP; | |
1081 | } | |
1082 | ||
1083 | memset(&sparms, 0, sizeof(sparms)); | |
f4a626e2 | 1084 | err = ops->parms_set(mlxsw_sp, to_dev, &sparms); |
46601034 IS |
1085 | if (err) |
1086 | return err; | |
1087 | ||
4039504e IS |
1088 | sparms.policer_id = parms->policer_id; |
1089 | sparms.policer_enable = parms->policer_enable; | |
5c7659eb | 1090 | sparms.session_id = parms->session_id; |
46601034 IS |
1091 | span_entry = mlxsw_sp_span_entry_get(mlxsw_sp, to_dev, ops, sparms); |
1092 | if (!span_entry) | |
1093 | return -ENOBUFS; | |
1094 | ||
1095 | *p_span_id = span_entry->id; | |
1096 | ||
1097 | return 0; | |
1098 | } | |
1099 | ||
1100 | void mlxsw_sp_span_agent_put(struct mlxsw_sp *mlxsw_sp, int span_id) | |
1101 | { | |
1102 | struct mlxsw_sp_span_entry *span_entry; | |
1103 | ||
1104 | ASSERT_RTNL(); | |
1105 | ||
1106 | span_entry = mlxsw_sp_span_entry_find_by_id(mlxsw_sp, span_id); | |
1107 | if (WARN_ON_ONCE(!span_entry)) | |
1108 | return; | |
1109 | ||
1110 | mlxsw_sp_span_entry_put(mlxsw_sp, span_entry); | |
1111 | } | |
ed04458d | 1112 | |
ed04458d IS |
1113 | static struct mlxsw_sp_span_analyzed_port * |
1114 | mlxsw_sp_span_analyzed_port_create(struct mlxsw_sp_span *span, | |
1115 | struct mlxsw_sp_port *mlxsw_sp_port, | |
1116 | bool ingress) | |
1117 | { | |
1118 | struct mlxsw_sp_span_analyzed_port *analyzed_port; | |
1119 | int err; | |
1120 | ||
1121 | analyzed_port = kzalloc(sizeof(*analyzed_port), GFP_KERNEL); | |
1122 | if (!analyzed_port) | |
1123 | return ERR_PTR(-ENOMEM); | |
1124 | ||
1125 | refcount_set(&analyzed_port->ref_count, 1); | |
1126 | analyzed_port->local_port = mlxsw_sp_port->local_port; | |
1127 | analyzed_port->ingress = ingress; | |
1128 | list_add_tail(&analyzed_port->list, &span->analyzed_ports_list); | |
1129 | ||
1130 | /* An egress mirror buffer should be allocated on the egress port which | |
1131 | * does the mirroring. | |
1132 | */ | |
1133 | if (!ingress) { | |
532b49e4 | 1134 | err = mlxsw_sp_span_port_buffer_enable(mlxsw_sp_port); |
ed04458d | 1135 | if (err) |
eb773c3a | 1136 | goto err_buffer_update; |
ed04458d IS |
1137 | } |
1138 | ||
1139 | return analyzed_port; | |
1140 | ||
eb773c3a | 1141 | err_buffer_update: |
ed04458d IS |
1142 | list_del(&analyzed_port->list); |
1143 | kfree(analyzed_port); | |
1144 | return ERR_PTR(err); | |
1145 | } | |
1146 | ||
1147 | static void | |
22881adf | 1148 | mlxsw_sp_span_analyzed_port_destroy(struct mlxsw_sp_port *mlxsw_sp_port, |
ed04458d IS |
1149 | struct mlxsw_sp_span_analyzed_port * |
1150 | analyzed_port) | |
1151 | { | |
ed04458d IS |
1152 | /* Remove egress mirror buffer now that port is no longer analyzed |
1153 | * at egress. | |
1154 | */ | |
14366da6 | 1155 | if (!analyzed_port->ingress) |
22881adf | 1156 | mlxsw_sp_span_port_buffer_disable(mlxsw_sp_port); |
ed04458d IS |
1157 | |
1158 | list_del(&analyzed_port->list); | |
1159 | kfree(analyzed_port); | |
1160 | } | |
1161 | ||
1162 | int mlxsw_sp_span_analyzed_port_get(struct mlxsw_sp_port *mlxsw_sp_port, | |
1163 | bool ingress) | |
1164 | { | |
1165 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; | |
1166 | struct mlxsw_sp_span_analyzed_port *analyzed_port; | |
c934757d | 1167 | u16 local_port = mlxsw_sp_port->local_port; |
ed04458d IS |
1168 | int err = 0; |
1169 | ||
1170 | mutex_lock(&mlxsw_sp->span->analyzed_ports_lock); | |
1171 | ||
1172 | analyzed_port = mlxsw_sp_span_analyzed_port_find(mlxsw_sp->span, | |
1173 | local_port, ingress); | |
1174 | if (analyzed_port) { | |
1175 | refcount_inc(&analyzed_port->ref_count); | |
1176 | goto out_unlock; | |
1177 | } | |
1178 | ||
1179 | analyzed_port = mlxsw_sp_span_analyzed_port_create(mlxsw_sp->span, | |
1180 | mlxsw_sp_port, | |
1181 | ingress); | |
1182 | if (IS_ERR(analyzed_port)) | |
1183 | err = PTR_ERR(analyzed_port); | |
1184 | ||
1185 | out_unlock: | |
1186 | mutex_unlock(&mlxsw_sp->span->analyzed_ports_lock); | |
1187 | return err; | |
1188 | } | |
1189 | ||
1190 | void mlxsw_sp_span_analyzed_port_put(struct mlxsw_sp_port *mlxsw_sp_port, | |
1191 | bool ingress) | |
1192 | { | |
1193 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; | |
1194 | struct mlxsw_sp_span_analyzed_port *analyzed_port; | |
c934757d | 1195 | u16 local_port = mlxsw_sp_port->local_port; |
ed04458d IS |
1196 | |
1197 | mutex_lock(&mlxsw_sp->span->analyzed_ports_lock); | |
1198 | ||
1199 | analyzed_port = mlxsw_sp_span_analyzed_port_find(mlxsw_sp->span, | |
1200 | local_port, ingress); | |
1201 | if (WARN_ON_ONCE(!analyzed_port)) | |
1202 | goto out_unlock; | |
1203 | ||
1204 | if (!refcount_dec_and_test(&analyzed_port->ref_count)) | |
1205 | goto out_unlock; | |
1206 | ||
22881adf | 1207 | mlxsw_sp_span_analyzed_port_destroy(mlxsw_sp_port, analyzed_port); |
ed04458d IS |
1208 | |
1209 | out_unlock: | |
1210 | mutex_unlock(&mlxsw_sp->span->analyzed_ports_lock); | |
1211 | } | |
c056618c IS |
1212 | |
1213 | static int | |
08a3641f IS |
1214 | __mlxsw_sp_span_trigger_port_bind(struct mlxsw_sp_span *span, |
1215 | struct mlxsw_sp_span_trigger_entry * | |
1216 | trigger_entry, bool enable) | |
c056618c IS |
1217 | { |
1218 | char mpar_pl[MLXSW_REG_MPAR_LEN]; | |
1219 | enum mlxsw_reg_mpar_i_e i_e; | |
1220 | ||
1221 | switch (trigger_entry->trigger) { | |
1222 | case MLXSW_SP_SPAN_TRIGGER_INGRESS: | |
1223 | i_e = MLXSW_REG_MPAR_TYPE_INGRESS; | |
1224 | break; | |
1225 | case MLXSW_SP_SPAN_TRIGGER_EGRESS: | |
1226 | i_e = MLXSW_REG_MPAR_TYPE_EGRESS; | |
1227 | break; | |
1228 | default: | |
1229 | WARN_ON_ONCE(1); | |
1230 | return -EINVAL; | |
1231 | } | |
1232 | ||
2dcbd920 IS |
1233 | if (trigger_entry->parms.probability_rate > MLXSW_REG_MPAR_RATE_MAX) |
1234 | return -EINVAL; | |
1235 | ||
c056618c | 1236 | mlxsw_reg_mpar_pack(mpar_pl, trigger_entry->local_port, i_e, enable, |
2dcbd920 IS |
1237 | trigger_entry->parms.span_id, |
1238 | trigger_entry->parms.probability_rate); | |
c056618c IS |
1239 | return mlxsw_reg_write(span->mlxsw_sp->core, MLXSW_REG(mpar), mpar_pl); |
1240 | } | |
1241 | ||
1242 | static int | |
08a3641f IS |
1243 | mlxsw_sp_span_trigger_port_bind(struct mlxsw_sp_span_trigger_entry * |
1244 | trigger_entry) | |
c056618c | 1245 | { |
08a3641f IS |
1246 | return __mlxsw_sp_span_trigger_port_bind(trigger_entry->span, |
1247 | trigger_entry, true); | |
c056618c IS |
1248 | } |
1249 | ||
1250 | static void | |
08a3641f IS |
1251 | mlxsw_sp_span_trigger_port_unbind(struct mlxsw_sp_span_trigger_entry * |
1252 | trigger_entry) | |
c056618c | 1253 | { |
08a3641f IS |
1254 | __mlxsw_sp_span_trigger_port_bind(trigger_entry->span, trigger_entry, |
1255 | false); | |
1256 | } | |
1257 | ||
1258 | static bool | |
1259 | mlxsw_sp_span_trigger_port_matches(struct mlxsw_sp_span_trigger_entry * | |
1260 | trigger_entry, | |
1261 | enum mlxsw_sp_span_trigger trigger, | |
1262 | struct mlxsw_sp_port *mlxsw_sp_port) | |
1263 | { | |
1264 | return trigger_entry->trigger == trigger && | |
1265 | trigger_entry->local_port == mlxsw_sp_port->local_port; | |
1266 | } | |
1267 | ||
2bafb216 IS |
1268 | static int |
1269 | mlxsw_sp_span_trigger_port_enable(struct mlxsw_sp_span_trigger_entry * | |
1270 | trigger_entry, | |
1271 | struct mlxsw_sp_port *mlxsw_sp_port, u8 tc) | |
1272 | { | |
1273 | /* Port trigger are enabled during binding. */ | |
1274 | return 0; | |
1275 | } | |
1276 | ||
1277 | static void | |
1278 | mlxsw_sp_span_trigger_port_disable(struct mlxsw_sp_span_trigger_entry * | |
1279 | trigger_entry, | |
1280 | struct mlxsw_sp_port *mlxsw_sp_port, u8 tc) | |
1281 | { | |
1282 | } | |
1283 | ||
08a3641f IS |
1284 | static const struct mlxsw_sp_span_trigger_ops |
1285 | mlxsw_sp_span_trigger_port_ops = { | |
1286 | .bind = mlxsw_sp_span_trigger_port_bind, | |
1287 | .unbind = mlxsw_sp_span_trigger_port_unbind, | |
1288 | .matches = mlxsw_sp_span_trigger_port_matches, | |
2bafb216 IS |
1289 | .enable = mlxsw_sp_span_trigger_port_enable, |
1290 | .disable = mlxsw_sp_span_trigger_port_disable, | |
08a3641f IS |
1291 | }; |
1292 | ||
ab8c06b7 IS |
1293 | static int |
1294 | mlxsw_sp1_span_trigger_global_bind(struct mlxsw_sp_span_trigger_entry * | |
1295 | trigger_entry) | |
1296 | { | |
1297 | return -EOPNOTSUPP; | |
1298 | } | |
1299 | ||
1300 | static void | |
1301 | mlxsw_sp1_span_trigger_global_unbind(struct mlxsw_sp_span_trigger_entry * | |
1302 | trigger_entry) | |
1303 | { | |
1304 | } | |
1305 | ||
1306 | static bool | |
1307 | mlxsw_sp1_span_trigger_global_matches(struct mlxsw_sp_span_trigger_entry * | |
1308 | trigger_entry, | |
1309 | enum mlxsw_sp_span_trigger trigger, | |
1310 | struct mlxsw_sp_port *mlxsw_sp_port) | |
1311 | { | |
1312 | WARN_ON_ONCE(1); | |
1313 | return false; | |
1314 | } | |
1315 | ||
2bafb216 IS |
1316 | static int |
1317 | mlxsw_sp1_span_trigger_global_enable(struct mlxsw_sp_span_trigger_entry * | |
1318 | trigger_entry, | |
1319 | struct mlxsw_sp_port *mlxsw_sp_port, | |
1320 | u8 tc) | |
1321 | { | |
1322 | return -EOPNOTSUPP; | |
1323 | } | |
1324 | ||
1325 | static void | |
1326 | mlxsw_sp1_span_trigger_global_disable(struct mlxsw_sp_span_trigger_entry * | |
1327 | trigger_entry, | |
1328 | struct mlxsw_sp_port *mlxsw_sp_port, | |
1329 | u8 tc) | |
1330 | { | |
1331 | } | |
1332 | ||
ab8c06b7 IS |
1333 | static const struct mlxsw_sp_span_trigger_ops |
1334 | mlxsw_sp1_span_trigger_global_ops = { | |
1335 | .bind = mlxsw_sp1_span_trigger_global_bind, | |
1336 | .unbind = mlxsw_sp1_span_trigger_global_unbind, | |
1337 | .matches = mlxsw_sp1_span_trigger_global_matches, | |
2bafb216 IS |
1338 | .enable = mlxsw_sp1_span_trigger_global_enable, |
1339 | .disable = mlxsw_sp1_span_trigger_global_disable, | |
ab8c06b7 IS |
1340 | }; |
1341 | ||
1342 | static const struct mlxsw_sp_span_trigger_ops * | |
1343 | mlxsw_sp1_span_trigger_ops_arr[] = { | |
1344 | [MLXSW_SP_SPAN_TRIGGER_TYPE_PORT] = &mlxsw_sp_span_trigger_port_ops, | |
1345 | [MLXSW_SP_SPAN_TRIGGER_TYPE_GLOBAL] = | |
1346 | &mlxsw_sp1_span_trigger_global_ops, | |
1347 | }; | |
1348 | ||
1349 | static int | |
1350 | mlxsw_sp2_span_trigger_global_bind(struct mlxsw_sp_span_trigger_entry * | |
1351 | trigger_entry) | |
1352 | { | |
1353 | struct mlxsw_sp *mlxsw_sp = trigger_entry->span->mlxsw_sp; | |
1354 | enum mlxsw_reg_mpagr_trigger trigger; | |
1355 | char mpagr_pl[MLXSW_REG_MPAGR_LEN]; | |
1356 | ||
1357 | switch (trigger_entry->trigger) { | |
1358 | case MLXSW_SP_SPAN_TRIGGER_TAIL_DROP: | |
1359 | trigger = MLXSW_REG_MPAGR_TRIGGER_INGRESS_SHARED_BUFFER; | |
1360 | break; | |
1361 | case MLXSW_SP_SPAN_TRIGGER_EARLY_DROP: | |
1362 | trigger = MLXSW_REG_MPAGR_TRIGGER_INGRESS_WRED; | |
1363 | break; | |
1364 | case MLXSW_SP_SPAN_TRIGGER_ECN: | |
1365 | trigger = MLXSW_REG_MPAGR_TRIGGER_EGRESS_ECN; | |
1366 | break; | |
1367 | default: | |
1368 | WARN_ON_ONCE(1); | |
1369 | return -EINVAL; | |
1370 | } | |
1371 | ||
2dcbd920 IS |
1372 | if (trigger_entry->parms.probability_rate > MLXSW_REG_MPAGR_RATE_MAX) |
1373 | return -EINVAL; | |
1374 | ||
ab8c06b7 | 1375 | mlxsw_reg_mpagr_pack(mpagr_pl, trigger, trigger_entry->parms.span_id, |
2dcbd920 | 1376 | trigger_entry->parms.probability_rate); |
ab8c06b7 IS |
1377 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpagr), mpagr_pl); |
1378 | } | |
1379 | ||
1380 | static void | |
1381 | mlxsw_sp2_span_trigger_global_unbind(struct mlxsw_sp_span_trigger_entry * | |
1382 | trigger_entry) | |
1383 | { | |
1384 | /* There is no unbinding for global triggers. The trigger should be | |
1385 | * disabled on all ports by now. | |
1386 | */ | |
1387 | } | |
1388 | ||
1389 | static bool | |
1390 | mlxsw_sp2_span_trigger_global_matches(struct mlxsw_sp_span_trigger_entry * | |
1391 | trigger_entry, | |
1392 | enum mlxsw_sp_span_trigger trigger, | |
1393 | struct mlxsw_sp_port *mlxsw_sp_port) | |
1394 | { | |
1395 | return trigger_entry->trigger == trigger; | |
1396 | } | |
1397 | ||
2bafb216 IS |
1398 | static int |
1399 | __mlxsw_sp2_span_trigger_global_enable(struct mlxsw_sp_span_trigger_entry * | |
1400 | trigger_entry, | |
1401 | struct mlxsw_sp_port *mlxsw_sp_port, | |
1402 | u8 tc, bool enable) | |
1403 | { | |
1404 | struct mlxsw_sp *mlxsw_sp = trigger_entry->span->mlxsw_sp; | |
1405 | char momte_pl[MLXSW_REG_MOMTE_LEN]; | |
1406 | enum mlxsw_reg_momte_type type; | |
1407 | int err; | |
1408 | ||
1409 | switch (trigger_entry->trigger) { | |
1410 | case MLXSW_SP_SPAN_TRIGGER_TAIL_DROP: | |
1411 | type = MLXSW_REG_MOMTE_TYPE_SHARED_BUFFER_TCLASS; | |
1412 | break; | |
1413 | case MLXSW_SP_SPAN_TRIGGER_EARLY_DROP: | |
1414 | type = MLXSW_REG_MOMTE_TYPE_WRED; | |
1415 | break; | |
1416 | case MLXSW_SP_SPAN_TRIGGER_ECN: | |
1417 | type = MLXSW_REG_MOMTE_TYPE_ECN; | |
1418 | break; | |
1419 | default: | |
1420 | WARN_ON_ONCE(1); | |
1421 | return -EINVAL; | |
1422 | } | |
1423 | ||
1424 | /* Query existing configuration in order to only change the state of | |
1425 | * the specified traffic class. | |
1426 | */ | |
1427 | mlxsw_reg_momte_pack(momte_pl, mlxsw_sp_port->local_port, type); | |
1428 | err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(momte), momte_pl); | |
1429 | if (err) | |
1430 | return err; | |
1431 | ||
1432 | mlxsw_reg_momte_tclass_en_set(momte_pl, tc, enable); | |
1433 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(momte), momte_pl); | |
1434 | } | |
1435 | ||
1436 | static int | |
1437 | mlxsw_sp2_span_trigger_global_enable(struct mlxsw_sp_span_trigger_entry * | |
1438 | trigger_entry, | |
1439 | struct mlxsw_sp_port *mlxsw_sp_port, | |
1440 | u8 tc) | |
1441 | { | |
1442 | return __mlxsw_sp2_span_trigger_global_enable(trigger_entry, | |
1443 | mlxsw_sp_port, tc, true); | |
1444 | } | |
1445 | ||
1446 | static void | |
1447 | mlxsw_sp2_span_trigger_global_disable(struct mlxsw_sp_span_trigger_entry * | |
1448 | trigger_entry, | |
1449 | struct mlxsw_sp_port *mlxsw_sp_port, | |
1450 | u8 tc) | |
1451 | { | |
1452 | __mlxsw_sp2_span_trigger_global_enable(trigger_entry, mlxsw_sp_port, tc, | |
1453 | false); | |
1454 | } | |
1455 | ||
ab8c06b7 IS |
1456 | static const struct mlxsw_sp_span_trigger_ops |
1457 | mlxsw_sp2_span_trigger_global_ops = { | |
1458 | .bind = mlxsw_sp2_span_trigger_global_bind, | |
1459 | .unbind = mlxsw_sp2_span_trigger_global_unbind, | |
1460 | .matches = mlxsw_sp2_span_trigger_global_matches, | |
2bafb216 IS |
1461 | .enable = mlxsw_sp2_span_trigger_global_enable, |
1462 | .disable = mlxsw_sp2_span_trigger_global_disable, | |
ab8c06b7 IS |
1463 | }; |
1464 | ||
08a3641f | 1465 | static const struct mlxsw_sp_span_trigger_ops * |
ab8c06b7 | 1466 | mlxsw_sp2_span_trigger_ops_arr[] = { |
08a3641f | 1467 | [MLXSW_SP_SPAN_TRIGGER_TYPE_PORT] = &mlxsw_sp_span_trigger_port_ops, |
ab8c06b7 IS |
1468 | [MLXSW_SP_SPAN_TRIGGER_TYPE_GLOBAL] = |
1469 | &mlxsw_sp2_span_trigger_global_ops, | |
08a3641f IS |
1470 | }; |
1471 | ||
1472 | static void | |
1473 | mlxsw_sp_span_trigger_ops_set(struct mlxsw_sp_span_trigger_entry *trigger_entry) | |
1474 | { | |
1475 | struct mlxsw_sp_span *span = trigger_entry->span; | |
1476 | enum mlxsw_sp_span_trigger_type type; | |
1477 | ||
1478 | switch (trigger_entry->trigger) { | |
df561f66 | 1479 | case MLXSW_SP_SPAN_TRIGGER_INGRESS: |
08a3641f IS |
1480 | case MLXSW_SP_SPAN_TRIGGER_EGRESS: |
1481 | type = MLXSW_SP_SPAN_TRIGGER_TYPE_PORT; | |
1482 | break; | |
df561f66 GS |
1483 | case MLXSW_SP_SPAN_TRIGGER_TAIL_DROP: |
1484 | case MLXSW_SP_SPAN_TRIGGER_EARLY_DROP: | |
ab8c06b7 IS |
1485 | case MLXSW_SP_SPAN_TRIGGER_ECN: |
1486 | type = MLXSW_SP_SPAN_TRIGGER_TYPE_GLOBAL; | |
1487 | break; | |
08a3641f IS |
1488 | default: |
1489 | WARN_ON_ONCE(1); | |
1490 | return; | |
1491 | } | |
1492 | ||
1493 | trigger_entry->ops = span->span_trigger_ops_arr[type]; | |
c056618c IS |
1494 | } |
1495 | ||
1496 | static struct mlxsw_sp_span_trigger_entry * | |
1497 | mlxsw_sp_span_trigger_entry_create(struct mlxsw_sp_span *span, | |
1498 | enum mlxsw_sp_span_trigger trigger, | |
1499 | struct mlxsw_sp_port *mlxsw_sp_port, | |
1500 | const struct mlxsw_sp_span_trigger_parms | |
1501 | *parms) | |
1502 | { | |
1503 | struct mlxsw_sp_span_trigger_entry *trigger_entry; | |
1504 | int err; | |
1505 | ||
1506 | trigger_entry = kzalloc(sizeof(*trigger_entry), GFP_KERNEL); | |
1507 | if (!trigger_entry) | |
1508 | return ERR_PTR(-ENOMEM); | |
1509 | ||
1510 | refcount_set(&trigger_entry->ref_count, 1); | |
08a3641f IS |
1511 | trigger_entry->local_port = mlxsw_sp_port ? mlxsw_sp_port->local_port : |
1512 | 0; | |
c056618c IS |
1513 | trigger_entry->trigger = trigger; |
1514 | memcpy(&trigger_entry->parms, parms, sizeof(trigger_entry->parms)); | |
08a3641f IS |
1515 | trigger_entry->span = span; |
1516 | mlxsw_sp_span_trigger_ops_set(trigger_entry); | |
c056618c IS |
1517 | list_add_tail(&trigger_entry->list, &span->trigger_entries_list); |
1518 | ||
08a3641f | 1519 | err = trigger_entry->ops->bind(trigger_entry); |
c056618c IS |
1520 | if (err) |
1521 | goto err_trigger_entry_bind; | |
1522 | ||
1523 | return trigger_entry; | |
1524 | ||
1525 | err_trigger_entry_bind: | |
1526 | list_del(&trigger_entry->list); | |
1527 | kfree(trigger_entry); | |
1528 | return ERR_PTR(err); | |
1529 | } | |
1530 | ||
1531 | static void | |
1532 | mlxsw_sp_span_trigger_entry_destroy(struct mlxsw_sp_span *span, | |
1533 | struct mlxsw_sp_span_trigger_entry * | |
1534 | trigger_entry) | |
1535 | { | |
08a3641f | 1536 | trigger_entry->ops->unbind(trigger_entry); |
c056618c IS |
1537 | list_del(&trigger_entry->list); |
1538 | kfree(trigger_entry); | |
1539 | } | |
1540 | ||
1541 | static struct mlxsw_sp_span_trigger_entry * | |
1542 | mlxsw_sp_span_trigger_entry_find(struct mlxsw_sp_span *span, | |
1543 | enum mlxsw_sp_span_trigger trigger, | |
1544 | struct mlxsw_sp_port *mlxsw_sp_port) | |
1545 | { | |
1546 | struct mlxsw_sp_span_trigger_entry *trigger_entry; | |
1547 | ||
1548 | list_for_each_entry(trigger_entry, &span->trigger_entries_list, list) { | |
08a3641f IS |
1549 | if (trigger_entry->ops->matches(trigger_entry, trigger, |
1550 | mlxsw_sp_port)) | |
c056618c IS |
1551 | return trigger_entry; |
1552 | } | |
1553 | ||
1554 | return NULL; | |
1555 | } | |
1556 | ||
1557 | int mlxsw_sp_span_agent_bind(struct mlxsw_sp *mlxsw_sp, | |
1558 | enum mlxsw_sp_span_trigger trigger, | |
1559 | struct mlxsw_sp_port *mlxsw_sp_port, | |
1560 | const struct mlxsw_sp_span_trigger_parms *parms) | |
1561 | { | |
1562 | struct mlxsw_sp_span_trigger_entry *trigger_entry; | |
1563 | int err = 0; | |
1564 | ||
1565 | ASSERT_RTNL(); | |
1566 | ||
1567 | if (!mlxsw_sp_span_entry_find_by_id(mlxsw_sp, parms->span_id)) | |
1568 | return -EINVAL; | |
1569 | ||
1570 | trigger_entry = mlxsw_sp_span_trigger_entry_find(mlxsw_sp->span, | |
1571 | trigger, | |
1572 | mlxsw_sp_port); | |
1573 | if (trigger_entry) { | |
2dcbd920 IS |
1574 | if (trigger_entry->parms.span_id != parms->span_id || |
1575 | trigger_entry->parms.probability_rate != | |
1576 | parms->probability_rate) | |
c056618c IS |
1577 | return -EINVAL; |
1578 | refcount_inc(&trigger_entry->ref_count); | |
1579 | goto out; | |
1580 | } | |
1581 | ||
1582 | trigger_entry = mlxsw_sp_span_trigger_entry_create(mlxsw_sp->span, | |
1583 | trigger, | |
1584 | mlxsw_sp_port, | |
1585 | parms); | |
1586 | if (IS_ERR(trigger_entry)) | |
1587 | err = PTR_ERR(trigger_entry); | |
1588 | ||
1589 | out: | |
1590 | return err; | |
1591 | } | |
1592 | ||
1593 | void mlxsw_sp_span_agent_unbind(struct mlxsw_sp *mlxsw_sp, | |
1594 | enum mlxsw_sp_span_trigger trigger, | |
1595 | struct mlxsw_sp_port *mlxsw_sp_port, | |
1596 | const struct mlxsw_sp_span_trigger_parms *parms) | |
1597 | { | |
1598 | struct mlxsw_sp_span_trigger_entry *trigger_entry; | |
1599 | ||
1600 | ASSERT_RTNL(); | |
1601 | ||
1602 | if (WARN_ON_ONCE(!mlxsw_sp_span_entry_find_by_id(mlxsw_sp, | |
1603 | parms->span_id))) | |
1604 | return; | |
1605 | ||
1606 | trigger_entry = mlxsw_sp_span_trigger_entry_find(mlxsw_sp->span, | |
1607 | trigger, | |
1608 | mlxsw_sp_port); | |
1609 | if (WARN_ON_ONCE(!trigger_entry)) | |
1610 | return; | |
1611 | ||
1612 | if (!refcount_dec_and_test(&trigger_entry->ref_count)) | |
1613 | return; | |
1614 | ||
1615 | mlxsw_sp_span_trigger_entry_destroy(mlxsw_sp->span, trigger_entry); | |
1616 | } | |
4bafb85a | 1617 | |
2bafb216 IS |
1618 | int mlxsw_sp_span_trigger_enable(struct mlxsw_sp_port *mlxsw_sp_port, |
1619 | enum mlxsw_sp_span_trigger trigger, u8 tc) | |
1620 | { | |
1621 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; | |
1622 | struct mlxsw_sp_span_trigger_entry *trigger_entry; | |
1623 | ||
1624 | ASSERT_RTNL(); | |
1625 | ||
1626 | trigger_entry = mlxsw_sp_span_trigger_entry_find(mlxsw_sp->span, | |
1627 | trigger, | |
1628 | mlxsw_sp_port); | |
1629 | if (WARN_ON_ONCE(!trigger_entry)) | |
1630 | return -EINVAL; | |
1631 | ||
1632 | return trigger_entry->ops->enable(trigger_entry, mlxsw_sp_port, tc); | |
1633 | } | |
1634 | ||
1635 | void mlxsw_sp_span_trigger_disable(struct mlxsw_sp_port *mlxsw_sp_port, | |
1636 | enum mlxsw_sp_span_trigger trigger, u8 tc) | |
1637 | { | |
1638 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; | |
1639 | struct mlxsw_sp_span_trigger_entry *trigger_entry; | |
1640 | ||
1641 | ASSERT_RTNL(); | |
1642 | ||
1643 | trigger_entry = mlxsw_sp_span_trigger_entry_find(mlxsw_sp->span, | |
1644 | trigger, | |
1645 | mlxsw_sp_port); | |
1646 | if (WARN_ON_ONCE(!trigger_entry)) | |
1647 | return; | |
1648 | ||
1649 | return trigger_entry->ops->disable(trigger_entry, mlxsw_sp_port, tc); | |
1650 | } | |
1651 | ||
0908e42a PM |
1652 | bool mlxsw_sp_span_trigger_is_ingress(enum mlxsw_sp_span_trigger trigger) |
1653 | { | |
1654 | switch (trigger) { | |
1655 | case MLXSW_SP_SPAN_TRIGGER_INGRESS: | |
1656 | case MLXSW_SP_SPAN_TRIGGER_EARLY_DROP: | |
1657 | case MLXSW_SP_SPAN_TRIGGER_TAIL_DROP: | |
1658 | return true; | |
1659 | case MLXSW_SP_SPAN_TRIGGER_EGRESS: | |
1660 | case MLXSW_SP_SPAN_TRIGGER_ECN: | |
1661 | return false; | |
1662 | } | |
1663 | ||
1664 | WARN_ON_ONCE(1); | |
1665 | return false; | |
1666 | } | |
1667 | ||
08a3641f IS |
1668 | static int mlxsw_sp1_span_init(struct mlxsw_sp *mlxsw_sp) |
1669 | { | |
34e4ace5 IS |
1670 | size_t arr_size = ARRAY_SIZE(mlxsw_sp1_span_entry_ops_arr); |
1671 | ||
fa8c08b8 IS |
1672 | /* Must be first to avoid NULL pointer dereference by subsequent |
1673 | * can_handle() callbacks. | |
1674 | */ | |
1675 | if (WARN_ON(mlxsw_sp1_span_entry_ops_arr[0] != | |
1676 | &mlxsw_sp1_span_entry_ops_cpu)) | |
1677 | return -EINVAL; | |
1678 | ||
ab8c06b7 | 1679 | mlxsw_sp->span->span_trigger_ops_arr = mlxsw_sp1_span_trigger_ops_arr; |
34e4ace5 IS |
1680 | mlxsw_sp->span->span_entry_ops_arr = mlxsw_sp1_span_entry_ops_arr; |
1681 | mlxsw_sp->span->span_entry_ops_arr_size = arr_size; | |
08a3641f IS |
1682 | |
1683 | return 0; | |
1684 | } | |
1685 | ||
4039504e IS |
1686 | static int mlxsw_sp1_span_policer_id_base_set(struct mlxsw_sp *mlxsw_sp, |
1687 | u16 policer_id_base) | |
1688 | { | |
1689 | return -EOPNOTSUPP; | |
1690 | } | |
1691 | ||
4bafb85a | 1692 | const struct mlxsw_sp_span_ops mlxsw_sp1_span_ops = { |
08a3641f | 1693 | .init = mlxsw_sp1_span_init, |
4039504e | 1694 | .policer_id_base_set = mlxsw_sp1_span_policer_id_base_set, |
4bafb85a IS |
1695 | }; |
1696 | ||
08a3641f IS |
1697 | static int mlxsw_sp2_span_init(struct mlxsw_sp *mlxsw_sp) |
1698 | { | |
34e4ace5 IS |
1699 | size_t arr_size = ARRAY_SIZE(mlxsw_sp2_span_entry_ops_arr); |
1700 | ||
fa8c08b8 IS |
1701 | /* Must be first to avoid NULL pointer dereference by subsequent |
1702 | * can_handle() callbacks. | |
1703 | */ | |
1704 | if (WARN_ON(mlxsw_sp2_span_entry_ops_arr[0] != | |
1705 | &mlxsw_sp2_span_entry_ops_cpu)) | |
1706 | return -EINVAL; | |
1707 | ||
ab8c06b7 | 1708 | mlxsw_sp->span->span_trigger_ops_arr = mlxsw_sp2_span_trigger_ops_arr; |
34e4ace5 IS |
1709 | mlxsw_sp->span->span_entry_ops_arr = mlxsw_sp2_span_entry_ops_arr; |
1710 | mlxsw_sp->span->span_entry_ops_arr_size = arr_size; | |
08a3641f IS |
1711 | |
1712 | return 0; | |
1713 | } | |
1714 | ||
4bafb85a IS |
1715 | #define MLXSW_SP2_SPAN_EG_MIRROR_BUFFER_FACTOR 38 |
1716 | #define MLXSW_SP3_SPAN_EG_MIRROR_BUFFER_FACTOR 50 | |
1717 | ||
4039504e IS |
1718 | static int mlxsw_sp2_span_policer_id_base_set(struct mlxsw_sp *mlxsw_sp, |
1719 | u16 policer_id_base) | |
1720 | { | |
1721 | char mogcr_pl[MLXSW_REG_MOGCR_LEN]; | |
1722 | int err; | |
1723 | ||
1724 | err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(mogcr), mogcr_pl); | |
1725 | if (err) | |
1726 | return err; | |
1727 | ||
1728 | mlxsw_reg_mogcr_mirroring_pid_base_set(mogcr_pl, policer_id_base); | |
1729 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mogcr), mogcr_pl); | |
1730 | } | |
1731 | ||
4bafb85a | 1732 | const struct mlxsw_sp_span_ops mlxsw_sp2_span_ops = { |
08a3641f | 1733 | .init = mlxsw_sp2_span_init, |
4039504e | 1734 | .policer_id_base_set = mlxsw_sp2_span_policer_id_base_set, |
4bafb85a IS |
1735 | }; |
1736 | ||
4bafb85a | 1737 | const struct mlxsw_sp_span_ops mlxsw_sp3_span_ops = { |
08a3641f | 1738 | .init = mlxsw_sp2_span_init, |
4039504e | 1739 | .policer_id_base_set = mlxsw_sp2_span_policer_id_base_set, |
4bafb85a | 1740 | }; |