NXAST_SET_TUNNEL64, /* struct nx_action_set_tunnel64 */
NXAST_MULTIPATH, /* struct nx_action_multipath */
NXAST_AUTOPATH, /* struct nx_action_autopath */
- NXAST_BUNDLE /* struct nx_action_bundle */
+ NXAST_BUNDLE, /* struct nx_action_bundle */
+ NXAST_BUNDLE_LOAD /* struct nx_action_bundle */
};
/* Header for Nicira-defined actions. */
};
OFP_ASSERT(sizeof(struct nx_action_autopath) == 24);
\f
-/* Action structure for NXAST_BUNDLE.
+/* Action structure for NXAST_BUNDLE and NXAST_BUNDLE_LOAD.
*
- * NXAST_BUNDLE chooses a slave from a supplied list of options, and outputs to
- * its selection.
+ * The bundle actions choose a slave from a supplied list of options.
+ * NXAST_BUNDLE outputs to its selection. NXAST_BUNDLE_LOAD writes its
+ * selection to a register.
*
* The list of possible slaves follows the nx_action_bundle structure. The size
* of each slave is governed by its type as indicated by the 'slave_type'
*
* The 'zero' parameter at the end of the action structure is reserved for
* future use. Switches are required to reject actions which have nonzero
- * bytes in the 'zero' field. */
+ * bytes in the 'zero' field.
+ *
+ * NXAST_BUNDLE actions should have 'ofs_nbits' and 'dst' zeroed. Switches
+ * should reject actions which have nonzero bytes in either of these fields.
+ *
+ * NXAST_BUNDLE_LOAD stores the OpenFlow port number of the selected slave in
+ * dst[ofs:ofs+n_bits]. The format and semantics of 'dst' and 'ofs_nbits' are
+ * similar to those for the NXAST_REG_LOAD action. */
struct nx_action_bundle {
ovs_be16 type; /* OFPAT_VENDOR. */
ovs_be16 len; /* Length including slaves. */
ovs_be32 slave_type; /* NXM_OF_IN_PORT. */
ovs_be16 n_slaves; /* Number of slaves. */
- uint8_t zero[10]; /* Reserved. Must be zero. */
+ ovs_be16 ofs_nbits; /* (ofs << 6) | (n_bits - 1). */
+ ovs_be32 dst; /* Destination. */
+
+ uint8_t zero[4]; /* Reserved. Must be zero. */
};
OFP_ASSERT(sizeof(struct nx_action_bundle) == 32);
}
}
+void
+bundle_execute_load(const struct nx_action_bundle *nab, struct flow *flow,
+ bool (*slave_enabled)(uint16_t ofp_port, void *aux),
+ void *aux)
+{
+ nxm_reg_load(nab->dst, nab->ofs_nbits,
+ bundle_execute(nab, flow, slave_enabled, aux), flow);
+}
+
/* Checks that 'nab' specifies a bundle action which is supported by this
* bundle module. Uses the 'max_ports' parameter to validate each port using
* ofputil_check_output_port(). Returns 0 if 'nab' is supported, otherwise an
* OpenFlow error code (as returned by ofp_mkerr()). */
int
-bundle_check(const struct nx_action_bundle *nab, int max_ports)
+bundle_check(const struct nx_action_bundle *nab, int max_ports,
+ const struct flow *flow)
{
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
uint16_t n_slaves, fields, algorithm, subtype;
}
}
+ if (subtype == NXAST_BUNDLE && (nab->ofs_nbits || nab->dst)) {
+ VLOG_WARN_RL(&rl, "bundle action has nonzero reserved fields");
+ error = ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
+ }
+
+ if (subtype == NXAST_BUNDLE_LOAD) {
+ error = nxm_dst_check(nab->dst, nab->ofs_nbits, 16, flow) || error;
+ }
+
if (slaves_size < n_slaves * sizeof(ovs_be16)) {
VLOG_WARN_RL(&rl, "Nicira action %"PRIu16" only has %zu bytes "
"allocated for slaves. %zu bytes are required for "
return error;
}
-/* Converts a bundle action string contained in 's' to an nx_action_bundle and
- * stores it in 'b'. Sets 'b''s l2 pointer to NULL. */
-void
-bundle_parse(struct ofpbuf *b, const char *s)
+/* Helper for bundle_parse and bundle_parse_load. */
+static void
+bundle_parse__(struct ofpbuf *b, const char *s, char **save_ptr,
+ const char *fields, const char *basis, const char *algorithm,
+ const char *slave_type, const char *dst,
+ const char *slave_delim)
{
- char *fields, *basis, *algorithm, *slave_type, *slave_delim;
struct nx_action_bundle *nab;
- char *tokstr, *save_ptr;
uint16_t n_slaves;
- save_ptr = NULL;
- tokstr = xstrdup(s);
- fields = strtok_r(tokstr, ", ", &save_ptr);
- basis = strtok_r(NULL, ", ", &save_ptr);
- algorithm = strtok_r(NULL, ", ", &save_ptr);
- slave_type = strtok_r(NULL, ", ", &save_ptr);
- slave_delim = strtok_r(NULL, ": ", &save_ptr);
-
if (!slave_delim) {
ovs_fatal(0, "%s: not enough arguments to bundle action", s);
}
ovs_be16 slave_be;
char *slave;
- slave = strtok_r(NULL, ", ", &save_ptr);
+ slave = strtok_r(NULL, ", ", save_ptr);
if (!slave || n_slaves >= BUNDLE_MAX_SLAVES) {
break;
}
nab->type = htons(OFPAT_VENDOR);
nab->len = htons(b->size - ((char *) b->l2 - (char *) b->data));
nab->vendor = htonl(NX_VENDOR_ID);
- nab->subtype = htons(NXAST_BUNDLE);
+ nab->subtype = htons(dst ? NXAST_BUNDLE_LOAD: NXAST_BUNDLE);
nab->n_slaves = htons(n_slaves);
nab->basis = htons(atoi(basis));
ovs_fatal(0, "%s: unknown slave_type `%s'", s, slave_type);
}
+ if (dst) {
+ uint32_t reg;
+ int ofs, n_bits;
+
+ nxm_parse_field_bits(dst, ®, &ofs, &n_bits);
+
+ nab->dst = htonl(reg);
+ nab->ofs_nbits = nxm_encode_ofs_nbits(ofs, n_bits);
+ }
+
b->l2 = NULL;
+}
+
+/* Converts a bundle action string contained in 's' to an nx_action_bundle and
+ * stores it in 'b'. Sets 'b''s l2 pointer to NULL. */
+void
+bundle_parse(struct ofpbuf *b, const char *s)
+{
+ char *fields, *basis, *algorithm, *slave_type, *slave_delim;
+ char *tokstr, *save_ptr;
+
+ save_ptr = NULL;
+ tokstr = xstrdup(s);
+ fields = strtok_r(tokstr, ", ", &save_ptr);
+ basis = strtok_r(NULL, ", ", &save_ptr);
+ algorithm = strtok_r(NULL, ", ", &save_ptr);
+ slave_type = strtok_r(NULL, ", ", &save_ptr);
+ slave_delim = strtok_r(NULL, ": ", &save_ptr);
+
+ bundle_parse__(b, s, &save_ptr, fields, basis, algorithm, slave_type, NULL,
+ slave_delim);
+ free(tokstr);
+}
+
+/* Converts a bundle_load action string contained in 's' to an nx_action_bundle
+ * and stores it in 'b'. Sets 'b''s l2 pointer to NULL. */
+void
+bundle_parse_load(struct ofpbuf *b, const char *s)
+{
+ char *fields, *basis, *algorithm, *slave_type, *dst, *slave_delim;
+ char *tokstr, *save_ptr;
+
+ save_ptr = NULL;
+ tokstr = xstrdup(s);
+ fields = strtok_r(tokstr, ", ", &save_ptr);
+ basis = strtok_r(NULL, ", ", &save_ptr);
+ algorithm = strtok_r(NULL, ", ", &save_ptr);
+ slave_type = strtok_r(NULL, ", ", &save_ptr);
+ dst = strtok_r(NULL, ", ", &save_ptr);
+ slave_delim = strtok_r(NULL, ": ", &save_ptr);
+
+ bundle_parse__(b, s, &save_ptr, fields, basis, algorithm, slave_type, dst,
+ slave_delim);
+
free(tokstr);
}
void
bundle_format(const struct nx_action_bundle *nab, struct ds *s)
{
- const char *fields, *algorithm, *slave_type;
+ const char *action, *fields, *algorithm, *slave_type;
size_t i;
fields = flow_hash_fields_to_str(ntohs(nab->fields));
slave_type = "<unknown>";
}
- ds_put_format(s, "bundle(%s,%"PRIu16",%s,%s,slaves:", fields,
+ switch (ntohs(nab->subtype)) {
+ case NXAST_BUNDLE:
+ action = "bundle";
+ break;
+ case NXAST_BUNDLE_LOAD:
+ action = "bundle_load";
+ break;
+ default:
+ NOT_REACHED();
+ }
+
+ ds_put_format(s, "%s(%s,%"PRIu16",%s,%s,", action, fields,
ntohs(nab->basis), algorithm, slave_type);
+ if (nab->subtype == htons(NXAST_BUNDLE_LOAD)) {
+ nxm_format_field_bits(s, ntohl(nab->dst),
+ nxm_decode_ofs(nab->ofs_nbits),
+ nxm_decode_n_bits(nab->ofs_nbits));
+ ds_put_cstr(s, ",");
+ }
+
+ ds_put_cstr(s, "slaves:");
for (i = 0; i < ntohs(nab->n_slaves); i++) {
if (i) {
ds_put_cstr(s, ",");
uint16_t bundle_execute(const struct nx_action_bundle *, const struct flow *,
bool (*slave_enabled)(uint16_t ofp_port, void *aux),
void *aux);
-int bundle_check(const struct nx_action_bundle *, int max_ports);
+void bundle_execute_load(const struct nx_action_bundle *, struct flow *,
+ bool (*slave_enabled)(uint16_t ofp_port, void *aux),
+ void *aux);
+int bundle_check(const struct nx_action_bundle *, int max_ports,
+ const struct flow *);
void bundle_parse(struct ofpbuf *, const char *);
+void bundle_parse_load(struct ofpbuf *b, const char *);
void bundle_format(const struct nx_action_bundle *, struct ds *);
/* Returns the 'i'th slave in 'nab'. */
autopath_parse(naa, arg);
} else if (!strcasecmp(act, "bundle")) {
bundle_parse(b, arg);
+ } else if (!strcasecmp(act, "bundle_load")) {
+ bundle_parse_load(b, arg);
} else if (!strcasecmp(act, "output")) {
put_output_action(b, str_to_u32(arg));
} else if (!strcasecmp(act, "enqueue")) {
break;
case OFPUTIL_NXAST_BUNDLE:
+ case OFPUTIL_NXAST_BUNDLE_LOAD:
bundle_format((const struct nx_action_bundle *) a, s);
break;
break;
case OFPUTIL_NXAST_BUNDLE:
+ case OFPUTIL_NXAST_BUNDLE_LOAD:
error = bundle_check((const struct nx_action_bundle *) a,
- max_ports);
+ max_ports, flow);
break;
case OFPUTIL_OFPAT_STRIP_VLAN:
{ OFPUTIL_NXAST_MULTIPATH, 32, 32 },
{ OFPUTIL_NXAST_AUTOPATH, 24, 24 },
{ OFPUTIL_NXAST_BUNDLE, 32, UINT_MAX },
+ { OFPUTIL_NXAST_BUNDLE_LOAD, 32, UINT_MAX },
};
static int
OFPUTIL_NXAST_SET_TUNNEL64,
OFPUTIL_NXAST_MULTIPATH,
OFPUTIL_NXAST_AUTOPATH,
- OFPUTIL_NXAST_BUNDLE
+ OFPUTIL_NXAST_BUNDLE,
+ OFPUTIL_NXAST_BUNDLE_LOAD,
};
int ofputil_decode_action(const union ofp_action *);
slave_enabled_cb,
ctx->ofproto), 0);
break;
+
+ case OFPUTIL_NXAST_BUNDLE_LOAD:
+ ctx->ofproto->has_bundle_action = true;
+ nab = (const struct nx_action_bundle *) ia;
+ bundle_execute_load(nab, &ctx->flow, slave_enabled_cb,
+ ctx->ofproto);
+ break;
}
}
}
# if the test does fail.
AT_SETUP([hrw bundle link selection])
-AT_CHECK([[test-bundle 'symmetric_l4,60,hrw,ofport,slaves:1,2,3,4,5,6']],
+AT_CHECK([[test-bundle 'symmetric_l4,60,hrw,ofport,NXM_NX_REG0[],slaves:1,2,3,4,5']],
[0], [ignore])
# 100000: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00
# 110000: disruption=0.50 (perfect=0.50) 0.50 0.50 0.00 0.00 0.00 0.00
AT_CLEANUP
AT_SETUP([active_backup bundle link selection])
-AT_CHECK([[test-bundle 'symmetric_l4,60,active_backup,ofport,slaves:1,2,3,4,5,6']],
+AT_CHECK([[test-bundle 'symmetric_l4,60,active_backup,ofport,NXM_NX_REG0[],slaves:1,2,3,4,5,6']],
[0],
[100000: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00
110000: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00
AT_CLEANUP
AT_SETUP([hrw bundle single link selection])
-AT_CHECK([[test-bundle 'symmetric_l4,60,hrw,ofport,slaves:1']],
+AT_CHECK([[test-bundle 'symmetric_l4,60,hrw,ofport,NXM_NX_REG0[],slaves:1']],
[0], [ignore])
# 1: disruption=1.00 (perfect=1.00) 1.00
# 0: disruption=1.00 (perfect=1.00) 0.00
AT_CLEANUP
AT_SETUP([hrw bundle no link selection])
-AT_CHECK([[test-bundle 'symmetric_l4,60,hrw,ofport,slaves:']],
+AT_CHECK([[test-bundle 'symmetric_l4,60,hrw,ofport,NXM_NX_REG0[],slaves:']],
[0], [ignore])
AT_CLEANUP
#: disruption=0.00 (perfect=0.00)
actions=bundle(symmetric_l4,60,hrw,ofport,slaves:2,3)
actions=bundle(symmetric_l4,60,hrw,ofport,slaves:)
actions=output:1,bundle(eth_src,0,hrw,ofport,slaves:1),output:2
+actions=bundle_load(eth_src,50,active_backup,ofport,NXM_NX_REG0[],slaves:1)
+actions=bundle_load(symmetric_l4,60,hrw,ofport,NXM_NX_REG0[0..15],slaves:2,3)
+actions=bundle_load(symmetric_l4,60,hrw,ofport,NXM_NX_REG0[0..30],slaves:)
+actions=output:1,bundle_load(eth_src,0,hrw,ofport,NXM_NX_REG0[16..31],slaves:1),output:2
]])
AT_CHECK([ovs-ofctl parse-flows flows.txt
], [0], [stdout])
NXT_FLOW_MOD: ADD table:255 actions=bundle(symmetric_l4,60,hrw,ofport,slaves:2,3)
NXT_FLOW_MOD: ADD table:255 actions=bundle(symmetric_l4,60,hrw,ofport,slaves:)
NXT_FLOW_MOD: ADD table:255 actions=output:1,bundle(eth_src,0,hrw,ofport,slaves:1),output:2
+NXT_FLOW_MOD: ADD table:255 actions=bundle_load(eth_src,50,active_backup,ofport,NXM_NX_REG0[],slaves:1)
+NXT_FLOW_MOD: ADD table:255 actions=bundle_load(symmetric_l4,60,hrw,ofport,NXM_NX_REG0[0..15],slaves:2,3)
+NXT_FLOW_MOD: ADD table:255 actions=bundle_load(symmetric_l4,60,hrw,ofport,NXM_NX_REG0[0..30],slaves:)
+NXT_FLOW_MOD: ADD table:255 actions=output:1,bundle_load(eth_src,0,hrw,ofport,NXM_NX_REG0[16..31],slaves:1),output:2
]])
AT_CLEANUP
struct ofpbuf b;
ofpbuf_init(&b, 0);
- bundle_parse(&b, actions);
+ bundle_parse_load(&b, actions);
nab = ofpbuf_steal_data(&b);
ofpbuf_uninit(&b);
flows[i].regs[0] = OFPP_NONE;
}
- if (bundle_check(nab, 1024)) {
+ if (bundle_check(nab, 1024, flows)) {
ovs_fatal(0, "Bundle action fails to check.");
}
changed = 0;
for (j = 0; j < N_FLOWS; j++) {
struct flow *flow = &flows[j];
- uint16_t old_slave_id;
+ uint16_t old_slave_id, ofp_port;
old_slave_id = flow->regs[0];
- flow->regs[0] = bundle_execute(nab, flow, slave_enabled_cb, &sg);
+ ofp_port = bundle_execute(nab, flow, slave_enabled_cb, &sg);
+ bundle_execute_load(nab, flow, slave_enabled_cb, &sg);
+ if (flow->regs[0] != ofp_port) {
+ ovs_fatal(0, "bundle_execute_load() and bundle_execute() "
+ "disagree");
+ }
if (flow->regs[0] != OFPP_NONE) {
slave_lookup(&sg, flow->regs[0])->flow_count++;
Random Weight algorithm.
.IP
Refer to \fBnicira\-ext.h\fR for more details.
+.
+.IP "\fBbundle_load(\fIfields\fB, \fIbasis\fB, \fIalgorithm\fB, \fIslave_type\fB, \fIdst\fB[\fIstart\fB..\fIend\fB], slaves:[\fIs1\fB, \fIs2\fB, ...])\fR"
+Has the same behavior as the \fBbundle\fR action, with one exception. Instead
+of outputting to the selected slave, it writes its selection to
+\fIdst\fB[\fIstart\fB..\fIend\fB]\fR, which must be an NXM field as described
+above.
+.IP
+Example: \fBbundle_load(eth_src,0,hrw,ofport,NXM_NX_REG0[],slaves:4,8)\fR uses
+an Ethernet source hash with basis 0, to select between OpenFlow ports 4 and 8
+using the Highest Random Weight algorithm, and writes the selection to
+\fBNXM_NX_REG0[]\fR.
+.IP
+Refer to \fBnicira\-ext.h\fR for more details.
.RE
.
.IP