diff mbox

[ovs-dev,v6] Implement OFPT_TABLE_STATUS Message.

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

Commit Message

saloni.jain12@gmail.com Dec. 1, 2015, 7:21 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 v5 <-> v6
- Done changes in code so that TABLE_STATUS message will be sent
  only when it croses the mentioned threshold.
- Rebase 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                  |   81 +++++++++++++++++++++++++++++++++++++++
 lib/ofp-util.h                  |   12 ++++++
 lib/rconn.c                     |    1 +
 ofproto/connmgr.c               |   31 +++++++++++++++
 ofproto/connmgr.h               |    3 ++
 ofproto/ofproto.c               |   43 +++++++++++++++++++++
 tests/ofproto.at                |   27 +++++++++++--
 11 files changed, 236 insertions(+), 3 deletions(-)

Comments

Ben Pfaff Dec. 1, 2015, 7:14 p.m. UTC | #1
On Tue, Dec 01, 2015 at 12:51:11PM +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 v5 <-> v6
> - Done changes in code so that TABLE_STATUS message will be sent
>   only when it croses the mentioned threshold.
> - Rebase with latest master

I don't see anything that disables the event once it crosses a
threshold, so I think that this will still send a table status message
every time the flow table changes and the occupancy is past one
threshold or another.

The spec says:

    When the remaining space in the flow table decreases to less than
    vacancy_down, and if vacancy down events are enabled, a vacancy down
    event must be generated to the controller using the OFPT_TABLE_STATUS
    message type with reason OFPTR_VACANCY_DOWN (see 7.4.5). Further vacancy
    down events are disabled until a vacancy up event is generated.

    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. Further vacancy up events are
    disabled until a vacancy down event is generated.

That is, sending an event disables any further events of that type until
the opposite event is sent.
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 7ddf69b..6f92c42 100644
--- a/lib/learning-switch.c
+++ b/lib/learning-switch.c
@@ -420,6 +420,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 bce5283..81d086e 100644
--- a/lib/ofp-msgs.h
+++ b/lib/ofp-msgs.h
@@ -250,6 +250,9 @@  enum ofpraw {
     /* OFPT 1.4+ (32): struct ofp14_requestforward, uint8_t[8][]. */
     OFPRAW_OFPT14_REQUESTFORWARD,
 
+    /* OFPT 1.4+ (31): struct ofp14_table_status, uint8_t[8][]. */
+    OFPRAW_OFPT14_TABLE_STATUS,
+
     /* OFPT 1.4+ (33): struct ofp14_bundle_ctrl_msg, uint8_t[8][]. */
     OFPRAW_OFPT14_BUNDLE_CONTROL,
 
@@ -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 d4f1972..17a0d8c 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)
 {
@@ -3275,6 +3297,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 2141144..bad823a 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -9341,6 +9341,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_GENEVE_TABLE_REQUEST:
     case OFPTYPE_NXT_GENEVE_TABLE_REPLY:
         break;
@@ -9779,3 +9780,83 @@  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;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    ofpraw_pull_assert(&b);
+    ots = ofpbuf_pull(&b, sizeof *ots);
+
+    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;
+}
diff --git a/lib/ofp-util.h b/lib/ofp-util.h
index d106350..9d5c2e1 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 0a9966a..53240fd 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 ef2c06f..76dfaa9 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.c b/ofproto/ofproto.c
index 5688c6e..a1c988d 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -3645,6 +3645,24 @@  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;
+
+    query_table_desc__(&td, ofproto, table_id);
+    if (td.table_vacancy.vacancy <= td.table_vacancy.vacancy_down) {
+        connmgr_send_table_status(ofproto->connmgr, &td, OFPTR_VACANCY_UP);
+    } else if (td.table_vacancy.vacancy >= td.table_vacancy.vacancy_up) {
+        connmgr_send_table_status(ofproto->connmgr, &td, OFPTR_VACANCY_DOWN);
+    }
+}
+
 static void
 append_port_stat(struct ofport *port, struct ovs_list *replies)
 {
@@ -4680,6 +4698,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[fm->table_id].vacancy_enabled) {
+            send_table_status(ofproto, fm->table_id);
+        }
+
     }
 
     send_buffered_packet(req, fm->buffer_id, new_rule);
@@ -5075,6 +5100,23 @@  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.
+             * When all flows are deleted by ovs-ofctl del-flows br0, then
+             * in that case n_flows == 0 and OFPTR_VACANCY_UP reason should
+             * be sent only once for all deleted flows. */
+            if (ofproto->tables[rule->table_id].vacancy_enabled) {
+                if (i == 0 && ofproto->tables[rule->table_id].n_flows == 0) {
+                    struct ofputil_table_desc td;
+                    query_table_desc__(&td, ofproto, rule->table_id);
+                    connmgr_send_table_status(ofproto->connmgr, &td,
+                                              OFPTR_VACANCY_UP);
+                } else if (ofproto->tables[rule->table_id].n_flows != 0) {
+                    send_table_status(ofproto, rule->table_id);
+                }
+            }
+
             ofproto_rule_remove__(ofproto, rule);
             learned_cookies_dec(ofproto, rule_get_actions(rule),
                                 &dead_cookies);
@@ -7278,6 +7320,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_GENEVE_TABLE_REPLY:
     default:
         if (ofpmsg_is_stat_request(oh)) {
diff --git a/tests/ofproto.at b/tests/ofproto.at
index e96012a..69ce150 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -2849,7 +2849,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
@@ -2857,6 +2857,27 @@  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%
+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=20%"
+    fi
+
     AT_FAIL_IF([test X"$1" != X])
 
     ovs-appctl -t ovs-ofctl ofctl/barrier
@@ -2882,8 +2903,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