From: Saloni Jain <saloni.j...@tcs.com> This commit enables the eviction mechanism on the basis of importance as per the openflow specification 1.4.
ovs-ofctl -O OpenFlow14 mod-table <switch> <table> evict -Enable eviction on <table> of <switch>. Eviction adds a mechanism enabling the switch to automatically eliminate entries of lower importance to make space for newer entries.If want to enable eviction on all tables, user can set the <table> as 'ALL'. ovs-ofctl -O OpenFlow14 mod-table <switch> <table> noevict -Disable eviction on <table> of <switch>. ovs-ofctl -O OpenFlow14 dump-tables-desc <switch> -This command provides a way to list the current configuration (eviction for importance) of the tables on a <switch>, which is set using the mod-table command. Signed-off-by: Saloni Jain <saloni.j...@tcs.com> Signed-off-by: Hiteshi Madan <hiteshi.ma...@tcs.com> --- DESIGN.md | 19 ++-- NEWS | 2 + include/openflow/openflow-1.4.h | 10 ++ lib/learning-switch.c | 2 + lib/ofp-msgs.h | 10 ++ lib/ofp-parse.c | 4 + lib/ofp-print.c | 63 ++++++++++- lib/ofp-util.c | 234 ++++++++++++++++++++++++++++++++++++++- lib/ofp-util.h | 32 ++++++ lib/rconn.c | 2 + ofproto/ofproto-provider.h | 2 + ofproto/ofproto.c | 158 ++++++++++++++++++++++++-- tests/ofp-print.at | 2 +- tests/ofproto.at | 105 ++++++++++++++++++ utilities/ovs-ofctl.8.in | 25 ++++- utilities/ovs-ofctl.c | 21 ++++ 16 files changed, 669 insertions(+), 22 deletions(-) diff --git a/DESIGN.md b/DESIGN.md index bd0ed27..9d961e7 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -275,13 +275,18 @@ The table for 1.3 is the same as the one shown above for 1.2. OpenFlow 1.4 ------------- - -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.) +----------- +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.) + + - Eviction Mechanism to automatically delete entries of lower + importance to make space for newer entries. OFPT_PACKET_IN ============== diff --git a/NEWS b/NEWS index ea8c6c0..6e7e450 100644 --- a/NEWS +++ b/NEWS @@ -28,6 +28,8 @@ Post-v2.3.0 is executed last, and only if the action set has no "output" or "group" action. * OpenFlow 1.4+ flow "importance" is now maintained in the flow table. + * OpenFlow 1.4+ table-mod flow eviction on basis of importance and + table-description requests are now supported. - ovs-pki: Changed message digest algorithm from MD5 to SHA-1 because MD5 is no longer secure and some operating systems have started to disable it in OpenSSL. diff --git a/include/openflow/openflow-1.4.h b/include/openflow/openflow-1.4.h index 7631e47..c1ac227 100644 --- a/include/openflow/openflow-1.4.h +++ b/include/openflow/openflow-1.4.h @@ -155,6 +155,16 @@ struct ofp14_table_mod { }; OFP_ASSERT(sizeof(struct ofp14_table_mod) == 8); +/* Body of reply to OFPMP_TABLE_DESC request. */ +struct ofp14_table_desc { + ovs_be16 length; /* Length is padded to 64 bits. */ + uint8_t table_id; /* Identifier of table. Lower numbered tables are consulted first. */ + uint8_t pad[1]; /* Align to 32-bits. */ + ovs_be32 config; /* Bitmap of OFPTC_* values. */ + /* Followed by 0 or more OFPTMPT14_* properties. */ +}; +OFP_ASSERT(sizeof(struct ofp14_table_desc) == 8); + /* ## ---------------- ## */ /* ## ofp14_port_stats ## */ diff --git a/lib/learning-switch.c b/lib/learning-switch.c index af72ae1..63c5377 100644 --- a/lib/learning-switch.c +++ b/lib/learning-switch.c @@ -439,6 +439,8 @@ lswitch_process_packet(struct lswitch *sw, const struct ofpbuf *msg) case OFPTYPE_METER_FEATURES_STATS_REPLY: case OFPTYPE_TABLE_FEATURES_STATS_REQUEST: case OFPTYPE_TABLE_FEATURES_STATS_REPLY: + case OFPTYPE_TABLE_DESC_REQUEST: + case OFPTYPE_TABLE_DESC_REPLY: case OFPTYPE_BUNDLE_CONTROL: case OFPTYPE_BUNDLE_ADD_MESSAGE: default: diff --git a/lib/ofp-msgs.h b/lib/ofp-msgs.h index ad6dc6f..24aaa24 100644 --- a/lib/ofp-msgs.h +++ b/lib/ofp-msgs.h @@ -369,6 +369,12 @@ enum ofpraw { /* OFPST 1.3+ (12): struct ofp13_table_features, uint8_t[8][]. */ OFPRAW_OFPST13_TABLE_FEATURES_REPLY, + /* OFPST 1.4+ (15): void. */ + OFPRAW_OFPST14_TABLE_DESC_REQUEST, + + /* OFPST 1.4+ (15): struct ofp14_table_desc, uint8_t[8][]. */ + OFPRAW_OFPST14_TABLE_DESC_REPLY, + /* OFPST 1.0-1.4 (13): void. */ OFPRAW_OFPST10_PORT_DESC_REQUEST, /* OFPST 1.5+ (13): ovs_be32. */ @@ -602,6 +608,10 @@ enum ofptype { OFPTYPE_TABLE_FEATURES_STATS_REPLY, /* OFPRAW_OFPST13_TABLE_FEATURES_REPLY. */ + OFPTYPE_TABLE_DESC_REQUEST, /* OFPRAW_OFPST14_TABLE_DESC_REQUEST. */ + + OFPTYPE_TABLE_DESC_REPLY, /* OFPRAW_OFPST14_TABLE_DESC_REPLY. */ + OFPTYPE_PORT_DESC_STATS_REQUEST, /* OFPRAW_OFPST10_PORT_DESC_REQUEST. * OFPRAW_OFPST15_PORT_DESC_REQUEST. */ diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c index 16c7cdc..2507e2a 100644 --- a/lib/ofp-parse.c +++ b/lib/ofp-parse.c @@ -867,6 +867,10 @@ parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id, tm->miss_config = OFPUTIL_TABLE_MISS_CONTINUE; } else if (strcmp(flow_miss_handling, "drop") == 0) { tm->miss_config = OFPUTIL_TABLE_MISS_DROP; + } else if (strcmp(flow_miss_handling, "evict") == 0) { + tm->config = OFPUTIL_TABLE_CONFIG_EVICTION; + } else if (strcmp(flow_miss_handling, "noevict") == 0) { + tm->config = OFPUTIL_TABLE_CONFIG_NO_EVICTION; } else { return xasprintf("invalid flow_miss_handling %s", flow_miss_handling); } diff --git a/lib/ofp-print.c b/lib/ofp-print.c index c446770..3bb176a 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -979,6 +979,23 @@ ofp_print_table_miss_config(struct ds *string, enum ofputil_table_miss miss) } } +/* This Function will print the config properties of table set through + * mod-table command */ +static void +ofp_print_table_config(struct ds *string, enum ofputil_table_config config, + uint32_t flags) +{ + if (config & OFPUTIL_TABLE_CONFIG_EVICTION) { + ds_put_cstr(string, "Eviction"); + if (flags & OFPTMPEF14_IMPORTANCE) { + ds_put_cstr(string, " flag: Importance\n"); + } + } else if ((config & OFPUTIL_TABLE_CONFIG_NO_EVICTION) || + (config & OFPUTIL_TABLE_CONFIG_DEFAULT)) { + ds_put_cstr(string, "No Eviction\n"); + } +} + static void ofp_print_table_mod(struct ds *string, const struct ofp_header *oh) { @@ -998,11 +1015,26 @@ ofp_print_table_mod(struct ds *string, const struct ofp_header *oh) } if (pm.miss_config != OFPUTIL_TABLE_MISS_DEFAULT) { - ds_put_cstr(string, ", flow_miss_config="); - ofp_print_table_miss_config(string, pm.miss_config); + ds_put_cstr(string, ", flow_miss_config="); + ofp_print_table_miss_config(string, pm.miss_config); + } + if (oh->version >= OFP14_VERSION) { + ds_put_cstr(string, " config= "); + ofp_print_table_config(string, pm.config, pm.eviction_flags); } } +/* This function will print the Table description properties */ +static void +ofp_print_table_desc(struct ds *string, const struct ofputil_table_desc *td) +{ + ds_put_format(string, "\n table %"PRIu8, td->table_id); + ds_put_cstr(string, ":\n"); + + ds_put_cstr(string, " config= "); + ofp_print_table_config(string, td->config, td->eviction_flags); +} + static void ofp_print_queue_get_config_request(struct ds *string, const struct ofp_header *oh) @@ -2554,6 +2586,28 @@ ofp_print_table_features_reply(struct ds *s, const struct ofp_header *oh) } } +static void +ofp_print_table_desc_reply(struct ds *s, const struct ofp_header *oh) +{ + struct ofpbuf b; + + ofpbuf_use_const(&b, oh, ntohs(oh->length)); + + for (;;) { + struct ofputil_table_desc td; + int retval; + + retval = ofputil_decode_table_desc(&b, &td); + if (retval) { + if (retval != EOF) { + ofp_print_error(s, retval); + } + return; + } + ofp_print_table_desc(s, &td); + } +} + static const char * bundle_flags_to_name(uint32_t bit) { @@ -2682,6 +2736,11 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw, ofp_print_table_features_reply(string, oh); break; + case OFPTYPE_TABLE_DESC_REQUEST: + case OFPTYPE_TABLE_DESC_REPLY: + ofp_print_table_desc_reply(string, oh); + break; + case OFPTYPE_HELLO: ofp_print_hello(string, oh); break; diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 839f56f..803bf78 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -54,6 +54,9 @@ 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_config ofputil_table_utilconfig_from_config( + ovs_be32 config_, enum ofp_version version); + struct ofp_prop_header { ovs_be16 type; ovs_be16 len; @@ -4903,7 +4906,192 @@ ofputil_append_table_features_reply(const struct ofputil_table_features *tf, ofpmp_postappend(replies, start_ofs); } +/* Decodes the OpenFlow "table desc" message in '*msg' into an abstract form in + * '*td'. Returns 0 if successful, otherwise an OFPERR_* value. */ +int +ofputil_decode_table_desc(struct ofpbuf *msg, + struct ofputil_table_desc *td) +{ + struct ofp14_table_desc *otd; + size_t length; + size_t property_length; + + memset(td, 0, sizeof *td); + + if (!msg->frame) { + ofpraw_pull_assert(msg); + } + + if (!ofpbuf_size(msg)) { + return EOF; + } + + otd = ofpbuf_try_pull(msg, sizeof *otd); + + if (!otd) { + VLOG_WARN_RL(&bad_ofmsg_rl, "OFP14_TABLE_DESC reply has %"PRIu32" " + "leftover bytes at end", ofpbuf_size(msg)); + return OFPERR_OFPBRC_BAD_LEN; + } + + td->table_id = otd->table_id; + td->config = ntohl(otd->config); + length = ntohs(otd->length); + + if (length < sizeof *otd || length - sizeof *otd > ofpbuf_size(msg)) { + VLOG_WARN_RL(&bad_ofmsg_rl, "OFP14_TABLE_DESC reply claims invalid " + "length %"PRIuSIZE, length); + return OFPERR_OFPBRC_BAD_LEN; + } + + property_length = length - sizeof *otd; + + while (property_length > 0) { + size_t prop_len; + + if (td->config & OFPUTIL_TABLE_CONFIG_EVICTION) { + struct ofp14_table_mod_prop_eviction *ote; + + ote = (property_length >= sizeof *ote + ? ofpbuf_try_pull(msg, sizeof *ote) + : NULL); + + if (!ote) { + VLOG_WARN_RL(&bad_ofmsg_rl, "OFP14_TABLE_DESC property end with" + "%"PRIuSIZE" leftover bytes", property_length); + return OFPERR_OFPBRC_BAD_LEN; + } + + prop_len = ntohs(ote->length); + if (prop_len != sizeof *ote) { + VLOG_WARN_RL(&bad_ofmsg_rl, "Table property length " + "%"PRIuSIZE" is not valid", prop_len); + return OFPERR_OFPBRC_BAD_LEN; + } + + td->eviction_flags = ntohl(ote->flags); + property_length = property_length - prop_len; + } + } + + return 0; +} + +/* Encodes and returns a request to obtain the table desc feature of a switch. + * The message is encoded for OpenFlow version 'ofp_version'.*/ +struct ofpbuf * +ofputil_encode_table_desc_request(enum ofp_version ofp_version) +{ + struct ofpbuf *request = NULL; + + if (ofp_version >= OFP14_VERSION) { + request = ofpraw_alloc(OFPRAW_OFPST14_TABLE_DESC_REQUEST, + ofp_version, 0); + } else { + ovs_fatal(0, "dump-table-desc needs OpenFlow 1.4 or later " + "(\'-O OpenFlow14\')"); + } + + return request; +} + +/* Function to append Table desc information in a reply list. */ +void +ofputil_append_table_desc_reply(const struct ofputil_table_desc *td, + struct list *replies) +{ + struct ofpbuf *reply = ofpbuf_from_list(list_back(replies)); + size_t start_otd; + struct ofp14_table_desc *otd; + + start_otd = ofpbuf_size(reply); + ofpbuf_put_zeros(reply, sizeof *otd); + if (td->config & OFPUTIL_TABLE_CONFIG_EVICTION) { + struct ofp14_table_mod_prop_eviction *ote; + size_t start_ote; + + start_ote = ofpbuf_size(reply); + ofpbuf_put_zeros(reply, sizeof *ote); + ote = ofpbuf_at_assert(reply, start_ote, sizeof *ote); + ote->type = htons(OFPTMPT14_EVICTION); + ote->length = htons(ofpbuf_size(reply) - start_ote); + ote->flags = htonl(td->eviction_flags); + } + + otd = ofpbuf_at_assert(reply, start_otd, sizeof *otd); + otd->length = htons(ofpbuf_size(reply) - start_otd); + otd->table_id = td->table_id; + otd->config = htonl(td->config); + ofpmp_postappend(replies, start_otd); +} + +/* Given 'config', taken from an OpenFlow 'version' message that specifies + * table configuration (a table mod message), + * returns the table configuration that it specifies. */ +static enum ofputil_table_config +ofputil_table_utilconfig_from_config(ovs_be32 config_, enum ofp_version version) +{ + uint32_t config = ntohl(config_); + + if (version >= OFP14_VERSION) { + switch (config) { + case OFPTC14_EVICTION: + return OFPUTIL_TABLE_CONFIG_EVICTION; + + case OFPTC11_TABLE_MISS_MASK: + return OFPUTIL_TABLE_CONFIG_NO_EVICTION; + + default: + return OFPUTIL_TABLE_CONFIG_DEFAULT; + } + } else { + return OFPUTIL_TABLE_CONFIG_DEFAULT; + } +} + +/* Given a table configuration, returns the corresponding OpenFlow table + * configuration for use in an OpenFlow message of the given 'version'. + Point for using Miss Mask as return value of No Eviction + * OFPTC11_TABLE_MISS_MASK is using same value as OFPTC14_DEPRECATED_MASK + * as defined in Openflow 1.4. In openflow-common.h, OFPTC14_DEPRECATED_MASK + * is not defined. Hence using MISS_MASK value*/ +ovs_be32 +ofputil_table_utilconfig_to_config(enum ofputil_table_config config, + enum ofp_version version) +{ + if (version >= OFP14_VERSION) { + switch (config) { + case OFPUTIL_TABLE_CONFIG_EVICTION: + return htonl(OFPTC14_EVICTION); + + case OFPUTIL_TABLE_CONFIG_NO_EVICTION: + return htonl(OFPTC11_TABLE_MISS_MASK); + + case OFPUTIL_TABLE_CONFIG_DEFAULT: + return htonl(0); + + default: + OVS_NOT_REACHED(); + } + } else { + return htonl(0); + } +} + /* 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 = ofpbuf_data(property); + + if (ofpbuf_size(property) != sizeof *ote) { + return OFPERR_OFPBRC_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), @@ -4968,6 +5156,7 @@ ofputil_decode_table_mod(const struct ofp_header *oh, enum ofpraw raw; struct ofpbuf b; + memset(pm, 0, sizeof *pm); ofpbuf_use_const(&b, oh, ntohs(oh->length)); raw = ofpraw_pull_assert(&b); @@ -4983,8 +5172,34 @@ ofputil_decode_table_mod(const struct ofp_header *oh, pm->table_id = otm->table_id; pm->miss_config = ofputil_table_miss_from_config(otm->config, oh->version); + pm->config = ofputil_table_utilconfig_from_config(otm->config, + oh->version); /* We do not understand any properties yet, so we do not bother * parsing them. */ + while (ofpbuf_size(&b) > 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; } @@ -5023,12 +5238,25 @@ ofputil_encode_table_mod(const struct ofputil_table_mod *pm, case OFP14_VERSION: case OFP15_VERSION: { struct ofp14_table_mod *otm; + struct ofp14_table_mod_prop_eviction *ote; + ovs_be32 miss_config, config; 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, + otm = ofpbuf_put_zeros(b, sizeof *otm); + otm->table_id = pm->table_id; + miss_config = ofputil_table_miss_to_config(pm->miss_config, ofp_version); + config = ofputil_table_utilconfig_to_config(pm->config, + ofp_version); + otm->config = config | miss_config; + + if (pm->config & OFPUTIL_TABLE_CONFIG_EVICTION) { + ote = ofpbuf_put_zeros(b, sizeof *ote); + ote->type = htons(OFPTMPT14_EVICTION); + ote->length = htons(sizeof *ote); + ote->flags = htonl(OFPTMPEF14_IMPORTANCE); + } + break; } default: diff --git a/lib/ofp-util.h b/lib/ofp-util.h index 648d228..281d8f1 100644 --- a/lib/ofp-util.h +++ b/lib/ofp-util.h @@ -607,13 +607,33 @@ enum ofputil_table_miss { OFPUTIL_TABLE_MISS_DROP, /* Drop the packet. */ }; +/* Abstract version of OFPTC14_TABLE_CONFIG*.*/ +enum ofputil_table_config { + OFPUTIL_TABLE_CONFIG_DEFAULT = 1 << 0, /* Table Config default behaviour*/ + OFPUTIL_TABLE_CONFIG_NO_EVICTION = 1 << 1, /* No Eviction for Table*/ + OFPUTIL_TABLE_CONFIG_EVICTION = 1 << 2, /* Eviction for importance Configured */ +}; + ovs_be32 ofputil_table_miss_to_config(enum ofputil_table_miss, enum ofp_version); +ovs_be32 ofputil_table_utilconfig_to_config(enum ofputil_table_config config, + enum ofp_version version); + + /* Abstract ofp_table_mod. */ struct ofputil_table_mod { uint8_t table_id; /* ID of the table, 0xff indicates all tables. */ + enum ofputil_table_config config; enum ofputil_table_miss miss_config; + uint32_t eviction_flags; +}; + +/* Abstract ofp14_table_desc. */ +struct ofputil_table_desc { + uint8_t table_id; /* ID of the table. */ + enum ofputil_table_config config; + uint32_t eviction_flags; }; enum ofperr ofputil_decode_table_mod(const struct ofp_header *, @@ -682,11 +702,20 @@ struct ofputil_table_features { int ofputil_decode_table_features(struct ofpbuf *, struct ofputil_table_features *, bool loose); + +int ofputil_decode_table_desc(struct ofpbuf *, + struct ofputil_table_desc *); + struct ofpbuf *ofputil_encode_table_features_request(enum ofp_version); +struct ofpbuf *ofputil_encode_table_desc_request(enum ofp_version); + void ofputil_append_table_features_reply( const struct ofputil_table_features *tf, struct list *replies); +void ofputil_append_table_desc_reply( + const struct ofputil_table_desc *td, struct list *replies); + /* Meter band configuration for all supported band types. */ struct ofputil_meter_band { uint16_t type; @@ -813,6 +842,9 @@ struct ofputil_table_stats { }; struct ofpbuf *ofputil_encode_table_stats_reply(const struct ofp_header *rq); + +struct ofpbuf *ofputil_encode_table_desc_reply(const struct ofp_header *rq); + void ofputil_append_table_stats_reply(struct ofpbuf *reply, const struct ofputil_table_stats *, const struct ofputil_table_features *); diff --git a/lib/rconn.c b/lib/rconn.c index 5c28806..46f08ff 100644 --- a/lib/rconn.c +++ b/lib/rconn.c @@ -1389,6 +1389,8 @@ is_admitted_msg(const struct ofpbuf *b) case OFPTYPE_GROUP_FEATURES_STATS_REPLY: case OFPTYPE_TABLE_FEATURES_STATS_REQUEST: case OFPTYPE_TABLE_FEATURES_STATS_REPLY: + case OFPTYPE_TABLE_DESC_REQUEST: + case OFPTYPE_TABLE_DESC_REPLY: case OFPTYPE_BUNDLE_CONTROL: case OFPTYPE_BUNDLE_ADD_MESSAGE: return false; diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h index 0911333..1b5e317 100644 --- a/ofproto/ofproto-provider.h +++ b/ofproto/ofproto-provider.h @@ -243,6 +243,8 @@ struct oftable { /* Table configuration. */ ATOMIC(enum ofputil_table_miss) miss_config; + ATOMIC(enum ofputil_table_config) config; + uint32_t eviction_flags; /* Flags of value OFPTMPEF14_* */ atomic_ulong n_matched; atomic_ulong n_missed; diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 1485fb4..76a2292 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -69,6 +69,8 @@ COVERAGE_DEFINE(ofproto_recv_openflow); COVERAGE_DEFINE(ofproto_reinit_ports); COVERAGE_DEFINE(ofproto_update_port); +# define CLEAR_EVICTION_BIT 0x8 + /* Default fields to use for prefix tries in each flow table, unless something * else is configured. */ const enum mf_field_id default_prefix_fields[2] = @@ -88,6 +90,8 @@ static void oftable_enable_eviction(struct oftable *, const struct mf_subfield *fields, size_t n_fields); +static void enable_eviction_for_importance(struct oftable *); + static void oftable_remove_rule(struct rule *rule) OVS_REQUIRES(ofproto_mutex); /* A set of rules within a single OpenFlow table (oftable) that have the same @@ -1302,7 +1306,7 @@ ofproto_configure_table(struct ofproto *ofproto, int table_id, return; } - if (s->groups) { + if (s->groups || (table->config & OFPUTIL_TABLE_CONFIG_EVICTION)) { oftable_enable_eviction(table, s->groups, s->n_groups); } else { oftable_disable_eviction(table); @@ -3340,6 +3344,48 @@ handle_table_features_request(struct ofconn *ofconn, return 0; } +/* This function queries the database for dumping table-desc*/ +static void +query_tables_desc(struct ofproto *ofproto, + struct ofputil_table_desc **descp) +{ + struct ofputil_table_desc *table_desc; + size_t i; + table_desc = *descp = xcalloc(ofproto->n_tables, sizeof *table_desc); + for (i = 0; i < ofproto->n_tables; i++) { + struct ofputil_table_desc *td = &table_desc[i]; + td->table_id = i; + atomic_read_relaxed(&ofproto->tables[i].config, &td->config); + if (td->config & OFPUTIL_TABLE_CONFIG_EVICTION) { + td->eviction_flags = ofproto->tables[i].eviction_flags; + } + } +} + +/* Function to handle dump-table-desc request */ +static enum ofperr +handle_table_desc_request(struct ofconn *ofconn, + const struct ofp_header *request) +{ + struct ofproto *ofproto = ofconn_get_ofproto(ofconn); + struct ofputil_table_desc *table_desc; + struct list replies; + size_t i; + + query_tables_desc(ofproto, &table_desc); + + ofpmp_init(&replies, request); + for (i = 0; i < ofproto->n_tables; i++) { + if (!(ofproto->tables[i].flags & OFTABLE_HIDDEN)) { + ofputil_append_table_desc_reply(&table_desc[i], &replies); + } + } + ofconn_send_replies(ofconn, &replies); + free(table_desc); + + return 0; +} + static void append_port_stat(struct ofport *port, struct list *replies) { @@ -6078,26 +6124,89 @@ ofproto_table_get_miss_config(const struct ofproto *ofproto, uint8_t table_id) return value; } +/* This function configures or deconfigures eviction for all tables or + * for given table-id + * CLEAR_EVICTION_BIT - BIT-MASK for clearing the eviction/noeviction + * configuration and will have significance for other table configuartion + * properties */ +static void configure_eviction(const struct ofputil_table_mod *tm, + struct ofproto *ofproto) +{ + enum ofputil_table_config table_config; + if (tm->table_id == OFPTT_ALL) { + int i; + for (i = 0; i < ofproto->n_tables; i++) { + atomic_read_relaxed(&ofproto->tables[i].config, &table_config); + table_config = table_config & CLEAR_EVICTION_BIT; + table_config = table_config | tm->config; + atomic_store_relaxed(&ofproto->tables[i].config, + table_config); + ofproto->tables[i].eviction_flags = tm->eviction_flags; + if (tm->config & OFPUTIL_TABLE_CONFIG_EVICTION) { + enable_eviction_for_importance(&ofproto->tables[i]); + } else if (tm->config & OFPUTIL_TABLE_CONFIG_NO_EVICTION) { + oftable_disable_eviction(&ofproto->tables[i]); + } + } + } else { + atomic_read_relaxed(&ofproto->tables[tm->table_id].config, + &table_config); + table_config = table_config & CLEAR_EVICTION_BIT; + table_config = table_config | tm->config; + atomic_store_relaxed(&ofproto->tables[tm->table_id].config, + table_config); + ofproto->tables[tm->table_id].eviction_flags = tm->eviction_flags; + if (tm->config & OFPUTIL_TABLE_CONFIG_EVICTION) { + enable_eviction_for_importance(&ofproto->tables[tm->table_id]); + } else if (tm->config & OFPUTIL_TABLE_CONFIG_NO_EVICTION) { + oftable_disable_eviction(&ofproto->tables[tm->table_id]); + } + } +} + +/* OFPUTIL_TABLE_CONFIG_NO_EVICTION and OFPUTIL_TABLE_CONFIG_DEFAULT + * these two values are considered different because we have to understand in switch + * that user wants to disable Eviction by explicitly giving Noevict or no value is passed + * at all which is default case */ static enum ofperr table_mod(struct ofproto *ofproto, const struct ofputil_table_mod *tm) { if (!check_table_id(ofproto, tm->table_id)) { return OFPERR_OFPTMFC_BAD_TABLE; - } else if (tm->miss_config != OFPUTIL_TABLE_MISS_DEFAULT) { + } + + if ((tm->config & OFPUTIL_TABLE_CONFIG_EVICTION) || + (tm->config & OFPUTIL_TABLE_CONFIG_NO_EVICTION)) { + configure_eviction(tm, ofproto); + } + + 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); + } + } else { + atomic_store_relaxed(&ofproto->tables[tm->table_id].miss_config, + tm->miss_config); } - } else { - atomic_store_relaxed(&ofproto->tables[tm->table_id].miss_config, - tm->miss_config); - } } return 0; } +/* This function is only used when eviction is performed on the + basis of importance. This function enables eviction on a particular table given */ +static void +enable_eviction_for_importance(struct oftable *tables) +{ + struct mf_subfield *field = NULL; + size_t n_field = 0; + + field = xmalloc(n_field* sizeof *field); + oftable_enable_eviction(tables, field, n_field); +} + static enum ofperr handle_table_mod(struct ofconn *ofconn, const struct ofp_header *oh) { @@ -6285,6 +6394,9 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg) case OFPTYPE_TABLE_FEATURES_STATS_REQUEST: return handle_table_features_request(ofconn, oh); + case OFPTYPE_TABLE_DESC_REQUEST: + return handle_table_desc_request(ofconn, oh); + case OFPTYPE_PORT_STATS_REQUEST: return handle_port_stats_request(ofconn, oh); @@ -6350,6 +6462,7 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg) case OFPTYPE_METER_CONFIG_STATS_REPLY: case OFPTYPE_METER_FEATURES_STATS_REPLY: case OFPTYPE_TABLE_FEATURES_STATS_REPLY: + case OFPTYPE_TABLE_DESC_REPLY: case OFPTYPE_ROLE_STATUS: default: if (ofpmsg_is_stat_request(oh)) { @@ -6603,7 +6716,7 @@ eviction_group_find(struct oftable *table, uint32_t id) /* Returns an eviction priority for 'rule'. The return value should be * interpreted so that higher priorities make a rule more attractive candidates * for eviction. - * Called only if have a timeout. */ + * Called if have a timeout and for eviction on basis of importance */ static uint32_t rule_eviction_priority(struct ofproto *ofproto, struct rule *rule) OVS_REQUIRES(ofproto_mutex) @@ -6611,12 +6724,30 @@ rule_eviction_priority(struct ofproto *ofproto, struct rule *rule) long long int expiration = LLONG_MAX; long long int modified; uint32_t expiration_offset; + struct oftable *table = &ofproto->tables[rule->table_id]; + bool has_eviction_by_importance; /* 'modified' needs protection even when we hold 'ofproto_mutex'. */ ovs_mutex_lock(&rule->mutex); modified = rule->modified; ovs_mutex_unlock(&rule->mutex); + /* For eviction on basis of importance only those entries will be added + * which have eviction fields set, importance parameter given in flow + * entry and table config parameter for eviction is set. */ + has_eviction_by_importance = (table->eviction_fields && + rule->importance && + (table->config & + OFPUTIL_TABLE_CONFIG_EVICTION)); + + /* As per the openflow specification 1.4, flow entries with lesser importance + * will be evicted before flow entries with higher importance.So invert the + * value of importance for calculating the rule eviction priority as higher + * priorities make a rule more attractive candidates for eviction. */ + if (has_eviction_by_importance) { + return UINT32_MAX - (rule->importance); + } + if (rule->hard_timeout) { expiration = modified + rule->hard_timeout * 1000; } @@ -6658,12 +6789,22 @@ eviction_group_add_rule(struct rule *rule) struct ofproto *ofproto = rule->ofproto; struct oftable *table = &ofproto->tables[rule->table_id]; bool has_timeout; + bool has_eviction_by_importance; /* Timeouts may be modified only when holding 'ofproto_mutex'. We have it * so no additional protection is needed. */ has_timeout = rule->hard_timeout || rule->idle_timeout; - if (table->eviction_fields && has_timeout) { + /* For eviction on basis of importance only those entries will be added + * which have eviction fields set, importance parameter is not zero in flow + * entry and table config parameter for eviction is set. All rules are added + * in a single group when eviction is done on basis of importance. */ + has_eviction_by_importance = (table->eviction_fields && + rule->importance && + (table->config & + OFPUTIL_TABLE_CONFIG_EVICTION)); + + if ((table->eviction_fields && has_timeout) || has_eviction_by_importance) { struct eviction_group *evg; evg = eviction_group_find(table, eviction_group_hash_rule(rule)); @@ -6685,6 +6826,7 @@ oftable_init(struct oftable *table) classifier_init(&table->cls, flow_segment_u32s); table->max_flows = UINT_MAX; atomic_init(&table->miss_config, OFPUTIL_TABLE_MISS_DEFAULT); + atomic_init(&table->config, OFPUTIL_TABLE_CONFIG_DEFAULT); classifier_set_prefix_fields(&table->cls, default_prefix_fields, ARRAY_SIZE(default_prefix_fields)); diff --git a/tests/ofp-print.at b/tests/ofp-print.at index c58ab7d..0944aa6 100644 --- a/tests/ofp-print.at +++ b/tests/ofp-print.at @@ -1115,7 +1115,7 @@ AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 05 11 00 10 00 00 00 02 02 00 00 00 00 00 00 00 \ " 3], [0], [dnl -OFPT_TABLE_MOD (OF1.4) (xid=0x2): table_id=2 +OFPT_TABLE_MOD (OF1.4) (xid=0x2): table_id=2 config= No Eviction ]) AT_CLEANUP diff --git a/tests/ofproto.at b/tests/ofproto.at index 8cfecc6..57788bb 100644 --- a/tests/ofproto.at +++ b/tests/ofproto.at @@ -1488,6 +1488,111 @@ AT_CHECK([ovs-ofctl -O OpenFlow13 dump-table-features br0 | sed '/^$/d OVS_VSWITCHD_STOP AT_CLEANUP +AT_SETUP([ofproto - table description (OpenFlow 1.4)]) +OVS_VSWITCHD_START +(x=0 + while test $x -lt 254; do + y=`expr $x + 1` + echo " table $x: + config= No Eviction" + x=$y + done) > expout +AT_CHECK([ovs-ofctl -O OpenFlow14 dump-table-desc br0 | sed '/^$/d +/^OFPST_TABLE_DESC/d'], [0], [expout]) + +# Change the configuration. +AT_CHECK([ovs-ofctl -O Openflow14 mod-table br0 0 evict]) +# Check that the configuration was updated. +mv expout orig-expout +FLAG_STRING=" flag: Importance" +sed -e "2s/No Eviction/Eviction/ +2s/$/$FLAG_STRING/" <orig-expout > expout +AT_CHECK([ovs-ofctl -O OpenFlow14 dump-table-desc br0 | sed '/^$/d +/^OFPST_TABLE_DESC/d'], [0], [expout]) +OVS_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([ofproto - eviction using importance upon table overflow (OpenFlow 1.4)]) +OVS_VSWITCHD_START +# Configure a maximum of 4 flows. +AT_CHECK( + [ovs-vsctl \ + -- --id=@t0 create Flow_Table flow-limit=4 \ + -- set bridge br0 flow_tables:0=@t0 \ + | ${PERL} $srcdir/uuidfilt.pl], + [0], [<0> +]) +# set the Eviction configuration. +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 +done +AT_CHECK([ovs-ofctl -O Openflow14 dump-flows br0 | ofctl_strip | sort], [0], [dnl + hard_timeout=501, importance=31, priority=6 actions=drop + hard_timeout=502, importance=32, priority=7 actions=drop + hard_timeout=503, importance=33, priority=8 actions=drop + hard_timeout=504, importance=34, priority=9 actions=drop +OFPST_FLOW reply (OF1.4): +]) +# Adding another flow will cause the one with lowest importance to be evicted. +AT_CHECK([ovs-ofctl -O Openflow14 add-flow br0 hard_timeout=505,importance=35,priority=10,in_port=2,actions=drop]) +AT_CHECK([ovs-ofctl -O Openflow14 dump-flows br0 | ofctl_strip | sort], [0], [dnl + hard_timeout=502, importance=32, priority=7 actions=drop + hard_timeout=503, importance=33, priority=8 actions=drop + hard_timeout=504, importance=34, priority=9 actions=drop + hard_timeout=505, importance=35, priority=10,in_port=2 actions=drop +OFPST_FLOW reply (OF1.4): +]) +# Disable the Eviction configuration. +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], + [OFPT_ERROR (OF1.4): OFPFMFC_TABLE_FULL +]) +#Dump flows. It should show only the old values +AT_CHECK([ovs-ofctl -O Openflow14 dump-flows br0 | ofctl_strip | sort], [0], [dnl + hard_timeout=502, importance=32, priority=7 actions=drop + hard_timeout=503, importance=33, priority=8 actions=drop + hard_timeout=504, importance=34, priority=9 actions=drop + hard_timeout=505, importance=35, priority=10,in_port=2 actions=drop +OFPST_FLOW reply (OF1.4): +]) +# mod-flow that would modify a flow will be done successfully. +AT_CHECK([ovs-ofctl -O Openflow14 mod-flows br0 in_port=2,actions=NORMAL]) +AT_CHECK([ovs-ofctl -O Openflow14 dump-flows br0 | ofctl_strip | sort], [0], [dnl + hard_timeout=502, importance=32, priority=7 actions=drop + hard_timeout=503, importance=33, priority=8 actions=drop + hard_timeout=504, importance=34, priority=9 actions=drop + hard_timeout=505, importance=35, priority=10,in_port=2 actions=NORMAL +OFPST_FLOW reply (OF1.4): +]) +# Also a mod-flow that would add a flow will be refused. +AT_CHECK([ovs-ofctl mod-flows br0 in_port=5,actions=drop], [1], [], [stderr]) +AT_CHECK([head -n 1 stderr | ofctl_strip], [0], + [OFPT_ERROR: OFPFMFC_TABLE_FULL +]) +# Now set the eviction on timeout basis. +AT_CHECK( + [ovs-vsctl \ + -- --id=@t0 create Flow_Table flow-limit=4 overflow-policy=evict \ + -- set bridge br0 flow_tables:0=@t0 \ + | ${PERL} $srcdir/uuidfilt.pl], + [0], [<0> +]) +#Now add a new flow +AT_CHECK([ovs-ofctl -O Openflow14 add-flow br0 importance=37,hard_timeout=507,priority=11,in_port=6,actions=drop]) +AT_CHECK([ovs-ofctl -O Openflow14 dump-flows br0 | ofctl_strip | sort], [0], [dnl + hard_timeout=503, importance=33, priority=8 actions=drop + hard_timeout=504, importance=34, priority=9 actions=drop + hard_timeout=505, importance=35, priority=10,in_port=2 actions=NORMAL + hard_timeout=507, importance=37, priority=11,in_port=6 actions=drop +OFPST_FLOW reply (OF1.4): +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + AT_SETUP([ofproto - hard limits on flow table size (OpenFlow 1.0)]) OVS_VSWITCHD_START # Configure a maximum of 4 flows. diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in index 177ef35..e03013f 100644 --- a/utilities/ovs-ofctl.8.in +++ b/utilities/ovs-ofctl.8.in @@ -62,8 +62,14 @@ 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. +.TP +\fBdump\-table\-desc \fIswitch\fR +Prints to the console configuration for each of the flow tables used +by \fIswitch\fR for OpenFlow 1.4+. . -.IP "\fBmod\-table \fIswitch\fR \fItable_id\fR \fIflow_miss_handling\fR" +.IP "\fBmod\-table \fIswitch\fR \fItable_id\fR \fIflow_miss_handling/config\fR" +.RS +.IP \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. @@ -87,6 +93,23 @@ 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 +.IP \fIconfig\fR +An OpenFlow 1.4+ switch configures the behaviour of the table when the +table is full. +.IP +This command configures the flow table 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. \fIconfig\fR may be any one of the following: +.RS +.IP \fBevict\fR +Enable eviction on \fItable_id\fR of \fIswitch\fR on the basis of +importance. +.IP \fBnoevict\fR +Disable eviction on \fItable_id\fR of \fIswitch\fR on the basis of +importance. +.RE +.RE . .TP \fBdump\-ports \fIswitch\fR [\fInetdev\fR] diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c index b840a70..78711d8 100644 --- a/utilities/ovs-ofctl.c +++ b/utilities/ovs-ofctl.c @@ -316,6 +316,7 @@ usage(void) " dump-desc SWITCH print switch description\n" " dump-tables SWITCH print table stats\n" " dump-table-features SWITCH print table features\n" + " dump-tables-desc SWITCH print table description\n" " mod-port SWITCH IFACE ACT modify port behavior\n" " mod-table SWITCH MOD modify flow table behavior\n" " get-frags SWITCH print fragment handling behavior\n" @@ -701,6 +702,24 @@ ofctl_dump_table_features(int argc OVS_UNUSED, char *argv[]) vconn_close(vconn); } +/* ovs-ofctl dump-table-desc provides a way to list the current configuration of + * the tables on a switch, which is set using the ovs-ofctl table-mod command. */ +static void +ofctl_dump_table_desc(int argc OVS_UNUSED, char *argv[]) +{ + struct ofpbuf *request; + struct vconn *vconn; + + open_vconn(argv[1], &vconn); + request = ofputil_encode_table_desc_request(vconn_get_version(vconn)); + if (request) { + dump_stats_transaction(vconn, request); + } + + vconn_close(vconn); +} + + static bool fetch_port_by_stats(struct vconn *, const char *port_name, ofp_port_t port_no, struct ofputil_phy_port *); @@ -3496,6 +3515,8 @@ static const struct command all_commands[] = { 1, 1, ofctl_dump_tables }, { "dump-table-features", "switch", 1, 1, ofctl_dump_table_features }, + { "dump-table-desc", "switch", + 1, 1, ofctl_dump_table_desc}, { "dump-flows", "switch", 1, 2, ofctl_dump_flows }, { "dump-aggregate", "switch", -- 1.7.9.5 =====-----=====-----===== Notice: The information contained in this e-mail message and/or attachments to it may contain confidential or privileged information. If you are not the intended recipient, any dissemination, use, review, distribution, printing or copying of the information contained in this e-mail message and/or attachments to it are strictly prohibited. If you have received this communication in error, please notify us by reply e-mail or telephone and immediately and permanently delete the message and any attachments. Thank you _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev