request->nlmsg_flags = put->flags & DPIF_FP_MODIFY ? 0 : NLM_F_CREATE;
}
-static int
-dpif_linux_flow_put(struct dpif *dpif_, const struct dpif_flow_put *put)
-{
- struct dpif_linux *dpif = dpif_linux_cast(dpif_);
- struct dpif_linux_flow request, reply;
- struct ofpbuf *buf;
- int error;
-
- dpif_linux_init_flow_put(dpif, put, &request);
- error = dpif_linux_flow_transact(&request,
- put->stats ? &reply : NULL,
- put->stats ? &buf : NULL);
- if (!error && put->stats) {
- dpif_linux_flow_get_stats(&reply, put->stats);
- ofpbuf_delete(buf);
- }
- return error;
-}
-
static void
dpif_linux_init_flow_del(struct dpif_linux *dpif, const struct dpif_flow_del *del,
struct dpif_linux_flow *request)
request->key_len = del->key_len;
}
-static int
-dpif_linux_flow_del(struct dpif *dpif_, const struct dpif_flow_del *del)
-{
- struct dpif_linux *dpif = dpif_linux_cast(dpif_);
- struct dpif_linux_flow request, reply;
- struct ofpbuf *buf;
- int error;
-
- dpif_linux_init_flow_del(dpif, del, &request);
- error = dpif_linux_flow_transact(&request,
- del->stats ? &reply : NULL,
- del->stats ? &buf : NULL);
- if (!error && del->stats) {
- dpif_linux_flow_get_stats(&reply, del->stats);
- ofpbuf_delete(buf);
- }
- return error;
-}
-
struct dpif_linux_flow_dump {
struct dpif_flow_dump up;
struct nl_dump nl_dump;
d_exec->actions, d_exec->actions_len);
}
-static int
-dpif_linux_execute__(int dp_ifindex, const struct dpif_execute *execute)
-{
- uint64_t request_stub[1024 / 8];
- struct ofpbuf request;
- int error;
-
- ofpbuf_use_stub(&request, request_stub, sizeof request_stub);
- dpif_linux_encode_execute(dp_ifindex, execute, &request);
- error = nl_transact(NETLINK_GENERIC, &request, NULL);
- ofpbuf_uninit(&request);
-
- return error;
-}
-
-static int
-dpif_linux_execute(struct dpif *dpif_, struct dpif_execute *execute)
-{
- const struct dpif_linux *dpif = dpif_linux_cast(dpif_);
-
- return dpif_linux_execute__(dpif->dp_ifindex, execute);
-}
-
#define MAX_OPS 50
static void
dpif_linux_port_poll,
dpif_linux_port_poll_wait,
dpif_linux_flow_get,
- dpif_linux_flow_put,
- dpif_linux_flow_del,
dpif_linux_flow_flush,
dpif_linux_flow_dump_create,
dpif_linux_flow_dump_destroy,
dpif_linux_flow_dump_thread_create,
dpif_linux_flow_dump_thread_destroy,
dpif_linux_flow_dump_next,
- dpif_linux_execute,
dpif_linux_operate,
dpif_linux_recv_set,
dpif_linux_handlers_set,
struct nlattr **actionsp, size_t *acts_len,
struct dpif_flow_stats *stats);
- /* Adds or modifies a flow in 'dpif'. The flow is specified by the Netlink
- * attributes with types OVS_KEY_ATTR_* in the 'put->key_len' bytes
- * starting at 'put->key'. The associated actions are specified by the
- * Netlink attributes with types OVS_ACTION_ATTR_* in the
- * 'put->actions_len' bytes starting at 'put->actions'.
- *
- * - If the flow's key does not exist in 'dpif', then the flow will be
- * added if 'put->flags' includes DPIF_FP_CREATE. Otherwise the
- * operation will fail with ENOENT.
- *
- * If the operation succeeds, then 'put->stats', if nonnull, must be
- * zeroed.
- *
- * - If the flow's key does exist in 'dpif', then the flow's actions will
- * be updated if 'put->flags' includes DPIF_FP_MODIFY. Otherwise the
- * operation will fail with EEXIST. If the flow's actions are updated,
- * then its statistics will be zeroed if 'put->flags' includes
- * DPIF_FP_ZERO_STATS, and left as-is otherwise.
- *
- * If the operation succeeds, then 'put->stats', if nonnull, must be set
- * to the flow's statistics before the update.
- */
- int (*flow_put)(struct dpif *dpif, const struct dpif_flow_put *put);
-
- /* Deletes a flow from 'dpif' and returns 0, or returns ENOENT if 'dpif'
- * does not contain such a flow. The flow is specified by the Netlink
- * attributes with types OVS_KEY_ATTR_* in the 'del->key_len' bytes
- * starting at 'del->key'.
- *
- * If the operation succeeds, then 'del->stats', if nonnull, must be set to
- * the flow's statistics before its deletion. */
- int (*flow_del)(struct dpif *dpif, const struct dpif_flow_del *del);
-
/* Deletes all flows from 'dpif' and clears all of its queues of received
* packets. */
int (*flow_flush)(struct dpif *dpif);
int (*flow_dump_next)(struct dpif_flow_dump_thread *thread,
struct dpif_flow *flows, int max_flows);
- /* Performs the 'execute->actions_len' bytes of actions in
- * 'execute->actions' on the Ethernet frame in 'execute->packet'
- * and on the packet metadata in 'execute->md'.
- * May modify both packet and metadata. */
- int (*execute)(struct dpif *dpif, struct dpif_execute *execute);
-
/* Executes each of the 'n_ops' operations in 'ops' on 'dpif', in the order
* in which they are specified, placing each operation's results in the
- * "output" members documented in comments.
- *
- * This function is optional. It is only worthwhile to implement it if
- * 'dpif' can perform operations in batch faster than individually. */
+ * "output" members documented in comments and the 'error' member of each
+ * dpif_op. */
void (*operate)(struct dpif *dpif, struct dpif_op **ops, size_t n_ops);
/* Enables or disables receiving packets with dpif_recv() for 'dpif'.
return error;
}
-static int
-dpif_flow_put__(struct dpif *dpif, const struct dpif_flow_put *put)
-{
- int error;
-
- COVERAGE_INC(dpif_flow_put);
- ovs_assert(!(put->flags & ~(DPIF_FP_CREATE | DPIF_FP_MODIFY
- | DPIF_FP_ZERO_STATS)));
-
- error = dpif->dpif_class->flow_put(dpif, put);
- if (error && put->stats) {
- memset(put->stats, 0, sizeof *put->stats);
- }
- log_flow_put_message(dpif, put, error);
- return error;
-}
-
-/* Adds or modifies a flow in 'dpif'. The flow is specified by the Netlink
- * attribute OVS_FLOW_ATTR_KEY with types OVS_KEY_ATTR_* in the 'key_len' bytes
- * starting at 'key', and OVS_FLOW_ATTR_MASK with types of OVS_KEY_ATTR_* in
- * the 'mask_len' bytes starting at 'mask'. The associated actions are
- * specified by the Netlink attributes with types OVS_ACTION_ATTR_* in the
- * 'actions_len' bytes starting at 'actions'.
- *
- * - If the flow's key does not exist in 'dpif', then the flow will be added if
- * 'flags' includes DPIF_FP_CREATE. Otherwise the operation will fail with
- * ENOENT.
- *
- * The datapath may reject attempts to insert overlapping flows with EINVAL
- * or EEXIST, but clients should not rely on this: avoiding overlapping flows
- * is primarily the client's responsibility.
- *
- * If the operation succeeds, then 'stats', if nonnull, will be zeroed.
- *
- * - If the flow's key does exist in 'dpif', then the flow's actions will be
- * updated if 'flags' includes DPIF_FP_MODIFY. Otherwise the operation will
- * fail with EEXIST. If the flow's actions are updated, then its statistics
- * will be zeroed if 'flags' includes DPIF_FP_ZERO_STATS, and left as-is
- * otherwise.
- *
- * If the operation succeeds, then 'stats', if nonnull, will be set to the
- * flow's statistics before the update.
- */
+/* A dpif_operate() wrapper for performing a single DPIF_OP_FLOW_PUT. */
int
dpif_flow_put(struct dpif *dpif, enum dpif_flow_put_flags flags,
const struct nlattr *key, size_t key_len,
const struct nlattr *actions, size_t actions_len,
struct dpif_flow_stats *stats)
{
- struct dpif_flow_put put;
+ struct dpif_op *opp;
+ struct dpif_op op;
- put.flags = flags;
- put.key = key;
- put.key_len = key_len;
- put.mask = mask;
- put.mask_len = mask_len;
- put.actions = actions;
- put.actions_len = actions_len;
- put.stats = stats;
- return dpif_flow_put__(dpif, &put);
-}
-
-static int
-dpif_flow_del__(struct dpif *dpif, struct dpif_flow_del *del)
-{
- int error;
+ op.type = DPIF_OP_FLOW_PUT;
+ op.u.flow_put.flags = flags;
+ op.u.flow_put.key = key;
+ op.u.flow_put.key_len = key_len;
+ op.u.flow_put.mask = mask;
+ op.u.flow_put.mask_len = mask_len;
+ op.u.flow_put.actions = actions;
+ op.u.flow_put.actions_len = actions_len;
+ op.u.flow_put.stats = stats;
- COVERAGE_INC(dpif_flow_del);
+ opp = &op;
+ dpif_operate(dpif, &opp, 1);
- error = dpif->dpif_class->flow_del(dpif, del);
- if (error && del->stats) {
- memset(del->stats, 0, sizeof *del->stats);
- }
- log_flow_del_message(dpif, del, error);
- return error;
+ return op.error;
}
-/* Deletes a flow from 'dpif' and returns 0, or returns ENOENT if 'dpif' does
- * not contain such a flow. The flow is specified by the Netlink attributes
- * with types OVS_KEY_ATTR_* in the 'key_len' bytes starting at 'key'.
- *
- * If the operation succeeds, then 'stats', if nonnull, will be set to the
- * flow's statistics before its deletion. */
+/* A dpif_operate() wrapper for performing a single DPIF_OP_FLOW_DEL. */
int
dpif_flow_del(struct dpif *dpif,
const struct nlattr *key, size_t key_len,
struct dpif_flow_stats *stats)
{
- struct dpif_flow_del del;
+ struct dpif_op *opp;
+ struct dpif_op op;
- del.key = key;
- del.key_len = key_len;
- del.stats = stats;
- return dpif_flow_del__(dpif, &del);
+ op.type = DPIF_OP_FLOW_DEL;
+ op.u.flow_del.key = key;
+ op.u.flow_del.key_len = key_len;
+ op.u.flow_del.stats = stats;
+
+ opp = &op;
+ dpif_operate(dpif, &opp, 1);
+
+ return op.error;
}
/* Creates and returns a new 'struct dpif_flow_dump' for iterating through the
execute.packet = packet;
execute.md = *md;
execute.needs_help = false;
- aux->error = aux->dpif->dpif_class->execute(aux->dpif, &execute);
-
+ aux->error = dpif_execute(aux->dpif, &execute);
log_execute_message(aux->dpif, &execute, true, aux->error);
if (md->tunnel.ip_dst) {
return execute->needs_help || nl_attr_oversized(execute->actions_len);
}
-/* Causes 'dpif' to perform the 'execute->actions_len' bytes of actions in
- * 'execute->actions' on the Ethernet frame in 'execute->packet' and on packet
- * metadata in 'execute->md'. The implementation is allowed to modify both the
- * '*execute->packet' and 'execute->md'.
- *
- * Some dpif providers do not implement every action. The Linux kernel
- * datapath, in particular, does not implement ARP field modification. If
- * 'needs_help' is true, the dpif layer executes in userspace all of the
- * actions that it can, and for OVS_ACTION_ATTR_OUTPUT and
- * OVS_ACTION_ATTR_USERSPACE actions it passes the packet through to the dpif
- * implementation.
- *
- * This works even if 'execute->actions_len' is too long for a Netlink
- * attribute.
- *
- * Returns 0 if successful, otherwise a positive errno value. */
+/* A dpif_operate() wrapper for performing a single DPIF_OP_EXECUTE. */
int
dpif_execute(struct dpif *dpif, struct dpif_execute *execute)
{
- int error;
+ if (execute->actions_len) {
+ struct dpif_op *opp;
+ struct dpif_op op;
- COVERAGE_INC(dpif_execute);
- if (execute->actions_len > 0) {
- error = (dpif_execute_needs_help(execute)
- ? dpif_execute_with_help(dpif, execute)
- : dpif->dpif_class->execute(dpif, execute));
- } else {
- error = 0;
- }
+ op.type = DPIF_OP_EXECUTE;
+ op.u.execute = *execute;
- log_execute_message(dpif, execute, false, error);
+ opp = &op;
+ dpif_operate(dpif, &opp, 1);
- return error;
+ return op.error;
+ } else {
+ return 0;
+ }
}
/* Executes each of the 'n_ops' operations in 'ops' on 'dpif', in the order in
- * which they are specified, placing each operation's results in the "output"
- * members documented in comments.
- *
- * This function exists because some datapaths can perform batched operations
- * faster than individual operations. */
+ * which they are specified. Places each operation's results in the "output"
+ * members documented in comments, and 0 in the 'error' member on success or a
+ * positive errno on failure. */
void
dpif_operate(struct dpif *dpif, struct dpif_op **ops, size_t n_ops)
{
- if (dpif->dpif_class->operate) {
- while (n_ops > 0) {
- size_t chunk;
+ while (n_ops > 0) {
+ size_t chunk;
- /* Count 'chunk', the number of ops that can be executed without
- * needing any help. Ops that need help should be rare, so we
- * expect this to ordinarily be 'n_ops', that is, all the ops. */
- for (chunk = 0; chunk < n_ops; chunk++) {
- struct dpif_op *op = ops[chunk];
+ /* Count 'chunk', the number of ops that can be executed without
+ * needing any help. Ops that need help should be rare, so we
+ * expect this to ordinarily be 'n_ops', that is, all the ops. */
+ for (chunk = 0; chunk < n_ops; chunk++) {
+ struct dpif_op *op = ops[chunk];
- if (op->type == DPIF_OP_EXECUTE
- && dpif_execute_needs_help(&op->u.execute)) {
- break;
- }
+ if (op->type == DPIF_OP_EXECUTE
+ && dpif_execute_needs_help(&op->u.execute)) {
+ break;
}
+ }
- if (chunk) {
- /* Execute a chunk full of ops that the dpif provider can
- * handle itself, without help. */
- size_t i;
-
- dpif->dpif_class->operate(dpif, ops, chunk);
+ if (chunk) {
+ /* Execute a chunk full of ops that the dpif provider can
+ * handle itself, without help. */
+ size_t i;
- for (i = 0; i < chunk; i++) {
- struct dpif_op *op = ops[i];
+ dpif->dpif_class->operate(dpif, ops, chunk);
- switch (op->type) {
- case DPIF_OP_FLOW_PUT:
- log_flow_put_message(dpif, &op->u.flow_put, op->error);
- break;
+ for (i = 0; i < chunk; i++) {
+ struct dpif_op *op = ops[i];
+ int error = op->error;
- case DPIF_OP_FLOW_DEL:
- log_flow_del_message(dpif, &op->u.flow_del, op->error);
- break;
+ switch (op->type) {
+ case DPIF_OP_FLOW_PUT: {
+ struct dpif_flow_put *put = &op->u.flow_put;
- case DPIF_OP_EXECUTE:
- log_execute_message(dpif, &op->u.execute, false,
- op->error);
- break;
+ COVERAGE_INC(dpif_flow_put);
+ log_flow_put_message(dpif, put, error);
+ if (error && put->stats) {
+ memset(put->stats, 0, sizeof *put->stats);
}
+ break;
}
- ops += chunk;
- n_ops -= chunk;
- } else {
- /* Help the dpif provider to execute one op. */
- struct dpif_op *op = ops[0];
+ case DPIF_OP_FLOW_DEL: {
+ struct dpif_flow_del *del = &op->u.flow_del;
- op->error = dpif_execute(dpif, &op->u.execute);
- ops++;
- n_ops--;
- }
- }
- } else {
- size_t i;
-
- for (i = 0; i < n_ops; i++) {
- struct dpif_op *op = ops[i];
-
- switch (op->type) {
- case DPIF_OP_FLOW_PUT:
- op->error = dpif_flow_put__(dpif, &op->u.flow_put);
- break;
+ COVERAGE_INC(dpif_flow_del);
+ log_flow_del_message(dpif, del, error);
+ if (error && del->stats) {
+ memset(del->stats, 0, sizeof *del->stats);
+ }
+ break;
+ }
- case DPIF_OP_FLOW_DEL:
- op->error = dpif_flow_del__(dpif, &op->u.flow_del);
- break;
+ case DPIF_OP_EXECUTE:
+ COVERAGE_INC(dpif_execute);
+ log_execute_message(dpif, &op->u.execute, false, error);
+ break;
+ }
+ }
- case DPIF_OP_EXECUTE:
- op->error = dpif_execute(dpif, &op->u.execute);
- break;
+ ops += chunk;
+ n_ops -= chunk;
+ } else {
+ /* Help the dpif provider to execute one op. */
+ struct dpif_op *op = ops[0];
- default:
- OVS_NOT_REACHED();
- }
+ COVERAGE_INC(dpif_execute);
+ op->error = dpif_execute_with_help(dpif, &op->u.execute);
+ ops++;
+ n_ops--;
}
}
}
DPIF_OP_EXECUTE,
};
+/* Add or modify a flow.
+ *
+ * The flow is specified by the Netlink attributes with types OVS_KEY_ATTR_* in
+ * the 'key_len' bytes starting at 'key'. The associated actions are specified
+ * by the Netlink attributes with types OVS_ACTION_ATTR_* in the 'actions_len'
+ * bytes starting at 'actions'.
+ *
+ * - If the flow's key does not exist in the dpif, then the flow will be
+ * added if 'flags' includes DPIF_FP_CREATE. Otherwise the operation will
+ * fail with ENOENT.
+ *
+ * If the operation succeeds, then 'stats', if nonnull, will be zeroed.
+ *
+ * - If the flow's key does exist in the dpif, then the flow's actions will
+ * be updated if 'flags' includes DPIF_FP_MODIFY. Otherwise the operation
+ * will fail with EEXIST. If the flow's actions are updated, then its
+ * statistics will be zeroed if 'flags' includes DPIF_FP_ZERO_STATS, and
+ * left as-is otherwise.
+ *
+ * If the operation succeeds, then 'stats', if nonnull, will be set to the
+ * flow's statistics before the update.
+ */
struct dpif_flow_put {
/* Input. */
enum dpif_flow_put_flags flags; /* DPIF_FP_*. */
struct dpif_flow_stats *stats; /* Optional flow statistics. */
};
+/* Delete a flow.
+ *
+ * The flow is specified by the Netlink attributes with types OVS_KEY_ATTR_* in
+ * the 'key_len' bytes starting at 'key'. Succeeds with status 0 if the flow
+ * is deleted, or fails with ENOENT if the dpif does not contain such a flow.
+ *
+ * If the operation succeeds, then 'stats', if nonnull, will be set to the
+ * flow's statistics before its deletion. */
struct dpif_flow_del {
/* Input. */
const struct nlattr *key; /* Flow to delete. */
struct dpif_flow_stats *stats; /* Optional flow statistics. */
};
+/* Executes actions on a specified packet.
+ *
+ * Performs the 'actions_len' bytes of actions in 'actions' on the Ethernet
+ * frame in 'packet' and on the packet metadata in 'md'. May modify both
+ * 'packet' and 'md'.
+ *
+ * Some dpif providers do not implement every action. The Linux kernel
+ * datapath, in particular, does not implement ARP field modification. If
+ * 'needs_help' is true, the dpif layer executes in userspace all of the
+ * actions that it can, and for OVS_ACTION_ATTR_OUTPUT and
+ * OVS_ACTION_ATTR_USERSPACE actions it passes the packet through to the dpif
+ * implementation.
+ *
+ * This works even if 'actions_len' is too long for a Netlink attribute. */
struct dpif_execute {
- /* Raw support for execute passed along to the provider. */
+ /* Input. */
const struct nlattr *actions; /* Actions to execute on packet. */
size_t actions_len; /* Length of 'actions' in bytes. */
+ bool needs_help;
+
+ /* Input, but possibly modified as a side effect of execution. */
struct ofpbuf *packet; /* Packet to execute. */
struct pkt_metadata md; /* Packet metadata. */
-
- /* Some dpif providers do not implement every action. The Linux kernel
- * datapath, in particular, does not implement ARP field modification.
- *
- * If this member is set to true, the dpif layer executes in userspace all
- * of the actions that it can, and for OVS_ACTION_ATTR_OUTPUT and
- * OVS_ACTION_ATTR_USERSPACE actions it passes the packet through to the
- * dpif implementation. */
- bool needs_help;
};
int dpif_execute(struct dpif *, struct dpif_execute *);