[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