@@ -958,52 +958,88 @@ static enum macaccess_entry_type ocelot_classify_mdb(const unsigned char *addr)
958958 return ENTRYTYPE_MACv4 ;
959959 if (addr [0 ] == 0x33 && addr [1 ] == 0x33 )
960960 return ENTRYTYPE_MACv6 ;
961- return ENTRYTYPE_NORMAL ;
961+ return ENTRYTYPE_LOCKED ;
962962}
963963
964- static int ocelot_mdb_get_pgid (struct ocelot * ocelot ,
965- enum macaccess_entry_type entry_type )
964+ static struct ocelot_pgid * ocelot_pgid_alloc (struct ocelot * ocelot , int index ,
965+ unsigned long ports )
966966{
967- int pgid ;
967+ struct ocelot_pgid * pgid ;
968+
969+ pgid = kzalloc (sizeof (* pgid ), GFP_KERNEL );
970+ if (!pgid )
971+ return ERR_PTR (- ENOMEM );
972+
973+ pgid -> ports = ports ;
974+ pgid -> index = index ;
975+ refcount_set (& pgid -> refcount , 1 );
976+ list_add_tail (& pgid -> list , & ocelot -> pgids );
977+
978+ return pgid ;
979+ }
980+
981+ static void ocelot_pgid_free (struct ocelot * ocelot , struct ocelot_pgid * pgid )
982+ {
983+ if (!refcount_dec_and_test (& pgid -> refcount ))
984+ return ;
985+
986+ list_del (& pgid -> list );
987+ kfree (pgid );
988+ }
989+
990+ static struct ocelot_pgid * ocelot_mdb_get_pgid (struct ocelot * ocelot ,
991+ const struct ocelot_multicast * mc )
992+ {
993+ struct ocelot_pgid * pgid ;
994+ int index ;
968995
969996 /* According to VSC7514 datasheet 3.9.1.5 IPv4 Multicast Entries and
970997 * 3.9.1.6 IPv6 Multicast Entries, "Instead of a lookup in the
971998 * destination mask table (PGID), the destination set is programmed as
972999 * part of the entry MAC address.", and the DEST_IDX is set to 0.
9731000 */
974- if (entry_type == ENTRYTYPE_MACv4 ||
975- entry_type == ENTRYTYPE_MACv6 )
976- return 0 ;
1001+ if (mc -> entry_type == ENTRYTYPE_MACv4 ||
1002+ mc -> entry_type == ENTRYTYPE_MACv6 )
1003+ return ocelot_pgid_alloc ( ocelot , 0 , mc -> ports ) ;
9771004
978- for_each_nonreserved_multicast_dest_pgid (ocelot , pgid ) {
979- struct ocelot_multicast * mc ;
1005+ list_for_each_entry (pgid , & ocelot -> pgids , list ) {
1006+ /* When searching for a nonreserved multicast PGID, ignore the
1007+ * dummy PGID of zero that we have for MACv4/MACv6 entries
1008+ */
1009+ if (pgid -> index && pgid -> ports == mc -> ports ) {
1010+ refcount_inc (& pgid -> refcount );
1011+ return pgid ;
1012+ }
1013+ }
1014+
1015+ /* Search for a free index in the nonreserved multicast PGID area */
1016+ for_each_nonreserved_multicast_dest_pgid (ocelot , index ) {
9801017 bool used = false;
9811018
982- list_for_each_entry (mc , & ocelot -> multicast , list ) {
983- if (mc -> pgid == pgid ) {
1019+ list_for_each_entry (pgid , & ocelot -> pgids , list ) {
1020+ if (pgid -> index == index ) {
9841021 used = true;
9851022 break ;
9861023 }
9871024 }
9881025
9891026 if (!used )
990- return pgid ;
1027+ return ocelot_pgid_alloc ( ocelot , index , mc -> ports ) ;
9911028 }
9921029
993- return -1 ;
1030+ return ERR_PTR ( - ENOSPC ) ;
9941031}
9951032
9961033static void ocelot_encode_ports_to_mdb (unsigned char * addr ,
997- struct ocelot_multicast * mc ,
998- enum macaccess_entry_type entry_type )
1034+ struct ocelot_multicast * mc )
9991035{
1000- memcpy (addr , mc -> addr , ETH_ALEN );
1036+ ether_addr_copy (addr , mc -> addr );
10011037
1002- if (entry_type == ENTRYTYPE_MACv4 ) {
1038+ if (mc -> entry_type == ENTRYTYPE_MACv4 ) {
10031039 addr [0 ] = 0 ;
10041040 addr [1 ] = mc -> ports >> 8 ;
10051041 addr [2 ] = mc -> ports & 0xff ;
1006- } else if (entry_type == ENTRYTYPE_MACv6 ) {
1042+ } else if (mc -> entry_type == ENTRYTYPE_MACv6 ) {
10071043 addr [0 ] = mc -> ports >> 8 ;
10081044 addr [1 ] = mc -> ports & 0xff ;
10091045 }
@@ -1013,62 +1049,69 @@ int ocelot_port_mdb_add(struct ocelot *ocelot, int port,
10131049 const struct switchdev_obj_port_mdb * mdb )
10141050{
10151051 struct ocelot_port * ocelot_port = ocelot -> ports [port ];
1016- enum macaccess_entry_type entry_type ;
10171052 unsigned char addr [ETH_ALEN ];
10181053 struct ocelot_multicast * mc ;
1054+ struct ocelot_pgid * pgid ;
10191055 u16 vid = mdb -> vid ;
1020- bool new = false;
10211056
10221057 if (port == ocelot -> npi )
10231058 port = ocelot -> num_phys_ports ;
10241059
10251060 if (!vid )
10261061 vid = ocelot_port -> pvid ;
10271062
1028- entry_type = ocelot_classify_mdb (mdb -> addr );
1029-
10301063 mc = ocelot_multicast_get (ocelot , mdb -> addr , vid );
10311064 if (!mc ) {
1032- int pgid = ocelot_mdb_get_pgid (ocelot , entry_type );
1033-
1034- if (pgid < 0 ) {
1035- dev_err (ocelot -> dev ,
1036- "No more PGIDs available for mdb %pM vid %d\n" ,
1037- mdb -> addr , vid );
1038- return - ENOSPC ;
1039- }
1040-
1065+ /* New entry */
10411066 mc = devm_kzalloc (ocelot -> dev , sizeof (* mc ), GFP_KERNEL );
10421067 if (!mc )
10431068 return - ENOMEM ;
10441069
1045- memcpy (mc -> addr , mdb -> addr , ETH_ALEN );
1070+ mc -> entry_type = ocelot_classify_mdb (mdb -> addr );
1071+ ether_addr_copy (mc -> addr , mdb -> addr );
10461072 mc -> vid = vid ;
1047- mc -> pgid = pgid ;
10481073
10491074 list_add_tail (& mc -> list , & ocelot -> multicast );
1050- new = true;
1051- }
1052-
1053- if (!new ) {
1054- ocelot_encode_ports_to_mdb (addr , mc , entry_type );
1075+ } else {
1076+ /* Existing entry. Clean up the current port mask from
1077+ * hardware now, because we'll be modifying it.
1078+ */
1079+ ocelot_pgid_free (ocelot , mc -> pgid );
1080+ ocelot_encode_ports_to_mdb (addr , mc );
10551081 ocelot_mact_forget (ocelot , addr , vid );
10561082 }
10571083
10581084 mc -> ports |= BIT (port );
1059- ocelot_encode_ports_to_mdb (addr , mc , entry_type );
10601085
1061- return ocelot_mact_learn (ocelot , mc -> pgid , addr , vid , entry_type );
1086+ pgid = ocelot_mdb_get_pgid (ocelot , mc );
1087+ if (IS_ERR (pgid )) {
1088+ dev_err (ocelot -> dev ,
1089+ "Cannot allocate PGID for mdb %pM vid %d\n" ,
1090+ mc -> addr , mc -> vid );
1091+ devm_kfree (ocelot -> dev , mc );
1092+ return PTR_ERR (pgid );
1093+ }
1094+ mc -> pgid = pgid ;
1095+
1096+ ocelot_encode_ports_to_mdb (addr , mc );
1097+
1098+ if (mc -> entry_type != ENTRYTYPE_MACv4 &&
1099+ mc -> entry_type != ENTRYTYPE_MACv6 )
1100+ ocelot_write_rix (ocelot , pgid -> ports , ANA_PGID_PGID ,
1101+ pgid -> index );
1102+
1103+ return ocelot_mact_learn (ocelot , pgid -> index , addr , vid ,
1104+ mc -> entry_type );
10621105}
10631106EXPORT_SYMBOL (ocelot_port_mdb_add );
10641107
10651108int ocelot_port_mdb_del (struct ocelot * ocelot , int port ,
10661109 const struct switchdev_obj_port_mdb * mdb )
10671110{
10681111 struct ocelot_port * ocelot_port = ocelot -> ports [port ];
1069- enum macaccess_entry_type entry_type ;
10701112 unsigned char addr [ETH_ALEN ];
10711113 struct ocelot_multicast * mc ;
1114+ struct ocelot_pgid * pgid ;
10721115 u16 vid = mdb -> vid ;
10731116
10741117 if (port == ocelot -> npi )
@@ -1081,21 +1124,32 @@ int ocelot_port_mdb_del(struct ocelot *ocelot, int port,
10811124 if (!mc )
10821125 return - ENOENT ;
10831126
1084- entry_type = ocelot_classify_mdb (mdb -> addr );
1085-
1086- ocelot_encode_ports_to_mdb (addr , mc , entry_type );
1127+ ocelot_encode_ports_to_mdb (addr , mc );
10871128 ocelot_mact_forget (ocelot , addr , vid );
10881129
1130+ ocelot_pgid_free (ocelot , mc -> pgid );
10891131 mc -> ports &= ~BIT (port );
10901132 if (!mc -> ports ) {
10911133 list_del (& mc -> list );
10921134 devm_kfree (ocelot -> dev , mc );
10931135 return 0 ;
10941136 }
10951137
1096- ocelot_encode_ports_to_mdb (addr , mc , entry_type );
1138+ /* We have a PGID with fewer ports now */
1139+ pgid = ocelot_mdb_get_pgid (ocelot , mc );
1140+ if (IS_ERR (pgid ))
1141+ return PTR_ERR (pgid );
1142+ mc -> pgid = pgid ;
1143+
1144+ ocelot_encode_ports_to_mdb (addr , mc );
1145+
1146+ if (mc -> entry_type != ENTRYTYPE_MACv4 &&
1147+ mc -> entry_type != ENTRYTYPE_MACv6 )
1148+ ocelot_write_rix (ocelot , pgid -> ports , ANA_PGID_PGID ,
1149+ pgid -> index );
10971150
1098- return ocelot_mact_learn (ocelot , mc -> pgid , addr , vid , entry_type );
1151+ return ocelot_mact_learn (ocelot , pgid -> index , addr , vid ,
1152+ mc -> entry_type );
10991153}
11001154EXPORT_SYMBOL (ocelot_port_mdb_del );
11011155
@@ -1453,6 +1507,7 @@ int ocelot_init(struct ocelot *ocelot)
14531507 return - ENOMEM ;
14541508
14551509 INIT_LIST_HEAD (& ocelot -> multicast );
1510+ INIT_LIST_HEAD (& ocelot -> pgids );
14561511 ocelot_mact_init (ocelot );
14571512 ocelot_vlan_init (ocelot );
14581513 ocelot_vcap_init (ocelot );
0 commit comments