Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

virtio_net: support per queue interrupt coalesce command

Add interrupt_coalesce config in send_queue and receive_queue to cache user
config.

Send per virtqueue interrupt moderation config to underlying device in
order to have more efficient interrupt moderation and cpu utilization of
guest VM.

Additionally, address all the VQs when updating the global configuration,
as now the individual VQs configuration can diverge from the global
configuration.

Signed-off-by: Gavin Li <gavinl@nvidia.com>
Reviewed-by: Dragos Tatulea <dtatulea@nvidia.com>
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Heng Qi <hengqi@linux.alibaba.com>
Acked-by: Jason Wang <jasowang@redhat.com>
Link: https://lore.kernel.org/r/20230731070656.96411-3-gavinl@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Gavin Li and committed by
Jakub Kicinski
394bd877 308d7982

+155 -8
+141 -8
drivers/net/virtio_net.c
··· 144 144 145 145 struct virtnet_sq_stats stats; 146 146 147 + struct virtnet_interrupt_coalesce intr_coal; 148 + 147 149 struct napi_struct napi; 148 150 149 151 /* Record whether sq is in reset state. */ ··· 162 160 struct bpf_prog __rcu *xdp_prog; 163 161 164 162 struct virtnet_rq_stats stats; 163 + 164 + struct virtnet_interrupt_coalesce intr_coal; 165 165 166 166 /* Chain pages by the private ptr. */ 167 167 struct page *pages; ··· 216 212 struct virtio_net_ctrl_rss rss; 217 213 struct virtio_net_ctrl_coal_tx coal_tx; 218 214 struct virtio_net_ctrl_coal_rx coal_rx; 215 + struct virtio_net_ctrl_coal_vq coal_vq; 219 216 }; 220 217 221 218 struct virtnet_info { ··· 3083 3078 return 0; 3084 3079 } 3085 3080 3081 + static int virtnet_send_ctrl_coal_vq_cmd(struct virtnet_info *vi, 3082 + u16 vqn, u32 max_usecs, u32 max_packets) 3083 + { 3084 + struct scatterlist sgs; 3085 + 3086 + vi->ctrl->coal_vq.vqn = cpu_to_le16(vqn); 3087 + vi->ctrl->coal_vq.coal.max_usecs = cpu_to_le32(max_usecs); 3088 + vi->ctrl->coal_vq.coal.max_packets = cpu_to_le32(max_packets); 3089 + sg_init_one(&sgs, &vi->ctrl->coal_vq, sizeof(vi->ctrl->coal_vq)); 3090 + 3091 + if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_NOTF_COAL, 3092 + VIRTIO_NET_CTRL_NOTF_COAL_VQ_SET, 3093 + &sgs)) 3094 + return -EINVAL; 3095 + 3096 + return 0; 3097 + } 3098 + 3099 + static int virtnet_send_notf_coal_vq_cmds(struct virtnet_info *vi, 3100 + struct ethtool_coalesce *ec, 3101 + u16 queue) 3102 + { 3103 + int err; 3104 + 3105 + if (ec->rx_coalesce_usecs || ec->rx_max_coalesced_frames) { 3106 + err = virtnet_send_ctrl_coal_vq_cmd(vi, rxq2vq(queue), 3107 + ec->rx_coalesce_usecs, 3108 + ec->rx_max_coalesced_frames); 3109 + if (err) 3110 + return err; 3111 + /* Save parameters */ 3112 + vi->rq[queue].intr_coal.max_usecs = ec->rx_coalesce_usecs; 3113 + vi->rq[queue].intr_coal.max_packets = ec->rx_max_coalesced_frames; 3114 + } 3115 + 3116 + if (ec->tx_coalesce_usecs || ec->tx_max_coalesced_frames) { 3117 + err = virtnet_send_ctrl_coal_vq_cmd(vi, txq2vq(queue), 3118 + ec->tx_coalesce_usecs, 3119 + ec->tx_max_coalesced_frames); 3120 + if (err) 3121 + return err; 3122 + /* Save parameters */ 3123 + vi->sq[queue].intr_coal.max_usecs = ec->tx_coalesce_usecs; 3124 + vi->sq[queue].intr_coal.max_packets = ec->tx_max_coalesced_frames; 3125 + } 3126 + 3127 + return 0; 3128 + } 3129 + 3086 3130 static int virtnet_coal_params_supported(struct ethtool_coalesce *ec) 3087 3131 { 3088 3132 /* usecs coalescing is supported only if VIRTIO_NET_F_NOTF_COAL ··· 3147 3093 return 0; 3148 3094 } 3149 3095 3096 + static int virtnet_should_update_vq_weight(int dev_flags, int weight, 3097 + int vq_weight, bool *should_update) 3098 + { 3099 + if (weight ^ vq_weight) { 3100 + if (dev_flags & IFF_UP) 3101 + return -EBUSY; 3102 + *should_update = true; 3103 + } 3104 + 3105 + return 0; 3106 + } 3107 + 3150 3108 static int virtnet_set_coalesce(struct net_device *dev, 3151 3109 struct ethtool_coalesce *ec, 3152 3110 struct kernel_ethtool_coalesce *kernel_coal, 3153 3111 struct netlink_ext_ack *extack) 3154 3112 { 3155 3113 struct virtnet_info *vi = netdev_priv(dev); 3156 - int ret, i, napi_weight; 3114 + int ret, queue_number, napi_weight; 3157 3115 bool update_napi = false; 3158 3116 3159 3117 /* Can't change NAPI weight if the link is up */ 3160 3118 napi_weight = ec->tx_max_coalesced_frames ? NAPI_POLL_WEIGHT : 0; 3161 - if (napi_weight ^ vi->sq[0].napi.weight) { 3162 - if (dev->flags & IFF_UP) 3163 - return -EBUSY; 3164 - else 3165 - update_napi = true; 3119 + for (queue_number = 0; queue_number < vi->max_queue_pairs; queue_number++) { 3120 + ret = virtnet_should_update_vq_weight(dev->flags, napi_weight, 3121 + vi->sq[queue_number].napi.weight, 3122 + &update_napi); 3123 + if (ret) 3124 + return ret; 3125 + 3126 + if (update_napi) { 3127 + /* All queues that belong to [queue_number, vi->max_queue_pairs] will be 3128 + * updated for the sake of simplicity, which might not be necessary 3129 + */ 3130 + break; 3131 + } 3166 3132 } 3167 3133 3168 3134 if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_NOTF_COAL)) ··· 3194 3120 return ret; 3195 3121 3196 3122 if (update_napi) { 3197 - for (i = 0; i < vi->max_queue_pairs; i++) 3198 - vi->sq[i].napi.weight = napi_weight; 3123 + for (; queue_number < vi->max_queue_pairs; queue_number++) 3124 + vi->sq[queue_number].napi.weight = napi_weight; 3199 3125 } 3200 3126 3201 3127 return ret; ··· 3213 3139 ec->tx_coalesce_usecs = vi->intr_coal_tx.max_usecs; 3214 3140 ec->tx_max_coalesced_frames = vi->intr_coal_tx.max_packets; 3215 3141 ec->rx_max_coalesced_frames = vi->intr_coal_rx.max_packets; 3142 + } else { 3143 + ec->rx_max_coalesced_frames = 1; 3144 + 3145 + if (vi->sq[0].napi.weight) 3146 + ec->tx_max_coalesced_frames = 1; 3147 + } 3148 + 3149 + return 0; 3150 + } 3151 + 3152 + static int virtnet_set_per_queue_coalesce(struct net_device *dev, 3153 + u32 queue, 3154 + struct ethtool_coalesce *ec) 3155 + { 3156 + struct virtnet_info *vi = netdev_priv(dev); 3157 + int ret, napi_weight; 3158 + bool update_napi = false; 3159 + 3160 + if (queue >= vi->max_queue_pairs) 3161 + return -EINVAL; 3162 + 3163 + /* Can't change NAPI weight if the link is up */ 3164 + napi_weight = ec->tx_max_coalesced_frames ? NAPI_POLL_WEIGHT : 0; 3165 + ret = virtnet_should_update_vq_weight(dev->flags, napi_weight, 3166 + vi->sq[queue].napi.weight, 3167 + &update_napi); 3168 + if (ret) 3169 + return ret; 3170 + 3171 + if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_VQ_NOTF_COAL)) 3172 + ret = virtnet_send_notf_coal_vq_cmds(vi, ec, queue); 3173 + else 3174 + ret = virtnet_coal_params_supported(ec); 3175 + 3176 + if (ret) 3177 + return ret; 3178 + 3179 + if (update_napi) 3180 + vi->sq[queue].napi.weight = napi_weight; 3181 + 3182 + return 0; 3183 + } 3184 + 3185 + static int virtnet_get_per_queue_coalesce(struct net_device *dev, 3186 + u32 queue, 3187 + struct ethtool_coalesce *ec) 3188 + { 3189 + struct virtnet_info *vi = netdev_priv(dev); 3190 + 3191 + if (queue >= vi->max_queue_pairs) 3192 + return -EINVAL; 3193 + 3194 + if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_VQ_NOTF_COAL)) { 3195 + ec->rx_coalesce_usecs = vi->rq[queue].intr_coal.max_usecs; 3196 + ec->tx_coalesce_usecs = vi->sq[queue].intr_coal.max_usecs; 3197 + ec->tx_max_coalesced_frames = vi->sq[queue].intr_coal.max_packets; 3198 + ec->rx_max_coalesced_frames = vi->rq[queue].intr_coal.max_packets; 3216 3199 } else { 3217 3200 ec->rx_max_coalesced_frames = 1; 3218 3201 ··· 3410 3279 .set_link_ksettings = virtnet_set_link_ksettings, 3411 3280 .set_coalesce = virtnet_set_coalesce, 3412 3281 .get_coalesce = virtnet_get_coalesce, 3282 + .set_per_queue_coalesce = virtnet_set_per_queue_coalesce, 3283 + .get_per_queue_coalesce = virtnet_get_per_queue_coalesce, 3413 3284 .get_rxfh_key_size = virtnet_get_rxfh_key_size, 3414 3285 .get_rxfh_indir_size = virtnet_get_rxfh_indir_size, 3415 3286 .get_rxfh = virtnet_get_rxfh,
+14
include/uapi/linux/virtio_net.h
··· 56 56 #define VIRTIO_NET_F_MQ 22 /* Device supports Receive Flow 57 57 * Steering */ 58 58 #define VIRTIO_NET_F_CTRL_MAC_ADDR 23 /* Set MAC address */ 59 + #define VIRTIO_NET_F_VQ_NOTF_COAL 52 /* Device supports virtqueue notification coalescing */ 59 60 #define VIRTIO_NET_F_NOTF_COAL 53 /* Device supports notifications coalescing */ 60 61 #define VIRTIO_NET_F_GUEST_USO4 54 /* Guest can handle USOv4 in. */ 61 62 #define VIRTIO_NET_F_GUEST_USO6 55 /* Guest can handle USOv6 in. */ ··· 392 391 }; 393 392 394 393 #define VIRTIO_NET_CTRL_NOTF_COAL_RX_SET 1 394 + #define VIRTIO_NET_CTRL_NOTF_COAL_VQ_SET 2 395 + #define VIRTIO_NET_CTRL_NOTF_COAL_VQ_GET 3 396 + 397 + struct virtio_net_ctrl_coal { 398 + __le32 max_packets; 399 + __le32 max_usecs; 400 + }; 401 + 402 + struct virtio_net_ctrl_coal_vq { 403 + __le16 vqn; 404 + __le16 reserved; 405 + struct virtio_net_ctrl_coal coal; 406 + }; 395 407 396 408 #endif /* _UAPI_LINUX_VIRTIO_NET_H */