lib/list.h \
lib/mac-learning.c \
lib/mac-learning.h \
+ lib/netdev-linux.c \
+ lib/netdev-linux.h \
lib/netdev.c \
lib/netdev.h \
lib/odp-util.c \
lib/hmap.c \
lib/mac-learning.c \
lib/netdev.c \
+ lib/netdev-linux.c \
lib/netlink.c \
lib/odp-util.c \
lib/poll-loop.c \
#include <inttypes.h>
#include <net/if.h>
#include <linux/ethtool.h>
+#include <linux/rtnetlink.h>
#include <linux/sockios.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include "dpif-provider.h"
+#include "netdev-linux.h"
#include "ofpbuf.h"
#include "poll-loop.h"
+#include "svec.h"
#include "util.h"
#include "vlog.h"
struct dpif_linux {
struct dpif dpif;
int fd;
+
+ /* Change notification. */
+ int local_ifindex; /* Ifindex of local port. */
+ struct svec changed_ports; /* Ports that have changed. */
+ struct linux_netdev_notifier port_notifier;
};
static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(9999, 5);
static int do_ioctl(const struct dpif *, int cmd, const void *arg);
static int lookup_minor(const char *name, int *minor);
+static int finish_open(struct dpif *, const char *local_ifname);
static int create_minor(const char *name, int minor, struct dpif **dpifp);
static int open_minor(int minor, struct dpif **dpifp);
static int make_openvswitch_device(int minor, char **fnp);
+static void dpif_linux_port_changed(const struct linux_netdev_change *,
+ void *dpif);
static struct dpif_linux *
dpif_linux_cast(const struct dpif *dpif)
return CONTAINER_OF(dpif, struct dpif_linux, dpif);
}
+static void
+dpif_linux_run(void)
+{
+ linux_netdev_notifier_run();
+}
+
+static void
+dpif_linux_wait(void)
+{
+ linux_netdev_notifier_wait();
+}
+
static int
dpif_linux_open(const char *name UNUSED, char *suffix, bool create,
struct dpif **dpifp)
}
} else {
struct dpif_linux *dpif;
- int listen_mask;
+ struct odp_port port;
int error;
if (minor < 0) {
}
dpif = dpif_linux_cast(*dpifp);
- /* We can open the device, but that doesn't mean that it's been
- * created. If it hasn't been, then any command other than
- * ODP_DP_CREATE will return ENODEV. Try something innocuous. */
- listen_mask = 0; /* Make Valgrind happy. */
- error = do_ioctl(*dpifp, ODP_GET_LISTEN_MASK, &listen_mask);
- if (error) {
+ /* We need the local port's ifindex for the poll function. Start by
+ * getting the local port's name. */
+ memset(&port, 0, sizeof port);
+ port.port = ODPP_LOCAL;
+ if (ioctl(dpif->fd, ODP_PORT_QUERY, &port)) {
+ error = errno;
if (error != ENODEV) {
VLOG_WARN("%s: probe returned unexpected error: %s",
dpif_name(*dpifp), strerror(error));
}
dpif_close(*dpifp);
+ return error;
}
- return error;
+
+ /* Then use that to finish up opening. */
+ return finish_open(&dpif->dpif, port.devname);
}
}
dpif_linux_close(struct dpif *dpif_)
{
struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+ linux_netdev_notifier_unregister(&dpif->port_notifier);
+ svec_destroy(&dpif->changed_ports);
close(dpif->fd);
free(dpif);
}
return error ? -error : pv.n_ports;
}
+static int
+dpif_linux_port_poll(const struct dpif *dpif_, char **devnamep)
+{
+ struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+ int error;
+
+ error = linux_netdev_notifier_get_error(&dpif->port_notifier);
+ if (!error) {
+ if (!dpif->changed_ports.n) {
+ return EAGAIN;
+ }
+ *devnamep = dpif->changed_ports.names[--dpif->changed_ports.n];
+ } else {
+ svec_clear(&dpif->changed_ports);
+ }
+ return error;
+}
+
+static void
+dpif_linux_port_poll_wait(const struct dpif *dpif_)
+{
+ struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+ if (dpif->changed_ports.n
+ || linux_netdev_notifier_peek_error(&dpif->port_notifier)) {
+ poll_immediate_wake();
+ } else {
+ linux_netdev_notifier_wait();
+ }
+}
+
static int
dpif_linux_port_group_get(const struct dpif *dpif_, int group,
uint16_t ports[], int n)
const struct dpif_class dpif_linux_class = {
"", /* This is the default class. */
"linux",
- NULL, /* run */
- NULL, /* wait */
+ dpif_linux_run,
+ dpif_linux_wait,
dpif_linux_open,
dpif_linux_close,
dpif_linux_delete,
dpif_linux_port_query_by_number,
dpif_linux_port_query_by_name,
dpif_linux_port_list,
+ dpif_linux_port_poll,
+ dpif_linux_port_poll_wait,
dpif_linux_port_group_get,
dpif_linux_port_group_set,
dpif_linux_flow_get,
return default_major;
}
+static int
+finish_open(struct dpif *dpif_, const char *local_ifname)
+{
+ struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+ dpif->local_ifindex = if_nametoindex(local_ifname);
+ if (!dpif->local_ifindex) {
+ int error = errno;
+ dpif_close(dpif_);
+ VLOG_WARN("could not get ifindex of %s device: %s",
+ local_ifname, strerror(errno));
+ return error;
+ }
+ return 0;
+}
+
static int
create_minor(const char *name, int minor, struct dpif **dpifp)
{
int error = open_minor(minor, dpifp);
if (!error) {
error = do_ioctl(*dpifp, ODP_DP_CREATE, name);
- if (error) {
+ if (!error) {
+ error = finish_open(*dpifp, name);
+ } else {
dpif_close(*dpifp);
}
}
fd = open(fn, O_RDONLY | O_NONBLOCK);
if (fd >= 0) {
- struct dpif_linux *dpif;
- char *name;
-
- name = xasprintf("dp%d", minor);
-
- dpif = xmalloc(sizeof *dpif);
- dpif_init(&dpif->dpif, &dpif_linux_class, name, minor, minor);
- dpif->fd = fd;
- *dpifp = &dpif->dpif;
-
- free(name);
+ struct dpif_linux *dpif = xmalloc(sizeof *dpif);
+ error = linux_netdev_notifier_register(&dpif->port_notifier,
+ dpif_linux_port_changed, dpif);
+ if (!error) {
+ char *name;
+
+ name = xasprintf("dp%d", minor);
+ dpif_init(&dpif->dpif, &dpif_linux_class, name, minor, minor);
+ free(name);
+
+ dpif->fd = fd;
+ dpif->local_ifindex = 0;
+ svec_init(&dpif->changed_ports);
+ *dpifp = &dpif->dpif;
+ } else {
+ free(dpif);
+ }
} else {
error = errno;
VLOG_WARN("%s: open failed (%s)", fn, strerror(error));
return error;
}
+
+static void
+dpif_linux_port_changed(const struct linux_netdev_change *change, void *dpif_)
+{
+ struct dpif_linux *dpif = dpif_;
+
+ if (change->master_ifindex == dpif->local_ifindex
+ && (change->nlmsg_type == RTM_NEWLINK
+ || change->nlmsg_type == RTM_DELLINK))
+ {
+ /* Our datapath changed, either adding a new port or deleting an
+ * existing one. */
+ if (!svec_contains(&dpif->changed_ports, change->ifname)) {
+ svec_add(&dpif->changed_ports, change->ifname);
+ svec_sort(&dpif->changed_ports);
+ }
+ }
+}
* value. */
int (*port_list)(const struct dpif *dpif, struct odp_port *ports, int n);
+ /* Polls for changes in the set of ports in 'dpif'. If the set of ports in
+ * 'dpif' has changed, then this function should do one of the
+ * following:
+ *
+ * - Preferably: store the name of the device that was added to or deleted
+ * from 'dpif' in '*devnamep' and return 0. The caller is responsible
+ * for freeing '*devnamep' (with free()) when it no longer needs it.
+ *
+ * - Alternatively: return ENOBUFS, without indicating the device that was
+ * added or deleted.
+ *
+ * Occasional 'false positives', in which the function returns 0 while
+ * indicating a device that was not actually added or deleted or returns
+ * ENOBUFS without any change, are acceptable.
+ *
+ * If the set of ports in 'dpif' has not changed, returns EAGAIN. May also
+ * return other positive errno values to indicate that something has gone
+ * wrong. */
+ int (*port_poll)(const struct dpif *dpif, char **devnamep);
+
+ /* Arranges for the poll loop to wake up when 'port_poll' will return a
+ * value other than EAGAIN. */
+ void (*port_poll_wait)(const struct dpif *dpif);
+
/* Stores in 'ports' the port numbers of up to 'n' ports that belong to
* 'group' in 'dpif'. Returns the number of ports in 'group' (not the
* number stored), if successful, otherwise a negative errno value. */
return error;
}
+/* Polls for changes in the set of ports in 'dpif'. If the set of ports in
+ * 'dpif' has changed, this function does one of the following:
+ *
+ * - Stores the name of the device that was added to or deleted from 'dpif' in
+ * '*devnamep' and returns 0. The caller is responsible for freeing
+ * '*devnamep' (with free()) when it no longer needs it.
+ *
+ * - Returns ENOBUFS and sets '*devnamep' to NULL.
+ *
+ * This function may also return 'false positives', where it returns 0 and
+ * '*devnamep' names a device that was not actually added or deleted or it
+ * returns ENOBUFS without any change.
+ *
+ * Returns EAGAIN if the set of ports in 'dpif' has not changed. May also
+ * return other positive errno values to indicate that something has gone
+ * wrong. */
+int
+dpif_port_poll(const struct dpif *dpif, char **devnamep)
+{
+ int error = dpif->class->port_poll(dpif, devnamep);
+ if (error) {
+ *devnamep = NULL;
+ }
+ return error;
+}
+
+/* Arranges for the poll loop to wake up when port_poll(dpif) will return a
+ * value other than EAGAIN. */
+void
+dpif_port_poll_wait(const struct dpif *dpif)
+{
+ dpif->class->port_poll_wait(dpif);
+}
+
/* Retrieves a list of the port numbers in port group 'group' in 'dpif'.
*
* On success, returns 0 and points '*ports' to a newly allocated array of
memset(&flow->actions[0], 0xcc, sizeof flow->actions[0]);
}
}
-\f
-#include <net/if.h>
-#include <linux/rtnetlink.h>
-#include <linux/ethtool.h>
-#include <linux/sockios.h>
-#include <netinet/in.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sys/sysmacros.h>
-#include <unistd.h>
-
-struct dpifmon {
- struct dpif *dpif;
- struct nl_sock *sock;
- int local_ifindex;
-};
-
-int
-dpifmon_create(const char *datapath_name, struct dpifmon **monp)
-{
- struct dpifmon *mon;
- char local_name[IFNAMSIZ];
- int error;
-
- mon = *monp = xmalloc(sizeof *mon);
-
- error = dpif_open(datapath_name, &mon->dpif);
- if (error) {
- goto error;
- }
- error = dpif_port_get_name(mon->dpif, ODPP_LOCAL,
- local_name, sizeof local_name);
- if (error) {
- goto error_close_dpif;
- }
-
- mon->local_ifindex = if_nametoindex(local_name);
- if (!mon->local_ifindex) {
- error = errno;
- VLOG_WARN("could not get ifindex of %s device: %s",
- local_name, strerror(errno));
- goto error_close_dpif;
- }
-
- error = nl_sock_create(NETLINK_ROUTE, RTNLGRP_LINK, 0, 0, &mon->sock);
- if (error) {
- VLOG_WARN("could not create rtnetlink socket: %s", strerror(error));
- goto error_close_dpif;
- }
-
- return 0;
-
-error_close_dpif:
- dpif_close(mon->dpif);
-error:
- free(mon);
- *monp = NULL;
- return error;
-}
-
-void
-dpifmon_destroy(struct dpifmon *mon)
-{
- if (mon) {
- dpif_close(mon->dpif);
- nl_sock_destroy(mon->sock);
- }
-}
-
-int
-dpifmon_poll(struct dpifmon *mon, char **devnamep)
-{
- static struct vlog_rate_limit slow_rl = VLOG_RATE_LIMIT_INIT(1, 5);
- static const struct nl_policy rtnlgrp_link_policy[] = {
- [IFLA_IFNAME] = { .type = NL_A_STRING },
- [IFLA_MASTER] = { .type = NL_A_U32, .optional = true },
- };
- struct nlattr *attrs[ARRAY_SIZE(rtnlgrp_link_policy)];
- struct ofpbuf *buf;
- int error;
-
- *devnamep = NULL;
-again:
- error = nl_sock_recv(mon->sock, &buf, false);
- switch (error) {
- case 0:
- if (!nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct ifinfomsg),
- rtnlgrp_link_policy,
- attrs, ARRAY_SIZE(rtnlgrp_link_policy))) {
- VLOG_WARN_RL(&slow_rl, "received bad rtnl message");
- error = ENOBUFS;
- } else {
- const char *devname = nl_attr_get_string(attrs[IFLA_IFNAME]);
- bool for_us;
-
- if (attrs[IFLA_MASTER]) {
- uint32_t master_ifindex = nl_attr_get_u32(attrs[IFLA_MASTER]);
- for_us = master_ifindex == mon->local_ifindex;
- } else {
- /* It's for us if that device is one of our ports. */
- struct odp_port port;
- for_us = !dpif_port_query_by_name(mon->dpif, devname, &port);
- }
-
- if (!for_us) {
- /* Not for us, try again. */
- ofpbuf_delete(buf);
- COVERAGE_INC(dpifmon_poll_false_wakeup);
- goto again;
- }
- COVERAGE_INC(dpifmon_poll_changed);
- *devnamep = xstrdup(devname);
- }
- ofpbuf_delete(buf);
- break;
-
- case EAGAIN:
- /* Nothing to do. */
- break;
-
- case ENOBUFS:
- VLOG_WARN_RL(&slow_rl, "dpifmon socket overflowed");
- break;
-
- default:
- VLOG_WARN_RL(&slow_rl, "error on dpifmon socket: %s", strerror(error));
- break;
- }
- return error;
-}
-
-void
-dpifmon_run(struct dpifmon *mon UNUSED)
-{
- /* Nothing to do in this implementation. */
-}
-
-void
-dpifmon_wait(struct dpifmon *mon)
-{
- nl_sock_wait(mon->sock, POLLIN);
-}
char *name, size_t name_size);
int dpif_port_list(const struct dpif *, struct odp_port **, size_t *n_ports);
+int dpif_port_poll(const struct dpif *, char **devnamep);
+void dpif_port_poll_wait(const struct dpif *);
+
int dpif_port_group_get(const struct dpif *, uint16_t group,
uint16_t **ports, size_t *n_ports);
int dpif_port_group_set(struct dpif *, uint16_t group,
void dpif_get_netflow_ids(const struct dpif *,
uint8_t *engine_type, uint8_t *engine_id);
-\f
-struct dpifmon;
-
-int dpifmon_create(const char *datapath_name, struct dpifmon **);
-void dpifmon_destroy(struct dpifmon *);
-
-int dpifmon_poll(struct dpifmon *, char **devnamep);
-
-void dpifmon_run(struct dpifmon *);
-void dpifmon_wait(struct dpifmon *);
#endif /* dpif.h */
--- /dev/null
+/*
+ * Copyright (c) 2009 Nicira Networks.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include "netdev-linux.h"
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <linux/rtnetlink.h>
+#include <poll.h>
+
+#include "coverage.h"
+#include "netlink.h"
+#include "ofpbuf.h"
+
+#define THIS_MODULE VLM_netdev_linux
+#include "vlog.h"
+
+/* rtnetlink socket. */
+static struct nl_sock *rtnl_sock;
+
+/* All registered notifiers. */
+static struct list all_notifiers = LIST_INITIALIZER(&all_notifiers);
+
+static void linux_netdev_report_change(const struct nlmsghdr *,
+ const struct ifinfomsg *,
+ struct nlattr *attrs[]);
+static void linux_netdev_report_notify_error(int error);
+
+int
+linux_netdev_notifier_register(struct linux_netdev_notifier *notifier,
+ linux_netdev_notify_func *cb, void *aux)
+{
+ if (!rtnl_sock) {
+ int error = nl_sock_create(NETLINK_ROUTE, RTNLGRP_LINK, 0, 0,
+ &rtnl_sock);
+ if (error) {
+ VLOG_WARN("could not create rtnetlink socket: %s",
+ strerror(error));
+ return error;
+ }
+ } else {
+ /* Catch up on notification work so that the new notifier won't
+ * receive any stale notifications. */
+ linux_netdev_notifier_run();
+ }
+
+ list_push_back(&all_notifiers, ¬ifier->node);
+ notifier->error = 0;
+ notifier->cb = cb;
+ notifier->aux = aux;
+ return 0;
+}
+
+void
+linux_netdev_notifier_unregister(struct linux_netdev_notifier *notifier)
+{
+ list_remove(¬ifier->node);
+ if (list_is_empty(&all_notifiers)) {
+ nl_sock_destroy(rtnl_sock);
+ rtnl_sock = NULL;
+ }
+}
+
+int
+linux_netdev_notifier_get_error(struct linux_netdev_notifier *notifier)
+{
+ int error = notifier->error;
+ notifier->error = 0;
+ return error;
+}
+
+int
+linux_netdev_notifier_peek_error(const struct linux_netdev_notifier *notifier)
+{
+ return notifier->error;
+}
+
+static const struct nl_policy rtnlgrp_link_policy[] = {
+ [IFLA_IFNAME] = { .type = NL_A_STRING },
+ [IFLA_MASTER] = { .type = NL_A_U32, .optional = true },
+};
+
+void
+linux_netdev_notifier_run(void)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+ if (!rtnl_sock) {
+ return;
+ }
+
+ for (;;) {
+ struct nlattr *attrs[ARRAY_SIZE(rtnlgrp_link_policy)];
+ struct ofpbuf *buf;
+ int error;
+
+ error = nl_sock_recv(rtnl_sock, &buf, false);
+ if (!error) {
+ if (nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct ifinfomsg),
+ rtnlgrp_link_policy,
+ attrs, ARRAY_SIZE(rtnlgrp_link_policy))) {
+ struct ifinfomsg *ifinfo;
+
+ ifinfo = (void *) ((char *) buf->data + NLMSG_HDRLEN);
+ linux_netdev_report_change(buf->data, ifinfo, attrs);
+ } else {
+ VLOG_WARN_RL(&rl, "received bad rtnl message");
+ linux_netdev_report_notify_error(ENOBUFS);
+ }
+ ofpbuf_delete(buf);
+ } else if (error == EAGAIN) {
+ return;
+ } else {
+ if (error == ENOBUFS) {
+ VLOG_WARN_RL(&rl, "rtnetlink receive buffer overflowed");
+ } else {
+ VLOG_WARN_RL(&rl, "error reading rtnetlink socket: %s",
+ strerror(error));
+ }
+ linux_netdev_report_notify_error(error);
+ }
+ }
+}
+
+void
+linux_netdev_notifier_wait(void)
+{
+ if (rtnl_sock) {
+ nl_sock_wait(rtnl_sock, POLLIN);
+ }
+}
+
+static void
+linux_netdev_report_change(const struct nlmsghdr *nlmsg,
+ const struct ifinfomsg *ifinfo,
+ struct nlattr *attrs[])
+{
+ struct linux_netdev_notifier *notifier;
+ struct linux_netdev_change change;
+
+ COVERAGE_INC(linux_netdev_changed);
+
+ change.nlmsg_type = nlmsg->nlmsg_type;
+ change.ifi_index = ifinfo->ifi_index;
+ change.ifname = nl_attr_get_string(attrs[IFLA_IFNAME]);
+ change.master_ifindex = (attrs[IFLA_MASTER]
+ ? nl_attr_get_u32(attrs[IFLA_MASTER]) : 0);
+
+ LIST_FOR_EACH (notifier, struct linux_netdev_notifier, node,
+ &all_notifiers) {
+ if (!notifier->error) {
+ notifier->cb(&change, notifier->aux);
+ }
+ }
+}
+
+static void
+linux_netdev_report_notify_error(int error)
+{
+ struct linux_netdev_notifier *notifier;
+
+ LIST_FOR_EACH (notifier, struct linux_netdev_notifier, node,
+ &all_notifiers) {
+ if (error != ENOBUFS || !notifier->error) {
+ notifier->error = error;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2009 Nicira Networks.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NETDEV_LINUX_H
+#define NETDEV_LINUX_H 1
+
+/* These functions are specific to the Linux implementation of dpif and netdev.
+ * They should only be used directly by Linux-specific code. */
+
+#include "list.h"
+
+struct linux_netdev_change {
+ /* Copied from struct nlmsghdr. */
+ int nlmsg_type; /* e.g. RTM_NEWLINK, RTM_DELLINK. */
+
+ /* Copied from struct ifinfomsg. */
+ int ifi_index; /* Index of network device. */
+
+ /* Extracted from Netlink attributes. */
+ const char *ifname; /* Name of network device. */
+ int master_ifindex; /* Ifindex of datapath master (0 if none). */
+};
+
+typedef void linux_netdev_notify_func(const struct linux_netdev_change *,
+ void *aux);
+
+struct linux_netdev_notifier {
+ struct list node;
+ int error;
+ linux_netdev_notify_func *cb;
+ void *aux;
+};
+
+int linux_netdev_notifier_register(struct linux_netdev_notifier *,
+ linux_netdev_notify_func *, void *aux);
+void linux_netdev_notifier_unregister(struct linux_netdev_notifier *);
+int linux_netdev_notifier_get_error(struct linux_netdev_notifier *);
+int linux_netdev_notifier_peek_error(const struct linux_netdev_notifier *);
+void linux_netdev_notifier_run(void);
+void linux_netdev_notifier_wait(void);
+
+#endif /* netdev-linux.h */
#include "dynamic-string.h"
#include "fatal-signal.h"
#include "list.h"
+#include "netdev-linux.h"
#include "netlink.h"
#include "ofpbuf.h"
#include "openflow/openflow.h"
#include "packets.h"
#include "poll-loop.h"
+#include "shash.h"
#include "socket-util.h"
#include "svec.h"
return error;
}
\f
+struct netdev_monitor {
+ struct linux_netdev_notifier notifier;
+ struct shash polled_netdevs;
+ struct shash changed_netdevs;
+};
+
+static void netdev_monitor_change(const struct linux_netdev_change *change,
+ void *monitor);
+
+int
+netdev_monitor_create(struct netdev_monitor **monitorp)
+{
+ struct netdev_monitor *monitor;
+ int error;
+
+ monitor = xmalloc(sizeof *monitor);
+ error = linux_netdev_notifier_register(&monitor->notifier,
+ netdev_monitor_change, monitor);
+ if (error) {
+ free(monitor);
+ return error;
+ }
+ shash_init(&monitor->polled_netdevs);
+ shash_init(&monitor->changed_netdevs);
+ *monitorp = monitor;
+ return 0;
+}
+
+void
+netdev_monitor_destroy(struct netdev_monitor *monitor)
+{
+ if (monitor) {
+ linux_netdev_notifier_unregister(&monitor->notifier);
+ shash_destroy(&monitor->polled_netdevs);
+ free(monitor);
+ }
+}
+
+void
+netdev_monitor_add(struct netdev_monitor *monitor, struct netdev *netdev)
+{
+ if (!shash_find(&monitor->polled_netdevs, netdev_get_name(netdev))) {
+ shash_add(&monitor->polled_netdevs, netdev_get_name(netdev), NULL);
+ }
+}
+
+void
+netdev_monitor_remove(struct netdev_monitor *monitor, struct netdev *netdev)
+{
+ struct shash_node *node;
+
+ node = shash_find(&monitor->polled_netdevs, netdev_get_name(netdev));
+ if (node) {
+ shash_delete(&monitor->polled_netdevs, node);
+ node = shash_find(&monitor->changed_netdevs, netdev_get_name(netdev));
+ if (node) {
+ shash_delete(&monitor->changed_netdevs, node);
+ }
+ }
+}
+
+int
+netdev_monitor_poll(struct netdev_monitor *monitor, char **devnamep)
+{
+ int error = linux_netdev_notifier_get_error(&monitor->notifier);
+ *devnamep = NULL;
+ if (!error) {
+ struct shash_node *node = shash_first(&monitor->changed_netdevs);
+ if (!node) {
+ return EAGAIN;
+ }
+ *devnamep = xstrdup(node->name);
+ shash_delete(&monitor->changed_netdevs, node);
+ } else {
+ shash_clear(&monitor->changed_netdevs);
+ }
+ return error;
+}
+
+void
+netdev_monitor_poll_wait(const struct netdev_monitor *monitor)
+{
+ if (!shash_is_empty(&monitor->changed_netdevs)
+ || linux_netdev_notifier_peek_error(&monitor->notifier)) {
+ poll_immediate_wake();
+ } else {
+ linux_netdev_notifier_wait();
+ }
+}
+
+static void
+netdev_monitor_change(const struct linux_netdev_change *change, void *monitor_)
+{
+ struct netdev_monitor *monitor = monitor_;
+ if (shash_find(&monitor->polled_netdevs, change->ifname)
+ && !shash_find(&monitor->changed_netdevs, change->ifname)) {
+ shash_add(&monitor->changed_netdevs, change->ifname, NULL);
+ }
+}
+\f
static void restore_all_flags(void *aux);
/* Set up a signal hook to restore network device flags on program
int netdev_get_vlan_vid(const char *netdev_name, int *vlan_vid);
+struct netdev_monitor;
+int netdev_monitor_create(struct netdev_monitor **);
+void netdev_monitor_destroy(struct netdev_monitor *);
+void netdev_monitor_add(struct netdev_monitor *, struct netdev *);
+void netdev_monitor_remove(struct netdev_monitor *, struct netdev *);
+int netdev_monitor_poll(struct netdev_monitor *, char **devnamep);
+void netdev_monitor_poll_wait(const struct netdev_monitor *);
+
#endif /* netdev.h */
VLOG_MODULE(mac_learning)
VLOG_MODULE(mgmt)
VLOG_MODULE(netdev)
+VLOG_MODULE(netdev_linux)
VLOG_MODULE(netflow)
VLOG_MODULE(netlink)
VLOG_MODULE(ofctl)
/* Datapath. */
struct dpif *dpif;
- struct dpifmon *dpifmon;
+ struct netdev_monitor *netdev_monitor;
struct port_array ports; /* Index is ODP port nr; ofport->opp.port_no is
* OFP port nr. */
struct shash port_by_name;
ofproto_create(const char *datapath, const struct ofhooks *ofhooks, void *aux,
struct ofproto **ofprotop)
{
- struct dpifmon *dpifmon;
+ struct netdev_monitor *netdev_monitor;
struct odp_stats stats;
struct ofproto *p;
struct dpif *dpif;
dpif_flow_flush(dpif);
dpif_recv_purge(dpif);
- /* Start monitoring datapath ports for status changes. */
- error = dpifmon_create(datapath, &dpifmon);
+ /* Arrange to monitor datapath ports for status changes. */
+ error = netdev_monitor_create(&netdev_monitor);
if (error) {
VLOG_ERR("failed to starting monitoring datapath %s: %s",
datapath, strerror(error));
/* Initialize datapath. */
p->dpif = dpif;
- p->dpifmon = dpifmon;
+ p->netdev_monitor = netdev_monitor;
port_array_init(&p->ports);
shash_init(&p->port_by_name);
p->max_ports = stats.max_ports;
}
dpif_close(p->dpif);
- dpifmon_destroy(p->dpifmon);
+ netdev_monitor_destroy(p->netdev_monitor);
PORT_ARRAY_FOR_EACH (ofport, &p->ports, port_no) {
ofport_free(ofport);
}
return error;
}
+static void
+process_port_change(struct ofproto *ofproto, int error, char *devname)
+{
+ if (error == ENOBUFS) {
+ reinit_ports(ofproto);
+ } else if (!error) {
+ update_port(ofproto, devname);
+ free(devname);
+ }
+}
+
int
ofproto_run1(struct ofproto *p)
{
handle_odp_msg(p, buf);
}
- while ((error = dpifmon_poll(p->dpifmon, &devname)) != EAGAIN) {
- if (error == ENOBUFS) {
- reinit_ports(p);
- } else if (!error) {
- update_port(p, devname);
- free(devname);
- }
+ while ((error = dpif_port_poll(p->dpif, &devname)) != EAGAIN) {
+ process_port_change(p, error, devname);
+ }
+ while ((error = netdev_monitor_poll(p->netdev_monitor,
+ &devname)) != EAGAIN) {
+ process_port_change(p, error, devname);
}
if (p->in_band) {
size_t i;
dpif_recv_wait(p->dpif);
- dpifmon_wait(p->dpifmon);
+ dpif_port_poll_wait(p->dpif);
+ netdev_monitor_poll_wait(p->netdev_monitor);
LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) {
ofconn_wait(ofconn);
}
static void
ofport_install(struct ofproto *p, struct ofport *ofport)
{
+ netdev_monitor_add(p->netdev_monitor, ofport->netdev);
port_array_set(&p->ports, ofp_port_to_odp_port(ofport->opp.port_no),
ofport);
shash_add(&p->port_by_name, (char *) ofport->opp.name, ofport);
static void
ofport_remove(struct ofproto *p, struct ofport *ofport)
{
+ netdev_monitor_remove(p->netdev_monitor, ofport->netdev);
port_array_set(&p->ports, ofp_port_to_odp_port(ofport->opp.port_no), NULL);
shash_delete(&p->port_by_name,
shash_find(&p->port_by_name, (char *) ofport->opp.name));