On Tue, Jan 30, 2018 at 3:40 PM, Greg Rose <gvrose8...@gmail.com> wrote: > From: Andy Zhou <az...@ovn.org> > > Upstream commit: > commit 96fbc13d7e770b542d2d1fcf700d0baadc6e8063 > Author: Andy Zhou <az...@ovn.org> > Date: Fri Nov 10 12:09:42 2017 -0800 > > openvswitch: Add meter infrastructure > > OVS kernel datapath so far does not support Openflow meter action. > This is the first stab at adding kernel datapath meter support. > This implementation supports only drop band type. > > Signed-off-by: Andy Zhou <az...@ovn.org> > Signed-off-by: David S. Miller <da...@davemloft.net> > > Added a compat layer fixup for nla_parse. > Added another compat fixup for ktime_get_ns. > > Cc: Andy Zhou <az...@ovn.org> > Signed-off-by: Greg Rose <gvrose8...@gmail.com>
Recently another patch is merged related to metering, can you include that to have all fixes in kernel module: ---8<--- commit 5b7789e8fa8f353ad8f2c44de2385cb161b22d32 Author: Gustavo A. R. Silva <gust...@embeddedor.com> Date: Tue Jan 30 22:55:33 2018 -0600 openvswitch: meter: Use 64-bit arithmetic instead of 32-bit > --- > acinclude.m4 | 3 + > datapath/Modules.mk | 6 +- > datapath/datapath.c | 14 +- > datapath/datapath.h | 3 + > datapath/linux/compat/include/net/netlink.h | 9 + > datapath/meter.c | 614 > ++++++++++++++++++++++++++++ > datapath/meter.h | 54 +++ > 7 files changed, 699 insertions(+), 4 deletions(-) > create mode 100644 datapath/meter.c > create mode 100644 datapath/meter.h > > diff --git a/acinclude.m4 b/acinclude.m4 > index 1f0b592..bc1ec72 100644 > --- a/acinclude.m4 > +++ b/acinclude.m4 > @@ -804,6 +804,9 @@ AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [ > OVS_GREP_IFELSE([$KSRC/include/net/net_namespace.h], > [EXPORT_SYMBOL_GPL(peernet2id_alloc)], > [OVS_DEFINE([HAVE_PEERNET2ID_ALLOC])]) > + OVS_GREP_IFELSE([$KSRC/include/linux/timekeeping.h], > + [ktime_get_ns], > + [OVS_DEFINE([HAVE_KTIME_GET_NS])]) > > if cmp -s datapath/linux/kcompat.h.new \ > datapath/linux/kcompat.h >/dev/null 2>&1; then > diff --git a/datapath/Modules.mk b/datapath/Modules.mk > index 21f04a0..a9e2880 100644 > --- a/datapath/Modules.mk > +++ b/datapath/Modules.mk > @@ -26,7 +26,8 @@ openvswitch_sources = \ > flow_table.c \ > vport.c \ > vport-internal_dev.c \ > - vport-netdev.c > + vport-netdev.c \ > + meter.c > > vport_geneve_sources = vport-geneve.c > vport_vxlan_sources = vport-vxlan.c > @@ -43,7 +44,8 @@ openvswitch_headers = \ > flow_table.h \ > vport.h \ > vport-internal_dev.h \ > - vport-netdev.h > + vport-netdev.h \ > + meter.h > > dist_sources = $(foreach module,$(dist_modules),$($(module)_sources)) > dist_headers = $(foreach module,$(dist_modules),$($(module)_headers)) > diff --git a/datapath/datapath.c b/datapath/datapath.c > index 13b055a..07b6c71 100644 > --- a/datapath/datapath.c > +++ b/datapath/datapath.c > @@ -57,6 +57,7 @@ > #include "flow.h" > #include "flow_table.h" > #include "flow_netlink.h" > +#include "meter.h" > #include "gso.h" > #include "vport-internal_dev.h" > #include "vport-netdev.h" > @@ -177,6 +178,7 @@ static void destroy_dp_rcu(struct rcu_head *rcu) > ovs_flow_tbl_destroy(&dp->table); > free_percpu(dp->stats_percpu); > kfree(dp->ports); > + ovs_meters_exit(dp); > kfree(dp); > } > > @@ -1601,6 +1603,10 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct > genl_info *info) > for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) > INIT_HLIST_HEAD(&dp->ports[i]); > > + err = ovs_meters_init(dp); > + if (err) > + goto err_destroy_ports_array; > + > /* Set up our datapath device. */ > parms.name = nla_data(a[OVS_DP_ATTR_NAME]); > parms.type = OVS_VPORT_TYPE_INTERNAL; > @@ -1629,7 +1635,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct > genl_info *info) > ovs_dp_reset_user_features(skb, info); > } > > - goto err_destroy_ports_array; > + goto err_destroy_meters; > } > > err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid, > @@ -1644,8 +1650,10 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct > genl_info *info) > ovs_notify(&dp_datapath_genl_family, > &ovs_dp_datapath_multicast_group, reply, info); > return 0; > > -err_destroy_ports_array: > +err_destroy_meters: > ovs_unlock(); > + ovs_meters_exit(dp); > +err_destroy_ports_array: > kfree(dp->ports); > err_destroy_percpu: > free_percpu(dp->stats_percpu); > @@ -2295,6 +2303,7 @@ static struct genl_family *dp_genl_families[] = { > &dp_vport_genl_family, > &dp_flow_genl_family, > &dp_packet_genl_family, > + &dp_meter_genl_family, > }; > > static void dp_unregister_genl(int n_families) > @@ -2488,3 +2497,4 @@ MODULE_ALIAS_GENL_FAMILY(OVS_DATAPATH_FAMILY); > MODULE_ALIAS_GENL_FAMILY(OVS_VPORT_FAMILY); > MODULE_ALIAS_GENL_FAMILY(OVS_FLOW_FAMILY); > MODULE_ALIAS_GENL_FAMILY(OVS_PACKET_FAMILY); > +MODULE_ALIAS_GENL_FAMILY(OVS_METER_FAMILY); > diff --git a/datapath/datapath.h b/datapath/datapath.h > index 7481d6d..93c9ed5 100644 > --- a/datapath/datapath.h > +++ b/datapath/datapath.h > @@ -93,6 +93,9 @@ struct datapath { > u32 user_features; > > u32 max_headroom; > + > + /* Switch meters. */ > + struct hlist_head *meters; > }; > > /** > diff --git a/datapath/linux/compat/include/net/netlink.h > b/datapath/linux/compat/include/net/netlink.h > index 4325b9b..ba24a34 100644 > --- a/datapath/linux/compat/include/net/netlink.h > +++ b/datapath/linux/compat/include/net/netlink.h > @@ -169,6 +169,15 @@ static inline int rpl_nla_parse_nested(struct nlattr > *tb[], int maxtype, > return nla_parse_nested(tb, maxtype, nla, policy); > } > #define nla_parse_nested rpl_nla_parse_nested > + > +static inline int rpl_nla_parse(struct nlattr **tb, int maxtype, > + const struct nlattr *head, int len, > + const struct nla_policy *policy, > + struct netlink_ext_ack *extack) > +{ > + return nla_parse(tb, maxtype, head, len, policy); > +} > +#define nla_parse rpl_nla_parse > #endif > > #endif /* net/netlink.h */ > diff --git a/datapath/meter.c b/datapath/meter.c > new file mode 100644 > index 0000000..1ab965d > --- /dev/null > +++ b/datapath/meter.c > @@ -0,0 +1,614 @@ > +/* > + * Copyright (c) 2017 Nicira, Inc. > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of version 2 of the GNU General Public > + * License as published by the Free Software Foundation. > + */ > + > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt > + > +#include <linux/if.h> > +#include <linux/skbuff.h> > +#include <linux/ip.h> > +#include <linux/kernel.h> > +#include <linux/openvswitch.h> > +#include <linux/netlink.h> > +#include <linux/rculist.h> > + > +#include <net/netlink.h> > +#include <net/genetlink.h> > + > +#include "datapath.h" > +#include "meter.h" > + > +#define METER_HASH_BUCKETS 1024 > + > +static const struct nla_policy meter_policy[OVS_METER_ATTR_MAX + 1] = { > + [OVS_METER_ATTR_ID] = { .type = NLA_U32, }, > + [OVS_METER_ATTR_KBPS] = { .type = NLA_FLAG }, > + [OVS_METER_ATTR_STATS] = { .len = sizeof(struct ovs_flow_stats) }, > + [OVS_METER_ATTR_BANDS] = { .type = NLA_NESTED }, > + [OVS_METER_ATTR_USED] = { .type = NLA_U64 }, > + [OVS_METER_ATTR_CLEAR] = { .type = NLA_FLAG }, > + [OVS_METER_ATTR_MAX_METERS] = { .type = NLA_U32 }, > + [OVS_METER_ATTR_MAX_BANDS] = { .type = NLA_U32 }, > +}; > + > +static const struct nla_policy band_policy[OVS_BAND_ATTR_MAX + 1] = { > + [OVS_BAND_ATTR_TYPE] = { .type = NLA_U32, }, > + [OVS_BAND_ATTR_RATE] = { .type = NLA_U32, }, > + [OVS_BAND_ATTR_BURST] = { .type = NLA_U32, }, > + [OVS_BAND_ATTR_STATS] = { .len = sizeof(struct ovs_flow_stats) }, > +}; > + > +static void rcu_free_ovs_meter_callback(struct rcu_head *rcu) > +{ > + struct dp_meter *meter = container_of(rcu, struct dp_meter, rcu); > + > + kfree(meter); > +} > + > +static void ovs_meter_free(struct dp_meter *meter) > +{ > + if (!meter) > + return; > + > + call_rcu(&meter->rcu, rcu_free_ovs_meter_callback); > +} > + > +static struct hlist_head *meter_hash_bucket(const struct datapath *dp, > + u32 meter_id) > +{ > + return &dp->meters[meter_id & (METER_HASH_BUCKETS - 1)]; > +} > + > +/* Call with ovs_mutex or RCU read lock. */ > +static struct dp_meter *lookup_meter(const struct datapath *dp, > + u32 meter_id) > +{ > + struct dp_meter *meter; > + struct hlist_head *head; > + > + head = meter_hash_bucket(dp, meter_id); > + hlist_for_each_entry_rcu(meter, head, dp_hash_node) { > + if (meter->id == meter_id) > + return meter; > + } > + return NULL; > +} > + > +static void attach_meter(struct datapath *dp, struct dp_meter *meter) > +{ > + struct hlist_head *head = meter_hash_bucket(dp, meter->id); > + > + hlist_add_head_rcu(&meter->dp_hash_node, head); > +} > + > +static void detach_meter(struct dp_meter *meter) > +{ > + ASSERT_OVSL(); > + if (meter) > + hlist_del_rcu(&meter->dp_hash_node); > +} > + > +static struct sk_buff * > +ovs_meter_cmd_reply_start(struct genl_info *info, u8 cmd, > + struct ovs_header **ovs_reply_header) > +{ > + struct sk_buff *skb; > + struct ovs_header *ovs_header = info->userhdr; > + > + skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); > + if (!skb) > + return ERR_PTR(-ENOMEM); > + > + *ovs_reply_header = genlmsg_put(skb, info->snd_portid, > + info->snd_seq, > + &dp_meter_genl_family, 0, cmd); > + if (!ovs_reply_header) { > + nlmsg_free(skb); > + return ERR_PTR(-EMSGSIZE); > + } > + (*ovs_reply_header)->dp_ifindex = ovs_header->dp_ifindex; > + > + return skb; > +} > + > +static int ovs_meter_cmd_reply_stats(struct sk_buff *reply, u32 meter_id, > + struct dp_meter *meter) > +{ > + struct nlattr *nla; > + struct dp_meter_band *band; > + u16 i; > + > + if (nla_put_u32(reply, OVS_METER_ATTR_ID, meter_id)) > + goto error; > + > + if (!meter) > + return 0; > + > + if (nla_put(reply, OVS_METER_ATTR_STATS, > + sizeof(struct ovs_flow_stats), &meter->stats) || > + nla_put_u64_64bit(reply, OVS_METER_ATTR_USED, meter->used, > + OVS_METER_ATTR_PAD)) > + goto error; > + > + nla = nla_nest_start(reply, OVS_METER_ATTR_BANDS); > + if (!nla) > + goto error; > + > + band = meter->bands; > + > + for (i = 0; i < meter->n_bands; ++i, ++band) { > + struct nlattr *band_nla; > + > + band_nla = nla_nest_start(reply, OVS_BAND_ATTR_UNSPEC); > + if (!band_nla || nla_put(reply, OVS_BAND_ATTR_STATS, > + sizeof(struct ovs_flow_stats), > + &band->stats)) > + goto error; > + nla_nest_end(reply, band_nla); > + } > + nla_nest_end(reply, nla); > + > + return 0; > +error: > + return -EMSGSIZE; > +} > + > +static int ovs_meter_cmd_features(struct sk_buff *skb, struct genl_info > *info) > +{ > + struct sk_buff *reply; > + struct ovs_header *ovs_reply_header; > + struct nlattr *nla, *band_nla; > + int err; > + > + reply = ovs_meter_cmd_reply_start(info, OVS_METER_CMD_FEATURES, > + &ovs_reply_header); > + if (!reply) > + return PTR_ERR(reply); > + > + if (nla_put_u32(reply, OVS_METER_ATTR_MAX_METERS, U32_MAX) || > + nla_put_u32(reply, OVS_METER_ATTR_MAX_BANDS, DP_MAX_BANDS)) > + goto nla_put_failure; > + > + nla = nla_nest_start(reply, OVS_METER_ATTR_BANDS); > + if (!nla) > + goto nla_put_failure; > + > + band_nla = nla_nest_start(reply, OVS_BAND_ATTR_UNSPEC); > + if (!band_nla) > + goto nla_put_failure; > + /* Currently only DROP band type is supported. */ > + if (nla_put_u32(reply, OVS_BAND_ATTR_TYPE, OVS_METER_BAND_TYPE_DROP)) > + goto nla_put_failure; > + nla_nest_end(reply, band_nla); > + nla_nest_end(reply, nla); > + > + genlmsg_end(reply, ovs_reply_header); > + return genlmsg_reply(reply, info); > + > +nla_put_failure: > + nlmsg_free(reply); > + err = -EMSGSIZE; > + return err; > +} > + > +#ifndef HAVE_KTIME_GET_NS > +#ifndef ktime_to_ns > +#define ktime_to_ns(kt) ((kt).tv64) > +#endif > +static inline u64 ktime_get_ns(void) > +{ > + return ktime_to_ns(ktime_get()); > +} > +#endif > + Can you move compat code to compat header? > +static struct dp_meter *dp_meter_create(struct nlattr **a) > +{ > + struct nlattr *nla; > + int rem; > + u16 n_bands = 0; > + struct dp_meter *meter; > + struct dp_meter_band *band; > + int err; > + > + /* Validate attributes, count the bands. */ > + if (!a[OVS_METER_ATTR_BANDS]) > + return ERR_PTR(-EINVAL); > + > + nla_for_each_nested(nla, a[OVS_METER_ATTR_BANDS], rem) > + if (++n_bands > DP_MAX_BANDS) > + return ERR_PTR(-EINVAL); > + > + /* Allocate and set up the meter before locking anything. */ > + meter = kzalloc(n_bands * sizeof(struct dp_meter_band) + > + sizeof(*meter), GFP_KERNEL); > + if (!meter) > + return ERR_PTR(-ENOMEM); > + > + meter->used = div_u64(ktime_get_ns(), 1000 * 1000); > + meter->kbps = a[OVS_METER_ATTR_KBPS] ? 1 : 0; > + meter->keep_stats = !a[OVS_METER_ATTR_CLEAR]; > + spin_lock_init(&meter->lock); > + if (meter->keep_stats && a[OVS_METER_ATTR_STATS]) { > + meter->stats = *(struct ovs_flow_stats *) > + nla_data(a[OVS_METER_ATTR_STATS]); > + } > + meter->n_bands = n_bands; > + > + /* Set up meter bands. */ > + band = meter->bands; > + nla_for_each_nested(nla, a[OVS_METER_ATTR_BANDS], rem) { > + struct nlattr *attr[OVS_BAND_ATTR_MAX + 1]; > + u32 band_max_delta_t; > + > + err = nla_parse((struct nlattr **)&attr, OVS_BAND_ATTR_MAX, > + nla_data(nla), nla_len(nla), band_policy, > + NULL); > + if (err) > + goto exit_free_meter; > + > + if (!attr[OVS_BAND_ATTR_TYPE] || > + !attr[OVS_BAND_ATTR_RATE] || > + !attr[OVS_BAND_ATTR_BURST]) { > + err = -EINVAL; > + goto exit_free_meter; > + } > + > + band->type = nla_get_u32(attr[OVS_BAND_ATTR_TYPE]); > + band->rate = nla_get_u32(attr[OVS_BAND_ATTR_RATE]); > + band->burst_size = nla_get_u32(attr[OVS_BAND_ATTR_BURST]); > + /* Figure out max delta_t that is enough to fill any bucket. > + * Keep max_delta_t size to the bucket units: > + * pkts => 1/1000 packets, kilobits => bits. > + */ > + band_max_delta_t = (band->burst_size + band->rate) * 1000; > + /* Start with a full bucket. */ > + band->bucket = band_max_delta_t; > + if (band_max_delta_t > meter->max_delta_t) > + meter->max_delta_t = band_max_delta_t; > + band++; > + } > + > + return meter; > + > +exit_free_meter: > + kfree(meter); > + return ERR_PTR(err); > +} > + > +static int ovs_meter_cmd_set(struct sk_buff *skb, struct genl_info *info) > +{ > + struct nlattr **a = info->attrs; > + struct dp_meter *meter, *old_meter; > + struct sk_buff *reply; > + struct ovs_header *ovs_reply_header; > + struct ovs_header *ovs_header = info->userhdr; > + struct datapath *dp; > + int err; > + u32 meter_id; > + bool failed; > + > + meter = dp_meter_create(a); > + if (IS_ERR_OR_NULL(meter)) > + return PTR_ERR(meter); > + > + reply = ovs_meter_cmd_reply_start(info, OVS_METER_CMD_SET, > + &ovs_reply_header); > + if (IS_ERR(reply)) { > + err = PTR_ERR(reply); > + goto exit_free_meter; > + } > + > + ovs_lock(); > + dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); > + if (!dp) { > + err = -ENODEV; > + goto exit_unlock; > + } > + > + if (!a[OVS_METER_ATTR_ID]) { > + err = -ENODEV; > + goto exit_unlock; > + } > + > + meter_id = nla_get_u32(a[OVS_METER_ATTR_ID]); > + > + /* Cannot fail after this. */ > + old_meter = lookup_meter(dp, meter_id); > + detach_meter(old_meter); > + attach_meter(dp, meter); > + ovs_unlock(); > + > + /* Build response with the meter_id and stats from > + * the old meter, if any. > + */ > + failed = nla_put_u32(reply, OVS_METER_ATTR_ID, meter_id); > + WARN_ON(failed); > + if (old_meter) { > + spin_lock_bh(&old_meter->lock); > + if (old_meter->keep_stats) { > + err = ovs_meter_cmd_reply_stats(reply, meter_id, > + old_meter); > + WARN_ON(err); > + } > + spin_unlock_bh(&old_meter->lock); > + ovs_meter_free(old_meter); > + } > + > + genlmsg_end(reply, ovs_reply_header); > + return genlmsg_reply(reply, info); > + > +exit_unlock: > + ovs_unlock(); > + nlmsg_free(reply); > +exit_free_meter: > + kfree(meter); > + return err; > +} > + > +static int ovs_meter_cmd_get(struct sk_buff *skb, struct genl_info *info) > +{ > + struct nlattr **a = info->attrs; > + u32 meter_id; > + struct ovs_header *ovs_header = info->userhdr; > + struct ovs_header *ovs_reply_header; > + struct datapath *dp; > + int err; > + struct sk_buff *reply; > + struct dp_meter *meter; > + > + if (!a[OVS_METER_ATTR_ID]) > + return -EINVAL; > + > + meter_id = nla_get_u32(a[OVS_METER_ATTR_ID]); > + > + reply = ovs_meter_cmd_reply_start(info, OVS_METER_CMD_GET, > + &ovs_reply_header); > + if (IS_ERR(reply)) > + return PTR_ERR(reply); > + > + ovs_lock(); > + > + dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); > + if (!dp) { > + err = -ENODEV; > + goto exit_unlock; > + } > + > + /* Locate meter, copy stats. */ > + meter = lookup_meter(dp, meter_id); > + if (!meter) { > + err = -ENOENT; > + goto exit_unlock; > + } > + > + spin_lock_bh(&meter->lock); > + err = ovs_meter_cmd_reply_stats(reply, meter_id, meter); > + spin_unlock_bh(&meter->lock); > + if (err) > + goto exit_unlock; > + > + ovs_unlock(); > + > + genlmsg_end(reply, ovs_reply_header); > + return genlmsg_reply(reply, info); > + > +exit_unlock: > + ovs_unlock(); > + nlmsg_free(reply); > + return err; > +} > + > +static int ovs_meter_cmd_del(struct sk_buff *skb, struct genl_info *info) > +{ > + struct nlattr **a = info->attrs; > + u32 meter_id; > + struct ovs_header *ovs_header = info->userhdr; > + struct ovs_header *ovs_reply_header; > + struct datapath *dp; > + int err; > + struct sk_buff *reply; > + struct dp_meter *old_meter; > + > + if (!a[OVS_METER_ATTR_ID]) > + return -EINVAL; > + meter_id = nla_get_u32(a[OVS_METER_ATTR_ID]); > + > + reply = ovs_meter_cmd_reply_start(info, OVS_METER_CMD_DEL, > + &ovs_reply_header); > + if (IS_ERR(reply)) > + return PTR_ERR(reply); > + > + ovs_lock(); > + > + dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); > + if (!dp) { > + err = -ENODEV; > + goto exit_unlock; > + } > + > + old_meter = lookup_meter(dp, meter_id); > + if (old_meter) { > + spin_lock_bh(&old_meter->lock); > + err = ovs_meter_cmd_reply_stats(reply, meter_id, old_meter); > + WARN_ON(err); > + spin_unlock_bh(&old_meter->lock); > + detach_meter(old_meter); > + } > + ovs_unlock(); > + ovs_meter_free(old_meter); > + genlmsg_end(reply, ovs_reply_header); > + return genlmsg_reply(reply, info); > + > +exit_unlock: > + ovs_unlock(); > + nlmsg_free(reply); > + return err; > +} > + > +/* Meter action execution. > + * > + * Return true 'meter_id' drop band is triggered. The 'skb' should be > + * dropped by the caller'. > + */ > +bool ovs_meter_execute(struct datapath *dp, struct sk_buff *skb, > + struct sw_flow_key *key, u32 meter_id) > +{ > + struct dp_meter *meter; > + struct dp_meter_band *band; > + long long int now_ms = div_u64(ktime_get_ns(), 1000 * 1000); > + long long int long_delta_ms; > + u32 delta_ms; > + u32 cost; > + int i, band_exceeded_max = -1; > + u32 band_exceeded_rate = 0; > + > + meter = lookup_meter(dp, meter_id); > + /* Do not drop the packet when there is no meter. */ > + if (!meter) > + return false; > + > + /* Lock the meter while using it. */ > + spin_lock(&meter->lock); > + > + long_delta_ms = (now_ms - meter->used); /* ms */ > + > + /* Make sure delta_ms will not be too large, so that bucket will not > + * wrap around below. > + */ > + delta_ms = (long_delta_ms > (long long int)meter->max_delta_t) > + ? meter->max_delta_t : (u32)long_delta_ms; > + > + /* Update meter statistics. > + */ > + meter->used = now_ms; > + meter->stats.n_packets += 1; > + meter->stats.n_bytes += skb->len; > + > + /* Bucket rate is either in kilobits per second, or in packets per > + * second. We maintain the bucket in the units of either bits or > + * 1/1000th of a packet, correspondingly. > + * Then, when rate is multiplied with milliseconds, we get the > + * bucket units: > + * msec * kbps = bits, and > + * msec * packets/sec = 1/1000 packets. > + * > + * 'cost' is the number of bucket units in this packet. > + */ > + cost = (meter->kbps) ? skb->len * 8 : 1000; > + > + /* Update all bands and find the one hit with the highest rate. */ > + for (i = 0; i < meter->n_bands; ++i) { > + long long int max_bucket_size; > + > + band = &meter->bands[i]; > + max_bucket_size = (band->burst_size + band->rate) * 1000; > + > + band->bucket += delta_ms * band->rate; > + if (band->bucket > max_bucket_size) > + band->bucket = max_bucket_size; > + > + if (band->bucket >= cost) { > + band->bucket -= cost; > + } else if (band->rate > band_exceeded_rate) { > + band_exceeded_rate = band->rate; > + band_exceeded_max = i; > + } > + } > + > + if (band_exceeded_max >= 0) { > + /* Update band statistics. */ > + band = &meter->bands[band_exceeded_max]; > + band->stats.n_packets += 1; > + band->stats.n_bytes += skb->len; > + > + /* Drop band triggered, let the caller drop the 'skb'. */ > + if (band->type == OVS_METER_BAND_TYPE_DROP) { > + spin_unlock(&meter->lock); > + return true; > + } > + } > + > + spin_unlock(&meter->lock); > + return false; > +} > + > +static struct genl_ops dp_meter_genl_ops[] = { > + { .cmd = OVS_METER_CMD_FEATURES, > + .flags = 0, /* OK for unprivileged users. */ > + .policy = meter_policy, > + .doit = ovs_meter_cmd_features > + }, > + { .cmd = OVS_METER_CMD_SET, > + .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN > + * privilege. > + */ > + .policy = meter_policy, > + .doit = ovs_meter_cmd_set, > + }, > + { .cmd = OVS_METER_CMD_GET, > + .flags = 0, /* OK for unprivileged users. */ > + .policy = meter_policy, > + .doit = ovs_meter_cmd_get, > + }, > + { .cmd = OVS_METER_CMD_DEL, > + .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN > + * privilege. > + */ > + .policy = meter_policy, > + .doit = ovs_meter_cmd_del > + }, > +}; > + > +static const struct genl_multicast_group ovs_meter_multicast_group = { > + .name = OVS_METER_MCGROUP, > +}; > + > +struct genl_family dp_meter_genl_family __ro_after_init = { > + .hdrsize = sizeof(struct ovs_header), > + .name = OVS_METER_FAMILY, > + .version = OVS_METER_VERSION, > + .maxattr = OVS_METER_ATTR_MAX, > + .netnsok = true, > + .parallel_ops = true, > + .ops = dp_meter_genl_ops, > + .n_ops = ARRAY_SIZE(dp_meter_genl_ops), > + .mcgrps = &ovs_meter_multicast_group, > + .n_mcgrps = 1, > + .module = THIS_MODULE, > +}; > + > +int ovs_meters_init(struct datapath *dp) > +{ > + int i; > + > + dp->meters = kmalloc_array(METER_HASH_BUCKETS, > + sizeof(struct hlist_head), GFP_KERNEL); > + > + if (!dp->meters) > + return -ENOMEM; > + > + for (i = 0; i < METER_HASH_BUCKETS; i++) > + INIT_HLIST_HEAD(&dp->meters[i]); > + > + return 0; > +} > + > +void ovs_meters_exit(struct datapath *dp) > +{ > + int i; > + > + for (i = 0; i < METER_HASH_BUCKETS; i++) { > + struct hlist_head *head = &dp->meters[i]; > + struct dp_meter *meter; > + struct hlist_node *n; > + > + hlist_for_each_entry_safe(meter, n, head, dp_hash_node) > + kfree(meter); > + } > + > + kfree(dp->meters); > +} > diff --git a/datapath/meter.h b/datapath/meter.h > new file mode 100644 > index 0000000..964ace2 > --- /dev/null > +++ b/datapath/meter.h > @@ -0,0 +1,54 @@ > +/* > + * Copyright (c) 2017 Nicira, Inc. > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of version 2 of the GNU General Public > + * License as published by the Free Software Foundation. > + */ > + > +#ifndef METER_H > +#define METER_H 1 > + > +#include <linux/init.h> > +#include <linux/module.h> > +#include <linux/kernel.h> > +#include <linux/netlink.h> > +#include <linux/openvswitch.h> > +#include <linux/genetlink.h> > +#include <linux/skbuff.h> > + > +#include "flow.h" > +struct datapath; > + > +#define DP_MAX_BANDS 1 > + > +struct dp_meter_band { > + u32 type; > + u32 rate; > + u32 burst_size; > + u32 bucket; /* 1/1000 packets, or in bits */ > + struct ovs_flow_stats stats; > +}; > + > +struct dp_meter { > + spinlock_t lock; /* Per meter lock */ > + struct rcu_head rcu; > + struct hlist_node dp_hash_node; /*Element in datapath->meters > + * hash table. > + */ > + u32 id; > + u16 kbps:1, keep_stats:1; > + u16 n_bands; > + u32 max_delta_t; > + u64 used; > + struct ovs_flow_stats stats; > + struct dp_meter_band bands[]; > +}; > + > +extern struct genl_family dp_meter_genl_family; > +int ovs_meters_init(struct datapath *dp); > +void ovs_meters_exit(struct datapath *dp); > +bool ovs_meter_execute(struct datapath *dp, struct sk_buff *skb, > + struct sw_flow_key *key, u32 meter_id); > + > +#endif /* meter.h */ > -- > 1.8.3.1 > > _______________________________________________ > dev mailing list > d...@openvswitch.org > https://mail.openvswitch.org/mailman/listinfo/ovs-dev _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev