From patchwork Wed Sep 9 07:42:04 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: saloni.jain12@gmail.com X-Patchwork-Id: 515734 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from archives.nicira.com (li376-54.members.linode.com [96.126.127.54]) by ozlabs.org (Postfix) with ESMTP id CF414140663 for ; Wed, 9 Sep 2015 17:42:41 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=KU6OV5Yx; dkim-atps=neutral Received: from archives.nicira.com (localhost [127.0.0.1]) by archives.nicira.com (Postfix) with ESMTP id E55631091D; Wed, 9 Sep 2015 00:42:40 -0700 (PDT) X-Original-To: dev@openvswitch.org Delivered-To: dev@openvswitch.org Received: from mx3v1.cudamail.com (mx3.cudamail.com [64.34.241.5]) by archives.nicira.com (Postfix) with ESMTPS id 96D121091C for ; Wed, 9 Sep 2015 00:42:39 -0700 (PDT) Received: from bar4.cudamail.com (bar2 [192.168.15.2]) by mx3v1.cudamail.com (Postfix) with ESMTP id 0FBC96191EC for ; Wed, 9 Sep 2015 01:42:39 -0600 (MDT) X-ASG-Debug-ID: 1441784557-03dc2116b501c80001-byXFYA Received: from mx3-pf3.cudamail.com ([192.168.14.3]) by bar4.cudamail.com with ESMTP id yPBoyj0ExQBNbiba (version=TLSv1 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Wed, 09 Sep 2015 01:42:37 -0600 (MDT) X-Barracuda-Envelope-From: saloni.jain12@gmail.com X-Barracuda-RBL-Trusted-Forwarder: 192.168.14.3 Received: from unknown (HELO mail-pa0-f50.google.com) (209.85.220.50) by mx3-pf3.cudamail.com with ESMTPS (RC4-SHA encrypted); 9 Sep 2015 07:42:30 -0000 Received-SPF: pass (mx3-pf3.cudamail.com: SPF record at _netblocks.google.com designates 209.85.220.50 as permitted sender) X-Barracuda-Apparent-Source-IP: 209.85.220.50 X-Barracuda-RBL-IP: 209.85.220.50 Received: by padhy16 with SMTP id hy16so2411325pad.1 for ; Wed, 09 Sep 2015 00:42:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id; bh=1NvXA/ZjUYtKiPjyVUBV0LaiXl5jJnMsGTzDm4p1HbA=; b=KU6OV5Yxxb9Xg+29jkWsijR4j73GQWIQUMaw1s+eGLZBN4aQ7gmcp3aDcY1p+uhZ6b V3E4yczWzqxuLWAGdNF0DfWwaZFUKHrrdj72kMV2AlIZhf8DPUM4sQkDZ3NewaFyz+Rp Uakc2p6BmGDveJu4EiFjhcPC8j5aieSZ5VI+3zbU+qutIT92B4Ad1jeetV76mW/SMdPm yOULSvfJqppG/fXpsrH73OpXCYp1ygH/kqjq+wE0q4KMJnmaE2qE49zZYO1hs1mGn8OT GRMqKM5edKsw6+XR25UpEvXKBB7PASCXU52u/HFQncpkvEYG3dT04fCdg0ha7Bi+qB2u CaEg== X-Received: by 10.66.141.135 with SMTP id ro7mr8503904pab.115.1441784550083; Wed, 09 Sep 2015 00:42:30 -0700 (PDT) Received: from localhost.localdomain ([117.247.166.121]) by smtp.gmail.com with ESMTPSA id ip7sm5914245pbc.68.2015.09.09.00.42.26 (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Wed, 09 Sep 2015 00:42:28 -0700 (PDT) X-CudaMail-Envelope-Sender: saloni.jain12@gmail.com From: saloni.jain12@gmail.com X-Google-Original-From: saloni.jain@tcs.com To: dev@openvswitch.org X-CudaMail-MID: CM-V3-908001759 X-CudaMail-DTE: 090915 X-CudaMail-Originating-IP: 209.85.220.50 Date: Wed, 9 Sep 2015 13:12:04 +0530 X-ASG-Orig-Subj: [##CM-V3-908001759##][PATCH v4 1/3] Implement Openflow 1.4 Vacancy Events for OFPT_TABLE_MOD. Message-Id: <1441784524-8007-1-git-send-email-saloni.jain@tcs.com> X-Mailer: git-send-email 1.7.9.5 X-GBUdb-Analysis: 0, 209.85.220.50, Ugly c=0.460213 p=-0.425287 Source Normal X-MessageSniffer-Rules: 0-0-0-32767-c X-Barracuda-Connect: UNKNOWN[192.168.14.3] X-Barracuda-Start-Time: 1441784557 X-Barracuda-Encrypted: DHE-RSA-AES256-SHA X-Barracuda-URL: https://web.cudamail.com:443/cgi-mod/mark.cgi X-Barracuda-BRTS-Status: 1 X-Virus-Scanned: by bsmtpd at cudamail.com X-Barracuda-Spam-Score: 0.60 X-Barracuda-Spam-Status: No, SCORE=0.60 using per-user scores of TAG_LEVEL=3.0 QUARANTINE_LEVEL=1000.0 KILL_LEVEL=3.0 tests=BSF_SC5_MJ1963, DKIM_SIGNED, NO_REAL_NAME, RDNS_NONE X-Barracuda-Spam-Report: Code version 3.2, rules version 3.2.3.22365 Rule breakdown below pts rule name description ---- ---------------------- -------------------------------------------------- 0.00 NO_REAL_NAME From: does not include a real name 0.00 DKIM_SIGNED Domain Keys Identified Mail: message has a signature 0.10 RDNS_NONE Delivered to trusted network by a host with no rDNS 0.50 BSF_SC5_MJ1963 Custom Rule MJ1963 Cc: Sandeep Kumar , deepankar.gupta@tcs.com, partha.datta@tcs.com, Shashwat Srivastava Subject: [ovs-dev] [PATCH v4 1/3] Implement Openflow 1.4 Vacancy Events for OFPT_TABLE_MOD. X-BeenThere: dev@openvswitch.org X-Mailman-Version: 2.1.16 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: dev-bounces@openvswitch.org Sender: "dev" From: Saloni Jain OpenFlow 1.4 introduces the ability to turn on vacancy events with an OFPT_TABLE_MOD message specifying OFPTC_VACANCY_EVENTS. This commit adds support for the new feature in ovs-ofctl mod-table. As per the openflow specification-1.4, vacancy event adds a mechanism enabling the controller to get an early warning based on capacity threshold chosen by the controller. With this commit, vacancy events can be configured as: ovs-ofctl -O OpenFlow14 mod-table vacancy- The syntax of as . specify vacancy threshold values, vacancy down and vacancy up. To disable vacancy events, following command should be given: ovs-ofctl -O OpenFlow14 mod-table
novacancy Signed-off-by: Saloni Jain Co-authored-by: Shashwat Srivastava Signed-off-by: Shashwat Srivastava Co-authored-by: Sandeep Kumar Signed-off-by: Sandeep Kumar --- Difference between v3->v4 - Rebased with latest master. - Added Co-authored-by tags in commit message. lib/ofp-parse.c | 65 +++++++++++++++++++++++++++++--- lib/ofp-parse.h | 3 ++ lib/ofp-print.c | 22 +++++++++++ lib/ofp-util.c | 87 ++++++++++++++++++++++++++++++++++++++---- lib/ofp-util.h | 39 +++++++++++++++++++ ofproto/ofproto-provider.h | 11 ++++++ ofproto/ofproto.c | 24 ++++++++---- tests/ofp-print.at | 2 +- utilities/ovs-ofctl.c | 90 +++++++++++++++++++++++++++++++++++++++++++- 9 files changed, 319 insertions(+), 24 deletions(-) diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c index eaaa8ba..8f9e603 100644 --- a/lib/ofp-parse.c +++ b/lib/ofp-parse.c @@ -883,6 +883,49 @@ parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string, return error; } +/* Convert 'setting' (as described for the "mod-table" command + * in ovs-ofctl man page) into 'tm->table_vacancy->vacancy_up' and + * 'tm->table_vacancy->vacancy_down' threshold values. + * For the two threshold values, value of vacancy_up is always greater + * than value of vacancy_down. + * + * 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_vacancy(struct ofputil_table_mod *tm, const char *setting) +{ + char *save_ptr = NULL; + char *vac_up, *vac_down; + char *value = strdup(setting); + int vacancy_up, vacancy_down; + + strtok_r(value, "-", &save_ptr); + vac_down = strtok_r(NULL, "..", &save_ptr); + if (!vac_down) { + return xasprintf("Vacancy down value missing"); + } + if (!str_to_int(vac_down, 0, &vacancy_down) || + vacancy_down < 0 || vacancy_down > 100) { + return xasprintf("Invalid vacancy down value \"%s\"", vac_down); + } + vac_up = strtok_r(NULL, "..", &save_ptr); + if (!vac_up) { + return xasprintf("Vacancy up value missing"); + } + if (!str_to_int(vac_up, 0, &vacancy_up) || + vacancy_up < 0 || vacancy_up > 100) { + return xasprintf("Invalid vacancy up value \"%s\"", vac_up); + } + if (vacancy_down > vacancy_up) { + return xasprintf("Invalid vacancy range, vacancy up should be greater" + " than vacancy down ""(%s)", + ofperr_to_string(OFPERR_OFPBPC_BAD_VALUE)); + } + tm->table_vacancy.vacancy_down = vacancy_down; + tm->table_vacancy.vacancy_up = vacancy_up; + return NULL; +} + /* 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. @@ -909,13 +952,13 @@ parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id, tm->miss = OFPUTIL_TABLE_MISS_DEFAULT; tm->eviction = OFPUTIL_TABLE_EVICTION_DEFAULT; tm->eviction_flags = UINT32_MAX; - + tm->vacancy = OFPUTIL_TABLE_VACANCY_DEFAULT; + tm->table_vacancy.vacancy_down = 0; + tm->table_vacancy.vacancy_up = 0; + tm->table_vacancy.vacancy = 0; /* 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.) + * Only OpenFlow 1.4+ can configure eviction and vacancy events + * via table_mod. */ if (!strcmp(setting, "controller")) { tm->miss = OFPUTIL_TABLE_MISS_CONTROLLER; @@ -932,6 +975,16 @@ parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id, } else if (!strcmp(setting, "noevict")) { tm->eviction = OFPUTIL_TABLE_EVICTION_OFF; *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION); + } else if (!strncmp(setting, "vacancy", strcspn(setting, "-"))) { + tm->vacancy = OFPUTIL_TABLE_VACANCY_ON; + *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION); + char *error = parse_ofp_table_vacancy(tm, setting); + if (error) { + return error; + } + } else if (!strcmp(setting, "novacancy")) { + tm->vacancy = OFPUTIL_TABLE_VACANCY_OFF; + *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION); } else { return xasprintf("invalid table_mod setting %s", setting); } diff --git a/lib/ofp-parse.h b/lib/ofp-parse.h index b64a32e..a949698 100644 --- a/lib/ofp-parse.h +++ b/lib/ofp-parse.h @@ -99,5 +99,8 @@ char *str_to_u64(const char *str, uint64_t *valuep) OVS_WARN_UNUSED_RESULT; char *str_to_be64(const char *str, ovs_be64 *valuep) OVS_WARN_UNUSED_RESULT; char *str_to_mac(const char *str, struct eth_addr *mac) OVS_WARN_UNUSED_RESULT; char *str_to_ip(const char *str, ovs_be32 *ip) OVS_WARN_UNUSED_RESULT; +char *parse_ofp_table_vacancy(struct ofputil_table_mod *, + const char *flow_miss_handling) + OVS_WARN_UNUSED_RESULT; #endif /* ofp-parse.h */ diff --git a/lib/ofp-print.c b/lib/ofp-print.c index 6e32d4d..9fe05bd 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -990,6 +990,18 @@ ofputil_put_eviction_flags(struct ds *string, uint32_t eviction_flags) } } +static const char * +ofputil_table_vacancy_to_string(enum ofputil_table_vacancy vacancy) +{ + switch (vacancy) { + case OFPUTIL_TABLE_VACANCY_DEFAULT: return "default"; + case OFPUTIL_TABLE_VACANCY_ON: return "on"; + case OFPUTIL_TABLE_VACANCY_OFF: return "off"; + default: return "***error***"; + } + +} + static void ofp_print_table_mod(struct ds *string, const struct ofp_header *oh) { @@ -1020,6 +1032,16 @@ ofp_print_table_mod(struct ds *string, const struct ofp_header *oh) ds_put_cstr(string, "eviction_flags="); ofputil_put_eviction_flags(string, pm.eviction_flags); } + if (pm.vacancy != OFPUTIL_TABLE_VACANCY_DEFAULT) { + ds_put_format(string, ", vacancy=%s", + ofputil_table_vacancy_to_string(pm.vacancy)); + if (pm.vacancy == OFPUTIL_TABLE_VACANCY_ON) { + ds_put_format(string, " vacancy_down=%"PRIu8"%%", + pm.table_vacancy.vacancy_down); + ds_put_format(string, " vacancy_up=%"PRIu8"%%", + pm.table_vacancy.vacancy_up); + } + } } /* This function will print the Table description properties. */ diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 5331f8c..6c181f8 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -53,10 +53,13 @@ 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); +static enum ofputil_table_vacancy ofputil_decode_table_vacancy( + 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 ofputil_table_vacancy, enum ofp_version); struct ofp_prop_header { @@ -4999,9 +5002,54 @@ ofputil_append_table_desc_reply(const struct ofputil_table_desc *td, otd->length = htons(reply->size - start_otd); otd->table_id = td->table_id; otd->config = ofputil_encode_table_config(OFPUTIL_TABLE_MISS_DEFAULT, - td->eviction, version); + td->eviction, td->vacancy, + version); ofpmp_postappend(replies, start_otd); } +/* This function parses Vacancy property, and decodes the + * ofp14_table_mod_prop_vacancy in ofputil_table_mod. + * Returns OFPERR_OFPBPC_BAD_VALUE error code when vacancy_down is + * greater than vacancy_up and also when current vacancy has non-zero + * value. Returns 0 on success. */ +static enum ofperr +parse_table_mod_vacancy_property(struct ofpbuf *property, + struct ofputil_table_mod *tm) +{ + struct ofp14_table_mod_prop_vacancy *otv = property->data; + + if (property->size != sizeof *otv) { + return OFPERR_OFPBPC_BAD_LEN; + } + tm->table_vacancy.vacancy_down = otv->vacancy_down; + tm->table_vacancy.vacancy_up = otv->vacancy_up; + if (tm->table_vacancy.vacancy_down > tm->table_vacancy.vacancy_up) { + log_property(false, "Value of vacancy_down is greater than " + "vacancy_up"); + return OFPERR_OFPBPC_BAD_VALUE; + } + tm->table_vacancy.vacancy = otv->vacancy; + if (tm->table_vacancy.vacancy) { + log_property(false, "Vacancy value should be zero for table-mod " + "messages"); + return OFPERR_OFPBPC_BAD_VALUE; + } + 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 vacancy configuration that it specifies. + * + * Only OpenFlow 1.4 and later specify table vacancy configuration this way, + * so for other 'version' this function always returns + * OFPUTIL_TABLE_VACANCY_DEFAULT. */ +static enum ofputil_table_vacancy +ofputil_decode_table_vacancy(ovs_be32 config, enum ofp_version version) +{ + return (version < OFP14_VERSION ? OFPUTIL_TABLE_VACANCY_DEFAULT + : config & htonl(OFPTC14_VACANCY_EVENTS) ? OFPUTIL_TABLE_VACANCY_ON + : OFPUTIL_TABLE_VACANCY_OFF); +} static enum ofperr parse_table_mod_eviction_property(struct ofpbuf *property, @@ -5038,8 +5086,10 @@ ofputil_decode_table_eviction(ovs_be32 config, enum ofp_version version) static ovs_be32 ofputil_encode_table_config(enum ofputil_table_miss miss, enum ofputil_table_eviction eviction, + enum ofputil_table_vacancy vacancy, enum ofp_version version) { + uint32_t config = 0; /* See the section "OFPTC_* Table Configuration" in DESIGN.md for more * information on the crazy evolution of this field. */ switch (version) { @@ -5072,11 +5122,16 @@ ofputil_encode_table_config(enum ofputil_table_miss miss, 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); - } + /* OpenFlow 1.4 introduced OFPTC14_EVICTION and + * OFPTC14_VACANCY_EVENTS. */ + if (eviction == OFPUTIL_TABLE_EVICTION_ON) { + config |= OFPTC14_EVICTION; + } + if (vacancy == OFPUTIL_TABLE_VACANCY_ON) { + config |= OFPTC14_VACANCY_EVENTS; + } + return htonl(config); +} OVS_NOT_REACHED(); } @@ -5126,6 +5181,7 @@ ofputil_decode_table_mod(const struct ofp_header *oh, pm->miss = OFPUTIL_TABLE_MISS_DEFAULT; pm->eviction = OFPUTIL_TABLE_EVICTION_DEFAULT; pm->eviction_flags = UINT32_MAX; + pm->vacancy = OFPUTIL_TABLE_VACANCY_DEFAULT; ofpbuf_use_const(&b, oh, ntohs(oh->length)); raw = ofpraw_pull_assert(&b); @@ -5140,6 +5196,7 @@ ofputil_decode_table_mod(const struct ofp_header *oh, pm->table_id = otm->table_id; pm->miss = ofputil_decode_table_miss(otm->config, oh->version); pm->eviction = ofputil_decode_table_eviction(otm->config, oh->version); + pm->vacancy = ofputil_decode_table_vacancy(otm->config, oh->version); while (b.size > 0) { struct ofpbuf property; enum ofperr error; @@ -5155,6 +5212,10 @@ ofputil_decode_table_mod(const struct ofp_header *oh, error = parse_table_mod_eviction_property(&property, pm); break; + case OFPTMPT14_VACANCY: + error = parse_table_mod_vacancy_property(&property, pm); + break; + default: error = OFPERR_OFPBRC_BAD_TYPE; break; @@ -5196,19 +5257,20 @@ ofputil_encode_table_mod(const struct ofputil_table_mod *tm, otm = ofpbuf_put_zeros(b, sizeof *otm); otm->table_id = tm->table_id; otm->config = ofputil_encode_table_config(tm->miss, tm->eviction, - ofp_version); + tm->vacancy, ofp_version); break; } case OFP14_VERSION: case OFP15_VERSION: { struct ofp14_table_mod *otm; struct ofp14_table_mod_prop_eviction *ote; + struct ofp14_table_mod_prop_vacancy *otv; b = ofpraw_alloc(OFPRAW_OFPT14_TABLE_MOD, ofp_version, 0); otm = ofpbuf_put_zeros(b, sizeof *otm); otm->table_id = tm->table_id; otm->config = ofputil_encode_table_config(tm->miss, tm->eviction, - ofp_version); + tm->vacancy, ofp_version); if (tm->eviction_flags != UINT32_MAX) { ote = ofpbuf_put_zeros(b, sizeof *ote); @@ -5216,6 +5278,14 @@ ofputil_encode_table_mod(const struct ofputil_table_mod *tm, ote->length = htons(sizeof *ote); ote->flags = htonl(tm->eviction_flags); } + if (tm->vacancy == OFPUTIL_TABLE_VACANCY_ON) { + otv = ofpbuf_put_zeros(b, sizeof *otv); + otv->type = htons(OFPTMPT14_VACANCY); + otv->length = htons(sizeof *otv); + otv->vacancy_down = tm->table_vacancy.vacancy_down; + otv->vacancy_up = tm->table_vacancy.vacancy_up; + otv->vacancy = tm->table_vacancy.vacancy; + } break; } default: @@ -5563,6 +5633,7 @@ ofputil_put_ofp12_table_stats(const struct ofputil_table_stats *stats, features->nonmiss.instructions, OFP12_VERSION); out->config = ofputil_encode_table_config(features->miss_config, OFPUTIL_TABLE_EVICTION_DEFAULT, + OFPUTIL_TABLE_VACANCY_DEFAULT, OFP12_VERSION); out->max_entries = htonl(features->max_entries); out->active_count = htonl(stats->active_count); diff --git a/lib/ofp-util.h b/lib/ofp-util.h index 527a5ab..2290c4c 100644 --- a/lib/ofp-util.h +++ b/lib/ofp-util.h @@ -620,6 +620,33 @@ enum ofputil_table_eviction { OFPUTIL_TABLE_EVICTION_OFF /* Disable eviction. */ }; +/* Abstract version of OFPTC14_VACANCY_EVENTS. + * + * OpenFlow 1.0 through 1.3 don't know anything about vacancy events, so + * decoding a message for one of these protocols always yields + * OFPUTIL_TABLE_VACANCY_DEFAULT. */ +enum ofputil_table_vacancy { + OFPUTIL_TABLE_VACANCY_DEFAULT, /* No value. */ + OFPUTIL_TABLE_VACANCY_ON, /* Enable vacancy events. */ + OFPUTIL_TABLE_VACANCY_OFF /* Disable vacancy events. */ +}; + +/* Abstract version of OFPTMPT_VACANCY. + * + * Openflow 1.4+ defines vacancy events. + * The fields vacancy_down and vacancy_up are the threshold for generating + * vacancy events that should be configured on the flow table, expressed as + * a percent. + * The vacancy field is only used when this property in included in a + * OFPMP_TABLE_DESC multipart reply or a OFPT_TABLE_STATUS message and + * represent the current vacancy of the table, expressed as a percent. In + * OFP_TABLE_MOD requests, this field must be set to 0 */ +struct ofputil_table_mod_prop_vacancy { + uint8_t vacancy_down; /* Vacancy threshold when space decreases (%). */ + uint8_t vacancy_up; /* Vacancy threshold when space increases (%). */ + uint8_t vacancy; /* Current vacancy (%). */ +}; + /* Abstract ofp_table_mod. */ struct ofputil_table_mod { uint8_t table_id; /* ID of the table, 0xff indicates all tables. */ @@ -636,6 +663,16 @@ struct ofputil_table_mod { * absence. For other versions, ignored on encoding, decoded to * UINT32_MAX.*/ uint32_t eviction_flags; /* OFPTMPEF14_*. */ + + /* OpenFlow 1.4+ only. For other versions, ignored on encoding, decoded to + * OFPUTIL_TABLE_VACANCY_DEFAULT. */ + enum ofputil_table_vacancy vacancy; + + /* Openflow 1.4+ only. Defines threshold values of vacancy expressed as + * percent, value of Current vacancy is set to zero for table-mod. + * For other versions, ignored on encoding, all values decoded to + * zero. */ + struct ofputil_table_mod_prop_vacancy table_vacancy; }; /* Abstract ofp14_table_desc. */ @@ -643,6 +680,8 @@ struct ofputil_table_desc { uint8_t table_id; /* ID of the table. */ enum ofputil_table_eviction eviction; uint32_t eviction_flags; /* UINT32_MAX if not present. */ + enum ofputil_table_vacancy vacancy; + struct ofputil_table_mod_prop_vacancy table_vacancy; }; enum ofperr ofputil_decode_table_mod(const struct ofp_header *, diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h index 117cd1f..76b4289 100644 --- a/ofproto/ofproto-provider.h +++ b/ofproto/ofproto-provider.h @@ -256,6 +256,17 @@ struct oftable { #define EVICTION_OPENFLOW (1 << 1) /* Set to 1 if OpenFlow enables eviction. */ unsigned int eviction; + /* Either of the Vacancy Events are enabled when this value is set to + * OFPTC14_VACANCY_EVENTS. When this flag is unset, vacancy events are + * disabled */ + unsigned int vacancy; + + /* Non-zero values for vacancy_up and vacancy_down indicates that vacancy + * is enabled by table-mod, else these values are set to zero when + * vacancy is disabled */ + uint8_t vacancy_down; /* Vacancy threshold when space decreases (%). */ + uint8_t vacancy_up; /* Vacancy threshold when space increases (%). */ + atomic_ulong n_matched; atomic_ulong n_missed; }; diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index e6c0351..64f3a96 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -6610,20 +6610,20 @@ ofproto_table_get_miss_config(const struct ofproto *ofproto, uint8_t table_id) static void table_mod__(struct oftable *oftable, - enum ofputil_table_miss miss, enum ofputil_table_eviction eviction) + const struct ofputil_table_mod *tm) { - if (miss == OFPUTIL_TABLE_MISS_DEFAULT) { + if (tm->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); + atomic_store_relaxed(&oftable->miss_config, tm->miss); } unsigned int new_eviction = oftable->eviction; - if (eviction == OFPUTIL_TABLE_EVICTION_ON) { + if (tm->eviction == OFPUTIL_TABLE_EVICTION_ON) { new_eviction |= EVICTION_OPENFLOW; - } else if (eviction == OFPUTIL_TABLE_EVICTION_OFF) { + } else if (tm->eviction == OFPUTIL_TABLE_EVICTION_OFF) { new_eviction &= ~EVICTION_OPENFLOW; } @@ -6634,6 +6634,16 @@ table_mod__(struct oftable *oftable, oftable->n_eviction_fields); ovs_mutex_unlock(&ofproto_mutex); } + + if (tm->vacancy != OFPUTIL_TABLE_VACANCY_DEFAULT) { + ovs_mutex_lock(&ofproto_mutex); + oftable->vacancy = (tm->vacancy == OFPUTIL_TABLE_VACANCY_ON + ? oftable->vacancy | OFPTC14_VACANCY_EVENTS + : oftable->vacancy & ~OFPTC14_VACANCY_EVENTS); + oftable->vacancy_down = tm->table_vacancy.vacancy_down; + oftable->vacancy_up = tm->table_vacancy.vacancy_up; + ovs_mutex_unlock(&ofproto_mutex); + } } static enum ofperr @@ -6657,7 +6667,7 @@ table_mod(struct ofproto *ofproto, const struct ofputil_table_mod *tm) struct oftable *oftable; OFPROTO_FOR_EACH_TABLE (oftable, ofproto) { if (!(oftable->flags & (OFTABLE_HIDDEN | OFTABLE_READONLY))) { - table_mod__(oftable, tm->miss, tm->eviction); + table_mod__(oftable, tm); } } } else { @@ -6665,7 +6675,7 @@ table_mod(struct ofproto *ofproto, const struct ofputil_table_mod *tm) if (oftable->flags & OFTABLE_READONLY) { return OFPERR_OFPTMFC_EPERM; } - table_mod__(oftable, tm->miss, tm->eviction); + table_mod__(oftable, tm); } return 0; diff --git a/tests/ofp-print.at b/tests/ofp-print.at index 35a6262..a2b4359 100644 --- a/tests/ofp-print.at +++ b/tests/ofp-print.at @@ -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 -OFPT_TABLE_MOD (OF1.4) (xid=0x2): table_id=2, eviction=off +OFPT_TABLE_MOD (OF1.4) (xid=0x2): table_id=2, eviction=off, vacancy=off ]) AT_CLEANUP diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c index 75e84e2..bd1af2a 100644 --- a/utilities/ovs-ofctl.c +++ b/utilities/ovs-ofctl.c @@ -1929,6 +1929,70 @@ found: vconn_close(vconn); } +/* This function uses OFPMP14_TABLE_DESC request to get the current + * table configuration from switch. The function then modifies + * only that table-config property, which has been requested. */ +static void +fetch_table_desc(struct vconn *vconn, struct ofputil_table_mod *tm, + struct ofputil_table_desc *td) +{ + struct ofpbuf *request; + ovs_be32 send_xid; + bool done = false; + bool found = false; + + request = ofputil_encode_table_desc_request(vconn_get_version(vconn)); + send_xid = ((struct ofp_header *) request->data)->xid; + send_openflow_buffer(vconn, request); + while (!done) { + ovs_be32 recv_xid; + struct ofpbuf *reply; + + run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed"); + recv_xid = ((struct ofp_header *) reply->data)->xid; + if (send_xid == recv_xid) { + struct ofp_header *oh = reply->data; + enum ofptype type; + struct ofpbuf b; + uint16_t flags; + + ofpbuf_use_const(&b, oh, ntohs(oh->length)); + if (ofptype_pull(&type, &b) + || type != OFPTYPE_TABLE_DESC_REPLY) { + ovs_fatal(0, "received bad reply: %s", + ofp_to_string(reply->data, reply->size, + verbosity + 1)); + } + flags = ofpmp_flags(oh); + done = !(flags & OFPSF_REPLY_MORE); + if (found) { + /* We've already found the table desc consiting of current + * table configuration, but we need to drain the queue of + * any other replies for this request. */ + continue; + } + while (!ofputil_decode_table_desc(&b, td, oh->version)) { + if (td->table_id == tm->table_id) { + found = true; + break; + } + } + } else { + VLOG_DBG("received reply with xid %08"PRIx32" " + "!= expected %08"PRIx32, recv_xid, send_xid); + } + ofpbuf_delete(reply); + } + if (tm->eviction != OFPUTIL_TABLE_EVICTION_DEFAULT) { + tm->vacancy = td->vacancy; + tm->table_vacancy.vacancy_down = td->table_vacancy.vacancy_down; + tm->table_vacancy.vacancy_up = td->table_vacancy.vacancy_up; + } else if (tm->vacancy != OFPUTIL_TABLE_VACANCY_DEFAULT) { + tm->eviction = td->eviction; + tm->eviction_flags = td->eviction_flags; + } +} + static void ofctl_mod_table(struct ovs_cmdl_context *ctx) { @@ -1936,6 +2000,7 @@ ofctl_mod_table(struct ovs_cmdl_context *ctx) 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_versions); @@ -1952,9 +2017,30 @@ ofctl_mod_table(struct ovs_cmdl_context *ctx) 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)); + + /* For openflow 1.4+, ovs-ofctl mod-table should not affect table-config + * properties that the user didn't request, so it is necessary to restore + * the current configuration of table-config parameters using + * OFPMP14_TABLE_DESC request. */ + if ((usable_versions & (1u << OFP14_VERSION)) || + (usable_versions & (1u << OFP15_VERSION))) { + struct ofputil_table_desc td; + + if (tm.table_id == OFPTT_ALL) { + for (i=0; i < OFPTT_MAX; i++) { + tm.table_id = i; + fetch_table_desc(vconn, &tm, &td); + transact_noreply(vconn, + ofputil_encode_table_mod(&tm, protocol)); + } + } else { + fetch_table_desc(vconn, &tm, &td); + transact_noreply(vconn, ofputil_encode_table_mod(&tm, protocol)); + } + } else { + transact_noreply(vconn, ofputil_encode_table_mod(&tm, protocol)); + } vconn_close(vconn); }