@@ -679,6 +679,143 @@ mlxsw_sp_port_fdb_static_add(struct mlxsw_sp_port *mlxsw_sp_port,
679
679
true, false);
680
680
}
681
681
682
+ static int mlxsw_sp_port_mdb_op (struct mlxsw_sp * mlxsw_sp , const char * addr ,
683
+ u16 fid , u16 mid , bool adding )
684
+ {
685
+ char * sfd_pl ;
686
+ int err ;
687
+
688
+ sfd_pl = kmalloc (MLXSW_REG_SFD_LEN , GFP_KERNEL );
689
+ if (!sfd_pl )
690
+ return - ENOMEM ;
691
+
692
+ mlxsw_reg_sfd_pack (sfd_pl , mlxsw_sp_sfd_op (adding ), 0 );
693
+ mlxsw_reg_sfd_mc_pack (sfd_pl , 0 , addr , fid ,
694
+ MLXSW_REG_SFD_REC_ACTION_NOP , mid );
695
+ err = mlxsw_reg_write (mlxsw_sp -> core , MLXSW_REG (sfd ), sfd_pl );
696
+ kfree (sfd_pl );
697
+ return err ;
698
+ }
699
+
700
+ static int mlxsw_sp_port_smid_set (struct mlxsw_sp_port * mlxsw_sp_port , u16 mid ,
701
+ bool add , bool clear_all_ports )
702
+ {
703
+ struct mlxsw_sp * mlxsw_sp = mlxsw_sp_port -> mlxsw_sp ;
704
+ char * smid_pl ;
705
+ int err , i ;
706
+
707
+ smid_pl = kmalloc (MLXSW_REG_SMID_LEN , GFP_KERNEL );
708
+ if (!smid_pl )
709
+ return - ENOMEM ;
710
+
711
+ mlxsw_reg_smid_pack (smid_pl , mid , mlxsw_sp_port -> local_port , add );
712
+ if (clear_all_ports ) {
713
+ for (i = 1 ; i < MLXSW_PORT_MAX_PORTS ; i ++ )
714
+ if (mlxsw_sp -> ports [i ])
715
+ mlxsw_reg_smid_port_mask_set (smid_pl , i , 1 );
716
+ }
717
+ err = mlxsw_reg_write (mlxsw_sp -> core , MLXSW_REG (smid ), smid_pl );
718
+ kfree (smid_pl );
719
+ return err ;
720
+ }
721
+
722
+ static struct mlxsw_sp_mid * __mlxsw_sp_mc_get (struct mlxsw_sp * mlxsw_sp ,
723
+ const unsigned char * addr ,
724
+ u16 vid )
725
+ {
726
+ struct mlxsw_sp_mid * mid ;
727
+
728
+ list_for_each_entry (mid , & mlxsw_sp -> br_mids .list , list ) {
729
+ if (ether_addr_equal (mid -> addr , addr ) && mid -> vid == vid )
730
+ return mid ;
731
+ }
732
+ return NULL ;
733
+ }
734
+
735
+ static struct mlxsw_sp_mid * __mlxsw_sp_mc_alloc (struct mlxsw_sp * mlxsw_sp ,
736
+ const unsigned char * addr ,
737
+ u16 vid )
738
+ {
739
+ struct mlxsw_sp_mid * mid ;
740
+ u16 mid_idx ;
741
+
742
+ mid_idx = find_first_zero_bit (mlxsw_sp -> br_mids .mapped ,
743
+ MLXSW_SP_MID_MAX );
744
+ if (mid_idx == MLXSW_SP_MID_MAX )
745
+ return NULL ;
746
+
747
+ mid = kzalloc (sizeof (* mid ), GFP_KERNEL );
748
+ if (!mid )
749
+ return NULL ;
750
+
751
+ set_bit (mid_idx , mlxsw_sp -> br_mids .mapped );
752
+ ether_addr_copy (mid -> addr , addr );
753
+ mid -> vid = vid ;
754
+ mid -> mid = mid_idx ;
755
+ mid -> ref_count = 0 ;
756
+ list_add_tail (& mid -> list , & mlxsw_sp -> br_mids .list );
757
+
758
+ return mid ;
759
+ }
760
+
761
+ static int __mlxsw_sp_mc_dec_ref (struct mlxsw_sp * mlxsw_sp ,
762
+ struct mlxsw_sp_mid * mid )
763
+ {
764
+ if (-- mid -> ref_count == 0 ) {
765
+ list_del (& mid -> list );
766
+ clear_bit (mid -> mid , mlxsw_sp -> br_mids .mapped );
767
+ kfree (mid );
768
+ return 1 ;
769
+ }
770
+ return 0 ;
771
+ }
772
+
773
+ static int mlxsw_sp_port_mdb_add (struct mlxsw_sp_port * mlxsw_sp_port ,
774
+ const struct switchdev_obj_port_mdb * mdb ,
775
+ struct switchdev_trans * trans )
776
+ {
777
+ struct mlxsw_sp * mlxsw_sp = mlxsw_sp_port -> mlxsw_sp ;
778
+ struct net_device * dev = mlxsw_sp_port -> dev ;
779
+ struct mlxsw_sp_mid * mid ;
780
+ u16 fid = mlxsw_sp_port_vid_to_fid_get (mlxsw_sp_port , mdb -> vid );
781
+ int err = 0 ;
782
+
783
+ if (switchdev_trans_ph_prepare (trans ))
784
+ return 0 ;
785
+
786
+ mid = __mlxsw_sp_mc_get (mlxsw_sp , mdb -> addr , mdb -> vid );
787
+ if (!mid ) {
788
+ mid = __mlxsw_sp_mc_alloc (mlxsw_sp , mdb -> addr , mdb -> vid );
789
+ if (!mid ) {
790
+ netdev_err (dev , "Unable to allocate MC group\n" );
791
+ return - ENOMEM ;
792
+ }
793
+ }
794
+ mid -> ref_count ++ ;
795
+
796
+ err = mlxsw_sp_port_smid_set (mlxsw_sp_port , mid -> mid , true,
797
+ mid -> ref_count == 1 );
798
+ if (err ) {
799
+ netdev_err (dev , "Unable to set SMID\n" );
800
+ goto err_out ;
801
+ }
802
+
803
+ if (mid -> ref_count == 1 ) {
804
+ err = mlxsw_sp_port_mdb_op (mlxsw_sp , mdb -> addr , fid , mid -> mid ,
805
+ true);
806
+ if (err ) {
807
+ netdev_err (dev , "Unable to set MC SFD\n" );
808
+ goto err_out ;
809
+ }
810
+ }
811
+
812
+ return 0 ;
813
+
814
+ err_out :
815
+ __mlxsw_sp_mc_dec_ref (mlxsw_sp , mid );
816
+ return err ;
817
+ }
818
+
682
819
static int mlxsw_sp_port_obj_add (struct net_device * dev ,
683
820
const struct switchdev_obj * obj ,
684
821
struct switchdev_trans * trans )
@@ -704,6 +841,11 @@ static int mlxsw_sp_port_obj_add(struct net_device *dev,
704
841
SWITCHDEV_OBJ_PORT_FDB (obj ),
705
842
trans );
706
843
break ;
844
+ case SWITCHDEV_OBJ_ID_PORT_MDB :
845
+ err = mlxsw_sp_port_mdb_add (mlxsw_sp_port ,
846
+ SWITCHDEV_OBJ_PORT_MDB (obj ),
847
+ trans );
848
+ break ;
707
849
default :
708
850
err = - EOPNOTSUPP ;
709
851
break ;
@@ -817,6 +959,37 @@ mlxsw_sp_port_fdb_static_del(struct mlxsw_sp_port *mlxsw_sp_port,
817
959
false, false);
818
960
}
819
961
962
+ static int mlxsw_sp_port_mdb_del (struct mlxsw_sp_port * mlxsw_sp_port ,
963
+ const struct switchdev_obj_port_mdb * mdb )
964
+ {
965
+ struct mlxsw_sp * mlxsw_sp = mlxsw_sp_port -> mlxsw_sp ;
966
+ struct net_device * dev = mlxsw_sp_port -> dev ;
967
+ struct mlxsw_sp_mid * mid ;
968
+ u16 fid = mlxsw_sp_port_vid_to_fid_get (mlxsw_sp_port , mdb -> vid );
969
+ u16 mid_idx ;
970
+ int err = 0 ;
971
+
972
+ mid = __mlxsw_sp_mc_get (mlxsw_sp , mdb -> addr , mdb -> vid );
973
+ if (!mid ) {
974
+ netdev_err (dev , "Unable to remove port from MC DB\n" );
975
+ return - EINVAL ;
976
+ }
977
+
978
+ err = mlxsw_sp_port_smid_set (mlxsw_sp_port , mid -> mid , false, false);
979
+ if (err )
980
+ netdev_err (dev , "Unable to remove port from SMID\n" );
981
+
982
+ mid_idx = mid -> mid ;
983
+ if (__mlxsw_sp_mc_dec_ref (mlxsw_sp , mid )) {
984
+ err = mlxsw_sp_port_mdb_op (mlxsw_sp , mdb -> addr , fid , mid_idx ,
985
+ false);
986
+ if (err )
987
+ netdev_err (dev , "Unable to remove MC SFD\n" );
988
+ }
989
+
990
+ return err ;
991
+ }
992
+
820
993
static int mlxsw_sp_port_obj_del (struct net_device * dev ,
821
994
const struct switchdev_obj * obj )
822
995
{
@@ -839,6 +1012,9 @@ static int mlxsw_sp_port_obj_del(struct net_device *dev,
839
1012
err = mlxsw_sp_port_fdb_static_del (mlxsw_sp_port ,
840
1013
SWITCHDEV_OBJ_PORT_FDB (obj ));
841
1014
break ;
1015
+ case SWITCHDEV_OBJ_ID_PORT_MDB :
1016
+ err = mlxsw_sp_port_mdb_del (mlxsw_sp_port ,
1017
+ SWITCHDEV_OBJ_PORT_MDB (obj ));
842
1018
default :
843
1019
err = - EOPNOTSUPP ;
844
1020
break ;
0 commit comments