[ovs-dev] [RFC PATCH v2 11/11] datapath: Simple DROP meter implementation.
Jarno Rajahalme
jrajahalme at nicira.com
Fri Sep 13 22:03:40 UTC 2013
This is a first stab at kernel datapath meter support. Only drop band type
is implemented. Things to consider:
- Is this the right approach, or are there alternative facilities in Linux
kernel that could be used to get this functionality? Tc has been mentioned
before, but even then a meter action is needed. Also, the meter needs to be
executable during action execution time for an arbitrary packet.
- The meter table size should probably be configurable.
Signed-off-by: Jarno Rajahalme <jrajahalme at nicira.com>
---
datapath/actions.c | 4 +
datapath/datapath.c | 563 +++++++++++++++++++++++++++++++++++++++++++++++++++
datapath/datapath.h | 32 +++
3 files changed, 599 insertions(+)
diff --git a/datapath/actions.c b/datapath/actions.c
index 30ea1d2..7795a62 100644
--- a/datapath/actions.c
+++ b/datapath/actions.c
@@ -528,6 +528,10 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
}
switch (nla_type(a)) {
+ case OVS_ACTION_ATTR_METER:
+ ovs_execute_meter_action(dp, skb, nla_get_u32(a));
+ break;
+
case OVS_ACTION_ATTR_OUTPUT:
prev_port = nla_get_u32(a);
break;
diff --git a/datapath/datapath.c b/datapath/datapath.c
index 4defcdb..a4cffaf 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -160,10 +160,13 @@ static int get_dpifindex(struct datapath *dp)
return ifindex;
}
+static void destroy_meters(struct datapath *dp);
+
static void destroy_dp_rcu(struct rcu_head *rcu)
{
struct datapath *dp = container_of(rcu, struct datapath, rcu);
+ destroy_meters(dp);
ovs_flow_tbl_destroy((__force struct flow_table *)dp->table, false);
free_percpu(dp->stats_percpu);
release_net(ovs_dp_get_net(dp));
@@ -774,6 +777,7 @@ static int validate_and_copy_actions(const struct nlattr *attr,
nla_for_each_nested(a, attr, rem) {
/* Expected argument lengths, (u32)-1 for variable length. */
static const u32 action_lens[OVS_ACTION_ATTR_MAX + 1] = {
+ [OVS_ACTION_ATTR_METER] = sizeof(u32),
[OVS_ACTION_ATTR_OUTPUT] = sizeof(u32),
[OVS_ACTION_ATTR_USERSPACE] = (u32)-1,
[OVS_ACTION_ATTR_PUSH_VLAN] = sizeof(struct ovs_action_push_vlan),
@@ -795,6 +799,10 @@ static int validate_and_copy_actions(const struct nlattr *attr,
case OVS_ACTION_ATTR_UNSPEC:
return -EINVAL;
+ case OVS_ACTION_ATTR_METER:
+ /* Non-existent meters are simply ignored. */
+ break;
+
case OVS_ACTION_ATTR_USERSPACE:
err = validate_userspace(a);
if (err)
@@ -1711,6 +1719,9 @@ 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]);
+ for (i = 0; i < DP_N_METER_LOCKS; i++)
+ spin_lock_init(&dp->meter_locks[i]);
+
/* Set up our datapath device. */
parms.name = nla_data(a[OVS_DP_ATTR_NAME]);
parms.type = OVS_VPORT_TYPE_INTERNAL;
@@ -2285,6 +2296,555 @@ static struct genl_ops dp_vport_genl_ops[] = {
},
};
+bool
+ovs_execute_meter_action(struct datapath *dp, struct sk_buff *skb,
+ u32 meter_id)
+{
+ struct dp_meter *meter;
+ struct dp_meter_band *band;
+ long long int now = jiffies_to_msecs(jiffies);
+ long long int long_delta_t; /* msec */
+ u32 delta_t; /* msec */
+ u32 volume;
+ int i, band_exceeded_max = -1;
+ u32 band_exceeded_rate = 0;
+ spinlock_t *lock = METER_LOCK(dp, meter_id);
+
+ if (meter_id >= DP_MAX_METERS)
+ return false;
+
+ /* Lock the meter so that it will not be swapped, deleted, or modified
+ * while we work with it. */
+ spin_lock(lock);
+ meter = dp->meters[meter_id];
+ if (!meter)
+ goto out;
+
+ long_delta_t = (now - meter->used); /* msec */
+
+ /* Make sure delta_t will not be too large, so that bucket will not
+ * wrap around below. */
+ delta_t = (long_delta_t > (long long int)meter->max_delta_t)
+ ? meter->max_delta_t : (u32)long_delta_t;
+
+ /* Update meter statistics. */
+ meter->used = now;
+ 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.
+ *
+ * 'volume' is the number of bucket units in this packet. */
+ volume = (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) {
+ band = &meter->bands[i];
+
+ band->bucket += delta_t * band->rate;
+ if (band->bucket > band->burst_size)
+ band->bucket = band->burst_size;
+
+ if (band->bucket >= volume)
+ band->bucket -= volume;
+ else if (band->rate > band_exceeded_rate) {
+ band_exceeded_rate = band->rate;
+ band_exceeded_max = i;
+ }
+ }
+
+ if (band_exceeded_max < 0) {
+ goto out; /* No band exceeded, do nothing. */
+ }
+
+ /* Update band statistics. */
+ band = &meter->bands[band_exceeded_max];
+ band->stats.n_packets += 1;
+ band->stats.n_bytes += skb->len;
+
+ if (band->type == OVS_METER_BAND_TYPE_DROP) {
+ spin_unlock(lock);
+ return true; /* drop */
+ }
+ out:
+ spin_unlock(lock);
+ return false;
+}
+
+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 struct genl_family dp_meter_genl_family = {
+ .id = GENL_ID_GENERATE,
+ .hdrsize = sizeof(struct ovs_header),
+ .name = OVS_METER_FAMILY,
+ .version = OVS_METER_VERSION,
+ .maxattr = OVS_METER_ATTR_MAX,
+ .netnsok = true,
+ SET_PARALLEL_OPS
+};
+
+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(reply, OVS_METER_ATTR_USED, meter->used))
+ 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 datapath *dp;
+ struct ovs_header *ovs_header = info->userhdr;
+ struct sk_buff *reply;
+ struct ovs_header *ovs_reply_header;
+ struct nlattr *nla, *band_nla;
+ int err;
+
+ /* Check that the datapath exists */
+ ovs_lock();
+ dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
+ ovs_unlock();
+ if (!dp)
+ return -ENODEV;
+
+ 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, DP_MAX_METERS) ||
+ 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;
+}
+
+static void destroy_meters(struct datapath *dp)
+{
+ int i;
+
+ for (i = 0; i < DP_MAX_METERS; i++)
+ if (dp->meters[i]) {
+ kfree(dp->meters[i]);
+ dp->meters[i] = NULL;
+ }
+}
+
+/* Must be called with ovs_lock() held. */
+static struct dp_meter * dp_meter_swap(struct datapath *dp, u32 meter_id,
+ struct dp_meter *meter)
+{
+ struct dp_meter *old_meter;
+ spinlock_t *lock = METER_LOCK(dp, meter_id);
+
+ /* Spin lock keeps us from swapping a meter while it is being
+ * used. */
+ spin_lock_bh(lock);
+ old_meter = dp->meters[meter_id];
+
+ /* Keep meter counts if requested. */
+ if (meter && old_meter && meter->keep_stats) {
+ meter->used = old_meter->used;
+ meter->stats.n_packets += old_meter->stats.n_packets;
+ meter->stats.n_bytes += old_meter->stats.n_bytes;
+ }
+
+ dp->meters[meter_id] = meter;
+ spin_unlock_bh(lock);
+
+ return old_meter;
+}
+
+/* Must be called with ovs_lock() held.
+ * Returns the old_meter, or NULL. */
+static struct dp_meter * dp_meter_del(struct datapath *dp, u32 meter_id)
+{
+ /* Keep free meter index as low as possible */
+ if (meter_id < dp->meter_free)
+ dp->meter_free = meter_id;
+
+ return dp_meter_swap(dp, meter_id, NULL);
+}
+
+/* Must be called with ovs_lock() held.
+ * Returns the old_meter, or NULL. */
+static struct dp_meter * dp_meter_set(struct datapath *dp, u32 meter_id,
+ struct dp_meter *meter)
+{
+ if (meter_id == dp->meter_free) { /* dp->meter_free now taken. */
+ u32 next_free = meter_id;
+ do {
+ if (++next_free >= DP_MAX_METERS) { /* Wrap around. */
+ next_free = 0;
+ }
+ if (next_free == dp->meter_free) { /* Full circle. */
+ next_free = DP_MAX_METERS;
+ break;
+ }
+ } while (dp->meters[next_free]);
+ dp->meter_free = next_free; /* May be DP_MAX_METERS. */
+ }
+
+ return dp_meter_swap(dp, meter_id, meter);
+}
+
+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(sizeof *meter
+ + n_bands * sizeof(struct dp_meter_band), GFP_KERNEL);
+ if (!meter)
+ return ERR_PTR(-ENOMEM);
+
+ meter->used = jiffies_to_msecs(jiffies);
+ meter->kbps = a[OVS_METER_ATTR_KBPS] != NULL;
+ meter->keep_stats = a[OVS_METER_ATTR_CLEAR] == NULL;
+ 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);
+ if (err)
+ goto exit_free_meter;
+
+ if (!attr[OVS_BAND_ATTR_TYPE] ||
+ !attr[OVS_BAND_ATTR_RATE] ||
+ !attr[OVS_BAND_ATTR_BURST] ||
+ nla_get_u32(attr[OVS_BAND_ATTR_TYPE])
+ != OVS_METER_BAND_TYPE_DROP) {
+ 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]);
+ /* Convert burst size to the bucket units:
+ * pkts => 1/1000 packets, kilobits => bits. */
+ band->burst_size
+ = nla_get_u32(attr[OVS_BAND_ATTR_BURST]) * 1000;
+ /* Start with an empty bucket. */
+ band->bucket = 0;
+ /* Figure out max delta_t that is enough to fill any bucket. */
+ band_max_delta_t = band->burst_size / band->rate;
+ 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;
+ }
+
+ meter_id = a[OVS_METER_ATTR_ID] ? nla_get_u32(a[OVS_METER_ATTR_ID]) :
+ dp->meter_free;
+
+ if (meter_id >= DP_MAX_METERS) {
+ err = -EFBIG;
+ goto exit_unlock;
+ }
+
+ /* Cannot fail after this. */
+ old_meter = dp_meter_set(dp, meter_id, 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);
+ BUG_ON(failed);
+ err = ovs_meter_cmd_reply_stats(reply, meter_id, old_meter);
+ if (old_meter)
+ kfree(old_meter);
+ BUG_ON(err); /* XXX: Make sure we have reserved enough space! */
+
+ 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;
+ spinlock_t *lock;
+ 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]);
+ if (meter_id >= DP_MAX_METERS)
+ return -EFBIG;
+
+ 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 = dp->meters[meter_id];
+ if (!meter) {
+ err = -ENOENT;
+ goto exit_unlock;
+ }
+
+ lock = METER_LOCK(dp, meter_id);
+ spin_lock_bh(lock);
+ err = ovs_meter_cmd_reply_stats(reply, meter_id, meter);
+ spin_unlock_bh(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 *meter;
+
+ if (!a[OVS_METER_ATTR_ID])
+ return -EINVAL;
+ meter_id = nla_get_u32(a[OVS_METER_ATTR_ID]);
+ if (meter_id >= DP_MAX_METERS)
+ return -EFBIG;
+
+ 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;
+ }
+ meter = dp_meter_del(dp, meter_id);
+ err = ovs_meter_cmd_reply_stats(reply, meter_id, meter);
+ BUG_ON(err);
+
+ if (meter)
+ kfree(meter);
+
+ ovs_unlock();
+ genlmsg_end(reply, ovs_reply_header);
+ return genlmsg_reply(reply, info);
+
+exit_unlock:
+ ovs_unlock();
+ nlmsg_free(reply);
+ return err;
+}
+
+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
+ },
+};
+
struct genl_family_and_ops {
struct genl_family *family;
struct genl_ops *ops;
@@ -2305,6 +2865,9 @@ static const struct genl_family_and_ops dp_genl_families[] = {
{ &dp_packet_genl_family,
dp_packet_genl_ops, ARRAY_SIZE(dp_packet_genl_ops),
NULL },
+ { &dp_meter_genl_family,
+ dp_meter_genl_ops, ARRAY_SIZE(dp_meter_genl_ops),
+ NULL },
};
static void dp_unregister_genl(int n_families)
diff --git a/datapath/datapath.h b/datapath/datapath.h
index 4a49a7d..4bc992a 100644
--- a/datapath/datapath.h
+++ b/datapath/datapath.h
@@ -36,6 +36,30 @@
#define SAMPLE_ACTION_DEPTH 3
+#define DP_MAX_METERS 65536
+#define DP_MAX_BANDS 1
+#define DP_N_METER_LOCKS 17
+
+#define METER_LOCK(DP, METER_ID) \
+ &(DP)->meter_locks[(METER_ID) % DP_N_METER_LOCKS]
+
+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 {
+ 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[];
+};
+
/**
* struct dp_stats_percpu - per-cpu packet processing statistics for a given
* datapath.
@@ -87,6 +111,11 @@ struct datapath {
struct net *net;
#endif
unsigned long last_rehash;
+
+ /* Meters. */
+ spinlock_t meter_locks[DP_N_METER_LOCKS];
+ struct dp_meter *meters[DP_MAX_METERS]; /* Meter bands. */
+ uint32_t meter_free; /* Next free meter. */
};
/**
@@ -194,4 +223,7 @@ void ovs_dp_notify_wq(struct work_struct *work);
#define OVS_NLERR(fmt, ...) \
pr_info_once("netlink: " fmt, ##__VA_ARGS__)
+bool ovs_execute_meter_action(struct datapath *dp, struct sk_buff *skb,
+ uint32_t meter_id);
+
#endif /* datapath.h */
--
1.7.10.4
More information about the dev
mailing list