From patchwork Fri Jul 27 21:19:12 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Justin Pettit X-Patchwork-Id: 950368 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=ovn.org Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 41chg23MdQz9ryn for ; Sat, 28 Jul 2018 07:19:25 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 323D8FB6; Fri, 27 Jul 2018 21:19:23 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 31C37F0C for ; Fri, 27 Jul 2018 21:19:22 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from relay4-d.mail.gandi.net (relay4-d.mail.gandi.net [217.70.183.196]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 205F371D for ; Fri, 27 Jul 2018 21:19:19 +0000 (UTC) X-Originating-IP: 76.21.1.228 Received: from localhost.localdomain (unknown [76.21.1.228]) (Authenticated sender: jpettit@ovn.org) by relay4-d.mail.gandi.net (Postfix) with ESMTPSA id 457ABE0007 for ; Fri, 27 Jul 2018 21:19:16 +0000 (UTC) From: Justin Pettit To: dev@openvswitch.org Date: Fri, 27 Jul 2018 14:19:12 -0700 Message-Id: <20180727211913.18268-1-jpettit@ovn.org> X-Mailer: git-send-email 2.17.1 X-Spam-Level: X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_LOW autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCHv2 1/2] dpif: Move common meter checks into the dpif layer. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org Another dpif provider will soon add support for meters, so move some of the common sanity checks up into the dpif layer so that each provider doesn't need to re-implement them. Signed-off-by: Justin Pettit Acked-by: Ben Pfaff --- v2: This patch is new to the series. --- lib/dpif-netdev.c | 15 +++------------ lib/dpif.c | 22 +++++++++++++++++++--- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index 13a20f023554..26d07b39c9af 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -5172,21 +5172,12 @@ dpif_netdev_meter_set(struct dpif *dpif, ofproto_meter_id *meter_id, return EFBIG; /* Meter_id out of range. */ } - if (config->flags & ~DP_SUPPORTED_METER_FLAGS_MASK || - !(config->flags & (OFPMF13_KBPS | OFPMF13_PKTPS))) { + if (config->flags & ~DP_SUPPORTED_METER_FLAGS_MASK) { return EBADF; /* Unsupported flags set */ } - /* Validate bands */ - if (config->n_bands == 0 || config->n_bands > MAX_BANDS) { - return EINVAL; /* Too many bands */ - } - - /* Validate rates */ - for (i = 0; i < config->n_bands; i++) { - if (config->bands[i].rate == 0) { - return EDOM; /* rate must be non-zero */ - } + if (config->n_bands > MAX_BANDS) { + return EINVAL; } for (i = 0; i < config->n_bands; ++i) { diff --git a/lib/dpif.c b/lib/dpif.c index d78330bef3b8..c267bcfb0c55 100644 --- a/lib/dpif.c +++ b/lib/dpif.c @@ -1895,11 +1895,27 @@ int dpif_meter_set(struct dpif *dpif, ofproto_meter_id *meter_id, struct ofputil_meter_config *config) { - int error; - COVERAGE_INC(dpif_meter_set); - error = dpif->dpif_class->meter_set(dpif, meter_id, config); + if (!(config->flags & (OFPMF13_KBPS | OFPMF13_PKTPS))) { + return EBADF; /* Rate unit type not set. */ + } + + if ((config->flags & OFPMF13_KBPS) && (config->flags & OFPMF13_PKTPS)) { + return EBADF; /* Both rate units may not be set. */ + } + + if (config->n_bands == 0) { + return EINVAL; + } + + for (size_t i = 0; i < config->n_bands; i++) { + if (config->bands[i].rate == 0) { + return EDOM; /* Rate must be non-zero */ + } + } + + int error = dpif->dpif_class->meter_set(dpif, meter_id, config); if (!error) { VLOG_DBG_RL(&dpmsg_rl, "%s: DPIF meter %"PRIu32" set", dpif_name(dpif), meter_id->uint32); From patchwork Fri Jul 27 21:19:13 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Justin Pettit X-Patchwork-Id: 950369 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=ovn.org Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 41chgQ5VgTz9ryn for ; Sat, 28 Jul 2018 07:19:46 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 372A4FC6; Fri, 27 Jul 2018 21:19:26 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id E95F4F0C for ; Fri, 27 Jul 2018 21:19:22 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from relay4-d.mail.gandi.net (relay4-d.mail.gandi.net [217.70.183.196]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id B653A767 for ; Fri, 27 Jul 2018 21:19:20 +0000 (UTC) X-Originating-IP: 76.21.1.228 Received: from localhost.localdomain (unknown [76.21.1.228]) (Authenticated sender: jpettit@ovn.org) by relay4-d.mail.gandi.net (Postfix) with ESMTPSA id 6CDD2E000A; Fri, 27 Jul 2018 21:19:18 +0000 (UTC) From: Justin Pettit To: dev@openvswitch.org Date: Fri, 27 Jul 2018 14:19:13 -0700 Message-Id: <20180727211913.18268-2-jpettit@ovn.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180727211913.18268-1-jpettit@ovn.org> References: <20180727211913.18268-1-jpettit@ovn.org> X-Spam-Level: X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_LOW autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCHv2 2/2] dpif-netlink: Add meter support. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org From: Andy Zhou To work with kernel datapath that supports meter. Signed-off-by: Andy Zhou Co-authored-by: Justin Pettit Signed-off-by: Justin Pettit Acked-by: Alin Gabriel Serdean Acked-by: Ben Pfaff --- v1->v2: Addressed Ben's comments. --- Documentation/faq/releases.rst | 1 + lib/dpif-netlink.c | 291 ++++++++++++++++++++++++++++++--- 2 files changed, 272 insertions(+), 20 deletions(-) diff --git a/Documentation/faq/releases.rst b/Documentation/faq/releases.rst index 7021cec5943a..0f3bf0c0cba1 100644 --- a/Documentation/faq/releases.rst +++ b/Documentation/faq/releases.rst @@ -124,6 +124,7 @@ Q: Are all features available with all datapaths? Set action YES YES YES PARTIAL NIC Bonding YES YES YES YES Multiple VTEPs YES YES YES YES + Meters 4.15 YES YES NO ===================== ============== ============== ========= ======= Do note, however: diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c index 62c7120e8f54..f669b1108d61 100644 --- a/lib/dpif-netlink.c +++ b/lib/dpif-netlink.c @@ -212,6 +212,7 @@ static int ovs_datapath_family; static int ovs_vport_family; static int ovs_flow_family; static int ovs_packet_family; +static int ovs_meter_family; /* Generic Netlink multicast groups for OVS. * @@ -2920,41 +2921,286 @@ dpif_netlink_ct_flush(struct dpif *dpif OVS_UNUSED, const uint16_t *zone, /* Meters */ + +/* Set of supported meter flags */ +#define DP_SUPPORTED_METER_FLAGS_MASK \ + (OFPMF13_STATS | OFPMF13_PKTPS | OFPMF13_KBPS | OFPMF13_BURST) + +static void +dpif_netlink_meter_init(struct dpif_netlink *dpif, struct ofpbuf *buf, + void *stub, size_t size, uint32_t command) +{ + ofpbuf_use_stub(buf, stub, size); + + nl_msg_put_genlmsghdr(buf, 0, ovs_meter_family, NLM_F_REQUEST | NLM_F_ECHO, + command, OVS_METER_VERSION); + + struct ovs_header *ovs_header; + ovs_header = ofpbuf_put_uninit(buf, sizeof *ovs_header); + ovs_header->dp_ifindex = dpif->dp_ifindex; +} + +/* Execute meter 'request' in the kernel datapath. If the command + * fails, returns a positive errno value. Otherwise, stores the reply + * in '*replyp', parses the policy according to 'reply_policy' into the + * array of Netlink attribute in 'a', and returns 0. On success, the + * caller is responsible for calling ofpbuf_delete() on '*replyp' + * ('replyp' will contain pointers into 'a'). */ +static int +dpif_netlink_meter_transact(struct ofpbuf *request, struct ofpbuf **replyp, + const struct nl_policy *reply_policy, + struct nlattr **a, size_t size_a) +{ + int error = nl_transact(NETLINK_GENERIC, request, replyp); + ofpbuf_uninit(request); + + if (error) { + return error; + } + + struct nlmsghdr *nlmsg = ofpbuf_try_pull(*replyp, sizeof *nlmsg); + struct genlmsghdr *genl = ofpbuf_try_pull(*replyp, sizeof *genl); + struct ovs_header *ovs_header = ofpbuf_try_pull(*replyp, + sizeof *ovs_header); + if (!nlmsg || !genl || !ovs_header + || nlmsg->nlmsg_type != ovs_meter_family + || !nl_policy_parse(*replyp, 0, reply_policy, a, size_a)) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_DBG_RL(&rl, + "Kernel module response to meter tranaction is invalid"); + return EINVAL; + } + return 0; +} + static void -dpif_netlink_meter_get_features(const struct dpif * dpif OVS_UNUSED, +dpif_netlink_meter_get_features(const struct dpif *dpif_, struct ofputil_meter_features *features) { - features->max_meters = 0; - features->band_types = 0; - features->capabilities = 0; - features->max_bands = 0; - features->max_color = 0; + struct ofpbuf buf, *msg; + uint64_t stub[1024 / 8]; + + static const struct nl_policy ovs_meter_features_policy[] = { + [OVS_METER_ATTR_MAX_METERS] = { .type = NL_A_U32 }, + [OVS_METER_ATTR_MAX_BANDS] = { .type = NL_A_U32 }, + [OVS_METER_ATTR_BANDS] = { .type = NL_A_NESTED, .optional = true }, + }; + struct nlattr *a[ARRAY_SIZE(ovs_meter_features_policy)]; + + struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); + dpif_netlink_meter_init(dpif, &buf, stub, sizeof stub, + OVS_METER_CMD_FEATURES); + if (dpif_netlink_meter_transact(&buf, &msg, ovs_meter_features_policy, a, + ARRAY_SIZE(ovs_meter_features_policy))) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_INFO_RL(&rl, + "dpif_netlink_meter_transact OVS_METER_CMD_FEATURES failed"); + return; + } + + features->max_meters = nl_attr_get_u32(a[OVS_METER_ATTR_MAX_METERS]); + features->max_bands = nl_attr_get_u32(a[OVS_METER_ATTR_MAX_BANDS]); + + /* Bands is a nested attribute of zero or more nested + * band attributes. */ + if (a[OVS_METER_ATTR_BANDS]) { + const struct nlattr *nla; + size_t left; + + NL_NESTED_FOR_EACH (nla, left, a[OVS_METER_ATTR_BANDS]) { + const struct nlattr *band_nla; + size_t band_left; + + NL_NESTED_FOR_EACH (band_nla, band_left, nla) { + if (nl_attr_type(band_nla) == OVS_BAND_ATTR_TYPE) { + if (nl_attr_get_size(band_nla) == sizeof(uint32_t)) { + switch (nl_attr_get_u32(band_nla)) { + case OVS_METER_BAND_TYPE_DROP: + features->band_types |= 1 << OFPMBT13_DROP; + break; + } + } + } + } + } + } + features->capabilities = DP_SUPPORTED_METER_FLAGS_MASK; + + ofpbuf_delete(msg); } static int -dpif_netlink_meter_set(struct dpif *dpif OVS_UNUSED, - ofproto_meter_id *meter_id OVS_UNUSED, - struct ofputil_meter_config *config OVS_UNUSED) +dpif_netlink_meter_set(struct dpif *dpif_, ofproto_meter_id *meter_id, + struct ofputil_meter_config *config) { - return EFBIG; /* meter_id out of range */ + struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); + struct ofpbuf buf, *msg; + uint64_t stub[1024 / 8]; + + static const struct nl_policy ovs_meter_set_response_policy[] = { + [OVS_METER_ATTR_ID] = { .type = NL_A_U32 }, + }; + struct nlattr *a[ARRAY_SIZE(ovs_meter_set_response_policy)]; + + if (config->flags & ~DP_SUPPORTED_METER_FLAGS_MASK) { + return EBADF; /* Unsupported flags set */ + } + + for (size_t i = 0; i < config->n_bands; i++) { + switch (config->bands[i].type) { + case OFPMBT13_DROP: + break; + default: + return ENODEV; /* Unsupported band type */ + } + } + + dpif_netlink_meter_init(dpif, &buf, stub, sizeof stub, OVS_METER_CMD_SET); + + if (meter_id->uint32 != UINT32_MAX) { + nl_msg_put_u32(&buf, OVS_METER_ATTR_ID, meter_id->uint32); + } + if (config->flags & OFPMF13_KBPS) { + nl_msg_put_flag(&buf, OVS_METER_ATTR_KBPS); + } + + size_t bands_offset = nl_msg_start_nested(&buf, OVS_METER_ATTR_BANDS); + /* Bands */ + for (size_t i = 0; i < config->n_bands; ++i) { + struct ofputil_meter_band * band = &config->bands[i]; + uint32_t band_type; + + size_t band_offset = nl_msg_start_nested(&buf, OVS_BAND_ATTR_UNSPEC); + + switch (band->type) { + case OFPMBT13_DROP: + band_type = OVS_METER_BAND_TYPE_DROP; + break; + default: + band_type = OVS_METER_BAND_TYPE_UNSPEC; + } + nl_msg_put_u32(&buf, OVS_BAND_ATTR_TYPE, band_type); + nl_msg_put_u32(&buf, OVS_BAND_ATTR_RATE, band->rate); + nl_msg_put_u32(&buf, OVS_BAND_ATTR_BURST, + config->flags & OFPMF13_BURST ? + band->burst_size : band->rate); + nl_msg_end_nested(&buf, band_offset); + } + nl_msg_end_nested(&buf, bands_offset); + + int error = dpif_netlink_meter_transact(&buf, &msg, + ovs_meter_set_response_policy, a, + ARRAY_SIZE(ovs_meter_set_response_policy)); + if (error) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_INFO_RL(&rl, + "dpif_netlink_meter_transact OVS_METER_CMD_SET failed"); + return error; + } + + meter_id->uint32 = nl_attr_get_u32(a[OVS_METER_ATTR_ID]); + ofpbuf_delete(msg); + return 0; } +/* Retrieve statistics and/or delete meter 'meter_id'. Statistics are + * stored in 'stats', if it is not null. If 'command' is + * OVS_METER_CMD_DEL, the meter is deleted and statistics are optionally + * retrieved. If 'command' is OVS_METER_CMD_GET, then statistics are + * simply retrieved. */ static int -dpif_netlink_meter_get(const struct dpif *dpif OVS_UNUSED, - ofproto_meter_id meter_id OVS_UNUSED, - struct ofputil_meter_stats *stats OVS_UNUSED, - uint16_t n_bands OVS_UNUSED) +dpif_netlink_meter_get_stats(const struct dpif *dpif_, + ofproto_meter_id meter_id, + struct ofputil_meter_stats *stats, + uint16_t max_bands, + enum ovs_meter_cmd command) { - return EFBIG; /* meter_id out of range */ + struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); + struct ofpbuf buf, *msg; + uint64_t stub[1024 / 8]; + + static const struct nl_policy ovs_meter_stats_policy[] = { + [OVS_METER_ATTR_ID] = { .type = NL_A_U32, .optional = true}, + [OVS_METER_ATTR_STATS] = { NL_POLICY_FOR(struct ovs_flow_stats), + .optional = true}, + [OVS_METER_ATTR_BANDS] = { .type = NL_A_NESTED, .optional = true }, + }; + struct nlattr *a[ARRAY_SIZE(ovs_meter_stats_policy)]; + + dpif_netlink_meter_init(dpif, &buf, stub, sizeof stub, command); + + nl_msg_put_u32(&buf, OVS_METER_ATTR_ID, meter_id.uint32); + + int error = dpif_netlink_meter_transact(&buf, &msg, + ovs_meter_stats_policy, a, + ARRAY_SIZE(ovs_meter_stats_policy)); + if (error) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_INFO_RL(&rl, "dpif_netlink_meter_transact %s failed", + command == OVS_METER_CMD_GET ? "get" : "del"); + return error; + } + + if (stats + && a[OVS_METER_ATTR_ID] + && a[OVS_METER_ATTR_STATS] + && nl_attr_get_u32(a[OVS_METER_ATTR_ID]) == meter_id.uint32) { + /* return stats */ + const struct ovs_flow_stats *stat; + const struct nlattr *nla; + size_t left; + + stat = nl_attr_get(a[OVS_METER_ATTR_STATS]); + stats->packet_in_count = get_32aligned_u64(&stat->n_packets); + stats->byte_in_count = get_32aligned_u64(&stat->n_bytes); + + if (a[OVS_METER_ATTR_BANDS]) { + size_t n_bands = 0; + NL_NESTED_FOR_EACH (nla, left, a[OVS_METER_ATTR_BANDS]) { + const struct nlattr *band_nla; + band_nla = nl_attr_find_nested(nla, OVS_BAND_ATTR_STATS); + if (band_nla && nl_attr_get_size(band_nla) \ + == sizeof(struct ovs_flow_stats)) { + stat = nl_attr_get(band_nla); + + if (n_bands < max_bands) { + stats->bands[n_bands].packet_count + = get_32aligned_u64(&stat->n_packets); + stats->bands[n_bands].byte_count + = get_32aligned_u64(&stat->n_bytes); + ++n_bands; + } + } else { + stats->bands[n_bands].packet_count = 0; + stats->bands[n_bands].byte_count = 0; + ++n_bands; + } + } + stats->n_bands = n_bands; + } else { + /* For a non-existent meter, return 0 stats. */ + stats->n_bands = 0; + } + } + + ofpbuf_delete(msg); + return error; } static int -dpif_netlink_meter_del(struct dpif *dpif OVS_UNUSED, - ofproto_meter_id meter_id OVS_UNUSED, - struct ofputil_meter_stats *stats OVS_UNUSED, - uint16_t n_bands OVS_UNUSED) +dpif_netlink_meter_get(const struct dpif *dpif, ofproto_meter_id meter_id, + struct ofputil_meter_stats *stats, uint16_t max_bands) { - return EFBIG; /* meter_id out of range */ + return dpif_netlink_meter_get_stats(dpif, meter_id, stats, max_bands, + OVS_METER_CMD_GET); +} + +static int +dpif_netlink_meter_del(struct dpif *dpif, ofproto_meter_id meter_id, + struct ofputil_meter_stats *stats, uint16_t max_bands) +{ + return dpif_netlink_meter_get_stats(dpif, meter_id, stats, max_bands, + OVS_METER_CMD_DEL); } @@ -3040,6 +3286,11 @@ dpif_netlink_init(void) error = nl_lookup_genl_mcgroup(OVS_VPORT_FAMILY, OVS_VPORT_MCGROUP, &ovs_vport_mcgroup); } + if (!error) { + if (nl_lookup_genl_family(OVS_METER_FAMILY, &ovs_meter_family)) { + VLOG_INFO("The kernel module does not support meters."); + } + } ovs_tunnels_out_of_tree = dpif_netlink_rtnl_probe_oot_tunnels();