1 // SPDX-License-Identifier: GPL-2.0+
2 
3 #include <net/switchdev.h>
4 
5 #include "lan966x_main.h"
6 
7 struct lan966x_pgid_entry {
8 	struct list_head list;
9 	int index;
10 	refcount_t refcount;
11 	u16 ports;
12 };
13 
14 struct lan966x_mdb_entry {
15 	struct list_head list;
16 	unsigned char mac[ETH_ALEN];
17 	u16 vid;
18 	u16 ports;
19 	struct lan966x_pgid_entry *pgid;
20 	u8 cpu_copy;
21 };
22 
23 void lan966x_mdb_init(struct lan966x *lan966x)
24 {
25 	INIT_LIST_HEAD(&lan966x->mdb_entries);
26 	INIT_LIST_HEAD(&lan966x->pgid_entries);
27 }
28 
29 static void lan966x_mdb_purge_mdb_entries(struct lan966x *lan966x)
30 {
31 	struct lan966x_mdb_entry *mdb_entry, *tmp;
32 
33 	list_for_each_entry_safe(mdb_entry, tmp, &lan966x->mdb_entries, list) {
34 		list_del(&mdb_entry->list);
35 		kfree(mdb_entry);
36 	}
37 }
38 
39 static void lan966x_mdb_purge_pgid_entries(struct lan966x *lan966x)
40 {
41 	struct lan966x_pgid_entry *pgid_entry, *tmp;
42 
43 	list_for_each_entry_safe(pgid_entry, tmp, &lan966x->pgid_entries, list) {
44 		list_del(&pgid_entry->list);
45 		kfree(pgid_entry);
46 	}
47 }
48 
49 void lan966x_mdb_deinit(struct lan966x *lan966x)
50 {
51 	lan966x_mdb_purge_mdb_entries(lan966x);
52 	lan966x_mdb_purge_pgid_entries(lan966x);
53 }
54 
55 static struct lan966x_mdb_entry *
56 lan966x_mdb_entry_get(struct lan966x *lan966x,
57 		      const unsigned char *mac,
58 		      u16 vid)
59 {
60 	struct lan966x_mdb_entry *mdb_entry;
61 
62 	list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
63 		if (ether_addr_equal(mdb_entry->mac, mac) &&
64 		    mdb_entry->vid == vid)
65 			return mdb_entry;
66 	}
67 
68 	return NULL;
69 }
70 
71 static struct lan966x_mdb_entry *
72 lan966x_mdb_entry_add(struct lan966x *lan966x,
73 		      const struct switchdev_obj_port_mdb *mdb)
74 {
75 	struct lan966x_mdb_entry *mdb_entry;
76 
77 	mdb_entry = kzalloc(sizeof(*mdb_entry), GFP_KERNEL);
78 	if (!mdb_entry)
79 		return ERR_PTR(-ENOMEM);
80 
81 	ether_addr_copy(mdb_entry->mac, mdb->addr);
82 	mdb_entry->vid = mdb->vid;
83 
84 	list_add_tail(&mdb_entry->list, &lan966x->mdb_entries);
85 
86 	return mdb_entry;
87 }
88 
89 static void lan966x_mdb_encode_mac(unsigned char *mac,
90 				   struct lan966x_mdb_entry *mdb_entry,
91 				   enum macaccess_entry_type type)
92 {
93 	ether_addr_copy(mac, mdb_entry->mac);
94 
95 	if (type == ENTRYTYPE_MACV4) {
96 		mac[0] = 0;
97 		mac[1] = mdb_entry->ports >> 8;
98 		mac[2] = mdb_entry->ports & 0xff;
99 	} else if (type == ENTRYTYPE_MACV6) {
100 		mac[0] = mdb_entry->ports >> 8;
101 		mac[1] = mdb_entry->ports & 0xff;
102 	}
103 }
104 
105 static int lan966x_mdb_ip_add(struct lan966x_port *port,
106 			      const struct switchdev_obj_port_mdb *mdb,
107 			      enum macaccess_entry_type type)
108 {
109 	bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
110 	struct lan966x *lan966x = port->lan966x;
111 	struct lan966x_mdb_entry *mdb_entry;
112 	unsigned char mac[ETH_ALEN];
113 	bool cpu_copy = false;
114 
115 	mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
116 	if (!mdb_entry) {
117 		mdb_entry = lan966x_mdb_entry_add(lan966x, mdb);
118 		if (IS_ERR(mdb_entry))
119 			return PTR_ERR(mdb_entry);
120 	} else {
121 		lan966x_mdb_encode_mac(mac, mdb_entry, type);
122 		lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
123 	}
124 
125 	if (cpu_port)
126 		mdb_entry->cpu_copy++;
127 	else
128 		mdb_entry->ports |= BIT(port->chip_port);
129 
130 	/* Copy the frame to CPU only if the CPU is in the VLAN */
131 	if (lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, mdb_entry->vid) &&
132 	    mdb_entry->cpu_copy)
133 		cpu_copy = true;
134 
135 	lan966x_mdb_encode_mac(mac, mdb_entry, type);
136 	return lan966x_mac_ip_learn(lan966x, cpu_copy,
137 				    mac, mdb_entry->vid, type);
138 }
139 
140 static int lan966x_mdb_ip_del(struct lan966x_port *port,
141 			      const struct switchdev_obj_port_mdb *mdb,
142 			      enum macaccess_entry_type type)
143 {
144 	bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
145 	struct lan966x *lan966x = port->lan966x;
146 	struct lan966x_mdb_entry *mdb_entry;
147 	unsigned char mac[ETH_ALEN];
148 	u16 ports;
149 
150 	mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
151 	if (!mdb_entry)
152 		return -ENOENT;
153 
154 	ports = mdb_entry->ports;
155 	if (cpu_port) {
156 		/* If there are still other references to the CPU port then
157 		 * there is no point to delete and add again the same entry
158 		 */
159 		mdb_entry->cpu_copy--;
160 		if (mdb_entry->cpu_copy)
161 			return 0;
162 	} else {
163 		ports &= ~BIT(port->chip_port);
164 	}
165 
166 	lan966x_mdb_encode_mac(mac, mdb_entry, type);
167 	lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
168 
169 	mdb_entry->ports = ports;
170 
171 	if (!mdb_entry->ports && !mdb_entry->cpu_copy) {
172 		list_del(&mdb_entry->list);
173 		kfree(mdb_entry);
174 		return 0;
175 	}
176 
177 	lan966x_mdb_encode_mac(mac, mdb_entry, type);
178 	return lan966x_mac_ip_learn(lan966x, mdb_entry->cpu_copy,
179 				    mac, mdb_entry->vid, type);
180 }
181 
182 static struct lan966x_pgid_entry *
183 lan966x_pgid_entry_add(struct lan966x *lan966x, int index, u16 ports)
184 {
185 	struct lan966x_pgid_entry *pgid_entry;
186 
187 	pgid_entry = kzalloc(sizeof(*pgid_entry), GFP_KERNEL);
188 	if (!pgid_entry)
189 		return ERR_PTR(-ENOMEM);
190 
191 	pgid_entry->ports = ports;
192 	pgid_entry->index = index;
193 	refcount_set(&pgid_entry->refcount, 1);
194 
195 	list_add_tail(&pgid_entry->list, &lan966x->pgid_entries);
196 
197 	return pgid_entry;
198 }
199 
200 static struct lan966x_pgid_entry *
201 lan966x_pgid_entry_get(struct lan966x *lan966x,
202 		       struct lan966x_mdb_entry *mdb_entry)
203 {
204 	struct lan966x_pgid_entry *pgid_entry;
205 	int index;
206 
207 	/* Try to find an existing pgid that uses the same ports as the
208 	 * mdb_entry
209 	 */
210 	list_for_each_entry(pgid_entry, &lan966x->pgid_entries, list) {
211 		if (pgid_entry->ports == mdb_entry->ports) {
212 			refcount_inc(&pgid_entry->refcount);
213 			return pgid_entry;
214 		}
215 	}
216 
217 	/* Try to find an empty pgid entry and allocate one in case it finds it,
218 	 * otherwise it means that there are no more resources
219 	 */
220 	for (index = PGID_GP_START; index < PGID_GP_END; index++) {
221 		bool used = false;
222 
223 		list_for_each_entry(pgid_entry, &lan966x->pgid_entries, list) {
224 			if (pgid_entry->index == index) {
225 				used = true;
226 				break;
227 			}
228 		}
229 
230 		if (!used)
231 			return lan966x_pgid_entry_add(lan966x, index,
232 						      mdb_entry->ports);
233 	}
234 
235 	return ERR_PTR(-ENOSPC);
236 }
237 
238 static void lan966x_pgid_entry_del(struct lan966x *lan966x,
239 				   struct lan966x_pgid_entry *pgid_entry)
240 {
241 	if (!refcount_dec_and_test(&pgid_entry->refcount))
242 		return;
243 
244 	list_del(&pgid_entry->list);
245 	kfree(pgid_entry);
246 }
247 
248 static int lan966x_mdb_l2_add(struct lan966x_port *port,
249 			      const struct switchdev_obj_port_mdb *mdb,
250 			      enum macaccess_entry_type type)
251 {
252 	bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
253 	struct lan966x *lan966x = port->lan966x;
254 	struct lan966x_pgid_entry *pgid_entry;
255 	struct lan966x_mdb_entry *mdb_entry;
256 	unsigned char mac[ETH_ALEN];
257 
258 	mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
259 	if (!mdb_entry) {
260 		mdb_entry = lan966x_mdb_entry_add(lan966x, mdb);
261 		if (IS_ERR(mdb_entry))
262 			return PTR_ERR(mdb_entry);
263 	} else {
264 		lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
265 		lan966x_mdb_encode_mac(mac, mdb_entry, type);
266 		lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
267 	}
268 
269 	if (cpu_port) {
270 		mdb_entry->ports |= BIT(CPU_PORT);
271 		mdb_entry->cpu_copy++;
272 	} else {
273 		mdb_entry->ports |= BIT(port->chip_port);
274 	}
275 
276 	pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
277 	if (IS_ERR(pgid_entry)) {
278 		list_del(&mdb_entry->list);
279 		kfree(mdb_entry);
280 		return PTR_ERR(pgid_entry);
281 	}
282 	mdb_entry->pgid = pgid_entry;
283 
284 	/* Copy the frame to CPU only if the CPU is in the VLAN */
285 	if (!lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, mdb_entry->vid) &&
286 	    mdb_entry->cpu_copy)
287 		mdb_entry->ports &= BIT(CPU_PORT);
288 
289 	lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
290 		ANA_PGID_PGID,
291 		lan966x, ANA_PGID(pgid_entry->index));
292 
293 	return lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
294 				 mdb_entry->vid, type);
295 }
296 
297 static int lan966x_mdb_l2_del(struct lan966x_port *port,
298 			      const struct switchdev_obj_port_mdb *mdb,
299 			      enum macaccess_entry_type type)
300 {
301 	bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
302 	struct lan966x *lan966x = port->lan966x;
303 	struct lan966x_pgid_entry *pgid_entry;
304 	struct lan966x_mdb_entry *mdb_entry;
305 	unsigned char mac[ETH_ALEN];
306 	u16 ports;
307 
308 	mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
309 	if (!mdb_entry)
310 		return -ENOENT;
311 
312 	ports = mdb_entry->ports;
313 	if (cpu_port) {
314 		/* If there are still other references to the CPU port then
315 		 * there is no point to delete and add again the same entry
316 		 */
317 		mdb_entry->cpu_copy--;
318 		if (mdb_entry->cpu_copy)
319 			return 0;
320 
321 		ports &= ~BIT(CPU_PORT);
322 	} else {
323 		ports &= ~BIT(port->chip_port);
324 	}
325 
326 	lan966x_mdb_encode_mac(mac, mdb_entry, type);
327 	lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
328 	lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
329 
330 	mdb_entry->ports = ports;
331 
332 	if (!mdb_entry->ports) {
333 		list_del(&mdb_entry->list);
334 		kfree(mdb_entry);
335 		return 0;
336 	}
337 
338 	pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
339 	if (IS_ERR(pgid_entry)) {
340 		list_del(&mdb_entry->list);
341 		kfree(mdb_entry);
342 		return PTR_ERR(pgid_entry);
343 	}
344 	mdb_entry->pgid = pgid_entry;
345 
346 	lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
347 		ANA_PGID_PGID,
348 		lan966x, ANA_PGID(pgid_entry->index));
349 
350 	return lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
351 				 mdb_entry->vid, type);
352 }
353 
354 static enum macaccess_entry_type
355 lan966x_mdb_classify(const unsigned char *mac)
356 {
357 	if (mac[0] == 0x01 && mac[1] == 0x00 && mac[2] == 0x5e)
358 		return ENTRYTYPE_MACV4;
359 	if (mac[0] == 0x33 && mac[1] == 0x33)
360 		return ENTRYTYPE_MACV6;
361 	return ENTRYTYPE_LOCKED;
362 }
363 
364 int lan966x_handle_port_mdb_add(struct lan966x_port *port,
365 				const struct switchdev_obj *obj)
366 {
367 	const struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
368 	enum macaccess_entry_type type;
369 
370 	/* Split the way the entries are added for ipv4/ipv6 and for l2. The
371 	 * reason is that for ipv4/ipv6 it doesn't require to use any pgid
372 	 * entry, while for l2 is required to use pgid entries
373 	 */
374 	type = lan966x_mdb_classify(mdb->addr);
375 	if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
376 		return lan966x_mdb_ip_add(port, mdb, type);
377 
378 	return lan966x_mdb_l2_add(port, mdb, type);
379 }
380 
381 int lan966x_handle_port_mdb_del(struct lan966x_port *port,
382 				const struct switchdev_obj *obj)
383 {
384 	const struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
385 	enum macaccess_entry_type type;
386 
387 	/* Split the way the entries are removed for ipv4/ipv6 and for l2. The
388 	 * reason is that for ipv4/ipv6 it doesn't require to use any pgid
389 	 * entry, while for l2 is required to use pgid entries
390 	 */
391 	type = lan966x_mdb_classify(mdb->addr);
392 	if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
393 		return lan966x_mdb_ip_del(port, mdb, type);
394 
395 	return lan966x_mdb_l2_del(port, mdb, type);
396 }
397 
398 static void lan966x_mdb_ip_cpu_copy(struct lan966x *lan966x,
399 				    struct lan966x_mdb_entry *mdb_entry,
400 				    enum macaccess_entry_type type)
401 {
402 	unsigned char mac[ETH_ALEN];
403 
404 	lan966x_mdb_encode_mac(mac, mdb_entry, type);
405 	lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
406 	lan966x_mac_ip_learn(lan966x, true, mac, mdb_entry->vid, type);
407 }
408 
409 static void lan966x_mdb_l2_cpu_copy(struct lan966x *lan966x,
410 				    struct lan966x_mdb_entry *mdb_entry,
411 				    enum macaccess_entry_type type)
412 {
413 	struct lan966x_pgid_entry *pgid_entry;
414 	unsigned char mac[ETH_ALEN];
415 
416 	lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
417 	lan966x_mdb_encode_mac(mac, mdb_entry, type);
418 	lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
419 
420 	mdb_entry->ports |= BIT(CPU_PORT);
421 
422 	pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
423 	if (IS_ERR(pgid_entry))
424 		return;
425 
426 	mdb_entry->pgid = pgid_entry;
427 
428 	lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
429 		ANA_PGID_PGID,
430 		lan966x, ANA_PGID(pgid_entry->index));
431 
432 	lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
433 			  mdb_entry->vid, type);
434 }
435 
436 void lan966x_mdb_write_entries(struct lan966x *lan966x, u16 vid)
437 {
438 	struct lan966x_mdb_entry *mdb_entry;
439 	enum macaccess_entry_type type;
440 
441 	list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
442 		if (mdb_entry->vid != vid || !mdb_entry->cpu_copy)
443 			continue;
444 
445 		type = lan966x_mdb_classify(mdb_entry->mac);
446 		if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
447 			lan966x_mdb_ip_cpu_copy(lan966x, mdb_entry, type);
448 		else
449 			lan966x_mdb_l2_cpu_copy(lan966x, mdb_entry, type);
450 	}
451 }
452 
453 static void lan966x_mdb_ip_cpu_remove(struct lan966x *lan966x,
454 				      struct lan966x_mdb_entry *mdb_entry,
455 				      enum macaccess_entry_type type)
456 {
457 	unsigned char mac[ETH_ALEN];
458 
459 	lan966x_mdb_encode_mac(mac, mdb_entry, type);
460 	lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
461 	lan966x_mac_ip_learn(lan966x, false, mac, mdb_entry->vid, type);
462 }
463 
464 static void lan966x_mdb_l2_cpu_remove(struct lan966x *lan966x,
465 				      struct lan966x_mdb_entry *mdb_entry,
466 				      enum macaccess_entry_type type)
467 {
468 	struct lan966x_pgid_entry *pgid_entry;
469 	unsigned char mac[ETH_ALEN];
470 
471 	lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
472 	lan966x_mdb_encode_mac(mac, mdb_entry, type);
473 	lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
474 
475 	mdb_entry->ports &= ~BIT(CPU_PORT);
476 
477 	pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
478 	if (IS_ERR(pgid_entry))
479 		return;
480 
481 	mdb_entry->pgid = pgid_entry;
482 
483 	lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
484 		ANA_PGID_PGID,
485 		lan966x, ANA_PGID(pgid_entry->index));
486 
487 	lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
488 			  mdb_entry->vid, type);
489 }
490 
491 void lan966x_mdb_erase_entries(struct lan966x *lan966x, u16 vid)
492 {
493 	struct lan966x_mdb_entry *mdb_entry;
494 	enum macaccess_entry_type type;
495 
496 	list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
497 		if (mdb_entry->vid != vid || !mdb_entry->cpu_copy)
498 			continue;
499 
500 		type = lan966x_mdb_classify(mdb_entry->mac);
501 		if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
502 			lan966x_mdb_ip_cpu_remove(lan966x, mdb_entry, type);
503 		else
504 			lan966x_mdb_l2_cpu_remove(lan966x, mdb_entry, type);
505 	}
506 }
507 
508 void lan966x_mdb_clear_entries(struct lan966x *lan966x)
509 {
510 	struct lan966x_mdb_entry *mdb_entry;
511 	enum macaccess_entry_type type;
512 	unsigned char mac[ETH_ALEN];
513 
514 	list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
515 		type = lan966x_mdb_classify(mdb_entry->mac);
516 
517 		lan966x_mdb_encode_mac(mac, mdb_entry, type);
518 		/* Remove just the MAC entry, still keep the PGID in case of L2
519 		 * entries because this can be restored at later point
520 		 */
521 		lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
522 	}
523 }
524 
525 void lan966x_mdb_restore_entries(struct lan966x *lan966x)
526 {
527 	struct lan966x_mdb_entry *mdb_entry;
528 	enum macaccess_entry_type type;
529 	unsigned char mac[ETH_ALEN];
530 	bool cpu_copy = false;
531 
532 	list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
533 		type = lan966x_mdb_classify(mdb_entry->mac);
534 
535 		lan966x_mdb_encode_mac(mac, mdb_entry, type);
536 		if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6) {
537 			/* Copy the frame to CPU only if the CPU is in the VLAN */
538 			if (lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x,
539 								  mdb_entry->vid) &&
540 			    mdb_entry->cpu_copy)
541 				cpu_copy = true;
542 
543 			lan966x_mac_ip_learn(lan966x, cpu_copy, mac,
544 					     mdb_entry->vid, type);
545 		} else {
546 			lan966x_mac_learn(lan966x, mdb_entry->pgid->index,
547 					  mdb_entry->mac,
548 					  mdb_entry->vid, type);
549 		}
550 	}
551 }
552