/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2016 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "coverage.h"
#include "netlink.h"
#include "netlink-socket.h"
-#include "ofpbuf.h"
-#include "vlog.h"
+#include "openvswitch/ofpbuf.h"
+#include "openvswitch/vlog.h"
VLOG_DEFINE_THIS_MODULE(netlink_notifier);
COVERAGE_DEFINE(nln_changed);
-static void nln_report(struct nln *nln, void *change);
-
struct nln {
struct nl_sock *notify_sock; /* Netlink socket. */
- struct list all_notifiers; /* All nln notifiers. */
+ struct ovs_list all_notifiers; /* All nln notifiers. */
bool has_run; /* Guard for run and wait functions. */
/* Passed in by nln_create(). */
- int multicast_group; /* Multicast group we listen on. */
int protocol; /* Protocol passed to nl_sock_create(). */
nln_parse_func *parse; /* Message parsing function. */
void *change; /* Change passed to parse. */
};
struct nln_notifier {
+ struct ovs_list node; /* In struct nln's 'all_notifiers' list. */
struct nln *nln; /* Parent nln. */
- struct list node;
+ int multicast_group; /* Multicast group we listen on. */
nln_notify_func *cb;
void *aux;
};
* Incoming messages will be parsed with 'parse' which will be passed 'change'
* as an argument. */
struct nln *
-nln_create(int protocol, int multicast_group, nln_parse_func *parse,
- void *change)
+nln_create(int protocol, nln_parse_func *parse, void *change)
{
struct nln *nln;
nln = xzalloc(sizeof *nln);
nln->notify_sock = NULL;
nln->protocol = protocol;
- nln->multicast_group = multicast_group;
nln->parse = parse;
nln->change = change;
nln->has_run = false;
- list_init(&nln->all_notifiers);
+ ovs_list_init(&nln->all_notifiers);
return nln;
}
nln_destroy(struct nln *nln)
{
if (nln) {
- ovs_assert(list_is_empty(&nln->all_notifiers));
+ ovs_assert(ovs_list_is_empty(&nln->all_notifiers));
nl_sock_destroy(nln->notify_sock);
free(nln);
}
*
* Returns an initialized nln_notifier if successful, otherwise NULL. */
struct nln_notifier *
-nln_notifier_create(struct nln *nln, nln_notify_func *cb, void *aux)
+nln_notifier_create(struct nln *nln, int multicast_group, nln_notify_func *cb,
+ void *aux)
{
struct nln_notifier *notifier;
+ int error;
if (!nln->notify_sock) {
struct nl_sock *sock;
- int error;
error = nl_sock_create(nln->protocol, &sock);
- if (!error) {
- error = nl_sock_join_mcgroup(sock, nln->multicast_group);
- }
if (error) {
- nl_sock_destroy(sock);
- VLOG_WARN("could not create netlink socket: %s", strerror(error));
+ VLOG_WARN("could not create netlink socket: %s",
+ ovs_strerror(error));
return NULL;
}
nln->notify_sock = sock;
nln_run(nln);
}
+ error = nl_sock_join_mcgroup(nln->notify_sock, multicast_group);
+ if (error) {
+ VLOG_WARN("could not join netlink multicast group: %s",
+ ovs_strerror(error));
+ return NULL;
+ }
+
notifier = xmalloc(sizeof *notifier);
- list_push_back(&nln->all_notifiers, ¬ifier->node);
+ notifier->multicast_group = multicast_group;
notifier->cb = cb;
notifier->aux = aux;
notifier->nln = nln;
+
+ ovs_list_push_back(&nln->all_notifiers, ¬ifier->node);
+
return notifier;
}
{
if (notifier) {
struct nln *nln = notifier->nln;
+ struct nln_notifier *iter;
+ int count = 0;
- list_remove(¬ifier->node);
- if (list_is_empty(&nln->all_notifiers)) {
+ ovs_list_remove(¬ifier->node);
+
+ /* Leave the group if no other notifier is interested in it. */
+ LIST_FOR_EACH (iter, node, &nln->all_notifiers) {
+ if (iter->multicast_group == notifier->multicast_group) {
+ count++;
+ }
+ }
+ if (count == 0) {
+ nl_sock_leave_mcgroup(nln->notify_sock, notifier->multicast_group);
+ }
+
+ if (ovs_list_is_empty(&nln->all_notifiers)) {
nl_sock_destroy(nln->notify_sock);
nln->notify_sock = NULL;
}
int error;
ofpbuf_use_stub(&buf, buf_stub, sizeof buf_stub);
- error = nl_sock_recv(nln->notify_sock, &buf, false);
+ error = nl_sock_recv(nln->notify_sock, &buf, NULL, false);
if (!error) {
- if (nln->parse(&buf, nln->change)) {
- nln_report(nln, nln->change);
+ int group = nln->parse(&buf, nln->change);
+
+ if (group != 0) {
+ nln_report(nln, nln->change, group);
} else {
- VLOG_WARN_RL(&rl, "received bad netlink message");
- nln_report(nln, NULL);
+ VLOG_WARN_RL(&rl, "unexpected netlink message contents");
+ nln_report(nln, NULL, 0);
}
ofpbuf_uninit(&buf);
} else if (error == EAGAIN) {
return;
} else {
if (error == ENOBUFS) {
+ /* The socket buffer might be full, there could be too many
+ * notifications, so it makes sense to call nln_report() */
+ nln_report(nln, NULL, 0);
VLOG_WARN_RL(&rl, "netlink receive buffer overflowed");
} else {
VLOG_WARN_RL(&rl, "error reading netlink socket: %s",
- strerror(error));
+ ovs_strerror(error));
}
- nln_report(nln, NULL);
+ return;
}
}
}
}
}
-static void
-nln_report(struct nln *nln, void *change)
+void
+nln_report(const struct nln *nln, void *change, int group)
{
struct nln_notifier *notifier;
}
LIST_FOR_EACH (notifier, node, &nln->all_notifiers) {
- notifier->cb(change, notifier->aux);
+ if (!change || group == notifier->multicast_group) {
+ notifier->cb(change, notifier->aux);
+ }
}
}