error = (retval < 0 ? errno
: retval == 0 ? ECONNRESET /* not possible? */
: nlmsghdr->nlmsg_len != UINT32_MAX ? 0
- : -retval);
+ : retval);
} while (error == EINTR);
if (error) {
if (error == ENOBUFS) {
void
nl_dump_start(struct nl_dump *dump, int protocol, const struct ofpbuf *request)
{
- int status = nl_pool_alloc(protocol, &dump->sock);
-
- if (status) {
- return;
- }
+ int status;
nl_msg_nlmsghdr(request)->nlmsg_flags |= NLM_F_DUMP | NLM_F_ACK;
- status = nl_sock_send__(dump->sock, request,
- nl_sock_allocate_seq(dump->sock, 1), true);
+ status = nl_pool_alloc(protocol, &dump->sock);
+ if (!status) {
+ status = nl_sock_send__(dump->sock, request,
+ nl_sock_allocate_seq(dump->sock, 1), true);
+ }
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
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) {
}
nl_pool_release(dump->sock);
seq_destroy(dump->status_seq);
+ ovs_mutex_destroy(&dump->mutex);
return status >> 1;
}