@@ -1514,18 +1514,34 @@ static void net_vm_change_state_handler(void *opaque, bool running,
void net_cleanup(void)
{
- NetClientState *nc;
+ NetClientState *nc, **p = &QTAILQ_FIRST(&net_clients);
/*cleanup colo compare module for COLO*/
colo_compare_cleanup();
- /* We may del multiple entries during qemu_del_net_client(),
- * so QTAILQ_FOREACH_SAFE() is also not safe here.
+ /*
+ * Walk the net_clients list and remove the netdevs but *not* any
+ * NET_CLIENT_DRIVER_NIC entries. The latter are owned by the device
+ * model which created them, and in some cases (e.g. xen-net-device)
+ * the device itself may do cleanup at exit and will be upset if we
+ * just delete its NIC from underneath it.
+ *
+ * Since qemu_del_net_client() may delete multiple entries, using
+ * QTAILQ_FOREACH_SAFE() is not safe here. The only safe pointer
+ * to keep as a bookmark is a NET_CLIENT_DRIVER_NIC entry, so keep
+ * 'p' pointing to either the head of the list, or the 'next' field
+ * of the latest NET_CLIENT_DRIVER_NIC, and operate on *p as we walk
+ * the list.
+ *
+ * The 'nc' variable isn't part of the list traversal; it's purely
+ * for convenience as too much '(*p)->' has a tendency to make the
+ * readers' eyes bleed.
*/
- while (!QTAILQ_EMPTY(&net_clients)) {
- nc = QTAILQ_FIRST(&net_clients);
+ while (*p) {
+ nc = *p;
if (nc->info->type == NET_CLIENT_DRIVER_NIC) {
- qemu_del_nic(qemu_get_nic(nc));
+ /* Skip NET_CLIENT_DRIVER_NIC entries */
+ p = &QTAILQ_NEXT(nc, next);
} else {
qemu_del_net_client(nc);
}