Implement OpenFlow 1.4+ OFPTC_EVICTION.
authorBen Pfaff <blp@nicira.com>
Fri, 3 Jul 2015 03:33:08 +0000 (20:33 -0700)
committerBen Pfaff <blp@nicira.com>
Fri, 3 Jul 2015 15:46:20 +0000 (08:46 -0700)
OpenFlow 1.4 introduces the ability to turn on flow table eviction with an
OFPT_TABLE_MOD message specifying OFPTC_EVICTION.  It also adds related
machinery to other messages that mention OFPTC_* fields.  This commit adds
support for the new feature, implementing it as a second, parallel way to
enable flow table eviction.  It takes more work than it seems like it
should because there is so much weirdness with the treatment of OFPTC_*
flags over the evolution of OpenFlow; please refer to the explanation in
DESIGN.md for more information.

This commit also adds related support to ovs-ofctl, plus tests.

Signed-off-by: Ben Pfaff <blp@nicira.com>
Co-authored-by: Saloni Jain <saloni.jain@tcs.com>
Signed-off-by: Saloni Jain <saloni.jain@tcs.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
17 files changed:
DESIGN.md
NEWS
include/openflow/openflow-1.3.h
lib/ofp-parse.c
lib/ofp-parse.h
lib/ofp-print.c
lib/ofp-util.c
lib/ofp-util.h
ofproto/ofproto-provider.h
ofproto/ofproto.c
ofproto/ofproto.h
tests/ofp-print.at
tests/ofproto.at
utilities/ovs-ofctl.8.in
utilities/ovs-ofctl.c
vswitchd/bridge.c
vswitchd/vswitch.xml

index e533b7c..38413d7 100644 (file)
--- a/DESIGN.md
+++ b/DESIGN.md
@@ -277,13 +277,19 @@ The table for 1.3 is the same as the one shown above for 1.2.
 
 
 OpenFlow 1.4
 
 
 OpenFlow 1.4
-------------
+-----------
+
+OpenFlow 1.4 makes these changes:
+
+  - Adds the "importance" field to flow_mods, but it does not
+    explicitly specify which kinds of flow_mods set the importance.
+    For consistency, Open vSwitch uses the same rule for importance
+    as for idle_timeout and hard_timeout, that is, only an "ADD"
+    flow_mod sets the importance.  (This issue has been filed with
+    the ONF as EXT-496.)
 
 
-OpenFlow 1.4 adds the "importance" field to flow_mods, but it does not
-explicitly specify which kinds of flow_mods set the importance.  For
-consistency, Open vSwitch uses the same rule for importance as for
-idle_timeout and hard_timeout, that is, only an "ADD" flow_mod sets
-the importance.  (This issue has been filed with the ONF as EXT-496.)
+  - Eviction Mechanism to automatically delete entries of lower
+    importance to make space for newer entries.
 
 
 OpenFlow 1.4 Bundles
 
 
 OpenFlow 1.4 Bundles
@@ -606,6 +612,73 @@ Tables 128 and above are reserved for use by the switch itself.
 Controllers should use only tables 0 through 127.
 
 
 Controllers should use only tables 0 through 127.
 
 
+OFPTC_* Table Configuration
+===========================
+
+This section covers the history of the OFPTC_* table configuration
+bits across OpenFlow versions.
+
+OpenFlow 1.0 flow tables had fixed configurations.
+
+OpenFlow 1.1 enabled controllers to configure behavior upon flow table
+miss and added the OFPTC_MISS_* constants for that purpose.  OFPTC_*
+did not control anything else but it was nevertheless conceptualized
+as a set of bit-fields instead of an enum.  OF1.1 added the
+OFPT_TABLE_MOD message to set OFPTC_MISS_* for a flow table and added
+the 'config' field to the OFPST_TABLE reply to report the current
+setting.
+
+OpenFlow 1.2 did not change anything in this regard.
+
+OpenFlow 1.3 switched to another means to changing flow table miss
+behavior and deprecated OFPTC_MISS_* without adding any more OFPTC_*
+constants.  This meant that OFPT_TABLE_MOD now had no purpose at all,
+but OF1.3 kept it around "for backward compatibility with older and
+newer versions of the specification."  At the same time, OF1.3
+introduced a new message OFPMP_TABLE_FEATURES that included a field
+'config' documented as reporting the OFPTC_* values set with
+OFPT_TABLE_MOD; of course this served no real purpose because no
+OFPTC_* values are defined.  OF1.3 did remove the OFPTC_* field from
+OFPMP_TABLE (previously named OFPST_TABLE).
+
+OpenFlow 1.4 defined two new OFPTC_* constants, OFPTC_EVICTION and
+OFPTC_VACANCY_EVENTS, using bits that did not overlap with
+OFPTC_MISS_* even though those bits had not been defined since OF1.2.
+OFPT_TABLE_MOD still controlled these settings.  The field for OFPTC_*
+values in OFPMP_TABLE_FEATURES was renamed from 'config' to
+'capabilities' and documented as reporting the flags that are
+supported in a OFPT_TABLE_MOD message.  The OFPMP_TABLE_DESC message
+newly added in OF1.4 reported the OFPTC_* setting.
+
+OpenFlow 1.5 did not change anything in this regard.
+
+The following table summarizes.  The columns say:
+
+  - OpenFlow version(s).
+
+  - The OFPTC_* flags defined in those versions.
+
+  - Whether OFPT_TABLE_MOD can modify OFPTC_* flags.
+
+  - Whether OFPST_TABLE/OFPMP_TABLE reports the OFPTC_* flags.
+
+  - What OFPMP_TABLE_FEATURES reports (if it exists): either the
+    current configuration or the switch's capabilities.
+
+  - Whether OFPMP_TABLE_DESC reports the current configuration.
+
+OpenFlow   OFPTC_* flags            TABLE_MOD stats? TABLE_FEATURES TABLE_DESC
+---------  -----------------------  --------- ------ -------------- ----------
+OF1.0      none                     no[*][+]  no[*]  nothing[*][+]  no[*][+]
+OF1.1/1.2  MISS_*                   yes       yes    nothing[+]     no[+]
+OF1.3      none                     yes[*]    no[*]  config[*]      no[*][+]
+OF1.4/1.5  EVICTION/VACANCY_EVENTS  yes       no     capabilities   yes
+
+   [*] Nothing to report/change anyway.
+
+   [+] No such message.
+
+
 IPv6
 ====
 
 IPv6
 ====
 
diff --git a/NEWS b/NEWS
index 3da7cfd..57e4f89 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,7 @@ Post-v2.4.0
      * Group chaining (where one OpenFlow group triggers another) is
        now supported.
      * OpenFlow 1.4+ "importance" is now considered for flow eviction.
      * Group chaining (where one OpenFlow group triggers another) is
        now supported.
      * OpenFlow 1.4+ "importance" is now considered for flow eviction.
+     * OpenFlow 1.4+ OFPTC_EVICTION is now implemented.
    - Support for matching and generating options with Geneve tunnels.
    - Support Multicast Listener Discovery (MLDv1 and MLDv2).
 
    - Support for matching and generating options with Geneve tunnels.
    - Support Multicast Listener Discovery (MLDv1 and MLDv2).
 
index 142d32c..cf93429 100644 (file)
@@ -232,7 +232,14 @@ struct ofp13_table_features {
     char name[OFP_MAX_TABLE_NAME_LEN];
     ovs_be64 metadata_match;  /* Bits of metadata table can match. */
     ovs_be64 metadata_write;  /* Bits of metadata table can write. */
     char name[OFP_MAX_TABLE_NAME_LEN];
     ovs_be64 metadata_match;  /* Bits of metadata table can match. */
     ovs_be64 metadata_write;  /* Bits of metadata table can write. */
-    ovs_be32 config;          /* Bitmap of OFPTC_* values */
+
+    /* In OF1.3 this field was named 'config' and it was useless because OF1.3
+     * did not define any OFPTC_* bits.
+     *
+     * OF1.4 renamed this field to 'capabilities' and added OFPTC14_EVICTION
+     * and OFPTC14_VACANCY_EVENTS. */
+    ovs_be32 capabilities;    /* Bitmap of OFPTC_* values */
+
     ovs_be32 max_entries;     /* Max number of entries supported. */
 
     /* Table Feature Property list */
     ovs_be32 max_entries;     /* Max number of entries supported. */
 
     /* Table Feature Property list */
index df32819..9e88d6d 100644 (file)
@@ -869,20 +869,20 @@ parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string,
     return error;
 }
 
     return error;
 }
 
-/* Convert 'table_id' and 'flow_miss_handling' (as described for the
- * "mod-table" command in the ovs-ofctl man page) into 'tm' for sending the
- * specified table_mod 'command' to a switch.
+/* 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.
+ *
+ * Stores a bitmap of the OpenFlow versions that are usable for 'tm' into
+ * '*usable_versions'.
  *
  * 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_mod(struct ofputil_table_mod *tm, const char *table_id,
  *
  * 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_mod(struct ofputil_table_mod *tm, const char *table_id,
-                    const char *flow_miss_handling,
-                    enum ofputil_protocol *usable_protocols)
+                    const char *setting, uint32_t *usable_versions)
 {
 {
-    /* Table mod requires at least OF 1.1. */
-    *usable_protocols = OFPUTIL_P_OF11_UP;
-
+    *usable_versions = 0;
     if (!strcasecmp(table_id, "all")) {
         tm->table_id = OFPTT_ALL;
     } else {
     if (!strcasecmp(table_id, "all")) {
         tm->table_id = OFPTT_ALL;
     } else {
@@ -892,18 +892,38 @@ parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id,
         }
     }
 
         }
     }
 
-    if (strcmp(flow_miss_handling, "controller") == 0) {
-        tm->miss_config = OFPUTIL_TABLE_MISS_CONTROLLER;
-    } else if (strcmp(flow_miss_handling, "continue") == 0) {
-        tm->miss_config = OFPUTIL_TABLE_MISS_CONTINUE;
-    } else if (strcmp(flow_miss_handling, "drop") == 0) {
-        tm->miss_config = OFPUTIL_TABLE_MISS_DROP;
+    tm->miss = OFPUTIL_TABLE_MISS_DEFAULT;
+    tm->eviction = OFPUTIL_TABLE_EVICTION_DEFAULT;
+    tm->eviction_flags = UINT32_MAX;
+
+    /* 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.)
+     */
+    if (!strcmp(setting, "controller")) {
+        tm->miss = OFPUTIL_TABLE_MISS_CONTROLLER;
+        *usable_versions = (1u << OFP11_VERSION) | (1u << OFP12_VERSION);
+    } else if (!strcmp(setting, "continue")) {
+        tm->miss = OFPUTIL_TABLE_MISS_CONTINUE;
+        *usable_versions = (1u << OFP11_VERSION) | (1u << OFP12_VERSION);
+    } else if (!strcmp(setting, "drop")) {
+        tm->miss = OFPUTIL_TABLE_MISS_DROP;
+        *usable_versions = (1u << OFP11_VERSION) | (1u << OFP12_VERSION);
+    } else if (!strcmp(setting, "evict")) {
+        tm->eviction = OFPUTIL_TABLE_EVICTION_ON;
+        *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION);
+    } else if (!strcmp(setting, "noevict")) {
+        tm->eviction = OFPUTIL_TABLE_EVICTION_OFF;
+        *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION);
     } else {
     } else {
-        return xasprintf("invalid flow_miss_handling %s", flow_miss_handling);
+        return xasprintf("invalid table_mod setting %s", setting);
     }
 
     if (tm->table_id == 0xfe
     }
 
     if (tm->table_id == 0xfe
-        && tm->miss_config == OFPUTIL_TABLE_MISS_CONTINUE) {
+        && tm->miss == OFPUTIL_TABLE_MISS_CONTINUE) {
         return xstrdup("last table's flow miss handling can not be continue");
     }
 
         return xstrdup("last table's flow miss handling can not be continue");
     }
 
index 0749d5b..930e3d9 100644 (file)
@@ -49,7 +49,7 @@ char *parse_ofp_flow_mod_str(struct ofputil_flow_mod *, const char *string,
 
 char *parse_ofp_table_mod(struct ofputil_table_mod *,
                           const char *table_id, const char *flow_miss_handling,
 
 char *parse_ofp_table_mod(struct ofputil_table_mod *,
                           const char *table_id, const char *flow_miss_handling,
-                          enum ofputil_protocol *usable_protocols)
+                          uint32_t *usable_versions)
     OVS_WARN_UNUSED_RESULT;
 
 char *parse_ofp_flow_mod_file(const char *file_name, int command,
     OVS_WARN_UNUSED_RESULT;
 
 char *parse_ofp_flow_mod_file(const char *file_name, int command,
index 1b25180..d76134f 100644 (file)
@@ -942,23 +942,54 @@ ofp_print_port_mod(struct ds *string, const struct ofp_header *oh)
     }
 }
 
     }
 }
 
-static void
-ofp_print_table_miss_config(struct ds *string, enum ofputil_table_miss miss)
+static const char *
+ofputil_table_miss_to_string(enum ofputil_table_miss miss)
 {
     switch (miss) {
 {
     switch (miss) {
-    case OFPUTIL_TABLE_MISS_CONTROLLER:
-        ds_put_cstr(string, "controller\n");
-        break;
-    case OFPUTIL_TABLE_MISS_CONTINUE:
-        ds_put_cstr(string, "continue\n");
-        break;
-    case OFPUTIL_TABLE_MISS_DROP:
-        ds_put_cstr(string, "drop\n");
-        break;
-    case OFPUTIL_TABLE_MISS_DEFAULT:
-    default:
-        ds_put_format(string, "Unknown (%d)\n", miss);
-        break;
+    case OFPUTIL_TABLE_MISS_DEFAULT: return "default";
+    case OFPUTIL_TABLE_MISS_CONTROLLER: return "controller";
+    case OFPUTIL_TABLE_MISS_CONTINUE: return "continue";
+    case OFPUTIL_TABLE_MISS_DROP: return "drop";
+    default: return "***error***";
+    }
+}
+
+static const char *
+ofputil_table_eviction_to_string(enum ofputil_table_eviction eviction)
+{
+    switch (eviction) {
+    case OFPUTIL_TABLE_EVICTION_DEFAULT: return "default";
+    case OFPUTIL_TABLE_EVICTION_ON: return "on";
+    case OFPUTIL_TABLE_EVICTION_OFF: return "off";
+    default: return "***error***";
+    }
+
+}
+
+static const char *
+ofputil_eviction_flag_to_string(uint32_t bit)
+{
+    enum ofp14_table_mod_prop_eviction_flag eviction_flag = bit;
+
+    switch (eviction_flag) {
+    case OFPTMPEF14_OTHER:      return "OTHER";
+    case OFPTMPEF14_IMPORTANCE: return "IMPORTANCE";
+    case OFPTMPEF14_LIFETIME:   return "LIFETIME";
+    }
+
+    return NULL;
+}
+
+/* Appends to 'string' a description of the bitmap of OFPTMPEF14_* values in
+ * 'eviction_flags'. */
+static void
+ofputil_put_eviction_flags(struct ds *string, uint32_t eviction_flags)
+{
+    if (eviction_flags != UINT32_MAX) {
+        ofp_print_bit_names(string, eviction_flags,
+                            ofputil_eviction_flag_to_string, '|');
+    } else {
+        ds_put_cstr(string, "(default)");
     }
 }
 
     }
 }
 
@@ -980,9 +1011,17 @@ ofp_print_table_mod(struct ds *string, const struct ofp_header *oh)
         ds_put_format(string, " table_id=%"PRIu8, pm.table_id);
     }
 
         ds_put_format(string, " table_id=%"PRIu8, pm.table_id);
     }
 
-    if (pm.miss_config != OFPUTIL_TABLE_MISS_DEFAULT) {
-        ds_put_cstr(string, ", flow_miss_config=");
-        ofp_print_table_miss_config(string, pm.miss_config);
+    if (pm.miss != OFPUTIL_TABLE_MISS_DEFAULT) {
+        ds_put_format(string, ", flow_miss_config=%s",
+                      ofputil_table_miss_to_string(pm.miss));
+    }
+    if (pm.eviction != OFPUTIL_TABLE_EVICTION_DEFAULT) {
+        ds_put_format(string, ", eviction=%s",
+                      ofputil_table_eviction_to_string(pm.eviction));
+    }
+    if (pm.eviction_flags != UINT32_MAX) {
+        ds_put_cstr(string, "eviction_flags=");
+        ofputil_put_eviction_flags(string, pm.eviction_flags);
     }
 }
 
     }
 }
 
@@ -2500,8 +2539,19 @@ ofp_print_table_features(struct ds *s,
     }
 
     if (features->miss_config != OFPUTIL_TABLE_MISS_DEFAULT) {
     }
 
     if (features->miss_config != OFPUTIL_TABLE_MISS_DEFAULT) {
-        ds_put_cstr(s, "    config=");
-        ofp_print_table_miss_config(s, features->miss_config);
+        ds_put_format(s, "    config=%s\n",
+                      ofputil_table_miss_to_string(features->miss_config));
+    }
+
+    if (features->supports_eviction >= 0) {
+        ds_put_format(s, "    eviction: %ssupported\n",
+                      features->supports_eviction ? "" : "not ");
+
+    }
+    if (features->supports_vacancy_events >= 0) {
+        ds_put_format(s, "    vacancy events: %ssupported\n",
+                      features->supports_vacancy_events ? "" : "not ");
+
     }
 
     if (features->max_entries) {
     }
 
     if (features->max_entries) {
index 38dcb89..2e9ae47 100644 (file)
@@ -53,8 +53,11 @@ 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);
 
  * 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_miss ofputil_table_miss_from_config(
-    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 ofp_version);
 
 struct ofp_prop_header {
     ovs_be16 type;
 
 struct ofp_prop_header {
     ovs_be16 type;
@@ -4644,7 +4647,15 @@ ofputil_decode_table_features(struct ofpbuf *msg,
     ovs_strlcpy(tf->name, otf->name, OFP_MAX_TABLE_NAME_LEN);
     tf->metadata_match = otf->metadata_match;
     tf->metadata_write = otf->metadata_write;
     ovs_strlcpy(tf->name, otf->name, OFP_MAX_TABLE_NAME_LEN);
     tf->metadata_match = otf->metadata_match;
     tf->metadata_write = otf->metadata_write;
-    tf->miss_config = ofputil_table_miss_from_config(otf->config, oh->version);
+    tf->miss_config = OFPUTIL_TABLE_MISS_DEFAULT;
+    if (oh->version >= OFP14_VERSION) {
+        uint32_t caps = ntohl(otf->capabilities);
+        tf->supports_eviction = (caps & OFPTC14_EVICTION) != 0;
+        tf->supports_vacancy_events = (caps & OFPTC14_VACANCY_EVENTS) != 0;
+    } else {
+        tf->supports_eviction = -1;
+        tf->supports_vacancy_events = -1;
+    }
     tf->max_entries = ntohl(otf->max_entries);
 
     while (properties.size > 0) {
     tf->max_entries = ntohl(otf->max_entries);
 
     while (properties.size > 0) {
@@ -4852,7 +4863,14 @@ ofputil_append_table_features_reply(const struct ofputil_table_features *tf,
     ovs_strlcpy(otf->name, tf->name, sizeof otf->name);
     otf->metadata_match = tf->metadata_match;
     otf->metadata_write = tf->metadata_write;
     ovs_strlcpy(otf->name, tf->name, sizeof otf->name);
     otf->metadata_match = tf->metadata_match;
     otf->metadata_write = tf->metadata_write;
-    otf->config = ofputil_table_miss_to_config(tf->miss_config, version);
+    if (version >= OFP14_VERSION) {
+        if (tf->supports_eviction) {
+            otf->capabilities |= htonl(OFPTC14_EVICTION);
+        }
+        if (tf->supports_vacancy_events) {
+            otf->capabilities |= htonl(OFPTC14_VACANCY_EVENTS);
+        }
+    }
     otf->max_entries = htonl(tf->max_entries);
 
     put_table_instruction_features(reply, &tf->nonmiss, 0, version);
     otf->max_entries = htonl(tf->max_entries);
 
     put_table_instruction_features(reply, &tf->nonmiss, 0, version);
@@ -4868,17 +4886,97 @@ ofputil_append_table_features_reply(const struct ofputil_table_features *tf,
     ofpmp_postappend(replies, start_ofs);
 }
 
     ofpmp_postappend(replies, start_ofs);
 }
 
-/* ofputil_table_mod */
+static enum ofperr
+parse_table_mod_eviction_property(struct ofpbuf *property,
+                                  struct ofputil_table_mod *tm)
+{
+    struct ofp14_table_mod_prop_eviction *ote = property->data;
+
+    if (property->size != sizeof *ote) {
+        return OFPERR_OFPBPC_BAD_LEN;
+    }
+
+    tm->eviction_flags = ntohl(ote->flags);
+    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 eviction configuration that it specifies.
+ *
+ * Only OpenFlow 1.4 and later specify table eviction configuration this way,
+ * so for other 'version' values this function always returns
+ * OFPUTIL_TABLE_EVICTION_DEFAULT. */
+static enum ofputil_table_eviction
+ofputil_decode_table_eviction(ovs_be32 config, enum ofp_version version)
+{
+    return (version < OFP14_VERSION ? OFPUTIL_TABLE_EVICTION_DEFAULT
+            : config & htonl(OFPTC14_EVICTION) ? OFPUTIL_TABLE_EVICTION_ON
+            : OFPUTIL_TABLE_EVICTION_OFF);
+}
+
+/* Returns a bitmap of OFPTC* values suitable for 'config' fields in various
+ * OpenFlow messages of the given 'version', based on the provided 'miss' and
+ * 'eviction' values. */
+static ovs_be32
+ofputil_encode_table_config(enum ofputil_table_miss miss,
+                            enum ofputil_table_eviction eviction,
+                            enum ofp_version version)
+{
+    /* See the section "OFPTC_* Table Configuration" in DESIGN.md for more
+     * information on the crazy evolution of this field. */
+    switch (version) {
+    case OFP10_VERSION:
+        /* OpenFlow 1.0 didn't have such a field, any value ought to do. */
+        return htonl(0);
+
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+        /* OpenFlow 1.1 and 1.2 define only OFPTC11_TABLE_MISS_*. */
+        switch (miss) {
+        case OFPUTIL_TABLE_MISS_DEFAULT:
+            /* Really this shouldn't be used for encoding (the caller should
+             * provide a specific value) but I can't imagine that defaulting to
+             * the fall-through case here will hurt. */
+        case OFPUTIL_TABLE_MISS_CONTROLLER:
+        default:
+            return htonl(OFPTC11_TABLE_MISS_CONTROLLER);
+        case OFPUTIL_TABLE_MISS_CONTINUE:
+            return htonl(OFPTC11_TABLE_MISS_CONTINUE);
+        case OFPUTIL_TABLE_MISS_DROP:
+            return htonl(OFPTC11_TABLE_MISS_DROP);
+        }
+        OVS_NOT_REACHED();
+
+    case OFP13_VERSION:
+        /* OpenFlow 1.3 removed OFPTC11_TABLE_MISS_* and didn't define any new
+         * flags, so this is correct. */
+        return htonl(0);
+
+    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);
+    }
+
+    OVS_NOT_REACHED();
+}
 
 /* Given 'config', taken from an OpenFlow 'version' message that specifies
  * table configuration (a table mod, table stats, or table features message),
 
 /* Given 'config', taken from an OpenFlow 'version' message that specifies
  * table configuration (a table mod, table stats, or table features message),
- * returns the table miss configuration that it specifies.  */
+ * returns the table miss configuration that it specifies.
+ *
+ * Only OpenFlow 1.1 and 1.2 specify table miss configurations this way, so for
+ * other 'version' values this function always returns
+ * OFPUTIL_TABLE_MISS_DEFAULT. */
 static enum ofputil_table_miss
 static enum ofputil_table_miss
-ofputil_table_miss_from_config(ovs_be32 config_, enum ofp_version version)
+ofputil_decode_table_miss(ovs_be32 config_, enum ofp_version version)
 {
     uint32_t config = ntohl(config_);
 
 {
     uint32_t config = ntohl(config_);
 
-    if (version < OFP13_VERSION) {
+    if (version == OFP11_VERSION || version == OFP12_VERSION) {
         switch (config & OFPTC11_TABLE_MISS_MASK) {
         case OFPTC11_TABLE_MISS_CONTROLLER:
             return OFPUTIL_TABLE_MISS_CONTROLLER;
         switch (config & OFPTC11_TABLE_MISS_MASK) {
         case OFPTC11_TABLE_MISS_CONTROLLER:
             return OFPUTIL_TABLE_MISS_CONTROLLER;
@@ -4898,32 +4996,6 @@ ofputil_table_miss_from_config(ovs_be32 config_, enum ofp_version version)
     }
 }
 
     }
 }
 
-/* Given a table miss configuration, returns the corresponding OpenFlow table
- * configuration for use in an OpenFlow message of the given 'version'. */
-ovs_be32
-ofputil_table_miss_to_config(enum ofputil_table_miss miss,
-                             enum ofp_version version)
-{
-    if (version < OFP13_VERSION) {
-        switch (miss) {
-        case OFPUTIL_TABLE_MISS_CONTROLLER:
-        case OFPUTIL_TABLE_MISS_DEFAULT:
-            return htonl(OFPTC11_TABLE_MISS_CONTROLLER);
-
-        case OFPUTIL_TABLE_MISS_CONTINUE:
-            return htonl(OFPTC11_TABLE_MISS_CONTINUE);
-
-        case OFPUTIL_TABLE_MISS_DROP:
-            return htonl(OFPTC11_TABLE_MISS_DROP);
-
-        default:
-            OVS_NOT_REACHED();
-        }
-    } else {
-        return htonl(0);
-    }
-}
-
 /* Decodes the OpenFlow "table mod" message in '*oh' into an abstract form in
  * '*pm'.  Returns 0 if successful, otherwise an OFPERR_* value. */
 enum ofperr
 /* Decodes the OpenFlow "table mod" message in '*oh' into an abstract form in
  * '*pm'.  Returns 0 if successful, otherwise an OFPERR_* value. */
 enum ofperr
@@ -4933,6 +5005,10 @@ ofputil_decode_table_mod(const struct ofp_header *oh,
     enum ofpraw raw;
     struct ofpbuf b;
 
     enum ofpraw raw;
     struct ofpbuf b;
 
+    memset(pm, 0, sizeof *pm);
+    pm->miss = OFPUTIL_TABLE_MISS_DEFAULT;
+    pm->eviction = OFPUTIL_TABLE_EVICTION_DEFAULT;
+    pm->eviction_flags = UINT32_MAX;
     ofpbuf_use_const(&b, oh, ntohs(oh->length));
     raw = ofpraw_pull_assert(&b);
 
     ofpbuf_use_const(&b, oh, ntohs(oh->length));
     raw = ofpraw_pull_assert(&b);
 
@@ -4940,16 +5016,37 @@ ofputil_decode_table_mod(const struct ofp_header *oh,
         const struct ofp11_table_mod *otm = b.data;
 
         pm->table_id = otm->table_id;
         const struct ofp11_table_mod *otm = b.data;
 
         pm->table_id = otm->table_id;
-        pm->miss_config = ofputil_table_miss_from_config(otm->config,
-                                                         oh->version);
+        pm->miss = ofputil_decode_table_miss(otm->config, oh->version);
     } else if (raw == OFPRAW_OFPT14_TABLE_MOD) {
         const struct ofp14_table_mod *otm = ofpbuf_pull(&b, sizeof *otm);
 
         pm->table_id = otm->table_id;
     } else if (raw == OFPRAW_OFPT14_TABLE_MOD) {
         const struct ofp14_table_mod *otm = ofpbuf_pull(&b, sizeof *otm);
 
         pm->table_id = otm->table_id;
-        pm->miss_config = ofputil_table_miss_from_config(otm->config,
-                                                         oh->version);
-        /* We do not understand any properties yet, so we do not bother
-         * parsing them. */
+        pm->miss = ofputil_decode_table_miss(otm->config, oh->version);
+        pm->eviction = ofputil_decode_table_eviction(otm->config, oh->version);
+        while (b.size > 0) {
+            struct ofpbuf property;
+            enum ofperr error;
+            uint16_t type;
+
+            error = ofputil_pull_property(&b, &property, &type);
+            if (error) {
+                return error;
+            }
+
+            switch (type) {
+            case OFPTMPT14_EVICTION:
+                error = parse_table_mod_eviction_property(&property, pm);
+                break;
+
+            default:
+                error = OFPERR_OFPBRC_BAD_TYPE;
+                break;
+            }
+
+            if (error) {
+                return error;
+            }
+        }
     } else {
         return OFPERR_OFPBRC_BAD_TYPE;
     }
     } else {
         return OFPERR_OFPBRC_BAD_TYPE;
     }
@@ -4957,11 +5054,11 @@ ofputil_decode_table_mod(const struct ofp_header *oh,
     return 0;
 }
 
     return 0;
 }
 
-/* Converts the abstract form of a "table mod" message in '*pm' into an OpenFlow
- * message suitable for 'protocol', and returns that encoded form in a buffer
- * owned by the caller. */
+/* Converts the abstract form of a "table mod" message in '*tm' into an
+ * OpenFlow message suitable for 'protocol', and returns that encoded form in a
+ * buffer owned by the caller. */
 struct ofpbuf *
 struct ofpbuf *
-ofputil_encode_table_mod(const struct ofputil_table_mod *pm,
+ofputil_encode_table_mod(const struct ofputil_table_mod *tm,
                         enum ofputil_protocol protocol)
 {
     enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol);
                         enum ofputil_protocol protocol)
 {
     enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol);
@@ -4980,20 +5077,28 @@ ofputil_encode_table_mod(const struct ofputil_table_mod *pm,
 
         b = ofpraw_alloc(OFPRAW_OFPT11_TABLE_MOD, ofp_version, 0);
         otm = ofpbuf_put_zeros(b, sizeof *otm);
 
         b = ofpraw_alloc(OFPRAW_OFPT11_TABLE_MOD, ofp_version, 0);
         otm = ofpbuf_put_zeros(b, sizeof *otm);
-        otm->table_id = pm->table_id;
-        otm->config = ofputil_table_miss_to_config(pm->miss_config,
-                                                   ofp_version);
+        otm->table_id = tm->table_id;
+        otm->config = ofputil_encode_table_config(tm->miss, tm->eviction,
+                                                  ofp_version);
         break;
     }
     case OFP14_VERSION:
     case OFP15_VERSION: {
         struct ofp14_table_mod *otm;
         break;
     }
     case OFP14_VERSION:
     case OFP15_VERSION: {
         struct ofp14_table_mod *otm;
+        struct ofp14_table_mod_prop_eviction *ote;
 
         b = ofpraw_alloc(OFPRAW_OFPT14_TABLE_MOD, ofp_version, 0);
         otm = ofpbuf_put_zeros(b, sizeof *otm);
 
         b = ofpraw_alloc(OFPRAW_OFPT14_TABLE_MOD, ofp_version, 0);
         otm = ofpbuf_put_zeros(b, sizeof *otm);
-        otm->table_id = pm->table_id;
-        otm->config = ofputil_table_miss_to_config(pm->miss_config,
-                                                   ofp_version);
+        otm->table_id = tm->table_id;
+        otm->config = ofputil_encode_table_config(tm->miss, tm->eviction,
+                                                  ofp_version);
+
+        if (tm->eviction_flags != UINT32_MAX) {
+            ote = ofpbuf_put_zeros(b, sizeof *ote);
+            ote->type = htons(OFPTMPT14_EVICTION);
+            ote->length = htons(sizeof *ote);
+            ote->flags = htonl(tm->eviction_flags);
+        }
         break;
     }
     default:
         break;
     }
     default:
@@ -5339,8 +5444,9 @@ ofputil_put_ofp12_table_stats(const struct ofputil_table_stats *stats,
     out->metadata_write = features->metadata_write;
     out->instructions = ovsinst_bitmap_to_openflow(
         features->nonmiss.instructions, OFP12_VERSION);
     out->metadata_write = features->metadata_write;
     out->instructions = ovsinst_bitmap_to_openflow(
         features->nonmiss.instructions, OFP12_VERSION);
-    out->config = ofputil_table_miss_to_config(features->miss_config,
-                                               OFP12_VERSION);
+    out->config = ofputil_encode_table_config(features->miss_config,
+                                              OFPUTIL_TABLE_EVICTION_DEFAULT,
+                                              OFP12_VERSION);
     out->max_entries = htonl(features->max_entries);
     out->active_count = htonl(stats->active_count);
     out->lookup_count = htonll(stats->lookup_count);
     out->max_entries = htonl(features->max_entries);
     out->active_count = htonl(stats->active_count);
     out->lookup_count = htonll(stats->lookup_count);
@@ -5446,8 +5552,8 @@ ofputil_decode_ofp11_table_stats(struct ofpbuf *msg,
     features->nonmiss.apply.ofpacts = ofpact_bitmap_from_openflow(
         ots->write_actions, OFP11_VERSION);
     features->miss = features->nonmiss;
     features->nonmiss.apply.ofpacts = ofpact_bitmap_from_openflow(
         ots->write_actions, OFP11_VERSION);
     features->miss = features->nonmiss;
-    features->miss_config = ofputil_table_miss_from_config(ots->config,
-                                                           OFP11_VERSION);
+    features->miss_config = ofputil_decode_table_miss(ots->config,
+                                                      OFP11_VERSION);
     features->match = mf_bitmap_from_of11(ots->match);
     features->wildcard = mf_bitmap_from_of11(ots->wildcards);
     bitmap_or(features->match.bm, features->wildcard.bm, MFF_N_IDS);
     features->match = mf_bitmap_from_of11(ots->match);
     features->wildcard = mf_bitmap_from_of11(ots->wildcards);
     bitmap_or(features->match.bm, features->wildcard.bm, MFF_N_IDS);
@@ -5476,8 +5582,8 @@ ofputil_decode_ofp12_table_stats(struct ofpbuf *msg,
     ovs_strlcpy(features->name, ots->name, sizeof features->name);
     features->metadata_match = ots->metadata_match;
     features->metadata_write = ots->metadata_write;
     ovs_strlcpy(features->name, ots->name, sizeof features->name);
     features->metadata_match = ots->metadata_match;
     features->metadata_write = ots->metadata_write;
-    features->miss_config = ofputil_table_miss_from_config(ots->config,
-                                                           OFP12_VERSION);
+    features->miss_config = ofputil_decode_table_miss(ots->config,
+                                                      OFP12_VERSION);
     features->max_entries = ntohl(ots->max_entries);
 
     features->nonmiss.instructions = ovsinst_bitmap_from_openflow(
     features->max_entries = ntohl(ots->max_entries);
 
     features->nonmiss.instructions = ovsinst_bitmap_from_openflow(
@@ -5545,6 +5651,8 @@ ofputil_decode_table_stats_reply(struct ofpbuf *msg,
 
     memset(stats, 0, sizeof *stats);
     memset(features, 0, sizeof *features);
 
     memset(stats, 0, sizeof *stats);
     memset(features, 0, sizeof *features);
+    features->supports_eviction = -1;
+    features->supports_vacancy_events = -1;
 
     switch ((enum ofp_version) oh->version) {
     case OFP10_VERSION:
 
     switch ((enum ofp_version) oh->version) {
     case OFP10_VERSION:
index f90ac0c..fbc8abf 100644 (file)
@@ -609,13 +609,33 @@ enum ofputil_table_miss {
     OFPUTIL_TABLE_MISS_DROP,       /* Drop the packet. */
 };
 
     OFPUTIL_TABLE_MISS_DROP,       /* Drop the packet. */
 };
 
-ovs_be32 ofputil_table_miss_to_config(enum ofputil_table_miss,
-                                      enum ofp_version);
+/* Abstract version of OFPTC14_EVICTION.
+ *
+ * OpenFlow 1.0 through 1.3 don't know anything about eviction, so decoding a
+ * message for one of these protocols always yields
+ * OFPUTIL_TABLE_EVICTION_DEFAULT. */
+enum ofputil_table_eviction {
+    OFPUTIL_TABLE_EVICTION_DEFAULT, /* No value. */
+    OFPUTIL_TABLE_EVICTION_ON,      /* Enable eviction. */
+    OFPUTIL_TABLE_EVICTION_OFF      /* Disable eviction. */
+};
 
 /* Abstract ofp_table_mod. */
 struct ofputil_table_mod {
     uint8_t table_id;         /* ID of the table, 0xff indicates all tables. */
 
 /* Abstract ofp_table_mod. */
 struct ofputil_table_mod {
     uint8_t table_id;         /* ID of the table, 0xff indicates all tables. */
-    enum ofputil_table_miss miss_config;
+
+    /* OpenFlow 1.1 and 1.2 only.  For other versions, ignored on encoding,
+     * decoded to OFPUTIL_TABLE_MISS_DEFAULT. */
+    enum ofputil_table_miss miss;
+
+    /* OpenFlow 1.4+ only.  For other versions, ignored on encoding, decoded to
+     * OFPUTIL_TABLE_EVICTION_DEFAULT. */
+    enum ofputil_table_eviction eviction;
+
+    /* OpenFlow 1.4+ only and optional even there; UINT32_MAX indicates
+     * absence.  For other versions, ignored on encoding, decoded to
+     * UINT32_MAX.*/
+    uint32_t eviction_flags;    /* OFPTMPEF14_*. */
 };
 
 enum ofperr ofputil_decode_table_mod(const struct ofp_header *,
 };
 
 enum ofperr ofputil_decode_table_mod(const struct ofp_header *,
@@ -623,16 +643,38 @@ enum ofperr ofputil_decode_table_mod(const struct ofp_header *,
 struct ofpbuf *ofputil_encode_table_mod(const struct ofputil_table_mod *,
                                        enum ofputil_protocol);
 
 struct ofpbuf *ofputil_encode_table_mod(const struct ofputil_table_mod *,
                                        enum ofputil_protocol);
 
-/* Abstract ofp_table_features. */
+/* Abstract ofp_table_features.
+ *
+ * This is used for all versions of OpenFlow, even though ofp_table_features
+ * was only introduced in OpenFlow 1.3, because earlier versions of OpenFlow
+ * include support for a subset of ofp_table_features through OFPST_TABLE (aka
+ * OFPMP_TABLE). */
 struct ofputil_table_features {
     uint8_t table_id;         /* Identifier of table. Lower numbered tables
                                  are consulted first. */
     char name[OFP_MAX_TABLE_NAME_LEN];
     ovs_be64 metadata_match;  /* Bits of metadata table can match. */
     ovs_be64 metadata_write;  /* Bits of metadata table can write. */
 struct ofputil_table_features {
     uint8_t table_id;         /* Identifier of table. Lower numbered tables
                                  are consulted first. */
     char name[OFP_MAX_TABLE_NAME_LEN];
     ovs_be64 metadata_match;  /* Bits of metadata table can match. */
     ovs_be64 metadata_write;  /* Bits of metadata table can write. */
-    enum ofputil_table_miss miss_config;
     uint32_t max_entries;     /* Max number of entries supported. */
 
     uint32_t max_entries;     /* Max number of entries supported. */
 
+    /* Flags.
+     *
+     * 'miss_config' is relevant for OpenFlow 1.1 and 1.2 only, because those
+     * versions include OFPTC_MISS_* flags in OFPST_TABLE.  For other versions,
+     * it is decoded to OFPUTIL_TABLE_MISS_DEFAULT and ignored for encoding.
+     *
+     * 'supports_eviction' and 'supports_vacancy_events' are relevant only for
+     * OpenFlow 1.4 and later only.  For OF1.4, they are boolean: 1 if
+     * supported, otherwise 0.  For other versions, they are decoded as -1 and
+     * ignored for encoding.
+     *
+     * See the section "OFPTC_* Table Configuration" in DESIGN.md for more
+     * details of how OpenFlow has changed in this area.
+     */
+    enum ofputil_table_miss miss_config; /* OF1.1 and 1.2 only. */
+    int supports_eviction;               /* OF1.4+ only. */
+    int supports_vacancy_events;         /* OF1.4+ only. */
+
     /* Table features related to instructions.  There are two instances:
      *
      *   - 'miss' reports features available in the table miss flow.
     /* Table features related to instructions.  There are two instances:
      *
      *   - 'miss' reports features available in the table miss flow.
index f7e8ead..95c1c64 100644 (file)
@@ -246,9 +246,16 @@ struct oftable {
     struct hmap eviction_groups_by_id;
     struct heap eviction_groups_by_size;
 
     struct hmap eviction_groups_by_id;
     struct heap eviction_groups_by_size;
 
-    /* Table configuration. */
+    /* Flow table miss handling configuration. */
     ATOMIC(enum ofputil_table_miss) miss_config;
 
     ATOMIC(enum ofputil_table_miss) miss_config;
 
+    /* Eviction is enabled if either the client (vswitchd) enables it or an
+     * OpenFlow controller enables it; thus, a nonzero value indicates that
+     * eviction is enabled.  */
+#define EVICTION_CLIENT  (1 << 0)  /* Set to 1 if client enables eviction. */
+#define EVICTION_OPENFLOW (1 << 1) /* Set to 1 if OpenFlow enables eviction. */
+    unsigned int eviction;
+
     atomic_ulong n_matched;
     atomic_ulong n_missed;
 };
     atomic_ulong n_matched;
     atomic_ulong n_missed;
 };
index 4aa844e..970aca3 100644 (file)
@@ -84,13 +84,18 @@ static void oftable_set_name(struct oftable *, const char *name);
 
 static enum ofperr evict_rules_from_table(struct oftable *)
     OVS_REQUIRES(ofproto_mutex);
 
 static enum ofperr evict_rules_from_table(struct oftable *)
     OVS_REQUIRES(ofproto_mutex);
-static void oftable_disable_eviction(struct oftable *)
-    OVS_REQUIRES(ofproto_mutex);
-static void oftable_enable_eviction(struct oftable *,
-                                    const struct mf_subfield *fields,
-                                    size_t n_fields)
+static void oftable_configure_eviction(struct oftable *,
+                                       unsigned int eviction,
+                                       const struct mf_subfield *fields,
+                                       size_t n_fields)
     OVS_REQUIRES(ofproto_mutex);
 
     OVS_REQUIRES(ofproto_mutex);
 
+/* This is the only combination of OpenFlow eviction flags that OVS supports: a
+ * combination of OF1.4+ importance, the remaining lifetime of the flow, and
+ * fairness based on user-specified fields. */
+#define OFPROTO_EVICTION_FLAGS \
+    (OFPTMPEF14_OTHER | OFPTMPEF14_IMPORTANCE | OFPTMPEF14_LIFETIME)
+
 /* A set of rules within a single OpenFlow table (oftable) that have the same
  * values for the oftable's eviction_fields.  A rule to be evicted, when one is
  * needed, is taken from the eviction group that contains the greatest number
 /* A set of rules within a single OpenFlow table (oftable) that have the same
  * values for the oftable's eviction_fields.  A rule to be evicted, when one is
  * needed, is taken from the eviction group that contains the greatest number
@@ -1429,11 +1434,10 @@ ofproto_configure_table(struct ofproto *ofproto, int table_id,
     }
 
     ovs_mutex_lock(&ofproto_mutex);
     }
 
     ovs_mutex_lock(&ofproto_mutex);
-    if (s->groups) {
-        oftable_enable_eviction(table, s->groups, s->n_groups);
-    } else {
-        oftable_disable_eviction(table);
-    }
+    unsigned int new_eviction = (s->enable_eviction
+                                 ? table->eviction | EVICTION_CLIENT
+                                 : table->eviction & ~EVICTION_CLIENT);
+    oftable_configure_eviction(table, new_eviction, s->groups, s->n_groups);
     table->max_flows = s->max_flows;
     evict_rules_from_table(table);
     ovs_mutex_unlock(&ofproto_mutex);
     table->max_flows = s->max_flows;
     evict_rules_from_table(table);
     ovs_mutex_unlock(&ofproto_mutex);
@@ -1695,7 +1699,7 @@ ofproto_run(struct ofproto *p)
             struct eviction_group *evg;
             struct rule *rule;
 
             struct eviction_group *evg;
             struct rule *rule;
 
-            if (!table->eviction_fields) {
+            if (!table->eviction) {
                 continue;
             }
 
                 continue;
             }
 
@@ -6560,10 +6564,38 @@ handle_group_mod(struct ofconn *ofconn, const struct ofp_header *oh)
 enum ofputil_table_miss
 ofproto_table_get_miss_config(const struct ofproto *ofproto, uint8_t table_id)
 {
 enum ofputil_table_miss
 ofproto_table_get_miss_config(const struct ofproto *ofproto, uint8_t table_id)
 {
-    enum ofputil_table_miss value;
+    enum ofputil_table_miss miss;
+
+    atomic_read_relaxed(&ofproto->tables[table_id].miss_config, &miss);
+    return miss;
+}
+
+static void
+table_mod__(struct oftable *oftable,
+            enum ofputil_table_miss miss, enum ofputil_table_eviction eviction)
+{
+    if (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);
+    }
+
+    unsigned int new_eviction = oftable->eviction;
+    if (eviction == OFPUTIL_TABLE_EVICTION_ON) {
+        new_eviction |= EVICTION_OPENFLOW;
+    } else if (eviction == OFPUTIL_TABLE_EVICTION_OFF) {
+        new_eviction &= ~EVICTION_OPENFLOW;
+    }
 
 
-    atomic_read_relaxed(&ofproto->tables[table_id].miss_config, &value);
-    return value;
+    if (new_eviction != oftable->eviction) {
+        ovs_mutex_lock(&ofproto_mutex);
+        oftable_configure_eviction(oftable, new_eviction,
+                                   oftable->eviction_fields,
+                                   oftable->n_eviction_fields);
+        ovs_mutex_unlock(&ofproto_mutex);
+    }
 }
 
 static enum ofperr
 }
 
 static enum ofperr
@@ -6571,18 +6603,33 @@ table_mod(struct ofproto *ofproto, const struct ofputil_table_mod *tm)
 {
     if (!check_table_id(ofproto, tm->table_id)) {
         return OFPERR_OFPTMFC_BAD_TABLE;
 {
     if (!check_table_id(ofproto, tm->table_id)) {
         return OFPERR_OFPTMFC_BAD_TABLE;
-    } else if (tm->miss_config != OFPUTIL_TABLE_MISS_DEFAULT) {
-        if (tm->table_id == OFPTT_ALL) {
-            int i;
-            for (i = 0; i < ofproto->n_tables; i++) {
-                atomic_store_relaxed(&ofproto->tables[i].miss_config,
-                                     tm->miss_config);
+    }
+
+    /* Don't allow the eviction flags to be changed (except to the only fixed
+     * value that OVS supports).  OF1.4 says this is normal: "The
+     * OFPTMPT_EVICTION property usually cannot be modified using a
+     * OFP_TABLE_MOD request, because the eviction mechanism is switch
+     * defined". */
+    if (tm->eviction_flags != UINT32_MAX
+        && tm->eviction_flags != OFPROTO_EVICTION_FLAGS) {
+        return OFPERR_OFPTMFC_BAD_CONFIG;
+    }
+
+    if (tm->table_id == OFPTT_ALL) {
+        struct oftable *oftable;
+        OFPROTO_FOR_EACH_TABLE (oftable, ofproto) {
+            if (!(oftable->flags & (OFTABLE_HIDDEN | OFTABLE_READONLY))) {
+                table_mod__(oftable, tm->miss, tm->eviction);
             }
             }
-        } else {
-            atomic_store_relaxed(&ofproto->tables[tm->table_id].miss_config,
-                                 tm->miss_config);
         }
         }
+    } else {
+        struct oftable *oftable = &ofproto->tables[tm->table_id];
+        if (oftable->flags & OFTABLE_READONLY) {
+            return OFPERR_OFPTMFC_EPERM;
+        }
+        table_mod__(oftable, tm->miss, tm->eviction);
     }
     }
+
     return 0;
 }
 
     return 0;
 }
 
@@ -7231,7 +7278,7 @@ choose_rule_to_evict(struct oftable *table, struct rule **rulep)
     struct eviction_group *evg;
 
     *rulep = NULL;
     struct eviction_group *evg;
 
     *rulep = NULL;
-    if (!table->eviction_fields) {
+    if (!table->eviction) {
         return false;
     }
 
         return false;
     }
 
@@ -7452,7 +7499,7 @@ eviction_group_add_rule(struct rule *rule)
      * so no additional protection is needed. */
     has_timeout = rule->hard_timeout || rule->idle_timeout;
 
      * so no additional protection is needed. */
     has_timeout = rule->hard_timeout || rule->idle_timeout;
 
-    if (table->eviction_fields && has_timeout) {
+    if (table->eviction && has_timeout) {
         struct eviction_group *evg;
 
         evg = eviction_group_find(table, eviction_group_hash_rule(rule));
         struct eviction_group *evg;
 
         evg = eviction_group_find(table, eviction_group_hash_rule(rule));
@@ -7474,6 +7521,8 @@ oftable_init(struct oftable *table)
     classifier_init(&table->cls, flow_segment_u64s);
     table->max_flows = UINT_MAX;
     table->n_flows = 0;
     classifier_init(&table->cls, flow_segment_u64s);
     table->max_flows = UINT_MAX;
     table->n_flows = 0;
+    hmap_init(&table->eviction_groups_by_id);
+    heap_init(&table->eviction_groups_by_size);
     atomic_init(&table->miss_config, OFPUTIL_TABLE_MISS_DEFAULT);
 
     classifier_set_prefix_fields(&table->cls, default_prefix_fields,
     atomic_init(&table->miss_config, OFPUTIL_TABLE_MISS_DEFAULT);
 
     classifier_set_prefix_fields(&table->cls, default_prefix_fields,
@@ -7490,9 +7539,13 @@ static void
 oftable_destroy(struct oftable *table)
 {
     ovs_assert(classifier_is_empty(&table->cls));
 oftable_destroy(struct oftable *table)
 {
     ovs_assert(classifier_is_empty(&table->cls));
+
     ovs_mutex_lock(&ofproto_mutex);
     ovs_mutex_lock(&ofproto_mutex);
-    oftable_disable_eviction(table);
+    oftable_configure_eviction(table, 0, NULL, 0);
     ovs_mutex_unlock(&ofproto_mutex);
     ovs_mutex_unlock(&ofproto_mutex);
+
+    hmap_destroy(&table->eviction_groups_by_id);
+    heap_destroy(&table->eviction_groups_by_size);
     classifier_destroy(&table->cls);
     free(table->name);
 }
     classifier_destroy(&table->cls);
     free(table->name);
 }
@@ -7517,30 +7570,6 @@ oftable_set_name(struct oftable *table, const char *name)
     }
 }
 
     }
 }
 
-/* oftables support a choice of two policies when adding a rule would cause the
- * number of flows in the table to exceed the configured maximum number: either
- * they can refuse to add the new flow or they can evict some existing flow.
- * This function configures the former policy on 'table'. */
-static void
-oftable_disable_eviction(struct oftable *table)
-    OVS_REQUIRES(ofproto_mutex)
-{
-    if (table->eviction_fields) {
-        struct eviction_group *evg, *next;
-
-        HMAP_FOR_EACH_SAFE (evg, next, id_node,
-                            &table->eviction_groups_by_id) {
-            eviction_group_destroy(table, evg);
-        }
-        hmap_destroy(&table->eviction_groups_by_id);
-        heap_destroy(&table->eviction_groups_by_size);
-
-        free(table->eviction_fields);
-        table->eviction_fields = NULL;
-        table->n_eviction_fields = 0;
-    }
-}
-
 /* oftables support a choice of two policies when adding a rule would cause the
  * number of flows in the table to exceed the configured maximum number: either
  * they can refuse to add the new flow or they can evict some existing flow.
 /* oftables support a choice of two policies when adding a rule would cause the
  * number of flows in the table to exceed the configured maximum number: either
  * they can refuse to add the new flow or they can evict some existing flow.
@@ -7548,32 +7577,52 @@ oftable_disable_eviction(struct oftable *table)
  * on the values of the 'n_fields' fields specified in 'fields'.  (Specifying
  * 'n_fields' as 0 disables fairness.) */
 static void
  * on the values of the 'n_fields' fields specified in 'fields'.  (Specifying
  * 'n_fields' as 0 disables fairness.) */
 static void
-oftable_enable_eviction(struct oftable *table,
-                        const struct mf_subfield *fields, size_t n_fields)
+oftable_configure_eviction(struct oftable *table, unsigned int eviction,
+                           const struct mf_subfield *fields, size_t n_fields)
     OVS_REQUIRES(ofproto_mutex)
 {
     struct rule *rule;
 
     OVS_REQUIRES(ofproto_mutex)
 {
     struct rule *rule;
 
-    if (table->eviction_fields
+    if ((table->eviction != 0) == (eviction != 0)
         && n_fields == table->n_eviction_fields
         && (!n_fields
             || !memcmp(fields, table->eviction_fields,
                        n_fields * sizeof *fields))) {
         && n_fields == table->n_eviction_fields
         && (!n_fields
             || !memcmp(fields, table->eviction_fields,
                        n_fields * sizeof *fields))) {
-        /* No change. */
+        /* The set of eviction fields did not change.  If 'eviction' changed,
+         * it remains nonzero, so that we can just update table->eviction
+         * without fussing with the eviction groups. */
+        table->eviction = eviction;
         return;
     }
 
         return;
     }
 
-    oftable_disable_eviction(table);
-
-    table->n_eviction_fields = n_fields;
-    table->eviction_fields = xmemdup(fields, n_fields * sizeof *fields);
-
-    table->eviction_group_id_basis = random_uint32();
+    /* Destroy existing eviction groups, then destroy and recreate data
+     * structures to recover memory. */
+    struct eviction_group *evg, *next;
+    HMAP_FOR_EACH_SAFE (evg, next, id_node, &table->eviction_groups_by_id) {
+        eviction_group_destroy(table, evg);
+    }
+    hmap_destroy(&table->eviction_groups_by_id);
     hmap_init(&table->eviction_groups_by_id);
     hmap_init(&table->eviction_groups_by_id);
+    heap_destroy(&table->eviction_groups_by_size);
     heap_init(&table->eviction_groups_by_size);
 
     heap_init(&table->eviction_groups_by_size);
 
-    CLS_FOR_EACH (rule, cr, &table->cls) {
-        eviction_group_add_rule(rule);
+    /* Replace eviction groups by the new ones, if there is a change.  Free the
+     * old fields only after allocating the new ones, because 'fields ==
+     * table->eviction_fields' is possible. */
+    struct mf_subfield *old_fields = table->eviction_fields;
+    table->n_eviction_fields = n_fields;
+    table->eviction_fields = (fields
+                              ? xmemdup(fields, n_fields * sizeof *fields)
+                              : NULL);
+    free(old_fields);
+
+    /* Add the new eviction groups, if enabled. */
+    table->eviction = eviction;
+    if (table->eviction) {
+        table->eviction_group_id_basis = random_uint32();
+        CLS_FOR_EACH (rule, cr, &table->cls) {
+            eviction_group_add_rule(rule);
+        }
     }
 }
 
     }
 }
 
index 7dc1874..7504027 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -459,14 +459,18 @@ struct ofproto_table_settings {
     char *name;                 /* Name exported via OpenFlow or NULL. */
     unsigned int max_flows;     /* Maximum number of flows or UINT_MAX. */
 
     char *name;                 /* Name exported via OpenFlow or NULL. */
     unsigned int max_flows;     /* Maximum number of flows or UINT_MAX. */
 
-    /* These members determine the handling of an attempt to add a flow that
-     * would cause the table to have more than 'max_flows' flows.
+    /* These members, together with OpenFlow OFPT_TABLE_MOD, determine the
+     * handling of an attempt to add a flow that would cause the table to have
+     * more than 'max_flows' flows:
      *
      *
-     * If 'groups' is NULL, overflows will be rejected with an error.
+     *    - If 'enable_eviction' is false and OFPT_TABLE_MOD does not enable
+     *      eviction, overflows will be rejected with an error.
      *
      *
-     * If 'groups' is nonnull, an overflow will cause a flow to be removed.
-     * The flow to be removed is chosen to give fairness among groups
-     * distinguished by different values for the subfields within 'groups'. */
+     *    - If 'enable_eviction' is true or OFPT_TABLE_MOD enables eviction, an
+     *      overflow will cause a flow to be removed.  The flow to be removed
+     *      is chosen to give fairness among groups distinguished by different
+     *      values for the 'n_groups' subfields within 'groups'. */
+    bool enable_eviction;
     struct mf_subfield *groups;
     size_t n_groups;
 
     struct mf_subfield *groups;
     size_t n_groups;
 
index 39a5bbb..e08a201 100644 (file)
@@ -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
 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
+OFPT_TABLE_MOD (OF1.4) (xid=0x2): table_id=2, eviction=off
 ])
 AT_CLEANUP
 
 ])
 AT_CLEANUP
 
index c4556f7..54c077d 100644 (file)
@@ -1869,11 +1869,13 @@ OVS_VSWITCHD_START
 # Configure a maximum of 4 flows.
 AT_CHECK(
   [ovs-vsctl \
 # Configure a maximum of 4 flows.
 AT_CHECK(
   [ovs-vsctl \
-     -- --id=@t0 create Flow_Table name=evict flow-limit=4 overflow-policy=evict \
+     -- --id=@t0 create Flow_Table name=evict flow-limit=4 \
      -- set bridge br0 flow_tables:0=@t0 \
    | ${PERL} $srcdir/uuidfilt.pl],
   [0], [<0>
 ])
      -- set bridge br0 flow_tables:0=@t0 \
    | ${PERL} $srcdir/uuidfilt.pl],
   [0], [<0>
 ])
+# Use mod-table to turn on eviction just to demonstrate that it works.
+AT_CHECK([ovs-ofctl -O OpenFlow14 mod-table br0 0 evict])
 # Add 4 flows.
 for in_port in 4 3 2 1; do
     ovs-ofctl -O Openflow14 add-flow br0 importance=$((in_port + 30)),priority=$((in_port + 5)),hard_timeout=$((in_port + 500)),actions=drop
 # Add 4 flows.
 for in_port in 4 3 2 1; do
     ovs-ofctl -O Openflow14 add-flow br0 importance=$((in_port + 30)),priority=$((in_port + 5)),hard_timeout=$((in_port + 500)),actions=drop
@@ -1895,7 +1897,7 @@ AT_CHECK([ovs-ofctl -O Openflow14 dump-flows br0 | ofctl_strip | sort], [0], [dn
 OFPST_FLOW reply (OF1.4):
 ])
 # Disable the Eviction configuration.
 OFPST_FLOW reply (OF1.4):
 ])
 # Disable the Eviction configuration.
-AT_CHECK([ovs-vsctl set Flow_Table evict overflow-policy=refuse])
+AT_CHECK([ovs-ofctl -O OpenFlow14 mod-table br0 0 noevict])
 # Adding another flow will cause the system to give error for FULL TABLE.
 AT_CHECK([ovs-ofctl -O Openflow14 add-flow br0 hard_timeout=506,importance=36,priority=11,actions=drop],[1], [], [stderr])
 AT_CHECK([head -n 1 stderr | ofctl_strip], [0],
 # Adding another flow will cause the system to give error for FULL TABLE.
 AT_CHECK([ovs-ofctl -O Openflow14 add-flow br0 hard_timeout=506,importance=36,priority=11,actions=drop],[1], [], [stderr])
 AT_CHECK([head -n 1 stderr | ofctl_strip], [0],
index 395d851..058d205 100644 (file)
@@ -62,20 +62,13 @@ Prints to the console statistics for each of the flow tables used by
 \fBdump\-table\-features \fIswitch\fR
 Prints to the console features for each of the flow tables used by
 \fIswitch\fR.
 \fBdump\-table\-features \fIswitch\fR
 Prints to the console features for each of the flow tables used by
 \fIswitch\fR.
-.
-.IP "\fBmod\-table \fIswitch\fR \fItable_id\fR  \fIflow_miss_handling\fR"
-An OpenFlow 1.0 switch looks up each packet that arrives at the switch
-in table 0, then in table 1 if there is no match in table 0, then in
-table 2, and so on until the packet finds a match in some table.
-Finally, if no match was found, the switch sends the packet to the
-controller
-.IP
-OpenFlow 1.1 and later offer more flexibility.  This command
-configures the flow table miss handling configuration for table
-\fItable_id\fR in \fIswitch\fR.  \fItable_id\fR may be an OpenFlow
-table number between 0 and 254, inclusive, or the keyword \fBALL\fR to
-modify all tables.  \fIflow_miss_handling\fR may be any one of the
-following:
+.IP "\fBmod\-table \fIswitch\fR \fItable_id\fR \fIsetting\fR"
+This command configures flow table settings for OpenFlow table
+\fItable_id\fR within \fIswitch\fR.  The available settings depend on
+the OpenFlow version in use.  In OpenFlow 1.1 and 1.2 (which must be
+enabled with the \fB\-O\fR option) only, \fBmod\-table\fR configures
+behavior when no flow is found when a packet is looked up in a flow
+table.  The following \fIsetting\fR values are available:
 .RS
 .IP \fBdrop\fR
 Drop the packet.
 .RS
 .IP \fBdrop\fR
 Drop the packet.
@@ -87,6 +80,21 @@ tables other than the last one.)
 Send to controller.  (This is how an OpenFlow 1.0 switch always
 handles packets that do not match any flow in the last table.)
 .RE
 Send to controller.  (This is how an OpenFlow 1.0 switch always
 handles packets that do not match any flow in the last table.)
 .RE
+.IP
+In OpenFlow 1.4 and later (which must be enabled with the \fB\-O\fR
+option) only, \fBmod\-table\fR configures the behavior when a
+controller attempts to add a flow to a flow table that is full.  The
+following \fIsetting\fR values are available:
+.RS
+.IP \fBevict\fR
+Delete some existing flow from the flow table, according to the
+algorithm described for the \fBFlow_Table\fR table in
+\fBovs-vswitchd.conf.db\fR(5).
+.IP \fBnoevict\fR
+Refuse to add the new flow.  (Eviction might still be enabled through
+the \fBoverflow_policy\fR oclumn in the \fBFlow_Table\fR table
+documented in \fBovs-vswitchd.conf.db\fR(5).)
+.RE
 .
 .TP
 \fBdump\-ports \fIswitch\fR [\fInetdev\fR]
 .
 .TP
 \fBdump\-ports \fIswitch\fR [\fInetdev\fR]
index 6ef7070..f16cc25 100644 (file)
@@ -340,8 +340,11 @@ usage(void)
            "  dump-table-features SWITCH  print table features\n"
            "  mod-port SWITCH IFACE ACT   modify port behavior\n"
            "  mod-table SWITCH MOD        modify flow table behavior\n"
            "  dump-table-features SWITCH  print table features\n"
            "  mod-port SWITCH IFACE ACT   modify port behavior\n"
            "  mod-table SWITCH MOD        modify flow table behavior\n"
+           "      OF1.1/1.2 MOD: controller, continue, drop\n"
+           "      OF1.4+ MOD: evict, noevict\n"
            "  get-frags SWITCH            print fragment handling behavior\n"
            "  set-frags SWITCH FRAG_MODE  set fragment handling behavior\n"
            "  get-frags SWITCH            print fragment handling behavior\n"
            "  set-frags SWITCH FRAG_MODE  set fragment handling behavior\n"
+           "      FRAG_MODE: normal, drop, reassemble, nx-match\n"
            "  dump-ports SWITCH [PORT]    print port statistics\n"
            "  dump-ports-desc SWITCH [PORT]  print port descriptions\n"
            "  dump-flows SWITCH           print all flow entries\n"
            "  dump-ports SWITCH [PORT]    print port statistics\n"
            "  dump-ports-desc SWITCH [PORT]  print port descriptions\n"
            "  dump-flows SWITCH           print all flow entries\n"
@@ -1848,35 +1851,28 @@ found:
 static void
 ofctl_mod_table(struct ovs_cmdl_context *ctx)
 {
 static void
 ofctl_mod_table(struct ovs_cmdl_context *ctx)
 {
-    enum ofputil_protocol protocol, usable_protocols;
+    uint32_t usable_versions;
     struct ofputil_table_mod tm;
     struct vconn *vconn;
     char *error;
     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_protocols);
+    error = parse_ofp_table_mod(&tm, ctx->argv[2], ctx->argv[3],
+                                &usable_versions);
     if (error) {
         ovs_fatal(0, "%s", error);
     }
 
     if (error) {
         ovs_fatal(0, "%s", error);
     }
 
-    protocol = open_vconn(ctx->argv[1], &vconn);
-    if (!(protocol & usable_protocols)) {
-        for (i = 0; i < sizeof(enum ofputil_protocol) * CHAR_BIT; i++) {
-            enum ofputil_protocol f = 1 << i;
-            if (f != protocol
-                && f & usable_protocols
-                && try_set_protocol(vconn, f, &protocol)) {
-                protocol = f;
-                break;
-            }
-        }
-    }
-
-    if (!(protocol & usable_protocols)) {
-        char *usable_s = ofputil_protocols_to_string(usable_protocols);
-        ovs_fatal(0, "Switch does not support table mod message(%s)", usable_s);
+    uint32_t allowed_versions = get_allowed_ofp_versions();
+    if (!(allowed_versions & usable_versions)) {
+        struct ds versions = DS_EMPTY_INITIALIZER;
+        ofputil_format_version_bitmap_names(&versions, allowed_versions);
+        ovs_fatal(0, "table_mod '%s' requires one of the OpenFlow "
+                  "versions %s but none is enabled (use -O)",
+                  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));
     vconn_close(vconn);
 }
     transact_noreply(vconn, ofputil_encode_table_mod(&tm, protocol));
     vconn_close(vconn);
 }
index d48cf7f..dfb581e 100644 (file)
@@ -3603,6 +3603,7 @@ bridge_configure_tables(struct bridge *br)
         s.name = NULL;
         s.max_flows = UINT_MAX;
         s.groups = NULL;
         s.name = NULL;
         s.max_flows = UINT_MAX;
         s.groups = NULL;
+        s.enable_eviction = false;
         s.n_groups = 0;
         s.n_prefix_fields = 0;
         memset(s.prefix_fields, ~0, sizeof(s.prefix_fields));
         s.n_groups = 0;
         s.n_prefix_fields = 0;
         memset(s.prefix_fields, ~0, sizeof(s.prefix_fields));
@@ -3614,9 +3615,10 @@ bridge_configure_tables(struct bridge *br)
             if (cfg->n_flow_limit && *cfg->flow_limit < UINT_MAX) {
                 s.max_flows = *cfg->flow_limit;
             }
             if (cfg->n_flow_limit && *cfg->flow_limit < UINT_MAX) {
                 s.max_flows = *cfg->flow_limit;
             }
-            if (cfg->overflow_policy
-                && !strcmp(cfg->overflow_policy, "evict")) {
 
 
+            s.enable_eviction = (cfg->overflow_policy
+                                 && !strcmp(cfg->overflow_policy, "evict"));
+            if (cfg->n_groups) {
                 s.groups = xmalloc(cfg->n_groups * sizeof *s.groups);
                 for (k = 0; k < cfg->n_groups; k++) {
                     const char *string = cfg->groups[k];
                 s.groups = xmalloc(cfg->n_groups * sizeof *s.groups);
                 for (k = 0; k < cfg->n_groups; k++) {
                     const char *string = cfg->groups[k];
@@ -3636,6 +3638,7 @@ bridge_configure_tables(struct bridge *br)
                     }
                 }
             }
                     }
                 }
             }
+
             /* Prefix lookup fields. */
             s.n_prefix_fields = 0;
             for (k = 0; k < cfg->n_prefixes; k++) {
             /* Prefix lookup fields. */
             s.n_prefix_fields = 0;
             for (k = 0; k < cfg->n_prefixes; k++) {
index 9f108f0..483a9de 100644 (file)
       dump-tables</code>.  The name does not affect switch behavior.
     </column>
 
       dump-tables</code>.  The name does not affect switch behavior.
     </column>
 
-    <column name="flow_limit">
-      If set, limits the number of flows that may be added to the table.  Open
-      vSwitch may limit the number of flows in a table for other reasons,
-      e.g. due to hardware limitations or for resource availability or
-      performance reasons.
-    </column>
-
-    <column name="overflow_policy">
+    <group title="Eviction Policy">
       <p>
       <p>
-        Controls the switch's behavior when an OpenFlow flow table modification
-        request would add flows in excess of <ref column="flow_limit"/>.  The
-        supported values are:
+        Open vSwitch supports limiting the number of flows that may be
+        installed in a flow table, via the <ref column="flow_limit"/> column.
+        When adding a flow would exceed this limit, by default Open vSwitch
+        reports an error, but there are two ways to configure Open vSwitch to
+        instead delete (``evict'') a flow to make room for the new one:
       </p>
 
       </p>
 
-      <dl>
-        <dt><code>refuse</code></dt>
-        <dd>
-          Refuse to add the flow or flows.  This is also the default policy
-          when <ref column="overflow_policy"/> is unset.
-        </dd>
-
-        <dt><code>evict</code></dt>
-        <dd>
-          Delete the flow that will expire soonest.  See <ref column="groups"/>
-          for details.
-        </dd>
-      </dl>
-    </column>
+      <ul>
+        <li>
+          Set the <ref column="overflow_policy"/> column to <code>evict</code>.
+        </li>
 
 
-    <column name="groups">
-      <p>
-        When <ref column="overflow_policy"/> is <code>evict</code>, this
-        controls how flows are chosen for eviction when the flow table would
-        otherwise exceed <ref column="flow_limit"/> flows.  Its value is a set
-        of NXM fields or sub-fields, each of which takes one of the forms
-        <code><var>field</var>[]</code> or
-        <code><var>field</var>[<var>start</var>..<var>end</var>]</code>,
-        e.g. <code>NXM_OF_IN_PORT[]</code>.  Please see
-        <code>nicira-ext.h</code> for a complete list of NXM field names.
-      </p>
+        <li>
+          Send an OpenFlow 1.4+ ``table mod request'' to enable eviction for
+          the flow table (e.g. <code>ovs-ofctl -O OpenFlow14 mod-table br0 0
+          evict</code> to enable eviction on flow table 0 of bridge
+          <code>br0</code>).
+        </li>
+      </ul>
 
       <p>
         When a flow must be evicted due to overflow, the flow to evict is
 
       <p>
         When a flow must be evicted due to overflow, the flow to evict is
       </ol>
 
       <p>
       </ol>
 
       <p>
-        The eviction process only considers flows that have an idle timeout or
-        a hard timeout.  That is, eviction never deletes permanent flows.
+        The eviction process only considers flows that have an idle timeout
+        or a hard timeout.  That is, eviction never deletes permanent flows.
         (Permanent flows do count against <ref column="flow_limit"/>.)
       </p>
 
         (Permanent flows do count against <ref column="flow_limit"/>.)
       </p>
 
-      <p>
-        Open vSwitch ignores any invalid or unknown field specifications.
-      </p>
+      <column name="flow_limit">
+        If set, limits the number of flows that may be added to the table.
+        Open vSwitch may limit the number of flows in a table for other
+        reasons, e.g. due to hardware limitations or for resource availability
+        or performance reasons.
+      </column>
 
 
-      <p>
-        When <ref column="overflow_policy"/> is not <code>evict</code>, this
-        column has no effect.
-      </p>
-    </column>
+      <column name="overflow_policy">
+        <p>
+          Controls the switch's behavior when an OpenFlow flow table
+          modification request would add flows in excess of <ref
+          column="flow_limit"/>.  The supported values are:
+        </p>
 
 
-    <column name="prefixes">
-      <p>
-        This string set specifies which fields should be used for
-        address prefix tracking.  Prefix tracking allows the
-        classifier to skip rules with longer than necessary prefixes,
-        resulting in better wildcarding for datapath flows.
-      </p>
-      <p>
-        Prefix tracking may be beneficial when a flow table contains
-        matches on IP address fields with different prefix lengths.
-        For example, when a flow table contains IP address matches on
-        both full addresses and proper prefixes, the full address
-        matches will typically cause the datapath flow to un-wildcard
-        the whole address field (depending on flow entry priorities).
-        In this case each packet with a different address gets handed
-        to the userspace for flow processing and generates its own
-        datapath flow.  With prefix tracking enabled for the address
-        field in question packets with addresses matching shorter
-        prefixes would generate datapath flows where the irrelevant
-        address bits are wildcarded, allowing the same datapath flow
-        to handle all the packets within the prefix in question.  In
-        this case many userspace upcalls can be avoided and the
-        overall performance can be better.
-      </p>
-      <p>
-        This is a performance optimization only, so packets will
-        receive the same treatment with or without prefix tracking.
-      </p>
-      <p>
-        The supported fields are: <code>tun_id</code>,
-        <code>tun_src</code>, <code>tun_dst</code>,
-        <code>nw_src</code>, <code>nw_dst</code> (or aliases
-        <code>ip_src</code> and <code>ip_dst</code>),
-        <code>ipv6_src</code>, and <code>ipv6_dst</code>.  (Using this
-        feature for <code>tun_id</code> would only make sense if the
-        tunnel IDs have prefix structure similar to IP addresses.)
-      </p>
+        <dl>
+          <dt><code>refuse</code></dt>
+          <dd>
+            Refuse to add the flow or flows.  This is also the default policy
+            when <ref column="overflow_policy"/> is unset.
+          </dd>
 
 
-      <p>
-        By default, the <code>prefixes=ip_dst,ip_src</code> are used
-        on each flow table.  This instructs the flow classifier to
-        track the IP destination and source addresses used by the
-        rules in this specific flow table.
-      </p>
+          <dt><code>evict</code></dt>
+          <dd>
+            Delete a flow chosen according to the algorithm described above.
+          </dd>
+        </dl>
+      </column>
 
 
-      <p>
-        The keyword <code>none</code> is recognized as an explicit
-        override of the default values, causing no prefix fields to be
-        tracked.
-      </p>
+      <column name="groups">
+        <p>
+          When <ref column="overflow_policy"/> is <code>evict</code>, this
+          controls how flows are chosen for eviction when the flow table would
+          otherwise exceed <ref column="flow_limit"/> flows.  Its value is a
+          set of NXM fields or sub-fields, each of which takes one of the forms
+          <code><var>field</var>[]</code> or
+          <code><var>field</var>[<var>start</var>..<var>end</var>]</code>,
+          e.g. <code>NXM_OF_IN_PORT[]</code>.  Please see
+          <code>nicira-ext.h</code> for a complete list of NXM field names.
+        </p>
 
 
-      <p>
-        To set the prefix fields, the flow table record needs to
-        exist:
-      </p>
+        <p>
+          Open vSwitch ignores any invalid or unknown field specifications.
+        </p>
 
 
-      <dl>
-        <dt><code>ovs-vsctl set Bridge br0 flow_tables:0=@N1 -- --id=@N1 create Flow_Table name=table0</code></dt>
-        <dd>
-          Creates a flow table record for the OpenFlow table number 0.
-        </dd>
+        <p>
+          When eviction is not enabled, via <ref column="overflow_policy"/> or
+          an OpenFlow 1.4+ ``table mod,'' this column has no effect.
+        </p>
+      </column>
+    </group>
 
 
-        <dt><code>ovs-vsctl set Flow_Table table0 prefixes=ip_dst,ip_src</code></dt>
-        <dd>
-          Enables prefix tracking for IP source and destination
-          address fields.
-        </dd>
-      </dl>
+    <group title="Classifier Optimization">
+      <column name="prefixes">
+        <p>
+          This string set specifies which fields should be used for
+          address prefix tracking.  Prefix tracking allows the
+          classifier to skip rules with longer than necessary prefixes,
+          resulting in better wildcarding for datapath flows.
+        </p>
+        <p>
+          Prefix tracking may be beneficial when a flow table contains
+          matches on IP address fields with different prefix lengths.
+          For example, when a flow table contains IP address matches on
+          both full addresses and proper prefixes, the full address
+          matches will typically cause the datapath flow to un-wildcard
+          the whole address field (depending on flow entry priorities).
+          In this case each packet with a different address gets handed
+          to the userspace for flow processing and generates its own
+          datapath flow.  With prefix tracking enabled for the address
+          field in question packets with addresses matching shorter
+          prefixes would generate datapath flows where the irrelevant
+          address bits are wildcarded, allowing the same datapath flow
+          to handle all the packets within the prefix in question.  In
+          this case many userspace upcalls can be avoided and the
+          overall performance can be better.
+        </p>
+        <p>
+          This is a performance optimization only, so packets will
+          receive the same treatment with or without prefix tracking.
+        </p>
+        <p>
+          The supported fields are: <code>tun_id</code>,
+          <code>tun_src</code>, <code>tun_dst</code>,
+          <code>nw_src</code>, <code>nw_dst</code> (or aliases
+          <code>ip_src</code> and <code>ip_dst</code>),
+          <code>ipv6_src</code>, and <code>ipv6_dst</code>.  (Using this
+          feature for <code>tun_id</code> would only make sense if the
+          tunnel IDs have prefix structure similar to IP addresses.)
+        </p>
 
 
-      <p>
-        There is a maximum number of fields that can be enabled for any
-        one flow table.  Currently this limit is 3.
-      </p>
-    </column>
+        <p>
+          By default, the <code>prefixes=ip_dst,ip_src</code> are used
+          on each flow table.  This instructs the flow classifier to
+          track the IP destination and source addresses used by the
+          rules in this specific flow table.
+        </p>
+
+        <p>
+          The keyword <code>none</code> is recognized as an explicit
+          override of the default values, causing no prefix fields to be
+          tracked.
+        </p>
+
+        <p>
+          To set the prefix fields, the flow table record needs to
+          exist:
+        </p>
+
+        <dl>
+          <dt><code>ovs-vsctl set Bridge br0 flow_tables:0=@N1 -- --id=@N1 create Flow_Table name=table0</code></dt>
+          <dd>
+            Creates a flow table record for the OpenFlow table number 0.
+          </dd>
+
+          <dt><code>ovs-vsctl set Flow_Table table0 prefixes=ip_dst,ip_src</code></dt>
+          <dd>
+            Enables prefix tracking for IP source and destination
+            address fields.
+          </dd>
+        </dl>
+
+        <p>
+          There is a maximum number of fields that can be enabled for any
+          one flow table.  Currently this limit is 3.
+        </p>
+      </column>
+    </group>
 
     <group title="Common Columns">
       The overall purpose of these columns is described under <code>Common
 
     <group title="Common Columns">
       The overall purpose of these columns is described under <code>Common