dp_netdev_free() must free 'dp->upcall_rwlock', but when upcalls are disabled
(if the datapath is being freed upcalls should be disabled) 'dp->upcall_rwlock'
is taken and freeing it causes an assertion to fail.
This commit takes makes sure that the upcalls are disabled and releases
'dp->upcall_rwlock' before freeing it. A simple testcase is added to detect the
failure.
---
lib/dpif-netdev.c | 8 ++++++++
tests/dpif-netdev.at | 8 ++++++++
2 files changed, 16 insertions(+)
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 3d09326..6db325f 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -513,6 +513,7 @@ dpif_netdev_open(const struct dpif_class *class, const char
*name,
static void
dp_netdev_free(struct dp_netdev *dp)
OVS_REQUIRES(dp_netdev_mutex)
+ OVS_NO_THREAD_SAFETY_ANALYSIS
{
struct dp_netdev_port *port;
struct dp_netdev_stats *bucket;
@@ -541,7 +542,14 @@ dp_netdev_free(struct dp_netdev *dp)
ovs_mutex_destroy(&dp->flow_mutex);
seq_destroy(dp->port_seq);
cmap_destroy(&dp->ports);
+
+ /* Upcalls must be disabled at this point */
+ ovs_assert(fat_rwlock_tryrdlock(&dp->upcall_rwlock));
+ /* When upcalls are disabled the rwlock is taken. Before freeing it we
+ * should unlock it */
+ fat_rwlock_unlock(&dp->upcall_rwlock);
fat_rwlock_destroy(&dp->upcall_rwlock);
+
latch_destroy(&dp->exit_latch);
free(CONST_CAST(char *, dp->name));
free(dp);
diff --git a/tests/dpif-netdev.at b/tests/dpif-netdev.at
index c50b1a8..69d561b 100644
--- a/tests/dpif-netdev.at
+++ b/tests/dpif-netdev.at
@@ -122,3 +122,11 @@
skb_priority(0),skb_mark(0/0),recirc_id(0),dp_hash(0/0),in_port(1),eth(src=50:54
OVS_VSWITCHD_STOP
AT_CLEANUP
+
+AT_SETUP([dpif-netdev - Datapath removal])
+OVS_VSWITCHD_START()
+AT_CHECK([ovs-appctl dpctl/add-dp dummy@br0])
+AT_CHECK([ovs-appctl dpctl/del-dp dummy@br0])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
--
2.1.0.rc1
_______________________________________________
dev mailing list
[email protected]
http://openvswitch.org/mailman/listinfo/dev