@@ -368,15 +368,16 @@ struct receive_queue {
368368 * because table sizes may be differ according to the device configuration.
369369 */
370370#define VIRTIO_NET_RSS_MAX_KEY_SIZE 40
371- #define VIRTIO_NET_RSS_MAX_TABLE_LEN 128
372371struct virtio_net_ctrl_rss {
373372 u32 hash_types ;
374373 u16 indirection_table_mask ;
375374 u16 unclassified_queue ;
376- u16 indirection_table [ VIRTIO_NET_RSS_MAX_TABLE_LEN ];
375+ u16 hash_cfg_reserved ; /* for HASH_CONFIG (see virtio_net_hash_config for details) */
377376 u16 max_tx_vq ;
378377 u8 hash_key_length ;
379378 u8 key [VIRTIO_NET_RSS_MAX_KEY_SIZE ];
379+
380+ u16 * indirection_table ;
380381};
381382
382383/* Control VQ buffers: protected by the rtnl lock */
@@ -512,6 +513,25 @@ static struct sk_buff *virtnet_skb_append_frag(struct sk_buff *head_skb,
512513 struct page * page , void * buf ,
513514 int len , int truesize );
514515
516+ static int rss_indirection_table_alloc (struct virtio_net_ctrl_rss * rss , u16 indir_table_size )
517+ {
518+ if (!indir_table_size ) {
519+ rss -> indirection_table = NULL ;
520+ return 0 ;
521+ }
522+
523+ rss -> indirection_table = kmalloc_array (indir_table_size , sizeof (u16 ), GFP_KERNEL );
524+ if (!rss -> indirection_table )
525+ return - ENOMEM ;
526+
527+ return 0 ;
528+ }
529+
530+ static void rss_indirection_table_free (struct virtio_net_ctrl_rss * rss )
531+ {
532+ kfree (rss -> indirection_table );
533+ }
534+
515535static bool is_xdp_frame (void * ptr )
516536{
517537 return (unsigned long )ptr & VIRTIO_XDP_FLAG ;
@@ -3374,15 +3394,59 @@ static void virtnet_ack_link_announce(struct virtnet_info *vi)
33743394 dev_warn (& vi -> dev -> dev , "Failed to ack link announce.\n" );
33753395}
33763396
3397+ static bool virtnet_commit_rss_command (struct virtnet_info * vi );
3398+
3399+ static void virtnet_rss_update_by_qpairs (struct virtnet_info * vi , u16 queue_pairs )
3400+ {
3401+ u32 indir_val = 0 ;
3402+ int i = 0 ;
3403+
3404+ for (; i < vi -> rss_indir_table_size ; ++ i ) {
3405+ indir_val = ethtool_rxfh_indir_default (i , queue_pairs );
3406+ vi -> rss .indirection_table [i ] = indir_val ;
3407+ }
3408+ vi -> rss .max_tx_vq = queue_pairs ;
3409+ }
3410+
33773411static int virtnet_set_queues (struct virtnet_info * vi , u16 queue_pairs )
33783412{
33793413 struct virtio_net_ctrl_mq * mq __free (kfree ) = NULL ;
3380- struct scatterlist sg ;
3414+ struct virtio_net_ctrl_rss old_rss ;
33813415 struct net_device * dev = vi -> dev ;
3416+ struct scatterlist sg ;
33823417
33833418 if (!vi -> has_cvq || !virtio_has_feature (vi -> vdev , VIRTIO_NET_F_MQ ))
33843419 return 0 ;
33853420
3421+ /* Firstly check if we need update rss. Do updating if both (1) rss enabled and
3422+ * (2) no user configuration.
3423+ *
3424+ * During rss command processing, device updates queue_pairs using rss.max_tx_vq. That is,
3425+ * the device updates queue_pairs together with rss, so we can skip the sperate queue_pairs
3426+ * update (VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET below) and return directly.
3427+ */
3428+ if (vi -> has_rss && !netif_is_rxfh_configured (dev )) {
3429+ memcpy (& old_rss , & vi -> rss , sizeof (old_rss ));
3430+ if (rss_indirection_table_alloc (& vi -> rss , vi -> rss_indir_table_size )) {
3431+ vi -> rss .indirection_table = old_rss .indirection_table ;
3432+ return - ENOMEM ;
3433+ }
3434+
3435+ virtnet_rss_update_by_qpairs (vi , queue_pairs );
3436+
3437+ if (!virtnet_commit_rss_command (vi )) {
3438+ /* restore ctrl_rss if commit_rss_command failed */
3439+ rss_indirection_table_free (& vi -> rss );
3440+ memcpy (& vi -> rss , & old_rss , sizeof (old_rss ));
3441+
3442+ dev_warn (& dev -> dev , "Fail to set num of queue pairs to %d, because committing RSS failed\n" ,
3443+ queue_pairs );
3444+ return - EINVAL ;
3445+ }
3446+ rss_indirection_table_free (& old_rss );
3447+ goto succ ;
3448+ }
3449+
33863450 mq = kzalloc (sizeof (* mq ), GFP_KERNEL );
33873451 if (!mq )
33883452 return - ENOMEM ;
@@ -3395,12 +3459,12 @@ static int virtnet_set_queues(struct virtnet_info *vi, u16 queue_pairs)
33953459 dev_warn (& dev -> dev , "Fail to set num of queue pairs to %d\n" ,
33963460 queue_pairs );
33973461 return - EINVAL ;
3398- } else {
3399- vi -> curr_queue_pairs = queue_pairs ;
3400- /* virtnet_open() will refill when device is going to up. */
3401- if (dev -> flags & IFF_UP )
3402- schedule_delayed_work (& vi -> refill , 0 );
34033462 }
3463+ succ :
3464+ vi -> curr_queue_pairs = queue_pairs ;
3465+ /* virtnet_open() will refill when device is going to up. */
3466+ if (dev -> flags & IFF_UP )
3467+ schedule_delayed_work (& vi -> refill , 0 );
34043468
34053469 return 0 ;
34063470}
@@ -3828,11 +3892,15 @@ static bool virtnet_commit_rss_command(struct virtnet_info *vi)
38283892 /* prepare sgs */
38293893 sg_init_table (sgs , 4 );
38303894
3831- sg_buf_size = offsetof(struct virtio_net_ctrl_rss , indirection_table );
3895+ sg_buf_size = offsetof(struct virtio_net_ctrl_rss , hash_cfg_reserved );
38323896 sg_set_buf (& sgs [0 ], & vi -> rss , sg_buf_size );
38333897
3834- sg_buf_size = sizeof (uint16_t ) * (vi -> rss .indirection_table_mask + 1 );
3835- sg_set_buf (& sgs [1 ], vi -> rss .indirection_table , sg_buf_size );
3898+ if (vi -> has_rss ) {
3899+ sg_buf_size = sizeof (uint16_t ) * vi -> rss_indir_table_size ;
3900+ sg_set_buf (& sgs [1 ], vi -> rss .indirection_table , sg_buf_size );
3901+ } else {
3902+ sg_set_buf (& sgs [1 ], & vi -> rss .hash_cfg_reserved , sizeof (uint16_t ));
3903+ }
38363904
38373905 sg_buf_size = offsetof(struct virtio_net_ctrl_rss , key )
38383906 - offsetof(struct virtio_net_ctrl_rss , max_tx_vq );
@@ -3856,21 +3924,14 @@ static bool virtnet_commit_rss_command(struct virtnet_info *vi)
38563924
38573925static void virtnet_init_default_rss (struct virtnet_info * vi )
38583926{
3859- u32 indir_val = 0 ;
3860- int i = 0 ;
3861-
38623927 vi -> rss .hash_types = vi -> rss_hash_types_supported ;
38633928 vi -> rss_hash_types_saved = vi -> rss_hash_types_supported ;
38643929 vi -> rss .indirection_table_mask = vi -> rss_indir_table_size
38653930 ? vi -> rss_indir_table_size - 1 : 0 ;
38663931 vi -> rss .unclassified_queue = 0 ;
38673932
3868- for (; i < vi -> rss_indir_table_size ; ++ i ) {
3869- indir_val = ethtool_rxfh_indir_default (i , vi -> curr_queue_pairs );
3870- vi -> rss .indirection_table [i ] = indir_val ;
3871- }
3933+ virtnet_rss_update_by_qpairs (vi , vi -> curr_queue_pairs );
38723934
3873- vi -> rss .max_tx_vq = vi -> has_rss ? vi -> curr_queue_pairs : 0 ;
38743935 vi -> rss .hash_key_length = vi -> rss_key_size ;
38753936
38763937 netdev_rss_key_fill (vi -> rss .key , vi -> rss_key_size );
@@ -6420,10 +6481,19 @@ static int virtnet_probe(struct virtio_device *vdev)
64206481 virtio_cread16 (vdev , offsetof(struct virtio_net_config ,
64216482 rss_max_indirection_table_length ));
64226483 }
6484+ err = rss_indirection_table_alloc (& vi -> rss , vi -> rss_indir_table_size );
6485+ if (err )
6486+ goto free ;
64236487
64246488 if (vi -> has_rss || vi -> has_rss_hash_report ) {
64256489 vi -> rss_key_size =
64266490 virtio_cread8 (vdev , offsetof(struct virtio_net_config , rss_max_key_size ));
6491+ if (vi -> rss_key_size > VIRTIO_NET_RSS_MAX_KEY_SIZE ) {
6492+ dev_err (& vdev -> dev , "rss_max_key_size=%u exceeds the limit %u.\n" ,
6493+ vi -> rss_key_size , VIRTIO_NET_RSS_MAX_KEY_SIZE );
6494+ err = - EINVAL ;
6495+ goto free ;
6496+ }
64276497
64286498 vi -> rss_hash_types_supported =
64296499 virtio_cread32 (vdev , offsetof(struct virtio_net_config , supported_hash_types ));
@@ -6551,6 +6621,15 @@ static int virtnet_probe(struct virtio_device *vdev)
65516621
65526622 virtio_device_ready (vdev );
65536623
6624+ if (vi -> has_rss || vi -> has_rss_hash_report ) {
6625+ if (!virtnet_commit_rss_command (vi )) {
6626+ dev_warn (& vdev -> dev , "RSS disabled because committing failed.\n" );
6627+ dev -> hw_features &= ~NETIF_F_RXHASH ;
6628+ vi -> has_rss_hash_report = false;
6629+ vi -> has_rss = false;
6630+ }
6631+ }
6632+
65546633 virtnet_set_queues (vi , vi -> curr_queue_pairs );
65556634
65566635 /* a random MAC address has been assigned, notify the device.
@@ -6674,6 +6753,8 @@ static void virtnet_remove(struct virtio_device *vdev)
66746753
66756754 remove_vq_common (vi );
66766755
6756+ rss_indirection_table_free (& vi -> rss );
6757+
66776758 free_netdev (vi -> dev );
66786759}
66796760
0 commit comments