lib/multipath.c \
lib/multipath.h \
lib/netdev-dummy.c \
+ lib/netdev-tunnel.c \
+ lib/netdev-pltap.c \
lib/netdev-provider.h \
lib/netdev-vport.c \
lib/netdev-vport.h \
lib/shash.h \
lib/simap.c \
lib/simap.h \
- lib/signals.c \
- lib/signals.h \
lib/smap.c \
lib/smap.h \
lib/socket-util.c \
lib/sset.h \
lib/stp.c \
lib/stp.h \
- lib/stream-fd.c \
lib/stream-fd.h \
lib/stream-provider.h \
lib/stream-ssl.h \
lib/timeval.h \
lib/token-bucket.c \
lib/token-bucket.h \
+ lib/tunalloc.c \
+ lib/tunalloc.h \
lib/type-props.h \
lib/unaligned.h \
lib/unicode.c \
lib_libopenvswitch_la_SOURCES += \
lib/daemon-windows.c \
lib/getopt_long.c \
- lib/latch-windows.c
+ lib/latch-windows.c \
+ lib/stream-fd-windows.c
else
lib_libopenvswitch_la_SOURCES += \
lib/daemon.c \
- lib/latch.c \
+ lib/latch-unix.c \
+ lib/signals.c \
+ lib/signals.h \
+ lib/stream-fd-unix.c \
lib/stream-unix.c
endif
return class != &dpif_netdev_class;
}
+static bool
+dpif_netdev_class_is_planetlab(const struct dpif_class *class)
+{
+ return class == &dpif_planetlab_class;
+}
+
static const char *
dpif_netdev_port_open_type(const struct dpif_class *class, const char *type)
{
return strcmp(type, "internal") ? type
+ : dpif_netdev_class_is_planetlab(class) ? "pltap"
: dpif_netdev_class_is_dummy(class) ? "dummy"
: "tap";
}
{
uint32_t port_no;
- if (dp->class != &dpif_netdev_class) {
+ if (dp->class != &dpif_netdev_class &&
+ dp->class != &dpif_planetlab_class) {
const char *p;
int start_no = 0;
}
struct dp_netdev_flow_state {
- uint32_t bucket;
- uint32_t offset;
struct dp_netdev_actions *actions;
struct odputil_keybuf keybuf;
struct odputil_keybuf maskbuf;
struct dpif_flow_stats stats;
};
- static int
- dpif_netdev_flow_dump_start(const struct dpif *dpif OVS_UNUSED, void **statep)
+ struct dp_netdev_flow_iter {
+ uint32_t bucket;
+ uint32_t offset;
+ int status;
+ struct ovs_mutex mutex;
+ };
+
+ static void
+ dpif_netdev_flow_dump_state_init(void **statep)
{
struct dp_netdev_flow_state *state;
*statep = state = xmalloc(sizeof *state);
- state->bucket = 0;
- state->offset = 0;
state->actions = NULL;
+ }
+
+ static void
+ dpif_netdev_flow_dump_state_uninit(void *state_)
+ {
+ struct dp_netdev_flow_state *state = state_;
+
+ dp_netdev_actions_unref(state->actions);
+ free(state);
+ }
+
+ static int
+ dpif_netdev_flow_dump_start(const struct dpif *dpif OVS_UNUSED, void **iterp)
+ {
+ struct dp_netdev_flow_iter *iter;
+
+ *iterp = iter = xmalloc(sizeof *iter);
+ iter->bucket = 0;
+ iter->offset = 0;
+ iter->status = 0;
+ ovs_mutex_init(&iter->mutex);
return 0;
}
static int
- dpif_netdev_flow_dump_next(const struct dpif *dpif, void *state_,
+ dpif_netdev_flow_dump_next(const struct dpif *dpif, void *iter_, void *state_,
const struct nlattr **key, size_t *key_len,
const struct nlattr **mask, size_t *mask_len,
const struct nlattr **actions, size_t *actions_len,
const struct dpif_flow_stats **stats)
{
+ struct dp_netdev_flow_iter *iter = iter_;
struct dp_netdev_flow_state *state = state_;
struct dp_netdev *dp = get_dp_netdev(dpif);
struct dp_netdev_flow *netdev_flow;
- struct hmap_node *node;
+ int error;
- fat_rwlock_rdlock(&dp->cls.rwlock);
- node = hmap_at_position(&dp->flow_table, &state->bucket, &state->offset);
- if (node) {
- netdev_flow = CONTAINER_OF(node, struct dp_netdev_flow, node);
- dp_netdev_flow_ref(netdev_flow);
+ ovs_mutex_lock(&iter->mutex);
+ error = iter->status;
+ if (!error) {
+ struct hmap_node *node;
+
+ fat_rwlock_rdlock(&dp->cls.rwlock);
+ node = hmap_at_position(&dp->flow_table, &iter->bucket, &iter->offset);
+ if (node) {
+ netdev_flow = CONTAINER_OF(node, struct dp_netdev_flow, node);
+ dp_netdev_flow_ref(netdev_flow);
+ }
+ fat_rwlock_unlock(&dp->cls.rwlock);
+ if (!node) {
+ iter->status = error = EOF;
+ }
}
- fat_rwlock_unlock(&dp->cls.rwlock);
- if (!node) {
- return EOF;
+ ovs_mutex_unlock(&iter->mutex);
+ if (error) {
+ return error;
}
if (key) {
}
static int
- dpif_netdev_flow_dump_done(const struct dpif *dpif OVS_UNUSED, void *state_)
+ dpif_netdev_flow_dump_done(const struct dpif *dpif OVS_UNUSED, void *iter_)
{
- struct dp_netdev_flow_state *state = state_;
+ struct dp_netdev_flow_iter *iter = iter_;
- dp_netdev_actions_unref(state->actions);
- free(state);
+ ovs_mutex_destroy(&iter->mutex);
+ free(iter);
return 0;
}
}
/* Extract flow key. */
- flow_extract(execute->packet, md->skb_priority, md->pkt_mark, &md->tunnel,
- (union flow_in_port *)&md->in_port, &key);
+ flow_extract(execute->packet, md, &key);
ovs_rwlock_rdlock(&dp->port_rwlock);
dp_netdev_execute_actions(dp, &key, execute->packet, md, execute->actions,
if (!error) {
struct pkt_metadata md
= PKT_METADATA_INITIALIZER(port->port_no);
- dp_netdev_port_input(dp, &packet, &md);
+ dp_netdev_port_input(dp, &packet, &md);
received_anything = true;
} else if (error != EAGAIN && error != EOPNOTSUPP) {
static struct vlog_rate_limit rl
if (packet->size < ETH_HEADER_LEN) {
return;
}
- flow_extract(packet, md->skb_priority, md->pkt_mark, &md->tunnel,
- (union flow_in_port *)&md->in_port, &key);
+ flow_extract(packet, md, &key);
netdev_flow = dp_netdev_lookup_flow(dp, &key);
if (netdev_flow) {
struct dp_netdev_actions *actions;
odp_execute_actions(&aux, packet, md, actions, actions_len, dp_execute_cb);
}
+#define DPIF_NETDEV_CLASS_FUNCTIONS \
+ dpif_netdev_enumerate, \
+ dpif_netdev_port_open_type, \
+ dpif_netdev_open, \
+ dpif_netdev_close, \
+ dpif_netdev_destroy, \
+ NULL, \
+ NULL, \
+ dpif_netdev_get_stats, \
+ dpif_netdev_port_add, \
+ dpif_netdev_port_del, \
+ dpif_netdev_port_query_by_number, \
+ dpif_netdev_port_query_by_name, \
+ NULL, /* port_get_pid */ \
+ dpif_netdev_port_dump_start, \
+ dpif_netdev_port_dump_next, \
+ dpif_netdev_port_dump_done, \
+ dpif_netdev_port_poll, \
+ dpif_netdev_port_poll_wait, \
+ dpif_netdev_flow_get, \
+ dpif_netdev_flow_put, \
+ dpif_netdev_flow_del, \
+ dpif_netdev_flow_flush, \
++ dpif_netdev_flow_dump_state_init, \
+ dpif_netdev_flow_dump_start, \
+ dpif_netdev_flow_dump_next, \
++ NULL, \
+ dpif_netdev_flow_dump_done, \
++ dpif_netdev_flow_dump_state_uninit, \
+ dpif_netdev_execute, \
+ NULL, /* operate */ \
+ dpif_netdev_recv_set, \
+ dpif_netdev_queue_to_priority, \
+ dpif_netdev_recv, \
+ dpif_netdev_recv_wait, \
+ dpif_netdev_recv_purge, \
+
const struct dpif_class dpif_netdev_class = {
"netdev",
- dpif_netdev_enumerate,
- dpif_netdev_port_open_type,
- dpif_netdev_open,
- dpif_netdev_close,
- dpif_netdev_destroy,
- NULL, /* run */
- NULL, /* wait */
- dpif_netdev_get_stats,
- dpif_netdev_port_add,
- dpif_netdev_port_del,
- dpif_netdev_port_query_by_number,
- dpif_netdev_port_query_by_name,
- NULL, /* port_get_pid */
- dpif_netdev_port_dump_start,
- dpif_netdev_port_dump_next,
- dpif_netdev_port_dump_done,
- dpif_netdev_port_poll,
- dpif_netdev_port_poll_wait,
- dpif_netdev_flow_get,
- dpif_netdev_flow_put,
- dpif_netdev_flow_del,
- dpif_netdev_flow_flush,
- dpif_netdev_flow_dump_state_init,
- dpif_netdev_flow_dump_start,
- dpif_netdev_flow_dump_next,
- NULL,
- dpif_netdev_flow_dump_done,
- dpif_netdev_flow_dump_state_uninit,
- dpif_netdev_execute,
- NULL, /* operate */
- dpif_netdev_recv_set,
- dpif_netdev_queue_to_priority,
- dpif_netdev_recv,
- dpif_netdev_recv_wait,
- dpif_netdev_recv_purge,
+ DPIF_NETDEV_CLASS_FUNCTIONS
+};
+
+const struct dpif_class dpif_planetlab_class = {
+ "planetlab",
+ DPIF_NETDEV_CLASS_FUNCTIONS
};
static void
"DP PORT NEW-NUMBER",
3, 3, dpif_dummy_change_port_number, NULL);
}
+
* packets. */
int (*flow_flush)(struct dpif *dpif);
- /* Attempts to begin dumping the flows in a dpif. On success, returns 0
- * and initializes '*statep' with any data needed for iteration. On
- * failure, returns a positive errno value. */
- int (*flow_dump_start)(const struct dpif *dpif, void **statep);
+ /* Allocates thread-local state for use with the function 'flow_dump_next'.
+ * On return, initializes '*statep' with any private data needed for
+ * iteration. */
+ void (*flow_dump_state_init)(void **statep);
- /* Attempts to retrieve another flow from 'dpif' for 'state', which was
- * initialized by a successful call to the 'flow_dump_start' function for
- * 'dpif'. On success, updates the output parameters as described below
- * and returns 0. Returns EOF if the end of the flow table has been
- * reached, or a positive errno value on error. This function will not be
- * called again once it returns nonzero within a given iteration (but the
- * 'flow_dump_done' function will be called afterward).
+ /* Attempts to begin dumping the flows in a dpif. On success, returns 0
+ * and initializes '*iterp' with any shared data needed for iteration.
+ * On failure, returns a positive errno value. */
+ int (*flow_dump_start)(const struct dpif *dpif, void **iterp);
+
+ /* Attempts to retrieve another flow from 'dpif' for 'iter', using
+ * 'state' for storage. 'iter' must have been initialized by a successful
+ * call to the 'flow_dump_start' function for 'dpif'. 'state' must have
+ * been initialised with a call to the 'flow_dump_state_init' function for
+ * 'dpif.
+ *
+ * On success, updates the output parameters as described below and returns
+ * 0. Returns EOF if the end of the flow table has been reached, or a
+ * positive errno value on error. Multiple threads may use the same 'dpif'
+ * and 'iter' with this function, but all other parameters must be
+ * different for each thread. If this function returns non-zero,
+ * subsequent calls with the same arguments will also return non-zero.
*
* On success:
*
* All of the returned data is owned by 'dpif', not by the caller, and the
* caller must not modify or free it. 'dpif' must guarantee that it
* remains accessible and unchanging until at least the next call to
- * 'flow_dump_next' or 'flow_dump_done' for 'state'. */
- int (*flow_dump_next)(const struct dpif *dpif, void *state,
+ * 'flow_dump_next' or 'flow_dump_done' for 'iter' and 'state'. */
+ int (*flow_dump_next)(const struct dpif *dpif, void *iter, void *state,
const struct nlattr **key, size_t *key_len,
const struct nlattr **mask, size_t *mask_len,
const struct nlattr **actions, size_t *actions_len,
const struct dpif_flow_stats **stats);
- /* Releases resources from 'dpif' for 'state', which was initialized by a
- * successful call to the 'flow_dump_start' function for 'dpif'. */
- int (*flow_dump_done)(const struct dpif *dpif, void *state);
+ /* Determines whether the next call to 'flow_dump_next' with 'state' will
+ * modify or free the keys that it previously returned. 'state' must have
+ * been initialized by a call to 'flow_dump_state_init' for 'dpif'.
+ *
+ * 'dpif' guarantees that data returned by flow_dump_next() will remain
+ * accessible and unchanging until the next call. This function provides a
+ * way for callers to determine whether that guarantee extends beyond the
+ * next call.
+ *
+ * Returns true if the next call to flow_dump_next() is expected to be
+ * destructive to previously returned keys for 'state', false otherwise. */
+ bool (*flow_dump_next_may_destroy_keys)(void *state);
+
+ /* Releases resources from 'dpif' for 'iter', which was initialized by a
+ * successful call to the 'flow_dump_start' function for 'dpif'. Callers
+ * must ensure that this function is called once within a given iteration,
+ * as the final flow dump operation. */
+ int (*flow_dump_done)(const struct dpif *dpif, void *iter);
+
+ /* Releases 'state' which was initialized by a call to the
+ * 'flow_dump_state_init' function for this 'dpif'. */
+ void (*flow_dump_state_uninit)(void *statep);
/* Performs the 'execute->actions_len' bytes of actions in
* 'execute->actions' on the Ethernet frame in 'execute->packet'
extern const struct dpif_class dpif_linux_class;
extern const struct dpif_class dpif_netdev_class;
+extern const struct dpif_class dpif_planetlab_class;
#ifdef __cplusplus
}
&dpif_linux_class,
#endif
&dpif_netdev_class,
+ &dpif_planetlab_class,
};
struct registered_dpif_class {
return dpif_flow_del__(dpif, &del);
}
- /* Initializes 'dump' to begin dumping the flows in a dpif.
- *
- * This function provides no status indication. An error status for the entire
- * dump operation is provided when it is completed by calling
- * dpif_flow_dump_done().
- */
+ /* Allocates thread-local state for use with the 'flow_dump_next' function for
+ * 'dpif'. On return, initializes '*statep' with any private data needed for
+ * iteration. */
void
+ dpif_flow_dump_state_init(const struct dpif *dpif, void **statep)
+ {
+ dpif->dpif_class->flow_dump_state_init(statep);
+ }
+
+ /* Releases 'state' which was initialized by a call to the
+ * 'flow_dump_state_init' function for 'dpif'. */
+ void
+ dpif_flow_dump_state_uninit(const struct dpif *dpif, void *state)
+ {
+ dpif->dpif_class->flow_dump_state_uninit(state);
+ }
+
+ /* Initializes 'dump' to begin dumping the flows in a dpif. On sucess,
+ * initializes 'dump' with any data needed for iteration and returns 0.
+ * Otherwise, returns a positive errno value describing the problem. */
+ int
dpif_flow_dump_start(struct dpif_flow_dump *dump, const struct dpif *dpif)
{
+ int error;
dump->dpif = dpif;
- dump->error = dpif->dpif_class->flow_dump_start(dpif, &dump->state);
- log_operation(dpif, "flow_dump_start", dump->error);
+ error = dpif->dpif_class->flow_dump_start(dpif, &dump->iter);
+ log_operation(dpif, "flow_dump_start", error);
+ return error;
}
- /* Attempts to retrieve another flow from 'dump', which must have been
- * initialized with dpif_flow_dump_start(). On success, updates the output
- * parameters as described below and returns true. Otherwise, returns false.
- * Failure might indicate an actual error or merely the end of the flow table.
- * An error status for the entire dump operation is provided when it is
- * completed by calling dpif_flow_dump_done().
+ /* Attempts to retrieve another flow from 'dump', using 'state' for
+ * thread-local storage. 'dump' must have been initialized with a successful
+ * call to dpif_flow_dump_start(), and 'state' must have been initialized with
+ * dpif_flow_state_init().
+ *
+ * On success, updates the output parameters as described below and returns
+ * true. Otherwise, returns false. Failure might indicate an actual error or
+ * merely the end of the flow table. An error status for the entire dump
+ * operation is provided when it is completed by calling dpif_flow_dump_done().
+ * Multiple threads may use the same 'dump' with this function, but all other
+ * parameters must not be shared.
*
* On success, if 'key' and 'key_len' are nonnull then '*key' and '*key_len'
* will be set to Netlink attributes with types OVS_KEY_ATTR_* representing the
* All of the returned data is owned by 'dpif', not by the caller, and the
* caller must not modify or free it. 'dpif' guarantees that it remains
* accessible and unchanging until at least the next call to 'flow_dump_next'
- * or 'flow_dump_done' for 'dump'. */
+ * or 'flow_dump_done' for 'dump' and 'state'. */
bool
- dpif_flow_dump_next(struct dpif_flow_dump *dump,
+ dpif_flow_dump_next(struct dpif_flow_dump *dump, void *state,
const struct nlattr **key, size_t *key_len,
const struct nlattr **mask, size_t *mask_len,
const struct nlattr **actions, size_t *actions_len,
const struct dpif_flow_stats **stats)
{
const struct dpif *dpif = dump->dpif;
- int error = dump->error;
+ int error;
- if (!error) {
- error = dpif->dpif_class->flow_dump_next(dpif, dump->state,
- key, key_len,
- mask, mask_len,
- actions, actions_len,
- stats);
- if (error) {
- dpif->dpif_class->flow_dump_done(dpif, dump->state);
- }
- }
+ error = dpif->dpif_class->flow_dump_next(dpif, dump->iter, state,
+ key, key_len, mask, mask_len,
+ actions, actions_len, stats);
if (error) {
if (key) {
*key = NULL;
*stats = NULL;
}
}
- if (!dump->error) {
- if (error == EOF) {
- VLOG_DBG_RL(&dpmsg_rl, "%s: dumped all flows", dpif_name(dpif));
- } else if (should_log_flow_message(error)) {
- log_flow_message(dpif, error, "flow_dump",
- key ? *key : NULL, key ? *key_len : 0,
- mask ? *mask : NULL, mask ? *mask_len : 0,
- stats ? *stats : NULL, actions ? *actions : NULL,
- actions ? *actions_len : 0);
- }
+ if (error == EOF) {
+ VLOG_DBG_RL(&dpmsg_rl, "%s: dumped all flows", dpif_name(dpif));
+ } else if (should_log_flow_message(error)) {
+ log_flow_message(dpif, error, "flow_dump",
+ key ? *key : NULL, key ? *key_len : 0,
+ mask ? *mask : NULL, mask ? *mask_len : 0,
+ stats ? *stats : NULL, actions ? *actions : NULL,
+ actions ? *actions_len : 0);
}
- dump->error = error;
return !error;
}
+ /* Determines whether the next call to 'dpif_flow_dump_next' for 'dump' and
+ * 'state' will modify or free the keys that it previously returned. 'state'
+ * must have been initialized by a call to 'dpif_flow_dump_state_init' for
+ * 'dump'.
+ *
+ * 'dpif' guarantees that data returned by flow_dump_next() will remain
+ * accessible and unchanging until the next call. This function provides a way
+ * for callers to determine whether that guarantee extends beyond the next
+ * call.
+ *
+ * Returns true if the next call to flow_dump_next() is expected to be
+ * destructive to previously returned keys for 'state', false otherwise. */
+ bool
+ dpif_flow_dump_next_may_destroy_keys(struct dpif_flow_dump *dump, void *state)
+ {
+ const struct dpif *dpif = dump->dpif;
+ return (dpif->dpif_class->flow_dump_next_may_destroy_keys
+ ? dpif->dpif_class->flow_dump_next_may_destroy_keys(state)
+ : true);
+ }
+
/* Completes flow table dump operation 'dump', which must have been initialized
- * with dpif_flow_dump_start(). Returns 0 if the dump operation was
- * error-free, otherwise a positive errno value describing the problem. */
+ * with a successful call to dpif_flow_dump_start(). Returns 0 if the dump
+ * operation was error-free, otherwise a positive errno value describing the
+ * problem. */
int
dpif_flow_dump_done(struct dpif_flow_dump *dump)
{
const struct dpif *dpif = dump->dpif;
- if (!dump->error) {
- dump->error = dpif->dpif_class->flow_dump_done(dpif, dump->state);
- log_operation(dpif, "flow_dump_done", dump->error);
- }
- return dump->error == EOF ? 0 : dump->error;
+ int error = dpif->dpif_class->flow_dump_done(dpif, dump->iter);
+ log_operation(dpif, "flow_dump_done", error);
+ return error == EOF ? 0 : error;
}
struct dpif_execute_helper_aux {