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

virtio-net: send gratuitous packets when needed

As hypervior does not have the knowledge of guest network configuration, it's
better to ask guest to send gratuitous packets when needed.

This patch implements VIRTIO_NET_F_GUEST_ANNOUNCE feature: hypervisor would
notice the guest when it thinks it's time for guest to announce the link
presnece. Guest tests VIRTIO_NET_S_ANNOUNCE bit during config change interrupt
and woule send gratuitous packets through netif_notify_peers() and ack the
notification through ctrl vq.

We need to make sure the atomicy of read and ack in guest otherwise we may ack
more times than being notified. This is done through handling the whole config
change interrupt in an non-reentrant workqueue.

Signed-off-by: Jason Wang <jasowang@redhat.com>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Jason Wang and committed by
David S. Miller
586d17c5 8831a3f2

+73 -5
+59 -5
drivers/net/virtio_net.c
··· 66 66 /* Host will merge rx buffers for big packets (shake it! shake it!) */ 67 67 bool mergeable_rx_bufs; 68 68 69 + /* enable config space updates */ 70 + bool config_enable; 71 + 69 72 /* Active statistics */ 70 73 struct virtnet_stats __percpu *stats; 71 74 72 75 /* Work struct for refilling if we run low on memory. */ 73 76 struct delayed_work refill; 77 + 78 + /* Work struct for config space updates */ 79 + struct work_struct config_work; 80 + 81 + /* Lock for config space updates */ 82 + struct mutex config_lock; 74 83 75 84 /* Chain pages by the private ptr. */ 76 85 struct page *pages; ··· 789 780 return status == VIRTIO_NET_OK; 790 781 } 791 782 783 + static void virtnet_ack_link_announce(struct virtnet_info *vi) 784 + { 785 + rtnl_lock(); 786 + if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_ANNOUNCE, 787 + VIRTIO_NET_CTRL_ANNOUNCE_ACK, NULL, 788 + 0, 0)) 789 + dev_warn(&vi->dev->dev, "Failed to ack link announce.\n"); 790 + rtnl_unlock(); 791 + } 792 + 792 793 static int virtnet_close(struct net_device *dev) 793 794 { 794 795 struct virtnet_info *vi = netdev_priv(dev); ··· 970 951 #endif 971 952 }; 972 953 973 - static void virtnet_update_status(struct virtnet_info *vi) 954 + static void virtnet_config_changed_work(struct work_struct *work) 974 955 { 956 + struct virtnet_info *vi = 957 + container_of(work, struct virtnet_info, config_work); 975 958 u16 v; 959 + 960 + mutex_lock(&vi->config_lock); 961 + if (!vi->config_enable) 962 + goto done; 976 963 977 964 if (virtio_config_val(vi->vdev, VIRTIO_NET_F_STATUS, 978 965 offsetof(struct virtio_net_config, status), 979 966 &v) < 0) 980 - return; 967 + goto done; 968 + 969 + if (v & VIRTIO_NET_S_ANNOUNCE) { 970 + netif_notify_peers(vi->dev); 971 + virtnet_ack_link_announce(vi); 972 + } 981 973 982 974 /* Ignore unknown (future) status bits */ 983 975 v &= VIRTIO_NET_S_LINK_UP; 984 976 985 977 if (vi->status == v) 986 - return; 978 + goto done; 987 979 988 980 vi->status = v; 989 981 ··· 1005 975 netif_carrier_off(vi->dev); 1006 976 netif_stop_queue(vi->dev); 1007 977 } 978 + done: 979 + mutex_unlock(&vi->config_lock); 1008 980 } 1009 981 1010 982 static void virtnet_config_changed(struct virtio_device *vdev) 1011 983 { 1012 984 struct virtnet_info *vi = vdev->priv; 1013 985 1014 - virtnet_update_status(vi); 986 + queue_work(system_nrt_wq, &vi->config_work); 1015 987 } 1016 988 1017 989 static int init_vqs(struct virtnet_info *vi) ··· 1107 1075 goto free; 1108 1076 1109 1077 INIT_DELAYED_WORK(&vi->refill, refill_work); 1078 + mutex_init(&vi->config_lock); 1079 + vi->config_enable = true; 1080 + INIT_WORK(&vi->config_work, virtnet_config_changed_work); 1110 1081 sg_init_table(vi->rx_sg, ARRAY_SIZE(vi->rx_sg)); 1111 1082 sg_init_table(vi->tx_sg, ARRAY_SIZE(vi->tx_sg)); 1112 1083 ··· 1145 1110 otherwise get link status from config. */ 1146 1111 if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) { 1147 1112 netif_carrier_off(dev); 1148 - virtnet_update_status(vi); 1113 + queue_work(system_nrt_wq, &vi->config_work); 1149 1114 } else { 1150 1115 vi->status = VIRTIO_NET_S_LINK_UP; 1151 1116 netif_carrier_on(dev); ··· 1204 1169 { 1205 1170 struct virtnet_info *vi = vdev->priv; 1206 1171 1172 + /* Prevent config work handler from accessing the device. */ 1173 + mutex_lock(&vi->config_lock); 1174 + vi->config_enable = false; 1175 + mutex_unlock(&vi->config_lock); 1176 + 1207 1177 unregister_netdev(vi->dev); 1208 1178 1209 1179 remove_vq_common(vi); 1180 + 1181 + flush_work(&vi->config_work); 1210 1182 1211 1183 free_percpu(vi->stats); 1212 1184 free_netdev(vi->dev); ··· 1223 1181 static int virtnet_freeze(struct virtio_device *vdev) 1224 1182 { 1225 1183 struct virtnet_info *vi = vdev->priv; 1184 + 1185 + /* Prevent config work handler from accessing the device */ 1186 + mutex_lock(&vi->config_lock); 1187 + vi->config_enable = false; 1188 + mutex_unlock(&vi->config_lock); 1226 1189 1227 1190 virtqueue_disable_cb(vi->rvq); 1228 1191 virtqueue_disable_cb(vi->svq); ··· 1241 1194 napi_disable(&vi->napi); 1242 1195 1243 1196 remove_vq_common(vi); 1197 + 1198 + flush_work(&vi->config_work); 1244 1199 1245 1200 return 0; 1246 1201 } ··· 1264 1215 if (!try_fill_recv(vi, GFP_KERNEL)) 1265 1216 queue_delayed_work(system_nrt_wq, &vi->refill, 0); 1266 1217 1218 + mutex_lock(&vi->config_lock); 1219 + vi->config_enable = true; 1220 + mutex_unlock(&vi->config_lock); 1221 + 1267 1222 return 0; 1268 1223 } 1269 1224 #endif ··· 1285 1232 VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_UFO, 1286 1233 VIRTIO_NET_F_MRG_RXBUF, VIRTIO_NET_F_STATUS, VIRTIO_NET_F_CTRL_VQ, 1287 1234 VIRTIO_NET_F_CTRL_RX, VIRTIO_NET_F_CTRL_VLAN, 1235 + VIRTIO_NET_F_GUEST_ANNOUNCE, 1288 1236 }; 1289 1237 1290 1238 static struct virtio_driver virtio_net_driver = {
+14
include/linux/virtio_net.h
··· 49 49 #define VIRTIO_NET_F_CTRL_RX 18 /* Control channel RX mode support */ 50 50 #define VIRTIO_NET_F_CTRL_VLAN 19 /* Control channel VLAN filtering */ 51 51 #define VIRTIO_NET_F_CTRL_RX_EXTRA 20 /* Extra RX mode control support */ 52 + #define VIRTIO_NET_F_GUEST_ANNOUNCE 21 /* Guest can announce device on the 53 + * network */ 52 54 53 55 #define VIRTIO_NET_S_LINK_UP 1 /* Link is up */ 56 + #define VIRTIO_NET_S_ANNOUNCE 2 /* Announcement is needed */ 54 57 55 58 struct virtio_net_config { 56 59 /* The config defining mac address (if VIRTIO_NET_F_MAC) */ ··· 154 151 #define VIRTIO_NET_CTRL_VLAN 2 155 152 #define VIRTIO_NET_CTRL_VLAN_ADD 0 156 153 #define VIRTIO_NET_CTRL_VLAN_DEL 1 154 + 155 + /* 156 + * Control link announce acknowledgement 157 + * 158 + * The command VIRTIO_NET_CTRL_ANNOUNCE_ACK is used to indicate that 159 + * driver has recevied the notification; device would clear the 160 + * VIRTIO_NET_S_ANNOUNCE bit in the status field after it receives 161 + * this command. 162 + */ 163 + #define VIRTIO_NET_CTRL_ANNOUNCE 3 164 + #define VIRTIO_NET_CTRL_ANNOUNCE_ACK 0 157 165 158 166 #endif /* _LINUX_VIRTIO_NET_H */