|
@@ -428,7 +428,13 @@ void qemu_del_net_client(NetClientState *nc)
|
|
object_unparent(OBJECT(nf));
|
|
object_unparent(OBJECT(nf));
|
|
}
|
|
}
|
|
|
|
|
|
- /* If there is a peer NIC, delete and cleanup client, but do not free. */
|
|
|
|
|
|
+ /*
|
|
|
|
+ * If there is a peer NIC, transfer ownership to it. Delete the client
|
|
|
|
+ * from net_client list but do not cleanup nor free. This way NIC can
|
|
|
|
+ * still access to members of the backend.
|
|
|
|
+ *
|
|
|
|
+ * The cleanup and free will be done when the NIC is free.
|
|
|
|
+ */
|
|
if (nc->peer && nc->peer->info->type == NET_CLIENT_DRIVER_NIC) {
|
|
if (nc->peer && nc->peer->info->type == NET_CLIENT_DRIVER_NIC) {
|
|
NICState *nic = qemu_get_nic(nc->peer);
|
|
NICState *nic = qemu_get_nic(nc->peer);
|
|
if (nic->peer_deleted) {
|
|
if (nic->peer_deleted) {
|
|
@@ -438,16 +444,13 @@ void qemu_del_net_client(NetClientState *nc)
|
|
|
|
|
|
for (i = 0; i < queues; i++) {
|
|
for (i = 0; i < queues; i++) {
|
|
ncs[i]->peer->link_down = true;
|
|
ncs[i]->peer->link_down = true;
|
|
|
|
+ QTAILQ_REMOVE(&net_clients, ncs[i], next);
|
|
}
|
|
}
|
|
|
|
|
|
if (nc->peer->info->link_status_changed) {
|
|
if (nc->peer->info->link_status_changed) {
|
|
nc->peer->info->link_status_changed(nc->peer);
|
|
nc->peer->info->link_status_changed(nc->peer);
|
|
}
|
|
}
|
|
|
|
|
|
- for (i = 0; i < queues; i++) {
|
|
|
|
- qemu_cleanup_net_client(ncs[i], true);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -465,8 +468,12 @@ void qemu_del_nic(NICState *nic)
|
|
|
|
|
|
for (i = 0; i < queues; i++) {
|
|
for (i = 0; i < queues; i++) {
|
|
NetClientState *nc = qemu_get_subqueue(nic, i);
|
|
NetClientState *nc = qemu_get_subqueue(nic, i);
|
|
- /* If this is a peer NIC and peer has already been deleted, free it now. */
|
|
|
|
|
|
+ /*
|
|
|
|
+ * If this is a peer NIC and peer has already been deleted, clean it up
|
|
|
|
+ * and free it now.
|
|
|
|
+ */
|
|
if (nic->peer_deleted) {
|
|
if (nic->peer_deleted) {
|
|
|
|
+ qemu_cleanup_net_client(nc->peer, false);
|
|
qemu_free_net_client(nc->peer);
|
|
qemu_free_net_client(nc->peer);
|
|
} else if (nc->peer) {
|
|
} else if (nc->peer) {
|
|
/* if there are RX packets pending, complete them */
|
|
/* if there are RX packets pending, complete them */
|
|
@@ -1681,6 +1688,9 @@ void net_cleanup(void)
|
|
* of the latest NET_CLIENT_DRIVER_NIC, and operate on *p as we walk
|
|
* of the latest NET_CLIENT_DRIVER_NIC, and operate on *p as we walk
|
|
* the list.
|
|
* the list.
|
|
*
|
|
*
|
|
|
|
+ * However, the NIC may have peers that trust to be clean beyond this
|
|
|
|
+ * point. For example, if they have been removed with device_del.
|
|
|
|
+ *
|
|
* The 'nc' variable isn't part of the list traversal; it's purely
|
|
* 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
|
|
* for convenience as too much '(*p)->' has a tendency to make the
|
|
* readers' eyes bleed.
|
|
* readers' eyes bleed.
|
|
@@ -1688,6 +1698,17 @@ void net_cleanup(void)
|
|
while (*p) {
|
|
while (*p) {
|
|
nc = *p;
|
|
nc = *p;
|
|
if (nc->info->type == NET_CLIENT_DRIVER_NIC) {
|
|
if (nc->info->type == NET_CLIENT_DRIVER_NIC) {
|
|
|
|
+ NICState *nic = qemu_get_nic(nc);
|
|
|
|
+
|
|
|
|
+ if (nic->peer_deleted) {
|
|
|
|
+ int queues = MAX(nic->conf->peers.queues, 1);
|
|
|
|
+
|
|
|
|
+ for (int i = 0; i < queues; i++) {
|
|
|
|
+ nc = qemu_get_subqueue(nic, i);
|
|
|
|
+ qemu_cleanup_net_client(nc->peer, false);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
/* Skip NET_CLIENT_DRIVER_NIC entries */
|
|
/* Skip NET_CLIENT_DRIVER_NIC entries */
|
|
p = &QTAILQ_NEXT(nc, next);
|
|
p = &QTAILQ_NEXT(nc, next);
|
|
} else {
|
|
} else {
|