diff mbox

[ovs-dev,v8] Implement OFPT_TABLE_STATUS Message.

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

Commit Message

saloni.jain12@gmail.com Jan. 5, 2016, 11:15 a.m. UTC
From: Saloni Jain <saloni.jain@tcs.com>

On change in a table state, the controller needs to be informed with
the OFPT_TABLE_STATUS message. The message is sent with reason
OFPTR_VACANCY_DOWN or OFPTR_VACANCY_UP in case of change in remaining
space eventually crossing any one of the threshold.

Signed-off-by: Saloni Jain <saloni.jain@tcs.com>
Co-authored-by: Rishi Bamba <rishi.bamba@tcs.com>
Signed-off-by: Rishi Bamba <rishi.bamba@tcs.com>
---
Difference between v7 <-> v8
- Changes done in the calling of send_table_status() in add_flow_finish()
- Rebased with latest master

 include/openflow/openflow-1.4.h |    8 ++++
 lib/learning-switch.c           |    1 +
 lib/ofp-msgs.h                  |    6 +++
 lib/ofp-print.c                 |   26 +++++++++++
 lib/ofp-util.c                  |   91 +++++++++++++++++++++++++++++++++++++++
 lib/ofp-util.h                  |   12 ++++++
 lib/rconn.c                     |    1 +
 ofproto/connmgr.c               |   31 +++++++++++++
 ofproto/connmgr.h               |    3 ++
 ofproto/ofproto-provider.h      |    3 ++
 ofproto/ofproto.c               |   44 +++++++++++++++++++
 tests/ofproto.at                |   22 ++++++++--
 12 files changed, 245 insertions(+), 3 deletions(-)

Comments

Ben Pfaff Jan. 12, 2016, 7:01 p.m. UTC | #1
On Tue, Jan 05, 2016 at 04:45:12PM +0530, saloni.jain12@gmail.com wrote:
> From: Saloni Jain <saloni.jain@tcs.com>
> 
> On change in a table state, the controller needs to be informed with
> the OFPT_TABLE_STATUS message. The message is sent with reason
> OFPTR_VACANCY_DOWN or OFPTR_VACANCY_UP in case of change in remaining
> space eventually crossing any one of the threshold.
> 
> Signed-off-by: Saloni Jain <saloni.jain@tcs.com>
> Co-authored-by: Rishi Bamba <rishi.bamba@tcs.com>
> Signed-off-by: Rishi Bamba <rishi.bamba@tcs.com>
> ---
> Difference between v7 <-> v8
> - Changes done in the calling of send_table_status() in add_flow_finish()
> - Rebased with latest master

I think that this implementation confuses "occupancy" with "vacancy".
Vacancy is the amount of space free.  One of the tests that it adds
illustrates the confusion.  The test exercises vacancies from 100%
(completely empty) to 50% (half full), but the expected output includes
a message that says vacancy is 10% (which is never true during the
test):

    # OFPT_TABLE_STATUS, OFPTR_VACANCY_UP
    if test X"$1" = X"OFPTR_VACANCY_UP"; then shift;
        ovs-vsctl -- --id=@t1 create Flow_Table flow-limit=10 -- set bridge br0 flow_tables:1=@t1
        ovs-ofctl -O OpenFlow14 mod-table br0 1 vacancy:20,80
        ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=1,actions=2
        ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=2,actions=2
        ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=3,actions=2
        ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=4,actions=2
        ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=5,actions=2
        echo >>expout "OFPT_TABLE_STATUS (OF1.4): reason=VACANCY_UP
table_desc:-
  table 1:
   eviction=off eviction_flags=OTHER|IMPORTANCE|LIFETIME
   vacancy=on vacancy_down=20% vacancy_up=80% vacancy=10%"
    fi
Saloni Jain Jan. 13, 2016, 12:14 p.m. UTC | #2
Hi Ben,

Thanks for the review.
Regarding the test case, I want to clarify few points :

 # OFPT_TABLE_STATUS, OFPTR_VACANCY_UP
    if test X"$1" = X"OFPTR_VACANCY_UP"; then shift;
        ovs-vsctl -- --id=@t1 create Flow_Table flow-limit=10 -- set bridge br0 flow_tables:1=@t1
        ovs-ofctl -O OpenFlow14 mod-table br0 1 vacancy:20,80
        ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=1,actions=2
        ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=2,actions=2
        ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=3,actions=2
        ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=4,actions=2
        ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=5,actions=2
        echo >>expout "OFPT_TABLE_STATUS (OF1.4): reason=VACANCY_UP
table_desc:-
  table 1:
   eviction=off eviction_flags=OTHER|IMPORTANCE|LIFETIME
   vacancy=on vacancy_down=20% vacancy_up=80% vacancy=10%"
    fi

The above test case is for OFPT_TABLE_STATUS message. OFPT_TABLE_STATUS consists of table_desc also.
As per the specification, if the switch sends OFPT_TABLE_STATUS with reason VACANCY_UP then further messages with VACANCY_UP reasons are stopped until VACANCY_DOWN reason comes.
In the above test case, table limit is set to be 10 and 5 flows are added, also values of vacancy down and vacancy up are 20% and 80% respectively. 
So in this case  OFPT_TABLE_STATUS (OF1.4): reason=VACANCY_UP is sent when first flow is added as in this case current Vacancy is 10% which is less than Vacancy down threshold value 20%. Further TABLE_STATUS messages are stopped until VACANCY_DOWN reason comes up which will occur when the current vacancy is greater than or equal to vacancy up threshold value 80%.
Please verify the above understanding.

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: saloni.jain12@gmail.com
From: Ben Pfaff 
Sent by: "dev" 
Date: 01/13/2016 12:31AM
Cc: dev@openvswitch.org, deepankar.gupta@tcs.com, partha.datta@tcs.com, Rishi Bamba <rishi.bamba@tcs.com>
Subject: Re: [ovs-dev] [PATCH v8] Implement OFPT_TABLE_STATUS Message.

On Tue, Jan 05, 2016 at 04:45:12PM +0530, saloni.jain12@gmail.com wrote:
> From: Saloni Jain <saloni.jain@tcs.com>
> 
> On change in a table state, the controller needs to be informed with
> the OFPT_TABLE_STATUS message. The message is sent with reason
> OFPTR_VACANCY_DOWN or OFPTR_VACANCY_UP in case of change in remaining
> space eventually crossing any one of the threshold.
> 
> Signed-off-by: Saloni Jain <saloni.jain@tcs.com>
> Co-authored-by: Rishi Bamba <rishi.bamba@tcs.com>
> Signed-off-by: Rishi Bamba <rishi.bamba@tcs.com>
> ---
> Difference between v7 <-> v8
> - Changes done in the calling of send_table_status() in add_flow_finish()
> - Rebased with latest master

I think that this implementation confuses "occupancy" with "vacancy".
Vacancy is the amount of space free.  One of the tests that it adds
illustrates the confusion.  The test exercises vacancies from 100%
(completely empty) to 50% (half full), but the expected output includes
a message that says vacancy is 10% (which is never true during the
test):

    # OFPT_TABLE_STATUS, OFPTR_VACANCY_UP
    if test X"$1" = X"OFPTR_VACANCY_UP"; then shift;
        ovs-vsctl -- --id=@t1 create Flow_Table flow-limit=10 -- set bridge br0 flow_tables:1=@t1
        ovs-ofctl -O OpenFlow14 mod-table br0 1 vacancy:20,80
        ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=1,actions=2
        ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=2,actions=2
        ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=3,actions=2
        ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=4,actions=2
        ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=5,actions=2
        echo >>expout "OFPT_TABLE_STATUS (OF1.4): reason=VACANCY_UP
table_desc:-
  table 1:
   eviction=off eviction_flags=OTHER|IMPORTANCE|LIFETIME
   vacancy=on vacancy_down=20% vacancy_up=80% vacancy=10%"
    fi
Ben Pfaff Jan. 13, 2016, 5:12 p.m. UTC | #3
On Wed, Jan 13, 2016 at 05:44:16PM +0530, Saloni Jain wrote:
>  Hi Ben,
> 
> Thanks for the review.
> Regarding the test case, I want to clarify few points :
> 
>  # OFPT_TABLE_STATUS, OFPTR_VACANCY_UP
>     if test X"$1" = X"OFPTR_VACANCY_UP"; then shift;
>         ovs-vsctl -- --id=@t1 create Flow_Table flow-limit=10 -- set bridge br0 flow_tables:1=@t1
>         ovs-ofctl -O OpenFlow14 mod-table br0 1 vacancy:20,80
>         ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=1,actions=2
>         ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=2,actions=2
>         ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=3,actions=2
>         ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=4,actions=2
>         ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=5,actions=2
>         echo >>expout "OFPT_TABLE_STATUS (OF1.4): reason=VACANCY_UP
> table_desc:-
>   table 1:
>    eviction=off eviction_flags=OTHER|IMPORTANCE|LIFETIME
>    vacancy=on vacancy_down=20% vacancy_up=80% vacancy=10%"
>     fi
> 
> The above test case is for OFPT_TABLE_STATUS message. OFPT_TABLE_STATUS consists of table_desc also.
> As per the specification, if the switch sends OFPT_TABLE_STATUS with reason VACANCY_UP then further messages with VACANCY_UP reasons are stopped until VACANCY_DOWN reason comes.
> In the above test case, table limit is set to be 10 and 5 flows are added, also values of vacancy down and vacancy up are 20% and 80% respectively. 
> So in this case  OFPT_TABLE_STATUS (OF1.4): reason=VACANCY_UP is sent when first flow is added as in this case current Vacancy is 10% which is less than Vacancy down threshold value 20%. Further TABLE_STATUS messages are stopped until VACANCY_DOWN reason comes up which will occur when the current vacancy is greater than or equal to vacancy up threshold value 80%.

The misunderstanding here is the definition of "vacancy".  Vacancy is
the amount of free space, so when the first flow is added, the vacancy
is 90%, not 10%.
Saloni Jain Jan. 14, 2016, 12:26 p.m. UTC | #4
Hi Ben,

As per the Openflow Specification-1.4, Section: 7.4.3.1 Modify Flow Table Message, Page:73,74:

"When the remaining space in the flow table increases to more than vacancy_up, and if vacancy up events are enabled, a vacancy up event must be generated to the controller using the OFPT_TABLE_STATUS message type with reason OFPTR_VACANCY_UP.

When enabling events, if the current vacancy is less than vacancy_up, vacancy up events must be enabled, and vacancy down events must be disabled."

If current vacancy is same as remaining space(free space), the two statements of the specifications are contradictory.

Please help me in clarifying my doubt regarding these statements.

Thanks and Regards,
Saloni Jain
Tata Consultancy Services
Mailto: saloni.jain@tcs.com
Website: http://www.tcs.com
Ben Pfaff Jan. 21, 2016, 4:08 p.m. UTC | #5
On Thu, Jan 14, 2016 at 05:56:02PM +0530, Saloni Jain wrote:
> As per the Openflow Specification-1.4, Section: 7.4.3.1 Modify Flow Table Message, Page:73,74:
> 
> "When the remaining space in the flow table increases to more than vacancy_up, and if vacancy up events are enabled, a vacancy up event must be generated to the controller using the OFPT_TABLE_STATUS message type with reason OFPTR_VACANCY_UP.
> 
> When enabling events, if the current vacancy is less than vacancy_up, vacancy up events must be enabled, and vacancy down events must be disabled."
> 
> If current vacancy is same as remaining space(free space), the two statements of the specifications are contradictory.

Please specifically describe the contradiction.  I don't see it.
diff mbox

Patch

diff --git a/include/openflow/openflow-1.4.h b/include/openflow/openflow-1.4.h
index e600cff..e102248 100644
--- a/include/openflow/openflow-1.4.h
+++ b/include/openflow/openflow-1.4.h
@@ -173,6 +173,14 @@  struct ofp14_table_desc {
 };
 OFP_ASSERT(sizeof(struct ofp14_table_desc) == 8);
 
+/* A table config has changed in the datapath */
+struct ofp14_table_status {
+    uint8_t reason;    /* One of OFPTR_*. */
+    uint8_t pad[7];    /* Pad to 64 bits */
+    /* Followed by struct ofp14_table_desc */
+};
+OFP_ASSERT(sizeof(struct ofp14_table_status) == 8);
+
 /* ## ---------------- ## */
 /* ## ofp14_port_stats ## */
 /* ## ---------------- ## */
diff --git a/lib/learning-switch.c b/lib/learning-switch.c
index 172a8dc..ef9a9e8 100644
--- a/lib/learning-switch.c
+++ b/lib/learning-switch.c
@@ -412,6 +412,7 @@  lswitch_process_packet(struct lswitch *sw, const struct ofpbuf *msg)
     case OFPTYPE_ROLE_REPLY:
     case OFPTYPE_ROLE_STATUS:
     case OFPTYPE_REQUESTFORWARD:
+    case OFPTYPE_TABLE_STATUS:
     case OFPTYPE_SET_FLOW_FORMAT:
     case OFPTYPE_FLOW_MOD_TABLE_ID:
     case OFPTYPE_SET_PACKET_IN_FORMAT:
diff --git a/lib/ofp-msgs.h b/lib/ofp-msgs.h
index 6770fa4..9e6eb3e 100644
--- a/lib/ofp-msgs.h
+++ b/lib/ofp-msgs.h
@@ -247,6 +247,9 @@  enum ofpraw {
     /* OFPT 1.4+ (30): struct ofp14_role_status, uint8_t[8][]. */
     OFPRAW_OFPT14_ROLE_STATUS,
 
+    /* OFPT 1.4+ (31): struct ofp14_table_status, uint8_t[8][]. */
+    OFPRAW_OFPT14_TABLE_STATUS,
+
     /* OFPT 1.4+ (32): struct ofp14_requestforward, uint8_t[8][]. */
     OFPRAW_OFPT14_REQUESTFORWARD,
 
@@ -565,6 +568,9 @@  enum ofptype {
     /* Request forwarding by the switch. */
     OFPTYPE_REQUESTFORWARD,       /* OFPRAW_OFPT14_REQUESTFORWARD. */
 
+    /* Asynchronous messages. */
+    OFPTYPE_TABLE_STATUS,          /* OFPRAW_OFPT14_TABLE_STATUS. */
+
     OFPTYPE_BUNDLE_CONTROL,       /* OFPRAW_OFPT14_BUNDLE_CONTROL. */
 
     OFPTYPE_BUNDLE_ADD_MESSAGE,   /* OFPRAW_OFPT14_BUNDLE_ADD_MESSAGE. */
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 02bcd7a..dac5b9f 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -1067,6 +1067,28 @@  ofp_print_table_desc(struct ds *string, const struct ofputil_table_desc *td)
 }
 
 static void
+ofp_print_table_status_message(struct ds *string, const struct ofp_header *oh)
+{
+    struct ofputil_table_status ts;
+    enum ofperr error;
+
+    error = ofputil_decode_table_status(oh, &ts);
+    if (error) {
+        ofp_print_error(string, error);
+        return;
+    }
+
+    if (ts.reason == OFPTR_VACANCY_DOWN) {
+        ds_put_format(string, " reason=VACANCY_DOWN");
+    } else if (ts.reason == OFPTR_VACANCY_UP) {
+        ds_put_format(string, " reason=VACANCY_UP");
+    }
+
+    ds_put_format(string, "\ntable_desc:-");
+    ofp_print_table_desc(string, &ts.desc);
+}
+
+static void
 ofp_print_queue_get_config_request(struct ds *string,
                                    const struct ofp_header *oh)
 {
@@ -3272,6 +3294,10 @@  ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
         ofp_print_requestforward(string, oh);
         break;
 
+    case OFPTYPE_TABLE_STATUS:
+        ofp_print_table_status_message(string, oh);
+        break;
+
     case OFPTYPE_METER_STATS_REQUEST:
     case OFPTYPE_METER_CONFIG_STATS_REQUEST:
         ofp_print_stats(string, oh);
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index fd23a3a..3ec8226 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -9344,6 +9344,7 @@  ofputil_is_bundlable(enum ofptype type)
     case OFPTYPE_TABLE_DESC_REPLY:
     case OFPTYPE_ROLE_STATUS:
     case OFPTYPE_REQUESTFORWARD:
+    case OFPTYPE_TABLE_STATUS:
     case OFPTYPE_NXT_TLV_TABLE_REQUEST:
     case OFPTYPE_NXT_TLV_TABLE_REPLY:
         break;
@@ -9782,3 +9783,93 @@  ofputil_encode_get_async_config(const struct ofp_header *oh,
 
     return buf;
 }
+
+static void
+ofputil_put_ofp14_table_desc(const struct ofputil_table_desc *td,
+                             struct ofpbuf *b, enum ofp_version version)
+{
+    struct ofp14_table_desc *otd;
+    struct ofp14_table_mod_prop_eviction *ote;
+    struct ofp14_table_mod_prop_vacancy *otv;
+
+    ofpbuf_prealloc_tailroom(b, sizeof *otd + sizeof *ote + sizeof *otv);
+
+    otd = ofpbuf_put_zeros(b, sizeof *otd);
+    otd->length = htons(sizeof *otd + sizeof *ote + sizeof *otv);
+    otd->table_id = td->table_id;
+    otd->config = ofputil_encode_table_config(OFPUTIL_TABLE_MISS_DEFAULT,
+                                              td->eviction, td->vacancy,
+                                              version);
+
+    ote = ofpbuf_put_zeros(b, sizeof *ote);
+    ote->type = htons(OFPTMPT14_EVICTION);
+    ote->length = htons(sizeof *ote);
+    ote->flags = htonl(td->eviction_flags);
+
+    otv = ofpbuf_put_zeros(b, sizeof *otv);
+    otv->type = htons(OFPTMPT14_VACANCY);
+    otv->length = htons(sizeof *otv);
+    otv->vacancy_down = td->table_vacancy.vacancy_down;
+    otv->vacancy_up = td->table_vacancy.vacancy_up;
+    otv->vacancy = td->table_vacancy.vacancy;
+}
+
+/* Converts the abstract form of a "table status" message in '*ts' into an
+ * OpenFlow message suitable for 'protocol', and returns that encoded form in
+ * a buffer owned by the caller. */
+struct ofpbuf *
+ofputil_encode_table_status(const struct ofputil_table_status *ts,
+                            enum ofputil_protocol protocol)
+{
+    enum ofp_version version;
+    struct ofpbuf *b;
+
+    version = ofputil_protocol_to_ofp_version(protocol);
+    if (version >= OFP14_VERSION) {
+        enum ofpraw raw;
+        struct ofp14_table_status *ots;
+
+        raw = OFPRAW_OFPT14_TABLE_STATUS;
+        b = ofpraw_alloc_xid(raw, version, htonl(0), 0);
+        ots = ofpbuf_put_zeros(b, sizeof *ots);
+        ots->reason = ts->reason;
+        ofputil_put_ofp14_table_desc(&ts->desc, b, version);
+        ofpmsg_update_length(b);
+        return b;
+    } else {
+        return NULL;
+    }
+}
+
+/* Decodes the OpenFlow "table status" message in '*ots' into an abstract form
+ * in '*ts'.  Returns 0 if successful, otherwise an OFPERR_* value. */
+enum ofperr
+ofputil_decode_table_status(const struct ofp_header *oh,
+                            struct ofputil_table_status *ts)
+{
+    const struct ofp14_table_status *ots;
+    struct ofpbuf b;
+    enum ofperr error;
+    enum ofpraw raw;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    raw = ofpraw_pull_assert(&b);
+    ots = ofpbuf_pull(&b, sizeof *ots);
+
+    if (raw == OFPRAW_OFPT14_TABLE_STATUS)
+    {
+        if (ots->reason != OFPTR_VACANCY_DOWN
+            && ots->reason != OFPTR_VACANCY_UP) {
+            return OFPERR_OFPBPC_BAD_VALUE;
+        }
+        ts->reason = ots->reason;
+
+        error = ofputil_decode_table_desc(&b, &ts->desc, oh->version);
+        return error;
+
+    } else {
+        return OFPERR_OFPBRC_BAD_VERSION;
+    }
+
+    return 0;
+}
diff --git a/lib/ofp-util.h b/lib/ofp-util.h
index c0541d4..7b3b784 100644
--- a/lib/ofp-util.h
+++ b/lib/ofp-util.h
@@ -1323,4 +1323,16 @@  enum ofperr ofputil_decode_requestforward(const struct ofp_header *,
                                           struct ofputil_requestforward *);
 void ofputil_destroy_requestforward(struct ofputil_requestforward *);
 
+/* Abstract ofp14_table_status. */
+struct ofputil_table_status {
+    enum ofp14_table_reason reason;     /* One of OFPTR_*. */
+    struct ofputil_table_desc desc;   /* New table config. */
+};
+
+enum ofperr ofputil_decode_table_status(const struct ofp_header *oh,
+                                        struct ofputil_table_status *ts);
+
+struct ofpbuf *
+ofputil_encode_table_status(const struct ofputil_table_status *ts,
+                            enum ofputil_protocol protocol);
 #endif /* ofp-util.h */
diff --git a/lib/rconn.c b/lib/rconn.c
index b601eed..6a218f6 100644
--- a/lib/rconn.c
+++ b/lib/rconn.c
@@ -1404,6 +1404,7 @@  is_admitted_msg(const struct ofpbuf *b)
     case OFPTYPE_ROLE_REPLY:
     case OFPTYPE_ROLE_STATUS:
     case OFPTYPE_REQUESTFORWARD:
+    case OFPTYPE_TABLE_STATUS:
     case OFPTYPE_SET_FLOW_FORMAT:
     case OFPTYPE_FLOW_MOD_TABLE_ID:
     case OFPTYPE_SET_PACKET_IN_FORMAT:
diff --git a/ofproto/connmgr.c b/ofproto/connmgr.c
index 9a4da55..f6a762a 100644
--- a/ofproto/connmgr.c
+++ b/ofproto/connmgr.c
@@ -1754,6 +1754,37 @@  connmgr_send_flow_removed(struct connmgr *mgr,
     }
 }
 
+/* Sends an OFPT_TABLE_STATUS message with 'reason' to appropriate controllers
+ * managed by 'mgr'. When the table state changes, the controller needs to be
+ * informed with the OFPT_TABLE_STATUS message. The reason values
+ * OFPTR_VACANCY_DOWN and OFPTR_VACANCY_UP identify a vacancy message. The
+ * vacancy events are generated when the remaining space in the flow table
+ * changes and crosses one of the vacancy thereshold specified by
+ * OFPT_TABLE_MOD. */
+void
+connmgr_send_table_status(struct connmgr *mgr,
+                          const struct ofputil_table_desc *td,
+                          uint8_t reason)
+{
+    struct ofputil_table_status ts;
+    struct ofconn *ofconn;
+
+    ts.reason = reason;
+    ts.desc = *td;
+
+    LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
+        if (ofconn_receives_async_msg(ofconn, OAM_TABLE_STATUS, reason)) {
+            struct ofpbuf *msg;
+
+            msg = ofputil_encode_table_status(&ts,
+                                              ofconn_get_protocol(ofconn));
+            if (msg) {
+                ofconn_send(ofconn, msg, NULL);
+            }
+        }
+    }
+}
+
 /* Normally a send-to-controller action uses reason OFPR_ACTION.  However, in
  * OpenFlow 1.3 and later, packet_ins generated by a send-to-controller action
  * in a "table-miss" flow (one with priority 0 and completely wildcarded) are
diff --git a/ofproto/connmgr.h b/ofproto/connmgr.h
index 8048424..8b68f06 100644
--- a/ofproto/connmgr.h
+++ b/ofproto/connmgr.h
@@ -234,4 +234,7 @@  void ofmonitor_compose_refresh_updates(struct rule_collection *rules,
                                        struct ovs_list *msgs)
     OVS_REQUIRES(ofproto_mutex);
 
+void connmgr_send_table_status(struct connmgr *,
+                               const struct ofputil_table_desc *td,
+                               uint8_t reason);
 #endif /* connmgr.h */
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index 2a6bd91..ca45236 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -265,6 +265,9 @@  struct oftable {
     uint8_t vacancy_down; /* Vacancy threshold when space decreases (%). */
     uint8_t vacancy_up;   /* Vacancy threshold when space increases (%). */
 
+    /* Vacancy Reason sent through TABLE_STATUS */
+    enum ofp14_table_reason vacancy_reason;     /* One of OFPTR_*. */
+
     atomic_ulong n_matched;
     atomic_ulong n_missed;
 };
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 4fa045f..5e292fa 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -3639,6 +3639,34 @@  handle_table_desc_request(struct ofconn *ofconn,
     return 0;
 }
 
+/* This function determines and sends the vacancy event, based on the value
+ * of current vacancy and threshold vacancy. If the current vacancy is less
+ * than or equal to vacancy_down, vacancy up events must be enabled, and when
+ * the current vacancy is greater or equal to vacancy_up, vacancy down events
+ * must be enabled. */
+static void
+send_table_status(struct ofproto *ofproto, uint8_t table_id)
+{
+    struct ofputil_table_desc td;
+    enum ofp14_table_reason reason;
+
+    reason = ofproto->tables[table_id].vacancy_reason;
+    query_table_desc__(&td, ofproto, table_id);
+
+    if (td.table_vacancy.vacancy <= td.table_vacancy.vacancy_down
+        && reason != OFPTR_VACANCY_UP) {
+
+        ofproto->tables[table_id].vacancy_reason = OFPTR_VACANCY_UP;
+        connmgr_send_table_status(ofproto->connmgr, &td, OFPTR_VACANCY_UP);
+
+    } else if (td.table_vacancy.vacancy >= td.table_vacancy.vacancy_up
+               && reason != OFPTR_VACANCY_DOWN) {
+
+        ofproto->tables[table_id].vacancy_reason = OFPTR_VACANCY_DOWN;
+        connmgr_send_table_status(ofproto->connmgr, &td, OFPTR_VACANCY_DOWN);
+    }
+}
+
 static void
 append_port_stat(struct ofport *port, struct ovs_list *replies)
 {
@@ -4674,6 +4702,13 @@  add_flow_finish(struct ofproto *ofproto, struct ofproto_flow_mod *ofm,
         ofmonitor_report(ofproto->connmgr, new_rule, NXFME_ADDED, 0,
                          req ? req->ofconn : NULL,
                          req ? req->request->xid : 0, NULL);
+
+        /* Send Vacancy Events  for OF1.4+ when there is change in flow table.
+         * Provided OFPTC14_VACANCY_EVENTS is set for the table. */
+        if (ofproto->tables[new_rule->table_id].vacancy_enabled) {
+            send_table_status(ofproto, new_rule->table_id);
+        }
+
     }
 
     send_buffered_packet(req, fm->buffer_id, new_rule);
@@ -5069,6 +5104,14 @@  delete_flows_finish__(struct ofproto *ofproto,
             ofmonitor_report(ofproto->connmgr, rule, NXFME_DELETED, reason,
                              req ? req->ofconn : NULL,
                              req ? req->request->xid : 0, NULL);
+
+            /* Send Vacancy Events  for OF1.4+ when there is change in flow
+             * table. Provided OFPTC14_VACANCY_EVENTS is set for the table.
+             */
+            if (ofproto->tables[rule->table_id].vacancy_enabled) {
+                send_table_status(ofproto, rule->table_id);
+            }
+
             ofproto_rule_remove__(ofproto, rule);
             learned_cookies_dec(ofproto, rule_get_actions(rule),
                                 &dead_cookies);
@@ -7273,6 +7316,7 @@  handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     case OFPTYPE_TABLE_DESC_REPLY:
     case OFPTYPE_ROLE_STATUS:
     case OFPTYPE_REQUESTFORWARD:
+    case OFPTYPE_TABLE_STATUS:
     case OFPTYPE_NXT_TLV_TABLE_REPLY:
     default:
         if (ofpmsg_is_stat_request(oh)) {
diff --git a/tests/ofproto.at b/tests/ofproto.at
index 52e1ab4..1589628 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -2851,7 +2851,7 @@  OFPT_PORT_STATUS (OF1.4): MOD: ${INDEX}(test): addr:aa:55:aa:55:00:0x
         echo >>expout "OFPT_FLOW_REMOVED (OF1.4):  reason=delete table_id=0"
     fi
 
-        # OFPT_FLOW_REMOVED, OFPRR_GROUP_DELETE
+    # OFPT_FLOW_REMOVED, OFPRR_GROUP_DELETE
     ovs-ofctl -O OpenFlow14 add-group br0 group_id=1234,type=all,bucket=output:10
     ovs-ofctl -O OpenFlow14 add-flow br0 send_flow_rem,actions=group:1234
     ovs-ofctl -O OpenFlow14 --strict del-groups br0 group_id=1234
@@ -2859,6 +2859,22 @@  OFPT_PORT_STATUS (OF1.4): MOD: ${INDEX}(test): addr:aa:55:aa:55:00:0x
         echo >>expout "OFPT_FLOW_REMOVED (OF1.4):  reason=group_delete table_id=0"
     fi
 
+    # OFPT_TABLE_STATUS, OFPTR_VACANCY_UP
+    if test X"$1" = X"OFPTR_VACANCY_UP"; then shift;
+        ovs-vsctl -- --id=@t1 create Flow_Table flow-limit=10 -- set bridge br0 flow_tables:1=@t1
+        ovs-ofctl -O OpenFlow14 mod-table br0 1 vacancy:20,80
+        ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=1,actions=2
+        ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=2,actions=2
+        ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=3,actions=2
+        ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=4,actions=2
+        ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=5,actions=2
+        echo >>expout "OFPT_TABLE_STATUS (OF1.4): reason=VACANCY_UP
+table_desc:-
+  table 1:
+   eviction=off eviction_flags=OTHER|IMPORTANCE|LIFETIME
+   vacancy=on vacancy_down=20% vacancy_up=80% vacancy=10%"
+    fi
+
     AT_FAIL_IF([test X"$1" != X])
 
     ovs-appctl -t ovs-ofctl ofctl/barrier
@@ -2884,8 +2900,8 @@  ovs-appctl -t ovs-ofctl ofctl/send 051800180000000200000003000000000000000000000
 check_async 3 OFPPR_ADD OFPPR_MODIFY OFPPR_DELETE
 
 # Use OF 1.4 OFPT_SET_ASYNC to enable a patchwork of asynchronous messages.
-ovs-appctl -t ovs-ofctl ofctl/send 051c0038000000020000000800000005000100080000000200020008000000020003000800000005000400080000001c0005000800000005
-check_async 4 OFPR_INVALID_TTL OFPPR_DELETE OFPRR_DELETE OFPRR_GROUP_DELETE
+ovs-appctl -t ovs-ofctl ofctl/send 051c0040000000020000000800000005000100080000000200020008000000020003000800000005000400080000001c00050008000000050008000800000010
+check_async 4 OFPR_INVALID_TTL OFPPR_DELETE OFPRR_DELETE OFPRR_GROUP_DELETE OFPTR_VACANCY_UP
 
 # Set controller ID 123.
 ovs-appctl -t ovs-ofctl ofctl/send 05040018000000030000232000000014000000000000007b