From de7d3c0761a34232613ac60792c0f6cf75fdca8e Mon Sep 17 00:00:00 2001 From: Saloni Jain Date: Tue, 24 Nov 2015 17:49:42 +0530 Subject: [PATCH] Implement Openflow 1.4 Vacancy Events for OFPT_TABLE_MOD. OpenFlow 1.4 introduces the ability to turn on vacancy events with an OFPT_TABLE_MOD message specifying OFPTC_VACANCY_EVENTS. This commit adds support for the new feature in ovs-ofctl mod-table. As per the openflow specification-1.4, vacancy event adds a mechanism enabling the controller to get an early warning based on capacity threshold chosen by the controller. With this commit, vacancy events can be configured as: ovs-ofctl -O OpenFlow14 mod-table vacancy: specify vacancy threshold values in percentage for vacancy_down and vacancy_up respectively. To disable vacancy events, following command should be given: ovs-ofctl -O OpenFlow14 mod-table
novacancy Signed-off-by: Saloni Jain Co-authored-by: Shashwat Srivastava Signed-off-by: Shashwat Srivastava Co-authored-by: Sandeep Kumar Signed-off-by: Sandeep Kumar [blp@ovn.org fixed a few typos] Signed-off-by: Ben Pfaff --- NEWS | 1 + lib/ofp-parse.c | 65 +++++++++++++++++++++++--- lib/ofp-parse.h | 3 ++ lib/ofp-print.c | 21 +++++++++ lib/ofp-util.c | 91 +++++++++++++++++++++++++++++++++--- lib/ofp-util.h | 39 ++++++++++++++++ ofproto/ofproto-provider.h | 9 ++++ ofproto/ofproto.c | 24 +++++++--- tests/ofp-print.at | 2 +- tests/ofproto.at | 7 +++ utilities/ovs-ofctl.8.in | 7 ++- utilities/ovs-ofctl.c | 96 ++++++++++++++++++++++++++++++++++++-- 12 files changed, 338 insertions(+), 27 deletions(-) diff --git a/NEWS b/NEWS index d827084e8..cae265fec 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,7 @@ Post-v2.4.0 now supported. * OpenFlow 1.4+ "importance" is now considered for flow eviction. * OpenFlow 1.4+ OFPTC_EVICTION is now implemented. + * OpenFlow 1.4+ OFPTC_VACANCY_EVENTS is now implemented. * OpenFlow 1.4+ OFPMP_TABLE_DESC is now implemented. * Allow modifying the ICMPv4/ICMPv6 type and code fields. - ovs-ofctl: diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c index ea7938e67..10aacbcdf 100644 --- a/lib/ofp-parse.c +++ b/lib/ofp-parse.c @@ -886,6 +886,49 @@ parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string, return error; } +/* Convert 'setting' (as described for the "mod-table" command + * in ovs-ofctl man page) into 'tm->table_vacancy->vacancy_up' and + * 'tm->table_vacancy->vacancy_down' threshold values. + * For the two threshold values, value of vacancy_up is always greater + * than value of vacancy_down. + * + * Returns NULL if successful, otherwise a malloc()'d string describing the + * error. The caller is responsible for freeing the returned string. */ +char * OVS_WARN_UNUSED_RESULT +parse_ofp_table_vacancy(struct ofputil_table_mod *tm, const char *setting) +{ + char *save_ptr = NULL; + char *vac_up, *vac_down; + char *value = strdup(setting); + int vacancy_up, vacancy_down; + + strtok_r(value, ":", &save_ptr); + vac_down = strtok_r(NULL, ",", &save_ptr); + if (!vac_down) { + return xasprintf("Vacancy down value missing"); + } + if (!str_to_int(vac_down, 0, &vacancy_down) || + vacancy_down < 0 || vacancy_down > 100) { + return xasprintf("Invalid vacancy down value \"%s\"", vac_down); + } + vac_up = strtok_r(NULL, ",", &save_ptr); + if (!vac_up) { + return xasprintf("Vacancy up value missing"); + } + if (!str_to_int(vac_up, 0, &vacancy_up) || + vacancy_up < 0 || vacancy_up > 100) { + return xasprintf("Invalid vacancy up value \"%s\"", vac_up); + } + if (vacancy_down > vacancy_up) { + return xasprintf("Invalid vacancy range, vacancy up should be greater" + " than vacancy down ""(%s)", + ofperr_to_string(OFPERR_OFPBPC_BAD_VALUE)); + } + tm->table_vacancy.vacancy_down = vacancy_down; + tm->table_vacancy.vacancy_up = vacancy_up; + return NULL; +} + /* Convert 'table_id' and 'setting' (as described for the "mod-table" command * in the ovs-ofctl man page) into 'tm' for sending a table_mod command to a * switch. @@ -912,13 +955,13 @@ parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id, tm->miss = OFPUTIL_TABLE_MISS_DEFAULT; tm->eviction = OFPUTIL_TABLE_EVICTION_DEFAULT; tm->eviction_flags = UINT32_MAX; - + tm->vacancy = OFPUTIL_TABLE_VACANCY_DEFAULT; + tm->table_vacancy.vacancy_down = 0; + tm->table_vacancy.vacancy_up = 0; + tm->table_vacancy.vacancy = 0; /* Only OpenFlow 1.1 and 1.2 can configure table-miss via table_mod. - * Only OpenFlow 1.4+ can configure eviction via table_mod. - * - * (OpenFlow 1.4+ can also configure vacancy events via table_mod, but OVS - * doesn't support those yet and they're also logically a per-OpenFlow - * session setting so it wouldn't make sense to support them here anyway.) + * Only OpenFlow 1.4+ can configure eviction and vacancy events + * via table_mod. */ if (!strcmp(setting, "controller")) { tm->miss = OFPUTIL_TABLE_MISS_CONTROLLER; @@ -935,6 +978,16 @@ parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id, } else if (!strcmp(setting, "noevict")) { tm->eviction = OFPUTIL_TABLE_EVICTION_OFF; *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION); + } else if (!strncmp(setting, "vacancy", strcspn(setting, ":"))) { + tm->vacancy = OFPUTIL_TABLE_VACANCY_ON; + *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION); + char *error = parse_ofp_table_vacancy(tm, setting); + if (error) { + return error; + } + } else if (!strcmp(setting, "novacancy")) { + tm->vacancy = OFPUTIL_TABLE_VACANCY_OFF; + *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION); } else { return xasprintf("invalid table_mod setting %s", setting); } diff --git a/lib/ofp-parse.h b/lib/ofp-parse.h index 36f9acc2d..577fac1ac 100644 --- a/lib/ofp-parse.h +++ b/lib/ofp-parse.h @@ -100,5 +100,8 @@ char *str_to_be64(const char *str, ovs_be64 *valuep) OVS_WARN_UNUSED_RESULT; char *str_to_mac(const char *str, struct eth_addr *mac) OVS_WARN_UNUSED_RESULT; char *str_to_ip(const char *str, ovs_be32 *ip) OVS_WARN_UNUSED_RESULT; char *str_to_connhelper(const char *str, uint16_t *alg) OVS_WARN_UNUSED_RESULT; +char *parse_ofp_table_vacancy(struct ofputil_table_mod *, + const char *flow_miss_handling) + OVS_WARN_UNUSED_RESULT; #endif /* ofp-parse.h */ diff --git a/lib/ofp-print.c b/lib/ofp-print.c index 9451e7a46..1f22dd753 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -990,6 +990,18 @@ ofputil_put_eviction_flags(struct ds *string, uint32_t eviction_flags) } } +static const char * +ofputil_table_vacancy_to_string(enum ofputil_table_vacancy vacancy) +{ + switch (vacancy) { + case OFPUTIL_TABLE_VACANCY_DEFAULT: return "default"; + case OFPUTIL_TABLE_VACANCY_ON: return "on"; + case OFPUTIL_TABLE_VACANCY_OFF: return "off"; + default: return "***error***"; + } + +} + static void ofp_print_table_mod(struct ds *string, const struct ofp_header *oh) { @@ -1020,6 +1032,15 @@ ofp_print_table_mod(struct ds *string, const struct ofp_header *oh) ds_put_cstr(string, "eviction_flags="); ofputil_put_eviction_flags(string, pm.eviction_flags); } + if (pm.vacancy != OFPUTIL_TABLE_VACANCY_DEFAULT) { + ds_put_format(string, ", vacancy=%s", + ofputil_table_vacancy_to_string(pm.vacancy)); + if (pm.vacancy == OFPUTIL_TABLE_VACANCY_ON) { + ds_put_format(string, " vacancy:%"PRIu8"" + ",%"PRIu8"", pm.table_vacancy.vacancy_down, + pm.table_vacancy.vacancy_up); + } + } } /* This function will print the Table description properties. */ diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 2ebbfddaa..207c33527 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -53,10 +53,13 @@ VLOG_DEFINE_THIS_MODULE(ofp_util); * in the peer and so there's not much point in showing a lot of them. */ static struct vlog_rate_limit bad_ofmsg_rl = VLOG_RATE_LIMIT_INIT(1, 5); +static enum ofputil_table_vacancy ofputil_decode_table_vacancy( + ovs_be32 config, enum ofp_version); static enum ofputil_table_eviction ofputil_decode_table_eviction( ovs_be32 config, enum ofp_version); static ovs_be32 ofputil_encode_table_config(enum ofputil_table_miss, enum ofputil_table_eviction, + enum ofputil_table_vacancy, enum ofp_version); struct ofp_prop_header { @@ -4999,10 +5002,62 @@ ofputil_append_table_desc_reply(const struct ofputil_table_desc *td, otd->length = htons(reply->size - start_otd); otd->table_id = td->table_id; otd->config = ofputil_encode_table_config(OFPUTIL_TABLE_MISS_DEFAULT, - td->eviction, version); + td->eviction, td->vacancy, + version); ofpmp_postappend(replies, start_otd); } +/* This function parses Vacancy property, and decodes the + * ofp14_table_mod_prop_vacancy in ofputil_table_mod. + * Returns OFPERR_OFPBPC_BAD_VALUE error code when vacancy_down is + * greater than vacancy_up and also when current vacancy has non-zero + * value. Returns 0 on success. */ +static enum ofperr +parse_table_mod_vacancy_property(struct ofpbuf *property, + struct ofputil_table_mod *tm) +{ + struct ofp14_table_mod_prop_vacancy *otv = property->data; + + if (property->size != sizeof *otv) { + return OFPERR_OFPBPC_BAD_LEN; + } + tm->table_vacancy.vacancy_down = otv->vacancy_down; + tm->table_vacancy.vacancy_up = otv->vacancy_up; + if (tm->table_vacancy.vacancy_down > tm->table_vacancy.vacancy_up) { + log_property(false, "Value of vacancy_down is greater than " + "vacancy_up"); + return OFPERR_OFPBPC_BAD_VALUE; + } + if (tm->table_vacancy.vacancy_down > 100 || + tm->table_vacancy.vacancy_up > 100) { + log_property(false, "Vacancy threshold percentage should not be" + "greater than 100"); + return OFPERR_OFPBPC_BAD_VALUE; + } + tm->table_vacancy.vacancy = otv->vacancy; + if (tm->table_vacancy.vacancy) { + log_property(false, "Vacancy value should be zero for table-mod " + "messages"); + return OFPERR_OFPBPC_BAD_VALUE; + } + return 0; +} + +/* Given 'config', taken from an OpenFlow 'version' message that specifies + * table configuration (a table mod, table stats, or table features message), + * returns the table vacancy configuration that it specifies. + * + * Only OpenFlow 1.4 and later specify table vacancy configuration this way, + * so for other 'version' this function always returns + * OFPUTIL_TABLE_VACANCY_DEFAULT. */ +static enum ofputil_table_vacancy +ofputil_decode_table_vacancy(ovs_be32 config, enum ofp_version version) +{ + return (version < OFP14_VERSION ? OFPUTIL_TABLE_VACANCY_DEFAULT + : config & htonl(OFPTC14_VACANCY_EVENTS) ? OFPUTIL_TABLE_VACANCY_ON + : OFPUTIL_TABLE_VACANCY_OFF); +} + static enum ofperr parse_table_mod_eviction_property(struct ofpbuf *property, struct ofputil_table_mod *tm) @@ -5038,8 +5093,10 @@ ofputil_decode_table_eviction(ovs_be32 config, enum ofp_version version) static ovs_be32 ofputil_encode_table_config(enum ofputil_table_miss miss, enum ofputil_table_eviction eviction, + enum ofputil_table_vacancy vacancy, enum ofp_version version) { + uint32_t config = 0; /* See the section "OFPTC_* Table Configuration" in DESIGN.md for more * information on the crazy evolution of this field. */ switch (version) { @@ -5072,10 +5129,15 @@ ofputil_encode_table_config(enum ofputil_table_miss miss, case OFP14_VERSION: case OFP15_VERSION: - /* OpenFlow 1.4 introduced OFPTC14_EVICTION and OFPTC14_VACANCY_EVENTS - * and we don't support the latter yet. */ - return htonl(eviction == OFPUTIL_TABLE_EVICTION_ON - ? OFPTC14_EVICTION : 0); + /* OpenFlow 1.4 introduced OFPTC14_EVICTION and + * OFPTC14_VACANCY_EVENTS. */ + if (eviction == OFPUTIL_TABLE_EVICTION_ON) { + config |= OFPTC14_EVICTION; + } + if (vacancy == OFPUTIL_TABLE_VACANCY_ON) { + config |= OFPTC14_VACANCY_EVENTS; + } + return htonl(config); } OVS_NOT_REACHED(); @@ -5126,6 +5188,7 @@ ofputil_decode_table_mod(const struct ofp_header *oh, pm->miss = OFPUTIL_TABLE_MISS_DEFAULT; pm->eviction = OFPUTIL_TABLE_EVICTION_DEFAULT; pm->eviction_flags = UINT32_MAX; + pm->vacancy = OFPUTIL_TABLE_VACANCY_DEFAULT; ofpbuf_use_const(&b, oh, ntohs(oh->length)); raw = ofpraw_pull_assert(&b); @@ -5140,6 +5203,7 @@ ofputil_decode_table_mod(const struct ofp_header *oh, pm->table_id = otm->table_id; pm->miss = ofputil_decode_table_miss(otm->config, oh->version); pm->eviction = ofputil_decode_table_eviction(otm->config, oh->version); + pm->vacancy = ofputil_decode_table_vacancy(otm->config, oh->version); while (b.size > 0) { struct ofpbuf property; enum ofperr error; @@ -5155,6 +5219,10 @@ ofputil_decode_table_mod(const struct ofp_header *oh, error = parse_table_mod_eviction_property(&property, pm); break; + case OFPTMPT14_VACANCY: + error = parse_table_mod_vacancy_property(&property, pm); + break; + default: error = OFPERR_OFPBRC_BAD_TYPE; break; @@ -5196,19 +5264,20 @@ ofputil_encode_table_mod(const struct ofputil_table_mod *tm, otm = ofpbuf_put_zeros(b, sizeof *otm); otm->table_id = tm->table_id; otm->config = ofputil_encode_table_config(tm->miss, tm->eviction, - ofp_version); + tm->vacancy, ofp_version); break; } case OFP14_VERSION: case OFP15_VERSION: { struct ofp14_table_mod *otm; struct ofp14_table_mod_prop_eviction *ote; + struct ofp14_table_mod_prop_vacancy *otv; b = ofpraw_alloc(OFPRAW_OFPT14_TABLE_MOD, ofp_version, 0); otm = ofpbuf_put_zeros(b, sizeof *otm); otm->table_id = tm->table_id; otm->config = ofputil_encode_table_config(tm->miss, tm->eviction, - ofp_version); + tm->vacancy, ofp_version); if (tm->eviction_flags != UINT32_MAX) { ote = ofpbuf_put_zeros(b, sizeof *ote); @@ -5216,6 +5285,13 @@ ofputil_encode_table_mod(const struct ofputil_table_mod *tm, ote->length = htons(sizeof *ote); ote->flags = htonl(tm->eviction_flags); } + if (tm->vacancy == OFPUTIL_TABLE_VACANCY_ON) { + otv = ofpbuf_put_zeros(b, sizeof *otv); + otv->type = htons(OFPTMPT14_VACANCY); + otv->length = htons(sizeof *otv); + otv->vacancy_down = tm->table_vacancy.vacancy_down; + otv->vacancy_up = tm->table_vacancy.vacancy_up; + } break; } default: @@ -5682,6 +5758,7 @@ ofputil_put_ofp12_table_stats(const struct ofputil_table_stats *stats, features->nonmiss.instructions, OFP12_VERSION); out->config = ofputil_encode_table_config(features->miss_config, OFPUTIL_TABLE_EVICTION_DEFAULT, + OFPUTIL_TABLE_VACANCY_DEFAULT, OFP12_VERSION); out->max_entries = htonl(features->max_entries); out->active_count = htonl(stats->active_count); diff --git a/lib/ofp-util.h b/lib/ofp-util.h index f27f18060..a6cb58686 100644 --- a/lib/ofp-util.h +++ b/lib/ofp-util.h @@ -628,6 +628,33 @@ enum ofputil_table_eviction { OFPUTIL_TABLE_EVICTION_OFF /* Disable eviction. */ }; +/* Abstract version of OFPTC14_VACANCY_EVENTS. + * + * OpenFlow 1.0 through 1.3 don't know anything about vacancy events, so + * decoding a message for one of these protocols always yields + * OFPUTIL_TABLE_VACANCY_DEFAULT. */ +enum ofputil_table_vacancy { + OFPUTIL_TABLE_VACANCY_DEFAULT, /* No value. */ + OFPUTIL_TABLE_VACANCY_ON, /* Enable vacancy events. */ + OFPUTIL_TABLE_VACANCY_OFF /* Disable vacancy events. */ +}; + +/* Abstract version of OFPTMPT_VACANCY. + * + * Openflow 1.4+ defines vacancy events. + * The fields vacancy_down and vacancy_up are the threshold for generating + * vacancy events that should be configured on the flow table, expressed as + * a percent. + * The vacancy field is only used when this property in included in a + * OFPMP_TABLE_DESC multipart reply or a OFPT_TABLE_STATUS message and + * represent the current vacancy of the table, expressed as a percent. In + * OFP_TABLE_MOD requests, this field must be set to 0 */ +struct ofputil_table_mod_prop_vacancy { + uint8_t vacancy_down; /* Vacancy threshold when space decreases (%). */ + uint8_t vacancy_up; /* Vacancy threshold when space increases (%). */ + uint8_t vacancy; /* Current vacancy (%). */ +}; + /* Abstract ofp_table_mod. */ struct ofputil_table_mod { uint8_t table_id; /* ID of the table, 0xff indicates all tables. */ @@ -644,6 +671,16 @@ struct ofputil_table_mod { * absence. For other versions, ignored on encoding, decoded to * UINT32_MAX.*/ uint32_t eviction_flags; /* OFPTMPEF14_*. */ + + /* OpenFlow 1.4+ only. For other versions, ignored on encoding, decoded to + * OFPUTIL_TABLE_VACANCY_DEFAULT. */ + enum ofputil_table_vacancy vacancy; + + /* Openflow 1.4+ only. Defines threshold values of vacancy expressed as + * percent, value of current vacancy is set to zero for table-mod. + * For other versions, ignored on encoding, all values decoded to + * zero. */ + struct ofputil_table_mod_prop_vacancy table_vacancy; }; /* Abstract ofp14_table_desc. */ @@ -651,6 +688,8 @@ struct ofputil_table_desc { uint8_t table_id; /* ID of the table. */ enum ofputil_table_eviction eviction; uint32_t eviction_flags; /* UINT32_MAX if not present. */ + enum ofputil_table_vacancy vacancy; + struct ofputil_table_mod_prop_vacancy table_vacancy; }; enum ofperr ofputil_decode_table_mod(const struct ofp_header *, diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h index 98d439e99..2a6bd9156 100644 --- a/ofproto/ofproto-provider.h +++ b/ofproto/ofproto-provider.h @@ -256,6 +256,15 @@ struct oftable { #define EVICTION_OPENFLOW (1 << 1) /* Set to 1 if OpenFlow enables eviction. */ unsigned int eviction; + /* If true, vacancy events are enabled; otherwise they are disabled. */ + bool vacancy_enabled; + + /* Non-zero values for vacancy_up and vacancy_down indicates that vacancy + * is enabled by table-mod, else these values are set to zero when + * vacancy is disabled */ + uint8_t vacancy_down; /* Vacancy threshold when space decreases (%). */ + uint8_t vacancy_up; /* Vacancy threshold when space increases (%). */ + atomic_ulong n_matched; atomic_ulong n_missed; }; diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 2334cdb32..eeb3316be 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -6646,20 +6646,20 @@ ofproto_table_get_miss_config(const struct ofproto *ofproto, uint8_t table_id) static void table_mod__(struct oftable *oftable, - enum ofputil_table_miss miss, enum ofputil_table_eviction eviction) + const struct ofputil_table_mod *tm) { - if (miss == OFPUTIL_TABLE_MISS_DEFAULT) { + if (tm->miss == OFPUTIL_TABLE_MISS_DEFAULT) { /* This is how an OFPT_TABLE_MOD decodes if it doesn't specify any * table-miss configuration (because the protocol used doesn't have * such a concept), so there's nothing to do. */ } else { - atomic_store_relaxed(&oftable->miss_config, miss); + atomic_store_relaxed(&oftable->miss_config, tm->miss); } unsigned int new_eviction = oftable->eviction; - if (eviction == OFPUTIL_TABLE_EVICTION_ON) { + if (tm->eviction == OFPUTIL_TABLE_EVICTION_ON) { new_eviction |= EVICTION_OPENFLOW; - } else if (eviction == OFPUTIL_TABLE_EVICTION_OFF) { + } else if (tm->eviction == OFPUTIL_TABLE_EVICTION_OFF) { new_eviction &= ~EVICTION_OPENFLOW; } @@ -6670,6 +6670,16 @@ table_mod__(struct oftable *oftable, oftable->n_eviction_fields); ovs_mutex_unlock(&ofproto_mutex); } + + if (tm->vacancy != OFPUTIL_TABLE_VACANCY_DEFAULT) { + ovs_mutex_lock(&ofproto_mutex); + oftable->vacancy_enabled = (tm->vacancy == OFPUTIL_TABLE_VACANCY_ON + ? OFPTC14_VACANCY_EVENTS + : 0); + oftable->vacancy_down = tm->table_vacancy.vacancy_down; + oftable->vacancy_up = tm->table_vacancy.vacancy_up; + ovs_mutex_unlock(&ofproto_mutex); + } } static enum ofperr @@ -6693,7 +6703,7 @@ table_mod(struct ofproto *ofproto, const struct ofputil_table_mod *tm) struct oftable *oftable; OFPROTO_FOR_EACH_TABLE (oftable, ofproto) { if (!(oftable->flags & (OFTABLE_HIDDEN | OFTABLE_READONLY))) { - table_mod__(oftable, tm->miss, tm->eviction); + table_mod__(oftable, tm); } } } else { @@ -6701,7 +6711,7 @@ table_mod(struct ofproto *ofproto, const struct ofputil_table_mod *tm) if (oftable->flags & OFTABLE_READONLY) { return OFPERR_OFPTMFC_EPERM; } - table_mod__(oftable, tm->miss, tm->eviction); + table_mod__(oftable, tm); } return 0; diff --git a/tests/ofp-print.at b/tests/ofp-print.at index fce7671ff..d189efcbb 100644 --- a/tests/ofp-print.at +++ b/tests/ofp-print.at @@ -1132,7 +1132,7 @@ AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 05 11 00 10 00 00 00 02 02 00 00 00 00 00 00 00 \ " 3], [0], [dnl -OFPT_TABLE_MOD (OF1.4) (xid=0x2): table_id=2, eviction=off +OFPT_TABLE_MOD (OF1.4) (xid=0x2): table_id=2, eviction=off, vacancy=off ]) AT_CLEANUP diff --git a/tests/ofproto.at b/tests/ofproto.at index fff9335f8..eff2495b5 100644 --- a/tests/ofproto.at +++ b/tests/ofproto.at @@ -1936,6 +1936,13 @@ mv expout orig-expout sed -e '2s/eviction=off/eviction=on/' expout AT_CHECK([ovs-ofctl -O OpenFlow14 dump-table-desc br0 | sed '/^$/d /^OFPST_TABLE_DESC/d'], [0], [expout]) + +AT_CHECK([ovs-ofctl -O Openflow14 mod-table br0 0 vacancy:20,80]) +# Check that the configuration was updated. +mv expout orig-expout +sed -e '3s/vacancy=off/vacancy=on vacancy_down=20% vacancy_up=80% vacancy=0%/' expout +AT_CHECK([ovs-ofctl -O OpenFlow14 dump-table-desc br0 | sed '/^$/d +/^OFPST_TABLE_DESC/d'], [0], [expout]) OVS_VSWITCHD_STOP AT_CLEANUP diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in index a18605506..55cfb5757 100644 --- a/utilities/ovs-ofctl.8.in +++ b/utilities/ovs-ofctl.8.in @@ -96,8 +96,13 @@ algorithm described for the \fBFlow_Table\fR table in \fBovs-vswitchd.conf.db\fR(5). .IP \fBnoevict\fR Refuse to add the new flow. (Eviction might still be enabled through -the \fBoverflow_policy\fR oclumn in the \fBFlow_Table\fR table +the \fBoverflow_policy\fR column in the \fBFlow_Table\fR table documented in \fBovs-vswitchd.conf.db\fR(5).) +.IP \fBvacancy:\fIlow\fB,\fIhigh\fR +Enables sending vacancy events to controllers using \fBTABLE_STATUS\fR +messages, based on percentage thresholds \fIlow\fR and \fIhigh\fR. +.IP \fBnovacancy\fR +Disables vacancy events. .RE . .TP diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c index 869057525..bc37e77b3 100644 --- a/utilities/ovs-ofctl.c +++ b/utilities/ovs-ofctl.c @@ -347,7 +347,7 @@ usage(void) " mod-port SWITCH IFACE ACT modify port behavior\n" " mod-table SWITCH MOD modify flow table behavior\n" " OF1.1/1.2 MOD: controller, continue, drop\n" - " OF1.4+ MOD: evict, noevict\n" + " OF1.4+ MOD: evict, noevict, vacancy:low,high, novacancy\n" " get-frags SWITCH print fragment handling behavior\n" " set-frags SWITCH FRAG_MODE set fragment handling behavior\n" " FRAG_MODE: normal, drop, reassemble, nx-match\n" @@ -1934,6 +1934,70 @@ found: vconn_close(vconn); } +/* This function uses OFPMP14_TABLE_DESC request to get the current + * table configuration from switch. The function then modifies + * only that table-config property, which has been requested. */ +static void +fetch_table_desc(struct vconn *vconn, struct ofputil_table_mod *tm, + struct ofputil_table_desc *td) +{ + struct ofpbuf *request; + ovs_be32 send_xid; + bool done = false; + bool found = false; + + request = ofputil_encode_table_desc_request(vconn_get_version(vconn)); + send_xid = ((struct ofp_header *) request->data)->xid; + send_openflow_buffer(vconn, request); + while (!done) { + ovs_be32 recv_xid; + struct ofpbuf *reply; + + run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed"); + recv_xid = ((struct ofp_header *) reply->data)->xid; + if (send_xid == recv_xid) { + struct ofp_header *oh = reply->data; + enum ofptype type; + struct ofpbuf b; + uint16_t flags; + + ofpbuf_use_const(&b, oh, ntohs(oh->length)); + if (ofptype_pull(&type, &b) + || type != OFPTYPE_TABLE_DESC_REPLY) { + ovs_fatal(0, "received bad reply: %s", + ofp_to_string(reply->data, reply->size, + verbosity + 1)); + } + flags = ofpmp_flags(oh); + done = !(flags & OFPSF_REPLY_MORE); + if (found) { + /* We've already found the table desc consisting of current + * table configuration, but we need to drain the queue of + * any other replies for this request. */ + continue; + } + while (!ofputil_decode_table_desc(&b, td, oh->version)) { + if (td->table_id == tm->table_id) { + found = true; + break; + } + } + } else { + VLOG_DBG("received reply with xid %08"PRIx32" " + "!= expected %08"PRIx32, recv_xid, send_xid); + } + ofpbuf_delete(reply); + } + if (tm->eviction != OFPUTIL_TABLE_EVICTION_DEFAULT) { + tm->vacancy = td->vacancy; + tm->table_vacancy.vacancy_down = td->table_vacancy.vacancy_down; + tm->table_vacancy.vacancy_up = td->table_vacancy.vacancy_up; + } else if (tm->vacancy != OFPUTIL_TABLE_VACANCY_DEFAULT) { + tm->eviction = td->eviction; + tm->eviction_flags = td->eviction_flags; + } +} + static void ofctl_mod_table(struct ovs_cmdl_context *ctx) { @@ -1941,6 +2005,7 @@ ofctl_mod_table(struct ovs_cmdl_context *ctx) struct ofputil_table_mod tm; struct vconn *vconn; char *error; + int i; error = parse_ofp_table_mod(&tm, ctx->argv[2], ctx->argv[3], &usable_versions); @@ -1951,15 +2016,36 @@ ofctl_mod_table(struct ovs_cmdl_context *ctx) uint32_t allowed_versions = get_allowed_ofp_versions(); if (!(allowed_versions & usable_versions)) { struct ds versions = DS_EMPTY_INITIALIZER; - ofputil_format_version_bitmap_names(&versions, allowed_versions); + ofputil_format_version_bitmap_names(&versions, usable_versions); ovs_fatal(0, "table_mod '%s' requires one of the OpenFlow " - "versions %s but none is enabled (use -O)", + "versions %s", ctx->argv[3], ds_cstr(&versions)); } mask_allowed_ofp_versions(usable_versions); - enum ofputil_protocol protocol = open_vconn(ctx->argv[1], &vconn); - transact_noreply(vconn, ofputil_encode_table_mod(&tm, protocol)); + + /* For OpenFlow 1.4+, ovs-ofctl mod-table should not affect table-config + * properties that the user didn't ask to change, so it is necessary to + * restore the current configuration of table-config parameters using + * OFPMP14_TABLE_DESC request. */ + if ((allowed_versions & (1u << OFP14_VERSION)) || + (allowed_versions & (1u << OFP15_VERSION))) { + struct ofputil_table_desc td; + + if (tm.table_id == OFPTT_ALL) { + for (i = 0; i < OFPTT_MAX; i++) { + tm.table_id = i; + fetch_table_desc(vconn, &tm, &td); + transact_noreply(vconn, + ofputil_encode_table_mod(&tm, protocol)); + } + } else { + fetch_table_desc(vconn, &tm, &td); + transact_noreply(vconn, ofputil_encode_table_mod(&tm, protocol)); + } + } else { + transact_noreply(vconn, ofputil_encode_table_mod(&tm, protocol)); + } vconn_close(vconn); } -- 2.20.1