@@ -557,6 +557,23 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
557557 return ret ;
558558}
559559
560+ static int ethtool_copy_validate_indir (u32 * indir , void __user * useraddr ,
561+ struct ethtool_rxnfc * rx_rings ,
562+ u32 size )
563+ {
564+ int ret = 0 , i ;
565+
566+ if (copy_from_user (indir , useraddr , size * sizeof (indir [0 ])))
567+ ret = - EFAULT ;
568+
569+ /* Validate ring indices */
570+ for (i = 0 ; i < size ; i ++ ) {
571+ if (indir [i ] >= rx_rings -> data )
572+ ret = - EINVAL ;
573+ }
574+ return ret ;
575+ }
576+
560577static noinline_for_stack int ethtool_get_rxfh_indir (struct net_device * dev ,
561578 void __user * useraddr )
562579{
@@ -613,6 +630,7 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
613630 u32 * indir ;
614631 const struct ethtool_ops * ops = dev -> ethtool_ops ;
615632 int ret ;
633+ u32 ringidx_offset = offsetof(struct ethtool_rxfh_indir , ring_index [0 ]);
616634
617635 if (!ops -> get_rxfh_indir_size || !ops -> set_rxfh_indir ||
618636 !ops -> get_rxnfc )
@@ -643,28 +661,196 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
643661 for (i = 0 ; i < dev_size ; i ++ )
644662 indir [i ] = ethtool_rxfh_indir_default (i , rx_rings .data );
645663 } else {
646- if (copy_from_user (indir ,
647- useraddr +
648- offsetof(struct ethtool_rxfh_indir ,
649- ring_index [0 ]),
650- dev_size * sizeof (indir [0 ]))) {
664+ ret = ethtool_copy_validate_indir (indir ,
665+ useraddr + ringidx_offset ,
666+ & rx_rings ,
667+ dev_size );
668+ if (ret )
669+ goto out ;
670+ }
671+
672+ ret = ops -> set_rxfh_indir (dev , indir );
673+
674+ out :
675+ kfree (indir );
676+ return ret ;
677+ }
678+
679+ static noinline_for_stack int ethtool_get_rxfh (struct net_device * dev ,
680+ void __user * useraddr )
681+ {
682+ int ret ;
683+ const struct ethtool_ops * ops = dev -> ethtool_ops ;
684+ u32 user_indir_size = 0 , user_key_size = 0 ;
685+ u32 dev_indir_size = 0 , dev_key_size = 0 ;
686+ u32 total_size ;
687+ u32 indir_offset , indir_bytes ;
688+ u32 key_offset ;
689+ u32 * indir = NULL ;
690+ u8 * hkey = NULL ;
691+ u8 * rss_config ;
692+
693+ if (!(dev -> ethtool_ops -> get_rxfh_indir_size ||
694+ dev -> ethtool_ops -> get_rxfh_key_size ) ||
695+ !dev -> ethtool_ops -> get_rxfh )
696+ return - EOPNOTSUPP ;
697+
698+ if (ops -> get_rxfh_indir_size )
699+ dev_indir_size = ops -> get_rxfh_indir_size (dev );
700+
701+ indir_offset = offsetof(struct ethtool_rxfh , indir_size );
702+
703+ if (copy_from_user (& user_indir_size ,
704+ useraddr + indir_offset ,
705+ sizeof (user_indir_size )))
706+ return - EFAULT ;
707+
708+ if (copy_to_user (useraddr + indir_offset ,
709+ & dev_indir_size , sizeof (dev_indir_size )))
710+ return - EFAULT ;
711+
712+ if (ops -> get_rxfh_key_size )
713+ dev_key_size = ops -> get_rxfh_key_size (dev );
714+
715+ if ((dev_key_size + dev_indir_size ) == 0 )
716+ return - EOPNOTSUPP ;
717+
718+ key_offset = offsetof(struct ethtool_rxfh , key_size );
719+
720+ if (copy_from_user (& user_key_size ,
721+ useraddr + key_offset ,
722+ sizeof (user_key_size )))
723+ return - EFAULT ;
724+
725+ if (copy_to_user (useraddr + key_offset ,
726+ & dev_key_size , sizeof (dev_key_size )))
727+ return - EFAULT ;
728+
729+ /* If the user buffer size is 0, this is just a query for the
730+ * device table size and key size. Otherwise, if the User size is
731+ * not equal to device table size or key size it's an error.
732+ */
733+ if (!user_indir_size && !user_key_size )
734+ return 0 ;
735+
736+ if ((user_indir_size && (user_indir_size != dev_indir_size )) ||
737+ (user_key_size && (user_key_size != dev_key_size )))
738+ return - EINVAL ;
739+
740+ indir_bytes = user_indir_size * sizeof (indir [0 ]);
741+ total_size = indir_bytes + user_key_size ;
742+ rss_config = kzalloc (total_size , GFP_USER );
743+ if (!rss_config )
744+ return - ENOMEM ;
745+
746+ if (user_indir_size )
747+ indir = (u32 * )rss_config ;
748+
749+ if (user_key_size )
750+ hkey = rss_config + indir_bytes ;
751+
752+ ret = dev -> ethtool_ops -> get_rxfh (dev , indir , hkey );
753+ if (!ret ) {
754+ if (copy_to_user (useraddr +
755+ offsetof(struct ethtool_rxfh , rss_config [0 ]),
756+ rss_config , total_size ))
651757 ret = - EFAULT ;
758+ }
759+
760+ kfree (rss_config );
761+
762+ return ret ;
763+ }
764+
765+ static noinline_for_stack int ethtool_set_rxfh (struct net_device * dev ,
766+ void __user * useraddr )
767+ {
768+ int ret ;
769+ const struct ethtool_ops * ops = dev -> ethtool_ops ;
770+ struct ethtool_rxnfc rx_rings ;
771+ u32 user_indir_size = 0 , dev_indir_size = 0 , i ;
772+ u32 user_key_size = 0 , dev_key_size = 0 ;
773+ u32 * indir = NULL , indir_bytes = 0 ;
774+ u8 * hkey = NULL ;
775+ u8 * rss_config ;
776+ u32 indir_offset , key_offset ;
777+ u32 rss_cfg_offset = offsetof(struct ethtool_rxfh , rss_config [0 ]);
778+
779+ if (!(ops -> get_rxfh_indir_size || ops -> get_rxfh_key_size ) ||
780+ !ops -> get_rxnfc || !ops -> set_rxfh )
781+ return - EOPNOTSUPP ;
782+
783+ if (ops -> get_rxfh_indir_size )
784+ dev_indir_size = ops -> get_rxfh_indir_size (dev );
785+
786+ indir_offset = offsetof(struct ethtool_rxfh , indir_size );
787+ if (copy_from_user (& user_indir_size ,
788+ useraddr + indir_offset ,
789+ sizeof (user_indir_size )))
790+ return - EFAULT ;
791+
792+ if (ops -> get_rxfh_key_size )
793+ dev_key_size = dev -> ethtool_ops -> get_rxfh_key_size (dev );
794+
795+ if ((dev_key_size + dev_indir_size ) == 0 )
796+ return - EOPNOTSUPP ;
797+
798+ key_offset = offsetof(struct ethtool_rxfh , key_size );
799+ if (copy_from_user (& user_key_size ,
800+ useraddr + key_offset ,
801+ sizeof (user_key_size )))
802+ return - EFAULT ;
803+
804+ /* If either indir or hash key is valid, proceed further.
805+ */
806+ if ((user_indir_size && ((user_indir_size != 0xDEADBEEF ) &&
807+ user_indir_size != dev_indir_size )) ||
808+ (user_key_size && (user_key_size != dev_key_size )))
809+ return - EINVAL ;
810+
811+ if (user_indir_size != 0xDEADBEEF )
812+ indir_bytes = dev_indir_size * sizeof (indir [0 ]);
813+
814+ rss_config = kzalloc (indir_bytes + user_key_size , GFP_USER );
815+ if (!rss_config )
816+ return - ENOMEM ;
817+
818+ rx_rings .cmd = ETHTOOL_GRXRINGS ;
819+ ret = ops -> get_rxnfc (dev , & rx_rings , NULL );
820+ if (ret )
821+ goto out ;
822+
823+ /* user_indir_size == 0 means reset the indir table to default.
824+ * user_indir_size == 0xDEADBEEF means indir setting is not requested.
825+ */
826+ if (user_indir_size && user_indir_size != 0xDEADBEEF ) {
827+ indir = (u32 * )rss_config ;
828+ ret = ethtool_copy_validate_indir (indir ,
829+ useraddr + rss_cfg_offset ,
830+ & rx_rings ,
831+ user_indir_size );
832+ if (ret )
652833 goto out ;
653- }
834+ } else if (user_indir_size == 0 ) {
835+ indir = (u32 * )rss_config ;
836+ for (i = 0 ; i < dev_indir_size ; i ++ )
837+ indir [i ] = ethtool_rxfh_indir_default (i , rx_rings .data );
838+ }
654839
655- /* Validate ring indices */
656- for (i = 0 ; i < dev_size ; i ++ ) {
657- if (indir [i ] >= rx_rings .data ) {
658- ret = - EINVAL ;
659- goto out ;
660- }
840+ if (user_key_size ) {
841+ hkey = rss_config + indir_bytes ;
842+ if (copy_from_user (hkey ,
843+ useraddr + rss_cfg_offset + indir_bytes ,
844+ user_key_size )) {
845+ ret = - EFAULT ;
846+ goto out ;
661847 }
662848 }
663849
664- ret = ops -> set_rxfh_indir (dev , indir );
850+ ret = ops -> set_rxfh (dev , indir , hkey );
665851
666852out :
667- kfree (indir );
853+ kfree (rss_config );
668854 return ret ;
669855}
670856
@@ -1491,6 +1677,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
14911677 case ETHTOOL_GRXCLSRULE :
14921678 case ETHTOOL_GRXCLSRLALL :
14931679 case ETHTOOL_GRXFHINDIR :
1680+ case ETHTOOL_GRSSH :
14941681 case ETHTOOL_GFEATURES :
14951682 case ETHTOOL_GCHANNELS :
14961683 case ETHTOOL_GET_TS_INFO :
@@ -1628,6 +1815,12 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
16281815 case ETHTOOL_SRXFHINDIR :
16291816 rc = ethtool_set_rxfh_indir (dev , useraddr );
16301817 break ;
1818+ case ETHTOOL_GRSSH :
1819+ rc = ethtool_get_rxfh (dev , useraddr );
1820+ break ;
1821+ case ETHTOOL_SRSSH :
1822+ rc = ethtool_set_rxfh (dev , useraddr );
1823+ break ;
16311824 case ETHTOOL_GFEATURES :
16321825 rc = ethtool_get_features (dev , useraddr );
16331826 break ;
0 commit comments