summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/netlink-socket.c10
-rw-r--r--lib/netlink-socket.h2
2 files changed, 12 insertions, 0 deletions
diff --git a/lib/netlink-socket.c b/lib/netlink-socket.c
index 42ba7e1fc..8fd393340 100644
--- a/lib/netlink-socket.c
+++ b/lib/netlink-socket.c
@@ -714,6 +714,7 @@ nl_dump_start(struct nl_dump *dump, int protocol, const struct ofpbuf *request)
atomic_init(&dump->status, status << 1);
dump->nl_seq = nl_msg_nlmsghdr(request)->nlmsg_seq;
dump->status_seq = seq_create();
+ ovs_mutex_init(&dump->mutex);
}
/* Attempts to retrieve another reply from 'dump' into 'buffer'. 'dump' must
@@ -756,7 +757,15 @@ nl_dump_next(struct nl_dump *dump, struct ofpbuf *reply, struct ofpbuf *buffer)
return false;
}
+ /* Take the mutex here to avoid an in-kernel race. If two threads try
+ * to read from a Netlink dump socket at once, then the socket error
+ * can be set to EINVAL, which will be encountered on the next recv on
+ * that socket, which could be anywhere due to the way that we pool
+ * Netlink sockets. Serializing the recv calls avoids the issue. */
+ ovs_mutex_lock(&dump->mutex);
retval = nl_sock_recv__(dump->sock, buffer, false);
+ ovs_mutex_unlock(&dump->mutex);
+
if (retval) {
ofpbuf_clear(buffer);
if (retval == EAGAIN) {
@@ -835,6 +844,7 @@ nl_dump_done(struct nl_dump *dump)
}
nl_pool_release(dump->sock);
seq_destroy(dump->status_seq);
+ ovs_mutex_destroy(&dump->mutex);
return status >> 1;
}
diff --git a/lib/netlink-socket.h b/lib/netlink-socket.h
index dd3240907..8ac201aac 100644
--- a/lib/netlink-socket.h
+++ b/lib/netlink-socket.h
@@ -52,6 +52,7 @@
#include <stdint.h>
#include "ofpbuf.h"
#include "ovs-atomic.h"
+#include "ovs-thread.h"
struct nl_sock;
@@ -114,6 +115,7 @@ struct nl_dump {
atomic_uint status; /* Low bit set if we read final message.
* Other bits hold an errno (0 for success). */
struct seq *status_seq; /* Tracks changes to the above 'status'. */
+ struct ovs_mutex mutex;
};
void nl_dump_start(struct nl_dump *, int protocol,