[ovs-dev,v4,1/3] Implement Openflow 1.4 Vacancy Events for OFPT_TABLE_MOD.
diff mbox

Message ID 1441784524-8007-1-git-send-email-saloni.jain@tcs.com
State Changes Requested
Headers show

Commit Message

saloni.jain12@gmail.com Sept. 9, 2015, 7:42 a.m. UTC
From: Saloni Jain <saloni.jain@tcs.com>

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 <bridge> <table> vacancy-<range>
The syntax of <range> as <low..high>.
<range> specify vacancy threshold values, vacancy down and vacancy up.

To disable vacancy events, following command should be given:
ovs-ofctl -O OpenFlow14 mod-table <bridge> <table> novacancy

Signed-off-by: Saloni Jain <saloni.jain@tcs.com>
Co-authored-by: Shashwat Srivastava <shashwat.srivastava@tcs.com>
Signed-off-by: Shashwat Srivastava <shashwat.srivastava@tcs.com>
Co-authored-by: Sandeep Kumar <sandeep.kumar16@tcs.com>
Signed-off-by: Sandeep Kumar <sandeep.kumar16@tcs.com>
---
Difference between v3->v4
- Rebased with latest master.
- Added Co-authored-by tags in commit message.

 lib/ofp-parse.c            |   65 +++++++++++++++++++++++++++++---
 lib/ofp-parse.h            |    3 ++
 lib/ofp-print.c            |   22 +++++++++++
 lib/ofp-util.c             |   87 ++++++++++++++++++++++++++++++++++++++----
 lib/ofp-util.h             |   39 +++++++++++++++++++
 ofproto/ofproto-provider.h |   11 ++++++
 ofproto/ofproto.c          |   24 ++++++++----
 tests/ofp-print.at         |    2 +-
 utilities/ovs-ofctl.c      |   90 +++++++++++++++++++++++++++++++++++++++++++-
 9 files changed, 319 insertions(+), 24 deletions(-)

Comments

Ben Pfaff Sept. 17, 2015, 10:30 p.m. UTC | #1
On Wed, Sep 09, 2015 at 01:12:04PM +0530, saloni.jain12@gmail.com wrote:
> From: Saloni Jain <saloni.jain@tcs.com>
> 
> 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 <bridge> <table> vacancy-<range>
> The syntax of <range> as <low..high>.
> <range> specify vacancy threshold values, vacancy down and vacancy up.
> 
> To disable vacancy events, following command should be given:
> ovs-ofctl -O OpenFlow14 mod-table <bridge> <table> novacancy
> 
> Signed-off-by: Saloni Jain <saloni.jain@tcs.com>
> Co-authored-by: Shashwat Srivastava <shashwat.srivastava@tcs.com>
> Signed-off-by: Shashwat Srivastava <shashwat.srivastava@tcs.com>
> Co-authored-by: Sandeep Kumar <sandeep.kumar16@tcs.com>
> Signed-off-by: Sandeep Kumar <sandeep.kumar16@tcs.com>

Thanks for the patch!

This needs to update the documentation for ovs-ofctl to mention the new
feature and describe the syntax.

The syntax seems kind of odd actually.  How about "vacancy(low,high)"?

I think that the format output by ofp_print_table_mod() should match
that accepted by ofp-parse instead of being completely different.

One of the patches in this series should update NEWS to mention the new
feature.

The 'vacancy' member in struct oftable appears to have exactly two
values: 0 and OFPTC14_VACANCY_EVENTS.  Why is it an "unsigned int"
instead of a Boolean (e.g. "bool vacancy_enabled")?

I think that parse_table_mod_vacancy_property() should report an error
for a "percentage" that exceeds 100.

In a table_mod the 'vacancy' member of struct
ofp14_table_mod_prop_vacancy is supposed to be always zero, so I don't
see why ofputil_encode_table_mod() copies this in from its argument
instead of leaving it zeroed.

In ovs-ofctl.c, I don't think the table-mod code handles the case where
OF1.4 or OF1.5 is enabled but the switch does not support it.

Thanks,

Ben.
Saloni Jain Sept. 19, 2015, 9:53 a.m. UTC | #2
Hi Ben,

Thanks for the review. I will do all the suggested changes in v5 of the patch series.

Please validate my understanding for the following 2 comments:

>In ovs-ofctl.c, I don't think the table-mod code handles the case where
>OF1.4 or OF1.5 is enabled but the switch does not support it.

This means that in ovs-ofctl.c, for table-mod, it can happen that OF1.4 and OF1.5 are supported, but the switch in table-features capabilities does not support eviction or vacancy table-config parameters.
So after checking the usable version as OF1.4 and OF1.5 for table-mod in ovs-ofctl.c, table feature request should be sent to the switch from ofctl_mod_table() in order to get the supported capabilities for the given table-id and if eviction/vacancy events are supported by switch, then only table-mod config property should be set.


>The syntax seems kind of odd actually.  How about "vacancy(low,high)"?

Parentheses - "()" and "{}" are used for command grouping in shell and will give error "syntax error:bash: syntax error near unexpected token `('", when used with any command.
So in order to avoid the error, we have to use escape characters or single/double quotes around "vacancy(low,high)" in ovs-ofctl mod-table command, such that, the command looks like:
                     ovs-ofctl -O Openflow14 mod-table br0 0 'vacancy(low,high)'

I have also tried for "vacancy[low,high]", that is using square brackets [], but I am facing problem in test cases. In file ofproto.at square brackets are ignored in AT_CHECK[] and so test case for mod-table for vacancy is failing.
Other possible syntax are -- vacancy:low-high or vacancy:low,high

Please suggest whether to go with parentheses "vacancy(low,high)" or is there any other recommended syntax which can be used? 

Thanks and Regards,
Saloni Jain
Tata Consultancy Services
Mailto: saloni.jain@tcs.com
Website: http://www.tcs.com
Saloni Jain Oct. 27, 2015, 5:12 a.m. UTC | #3
Hi Ben,

Please provide inputs on queries mentioned in the previous mail, so that I can submit revised patch (v5) of  OF1.4-vacancy feature.

Thanks and Regards,
Saloni Jain
 Tata Consultancy Services
 Mailto: saloni.jain@tcs.com
 Website: http://www.tcs.com
 ____________________________________________
 Experience certainty.	IT Services
 			Business Solutions
 			Consulting
 ____________________________________________
 

-----"dev" <dev-bounces@openvswitch.org> wrote: -----
To: Ben Pfaff <blp@nicira.com>, dev@openvswitch.org
From: Saloni Jain 
Sent by: "dev" 
Date: 09/19/2015 03:24PM
Cc: saloni.jain12@gmail.com, Shashwat Srivastava <shashwat.srivastava@tcs.com>, Deepankar Gupta <deepankar.gupta@tcs.com>, Partha Datta <partha.datta@tcs.com>, sandeep.kumar16@tcs.com
Subject: Re: [ovs-dev] [PATCH v4 1/3] Implement Openflow 1.4 Vacancy Events for OFPT_TABLE_MOD.

Hi Ben,

Thanks for the review. I will do all the suggested changes in v5 of the patch series.

Please validate my understanding for the following 2 comments:

>In ovs-ofctl.c, I don't think the table-mod code handles the case where
>OF1.4 or OF1.5 is enabled but the switch does not support it.

This means that in ovs-ofctl.c, for table-mod, it can happen that OF1.4 and OF1.5 are supported, but the switch in table-features capabilities does not support eviction or vacancy table-config parameters.
So after checking the usable version as OF1.4 and OF1.5 for table-mod in ovs-ofctl.c, table feature request should be sent to the switch from ofctl_mod_table() in order to get the supported capabilities for the given table-id and if eviction/vacancy events are supported by switch, then only table-mod config property should be set.


>The syntax seems kind of odd actually.  How about "vacancy(low,high)"?

Parentheses - "()" and "{}" are used for command grouping in shell and will give error "syntax error:bash: syntax error near unexpected token `('", when used with any command.
So in order to avoid the error, we have to use escape characters or single/double quotes around "vacancy(low,high)" in ovs-ofctl mod-table command, such that, the command looks like:
                     ovs-ofctl -O Openflow14 mod-table br0 0 'vacancy(low,high)'

I have also tried for "vacancy[low,high]", that is using square brackets [], but I am facing problem in test cases. In file ofproto.at square brackets are ignored in AT_CHECK[] and so test case for mod-table for vacancy is failing.
Other possible syntax are -- vacancy:low-high or vacancy:low,high

Please suggest whether to go with parentheses "vacancy(low,high)" or is there any other recommended syntax which can be used? 

Thanks and Regards,
Saloni Jain
Tata Consultancy Services
Mailto: saloni.jain@tcs.com
Website: http://www.tcs.com
Ben Pfaff Nov. 3, 2015, 8:24 p.m. UTC | #4
On Tue, Oct 27, 2015 at 10:42:41AM +0530, Saloni Jain wrote:
> >In ovs-ofctl.c, I don't think the table-mod code handles the case where
> >OF1.4 or OF1.5 is enabled but the switch does not support it.
> 
> This means that in ovs-ofctl.c, for table-mod, it can happen that OF1.4 and OF1.5 are supported, but the switch in table-features capabilities does not support eviction or vacancy table-config parameters.
> So after checking the usable version as OF1.4 and OF1.5 for table-mod in ovs-ofctl.c, table feature request should be sent to the switch from ofctl_mod_table() in order to get the supported capabilities for the given table-id and if eviction/vacancy events are supported by switch, then only table-mod config property should be set.

I mean that the code should adapt its behavior based on the protocol
version that is actually in use in the OpenFlow connection, rather than
on the protocol versions that are enabled.

> >The syntax seems kind of odd actually.  How about "vacancy(low,high)"?
> 
> Parentheses - "()" and "{}" are used for command grouping in shell and will give error "syntax error:bash: syntax error near unexpected token `('", when used with any command.
> So in order to avoid the error, we have to use escape characters or single/double quotes around "vacancy(low,high)" in ovs-ofctl mod-table command, such that, the command looks like:
>                      ovs-ofctl -O Openflow14 mod-table br0 0 'vacancy(low,high)'
> 
> I have also tried for "vacancy[low,high]", that is using square brackets [], but I am facing problem in test cases. In file ofproto.at square brackets are ignored in AT_CHECK[] and so test case for mod-table for vacancy is failing.
> Other possible syntax are -- vacancy:low-high or vacancy:low,high

vacancy:low,high seems like a reasonable choice to me, too.

Patch
diff mbox

diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index eaaa8ba..8f9e603 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -883,6 +883,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.
@@ -909,13 +952,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;
@@ -932,6 +975,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 b64a32e..a949698 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, struct eth_addr *mac) 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 5331f8c..6c181f8 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,9 +5002,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,
@@ -5038,8 +5086,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,11 +5122,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();
 }
@@ -5126,6 +5181,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 +5196,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 +5212,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 +5257,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 +5278,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:
@@ -5563,6 +5633,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 527a5ab..2290c4c 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 e6c0351..64f3a96 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 75e84e2..bd1af2a 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);
 }