diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index ade664b..ceb07be 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -869,6 +869,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.
@@ -895,13 +938,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;
@@ -918,6 +961,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 930e3d9..b283e07 100644
--- a/lib/ofp-parse.h
+++ b/lib/ofp-parse.h
@@ -99,5 +99,8 @@ char *str_to_u64(const char *str, uint64_t *valuep) OVS_WARN_UNUSED_RESULT;
ÿchar *str_to_be64(const char *str, ovs_be64 *valuep) OVS_WARN_UNUSED_RESULT;
ÿchar *str_to_mac(const char *str, uint8_t mac[ETH_ADDR_LEN]) OVS_WARN_UNUSED_RESULT;
ÿchar *str_to_ip(const char *str, ovs_be32 *ip) 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 6e32d4d..9fe05bd 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,16 @@ 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_down=%"PRIu8"%%",
+ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿpm.table_vacancy.vacancy_down);
+ ÿ ÿ ÿ ÿ ÿ ÿds_put_format(string, " vacancy_up=%"PRIu8"%%",
+ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ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 0a5232d..eaaf0ab 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 {
@@ -5013,9 +5016,54 @@ 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;
+ ÿ ÿ}
+ ÿ ÿ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,
@@ -5052,8 +5100,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) {
@@ -5086,11 +5136,16 @@ 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();
ÿ}
@@ -5140,6 +5195,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);
ÿ
@@ -5154,6 +5210,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;
@@ -5169,6 +5226,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;
@@ -5210,19 +5271,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);
@@ -5230,6 +5292,14 @@ 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;
+ ÿ ÿ ÿ ÿ ÿ ÿotv->vacancy = tm->table_vacancy.vacancy;
+ ÿ ÿ ÿ ÿ}
ÿÿ ÿ ÿ ÿ break;
ÿÿ ÿ }
ÿÿ ÿ default:
@@ -5577,6 +5647,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 2668e75..770b33a 100644
--- a/lib/ofp-util.h
+++ b/lib/ofp-util.h
@@ -620,6 +620,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. */
@@ -636,6 +663,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. */
@@ -643,6 +680,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 117cd1f..76b4289 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -256,6 +256,17 @@ struct oftable {
ÿ#define EVICTION_OPENFLOW (1 << 1) /* Set to 1 if OpenFlow enables eviction. */
ÿÿ ÿ unsigned int eviction;
ÿ
+ ÿ ÿ/* Either of the Vacancy Events are enabled when this value is set to
+ ÿ ÿ * OFPTC14_VACANCY_EVENTS. When this flag is unset, vacancy events are
+ ÿ ÿ * disabled */
+ ÿ ÿunsigned int vacancy;
+
+ ÿ ÿ/* 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 e9b1472..1663749 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -6610,20 +6610,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;
ÿÿ ÿ }
ÿ
@@ -6634,6 +6634,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 = (tm->vacancy == OFPUTIL_TABLE_VACANCY_ON
+ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ? oftable->vacancy | OFPTC14_VACANCY_EVENTS
+ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ: oftable->vacancy & ~OFPTC14_VACANCY_EVENTS);
+ ÿ ÿ ÿ ÿoftable->vacancy_down = tm->table_vacancy.vacancy_down;
+ ÿ ÿ ÿ ÿoftable->vacancy_up = tm->table_vacancy.vacancy_up;
+ ÿ ÿ ÿ ÿovs_mutex_unlock(&ofproto_mutex);
+ ÿ ÿ}
ÿ}
ÿ
ÿstatic enum ofperr
@@ -6657,7 +6667,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 {
@@ -6665,7 +6675,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 35a6262..a2b4359 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/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 6ec7151..327770e 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -1929,6 +1929,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 consiting 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)
ÿ{
@@ -1936,6 +2000,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);
@@ -1952,9 +2017,30 @@ ofctl_mod_table(struct ovs_cmdl_context *ctx)
ÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ 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 request, so it is necessary to restore
+ ÿ ÿ * the current configuration of table-config parameters using
+ ÿ ÿ * OFPMP14_TABLE_DESC request. */
+ ÿ ÿif ((usable_versions & (1u << OFP14_VERSION)) ||
+ ÿ ÿ ÿ ÿ(usable_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);
ÿ}
ÿ
