Implement OFPT_TABLE_STATUS Message.
[cascardo/ovs.git] / ofproto / ofproto.c
index 5fa3493..1335da6 100644 (file)
@@ -3594,24 +3594,35 @@ handle_table_features_request(struct ofconn *ofconn,
     return 0;
 }
 
+/* Returns the vacancy of 'oftable', a number that ranges from 0 (if the table
+ * is full) to 100 (if the table is empty).
+ *
+ * A table without a limit on flows is considered to be empty. */
+static uint8_t
+oftable_vacancy(const struct oftable *t)
+{
+    return (!t->max_flows ? 100
+            : t->n_flows >= t->max_flows ? 0
+            : (t->max_flows - t->n_flows) * 100.0 / t->max_flows);
+}
+
 static void
 query_table_desc__(struct ofputil_table_desc *td,
                    struct ofproto *ofproto, uint8_t table_id)
 {
-    unsigned int count = ofproto->tables[table_id].n_flows;
-    unsigned int max_flows = ofproto->tables[table_id].max_flows;
+    const struct oftable *t = &ofproto->tables[table_id];
 
     td->table_id = table_id;
-    td->eviction = (ofproto->tables[table_id].eviction & EVICTION_OPENFLOW
+    td->eviction = (t->eviction & EVICTION_OPENFLOW
                     ? OFPUTIL_TABLE_EVICTION_ON
                     : OFPUTIL_TABLE_EVICTION_OFF);
     td->eviction_flags = OFPROTO_EVICTION_FLAGS;
-    td->vacancy = (ofproto->tables[table_id].vacancy_enabled
+    td->vacancy = (t->vacancy_event
                    ? OFPUTIL_TABLE_VACANCY_ON
                    : OFPUTIL_TABLE_VACANCY_OFF);
-    td->table_vacancy.vacancy_down = ofproto->tables[table_id].vacancy_down;
-    td->table_vacancy.vacancy_up = ofproto->tables[table_id].vacancy_up;
-    td->table_vacancy.vacancy = max_flows ? (count * 100) / max_flows : 0;
+    td->table_vacancy.vacancy_down = t->vacancy_down;
+    td->table_vacancy.vacancy_up = t->vacancy_up;
+    td->table_vacancy.vacancy = oftable_vacancy(t);
 }
 
 /* This function queries the database for dumping table-desc. */
@@ -3651,6 +3662,40 @@ 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 oftable *t = &ofproto->tables[table_id];
+    if (!t->vacancy_event) {
+        return;
+    }
+
+    uint8_t vacancy = oftable_vacancy(t);
+    enum ofp14_table_reason event;
+    if (vacancy < t->vacancy_down) {
+        event = OFPTR_VACANCY_DOWN;
+    } else if (vacancy > t->vacancy_up) {
+        event = OFPTR_VACANCY_UP;
+    } else {
+        return;
+    }
+
+    if (event == t->vacancy_event) {
+        struct ofputil_table_desc td;
+        query_table_desc__(&td, ofproto, table_id);
+        connmgr_send_table_status(ofproto->connmgr, &td, event);
+
+        t->vacancy_event = (event == OFPTR_VACANCY_DOWN
+                            ? OFPTR_VACANCY_UP
+                            : OFPTR_VACANCY_DOWN);
+    }
+}
+
 static void
 append_port_stat(struct ofport *port, struct ovs_list *replies)
 {
@@ -4686,6 +4731,9 @@ 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+. */
+        send_table_status(ofproto, new_rule->table_id);
     }
 
     send_buffered_packet(req, fm->buffer_id, new_rule);
@@ -5081,6 +5129,10 @@ 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 Event for OF1.4+. */
+            send_table_status(ofproto, rule->table_id);
+
             ofproto_rule_remove__(ofproto, rule);
             learned_cookies_dec(ofproto, rule_get_actions(rule),
                                 &dead_cookies);
@@ -6740,11 +6792,16 @@ table_mod__(struct oftable *oftable,
 
     if (tm->vacancy != OFPUTIL_TABLE_VACANCY_DEFAULT) {
         ovs_mutex_lock(&ofproto_mutex);
-        oftable->vacancy_enabled = (tm->vacancy == OFPUTIL_TABLE_VACANCY_ON
-                                    ? OFPTC14_VACANCY_EVENTS
-                                    : 0);
         oftable->vacancy_down = tm->table_vacancy.vacancy_down;
         oftable->vacancy_up = tm->table_vacancy.vacancy_up;
+        if (tm->vacancy == OFPUTIL_TABLE_VACANCY_OFF) {
+            oftable->vacancy_event = 0;
+        } else if (!oftable->vacancy_event) {
+            uint8_t vacancy = oftable_vacancy(oftable);
+            oftable->vacancy_event = (vacancy < oftable->vacancy_up
+                                      ? OFPTR_VACANCY_UP
+                                      : OFPTR_VACANCY_DOWN);
+        }
         ovs_mutex_unlock(&ofproto_mutex);
     }
 }
@@ -7328,6 +7385,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)) {