Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  1// SPDX-License-Identifier: GPL-2.0+
  2
  3#include <net/switchdev.h>
  4
  5#include "lan966x_main.h"
  6
  7struct lan966x_pgid_entry {
  8	struct list_head list;
  9	int index;
 10	refcount_t refcount;
 11	u16 ports;
 12};
 13
 14struct 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
 23void lan966x_mdb_init(struct lan966x *lan966x)
 24{
 25	INIT_LIST_HEAD(&lan966x->mdb_entries);
 26	INIT_LIST_HEAD(&lan966x->pgid_entries);
 27}
 28
 29static 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
 39static 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
 49void lan966x_mdb_deinit(struct lan966x *lan966x)
 50{
 51	lan966x_mdb_purge_mdb_entries(lan966x);
 52	lan966x_mdb_purge_pgid_entries(lan966x);
 53}
 54
 55static struct lan966x_mdb_entry *
 56lan966x_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
 71static struct lan966x_mdb_entry *
 72lan966x_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
 89static 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
105static 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
140static 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
182static struct lan966x_pgid_entry *
183lan966x_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
200static struct lan966x_pgid_entry *
201lan966x_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
238static 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
248static 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
297static 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
354static enum macaccess_entry_type
355lan966x_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
364int 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
381int 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
398static 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
409static 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
436void 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
453static 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
464static 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
491void 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
508void 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
525void 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}