X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fdpif.c;h=793eaa1189a9533ce7f469a8d720107165660af2;hb=efacbce62f4ca3baf305441641ee35aa5b657442;hp=4159dbd0f7f07373a1568289dcd7de693fc4a639;hpb=96fba48f52254c0cef942dcce130e33d290297da;p=sliver-openvswitch.git diff --git a/lib/dpif.c b/lib/dpif.c index 4159dbd0f..793eaa118 100644 --- a/lib/dpif.c +++ b/lib/dpif.c @@ -33,14 +33,16 @@ #include "ofpbuf.h" #include "packets.h" #include "poll-loop.h" +#include "svec.h" #include "util.h" #include "valgrind.h" #include "vlog.h" #define THIS_MODULE VLM_dpif -static struct dpif_class *dpif_classes[] = { +static const struct dpif_class *dpif_classes[] = { &dpif_linux_class, + &dpif_netdev_class, }; enum { N_DPIF_CLASSES = ARRAY_SIZE(dpif_classes) }; @@ -61,6 +63,66 @@ static void log_flow_put(struct dpif *, int error, static bool should_log_flow_message(int error); static void check_rw_odp_flow(struct odp_flow *); +/* Performs periodic work needed by all the various kinds of dpifs. + * + * If your program opens any dpifs, it must call both this function and + * netdev_run() within its main poll loop. */ +void +dp_run(void) +{ + int i; + for (i = 0; i < N_DPIF_CLASSES; i++) { + const struct dpif_class *class = dpif_classes[i]; + if (class->run) { + class->run(); + } + } +} + +/* Arranges for poll_block() to wake up when dp_run() needs to be called. + * + * If your program opens any dpifs, it must call both this function and + * netdev_wait() within its main poll loop. */ +void +dp_wait(void) +{ + int i; + for (i = 0; i < N_DPIF_CLASSES; i++) { + const struct dpif_class *class = dpif_classes[i]; + if (class->wait) { + class->wait(); + } + } +} + +/* Clears 'all_dps' and enumerates the names of all known created datapaths, + * where possible, into it. The caller must first initialize 'all_dps'. + * Returns 0 if successful, otherwise a positive errno value. + * + * Some kinds of datapaths might not be practically enumerable. This is not + * considered an error. */ +int +dp_enumerate(struct svec *all_dps) +{ + int error; + int i; + + svec_clear(all_dps); + error = 0; + for (i = 0; i < N_DPIF_CLASSES; i++) { + const struct dpif_class *class = dpif_classes[i]; + int retval = class->enumerate ? class->enumerate(all_dps) : 0; + if (retval) { + VLOG_WARN("failed to enumerate %s datapaths: %s", + class->name, strerror(retval)); + if (!error) { + error = retval; + } + } + } + return error; +} + static int do_open(const char *name_, bool create, struct dpif **dpifp) { @@ -107,13 +169,35 @@ dpif_open(const char *name, struct dpif **dpifp) /* Tries to create and open a new datapath with the given 'name'. Will fail if * a datapath named 'name' already exists. Returns 0 if successful, otherwise * a positive errno value. On success stores a pointer to the datapath in - * '*dpifp', otherwise a null pointer.*/ + * '*dpifp', otherwise a null pointer. */ int dpif_create(const char *name, struct dpif **dpifp) { return do_open(name, true, dpifp); } +/* Tries to open a datapath with the given 'name', creating it if it does not + * exist. Returns 0 if successful, otherwise a positive errno value. On + * success stores a pointer to the datapath in '*dpifp', otherwise a null + * pointer. */ +int +dpif_create_and_open(const char *name, struct dpif **dpifp) +{ + int error; + + error = dpif_create(name, dpifp); + if (error == EEXIST || error == EBUSY) { + error = dpif_open(name, dpifp); + if (error) { + VLOG_WARN("datapath %s already exists but cannot be opened: %s", + name, strerror(error)); + } + } else if (error) { + VLOG_WARN("failed to create datapath %s: %s", name, strerror(error)); + } + return error; +} + /* Closes and frees the connection to 'dpif'. Does not destroy the datapath * itself; call dpif_delete() first, instead, if that is desirable. */ void @@ -133,6 +217,32 @@ dpif_name(const struct dpif *dpif) return dpif->name; } +/* Enumerates all names that may be used to open 'dpif' into 'all_names'. The + * Linux datapath, for example, supports opening a datapath both by number, + * e.g. "dp0", and by the name of the datapath's local port. For some + * datapaths, this might be an infinite set (e.g. in a file name, slashes may + * be duplicated any number of times), in which case only the names most likely + * to be used will be enumerated. + * + * The caller must already have initialized 'all_names'. Any existing names in + * 'all_names' will not be disturbed. */ +int +dpif_get_all_names(const struct dpif *dpif, struct svec *all_names) +{ + if (dpif->class->get_all_names) { + int error = dpif->class->get_all_names(dpif, all_names); + if (error) { + VLOG_WARN_RL(&error_rl, + "failed to retrieve names for datpath %s: %s", + dpif_name(dpif), strerror(error)); + } + return error; + } else { + svec_add(all_names, dpif_name(dpif)); + return 0; + } +} + /* Destroys the datapath that 'dpif' is connected to, first removing all of its * ports. After calling this function, it does not make sense to pass 'dpif' * to any functions other than dpif_name() or dpif_close(). */ @@ -309,7 +419,7 @@ dpif_port_list(const struct dpif *dpif, struct odp_port **portsp, size_t *n_portsp) { struct odp_port *ports; - size_t n_ports; + size_t n_ports = 0; int error; for (;;) { @@ -351,6 +461,40 @@ exit: 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 @@ -900,145 +1044,3 @@ check_rw_odp_flow(struct odp_flow *flow) memset(&flow->actions[0], 0xcc, sizeof flow->actions[0]); } } - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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); -}