With refcnt, NetClientState's user can run against deleter. Signed-off-by: Liu Ping Fan <pingf...@linux.vnet.ibm.com> --- hw/core/qdev-properties-system.c | 35 +++++++++++++++++++++++++++++++++++ include/net/net.h | 3 +++ net/hub.c | 3 +++ net/net.c | 31 ++++++++++++++++++++++++++++--- net/queue.c | 3 +++ net/slirp.c | 3 ++- 6 files changed, 74 insertions(+), 4 deletions(-)
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 0eada32..6632a24 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -181,6 +181,7 @@ static int parse_netdev(DeviceState *dev, const char *str, void **ptr) int queues, i = 0; int ret; + /* When return, hold ref for each peer */ queues = qemu_find_net_clients_except(str, peers, NET_CLIENT_OPTIONS_KIND_NIC, MAX_QUEUE_NUM); @@ -236,10 +237,28 @@ static void set_netdev(Object *obj, Visitor *v, void *opaque, set_pointer(obj, v, opaque, parse_netdev, name, errp); } +static void release_netdev(Object *obj, const char *name, void *opaque) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop); + NICConf *conf = container_of(peers_ptr, NICConf, peers); + NetClientState **ptr = peers_ptr->ncs; + int i; + + for (i = 0; i < conf->queues; i++) { + if (ptr[i]) { + netclient_unref(ptr[i]); + ptr[i] = NULL; + } + } +} + PropertyInfo qdev_prop_netdev = { .name = "netdev", .get = get_netdev, .set = set_netdev, + .release = release_netdev, }; /* --- vlan --- */ @@ -302,6 +321,7 @@ static void set_vlan(Object *obj, Visitor *v, void *opaque, return; } + /* inc ref, released when unset property */ hubport = net_hub_port_find(id); if (!hubport) { error_set(errp, QERR_INVALID_PARAMETER_VALUE, @@ -311,11 +331,26 @@ static void set_vlan(Object *obj, Visitor *v, void *opaque, *ptr = hubport; } +static void release_vlan(Object *obj, const char *name, void *opaque) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop); + /* With vlan property, the peers can not have multi-queue */ + NetClientState **ptr = &peers_ptr->ncs[0]; + + if (*ptr) { + netclient_unref(*ptr); + ptr = NULL; + } +} + PropertyInfo qdev_prop_vlan = { .name = "vlan", .print = print_vlan, .get = get_vlan, .set = set_vlan, + .release = release_vlan, }; int qdev_prop_set_drive(DeviceState *dev, const char *name, diff --git a/include/net/net.h b/include/net/net.h index 27aa5cb..9ab1fc7 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -64,6 +64,7 @@ typedef struct NetClientInfo { } NetClientInfo; struct NetClientState { + int ref; NetClientInfo *info; int link_down; QTAILQ_ENTRY(NetClientState) next; @@ -93,6 +94,8 @@ typedef struct NICState { NetClientState *qemu_find_netdev(const char *id); int qemu_find_net_clients_except(const char *id, NetClientState **ncs, NetClientOptionsKind type, int max); +void netclient_ref(NetClientState *nc); +void netclient_unref(NetClientState *nc); NetClientState *qemu_new_net_client(NetClientInfo *info, NetClientState *peer, const char *model, diff --git a/net/hub.c b/net/hub.c index df32074..9c6c559 100644 --- a/net/hub.c +++ b/net/hub.c @@ -201,6 +201,7 @@ NetClientState *net_hub_find_client_by_name(int hub_id, const char *name) peer = port->nc.peer; if (peer && strcmp(peer->name, name) == 0) { + netclient_ref(peer); return peer; } } @@ -223,6 +224,7 @@ NetClientState *net_hub_port_find(int hub_id) QLIST_FOREACH(port, &hub->ports, next) { nc = port->nc.peer; if (!nc) { + netclient_ref(&port->nc); return &(port->nc); } } @@ -231,6 +233,7 @@ NetClientState *net_hub_port_find(int hub_id) } nc = net_hub_add_port(hub_id, NULL); + netclient_ref(nc); return nc; } diff --git a/net/net.c b/net/net.c index 9f951b8..1ff41a7 100644 --- a/net/net.c +++ b/net/net.c @@ -206,6 +206,11 @@ static void qemu_net_client_setup(NetClientState *nc, assert(!peer->peer); nc->peer = peer; peer->peer = nc; + /* This pair reflects the peers can see each other, they + * will be released together in qemu_net_client_detach_flush. + */ + netclient_ref(peer); + netclient_ref(nc); } qemu_mutex_init(&nc->peer_lock); qemu_mutex_lock(&net_clients_lock); @@ -226,6 +231,7 @@ NetClientState *qemu_new_net_client(NetClientInfo *info, assert(info->size >= sizeof(NetClientState)); nc = g_malloc0(info->size); + netclient_ref(nc); qemu_net_client_setup(nc, info, peer, model, name, qemu_net_client_destructor); @@ -310,6 +316,18 @@ static void qemu_free_net_client(NetClientState *nc) } } +void netclient_ref(NetClientState *nc) +{ + __sync_add_and_fetch(&nc->ref, 1); +} + +void netclient_unref(NetClientState *nc) +{ + if (__sync_sub_and_fetch(&nc->ref, 1) == 0) { + qemu_free_net_client(nc); + } +} + /* Elimate the reference and sync with exit of rx/tx action. * And flush out peer's queue. */ @@ -335,8 +353,10 @@ static void qemu_net_client_detach_flush(NetClientState *nc) nc->peer = NULL; if (peer) { qemu_net_queue_purge(peer->send_queue, nc); + netclient_unref(peer); } qemu_mutex_unlock(&nc->peer_lock); + netclient_unref(nc); } void qemu_del_net_client(NetClientState *nc) @@ -382,7 +402,7 @@ void qemu_del_net_client(NetClientState *nc) for (i = 0; i < queues; i++) { qemu_net_client_detach_flush(ncs[i]); qemu_cleanup_net_client(ncs[i]); - qemu_free_net_client(ncs[i]); + netclient_unref(ncs[i]); } } @@ -393,7 +413,7 @@ void qemu_del_nic(NICState *nic) /* If this is a peer NIC and peer has already been deleted, free it now. */ if (nic->peer_deleted) { for (i = 0; i < queues; i++) { - qemu_free_net_client(nic->pending_peer[i]); + netclient_unref(nic->pending_peer[i]); } } @@ -402,7 +422,7 @@ void qemu_del_nic(NICState *nic) qemu_net_client_detach_flush(nc); qemu_cleanup_net_client(nc); - qemu_free_net_client(nc); + netclient_unref(nc); } g_free(nic->pending_peer); @@ -629,6 +649,7 @@ NetClientState *qemu_find_netdev(const char *id) if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) continue; if (!strcmp(nc->name, id)) { + netclient_ref(nc); qemu_mutex_unlock(&net_clients_lock); return nc; } @@ -652,6 +673,7 @@ int qemu_find_net_clients_except(const char *id, NetClientState **ncs, if (!strcmp(nc->name, id)) { if (ret < max) { ncs[ret] = nc; + netclient_ref(nc); } ret++; } @@ -962,9 +984,11 @@ void net_host_device_remove(Monitor *mon, const QDict *qdict) } if (!net_host_check_device(nc->model)) { monitor_printf(mon, "invalid host network device %s\n", device); + netclient_unref(nc); return; } qemu_del_net_client(nc); + netclient_unref(nc); } void netdev_add(QemuOpts *opts, Error **errp) @@ -1020,6 +1044,7 @@ void qmp_netdev_del(const char *id, Error **errp) } qemu_del_net_client(nc); + netclient_unref(nc); qemu_opts_del(opts); } diff --git a/net/queue.c b/net/queue.c index 26399a1..b6905c1 100644 --- a/net/queue.c +++ b/net/queue.c @@ -74,6 +74,7 @@ static void qemu_net_queue_send_bh(void *opaque) qemu_net_queue_flush(queue); qemu_bh_delete(q_bh->bh); + netclient_unref(q_bh->nc); g_slice_free(NetQueueBH, q_bh); } @@ -222,6 +223,7 @@ ssize_t qemu_net_queue_send(NetQueue *queue, NetQueueBH *que_bh = g_slice_new(NetQueueBH); que_bh->bh = qemu_bh_new(qemu_net_queue_send_bh, que_bh); que_bh->nc = queue->nc; + netclient_ref(queue->nc); qemu_bh_schedule(que_bh->bh); --tls_var(net_enter); } @@ -259,6 +261,7 @@ ssize_t qemu_net_queue_send_iov(NetQueue *queue, NetQueueBH *que_bh = g_slice_new(NetQueueBH); que_bh->bh = qemu_bh_new(qemu_net_queue_send_bh, que_bh); que_bh->nc = queue->nc; + netclient_ref(queue->nc); qemu_bh_schedule(que_bh->bh); --tls_var(net_enter); } diff --git a/net/slirp.c b/net/slirp.c index b3f35d5..e541548 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -346,7 +346,7 @@ void net_slirp_hostfwd_remove(Monitor *mon, const QDict *qdict) err = slirp_remove_hostfwd(QTAILQ_FIRST(&slirp_stacks)->slirp, is_udp, host_addr, host_port); - + netclient_unref(&s->nc); monitor_printf(mon, "host forwarding rule for %s %s\n", src_str, err ? "not found" : "removed"); return; @@ -437,6 +437,7 @@ void net_slirp_hostfwd_add(Monitor *mon, const QDict *qdict) } if (s) { slirp_hostfwd(s, redir_str, 0); + netclient_unref(&s->nc); } } -- 1.8.1.4