[ovs-dev] [PATCH 06/13] ofproto: Add generic ofproto_collection.
Jarno Rajahalme
jarno at ovn.org
Fri Jul 15 10:19:12 UTC 2016
Define rule_collection in terms of a new ofproto_collection. This
makes it easier to add other types of collections later.
This patch makes no functional changes.
Signed-off-by: Jarno Rajahalme <jarno at ovn.org>
---
ofproto/ofproto-provider.h | 112 ++++++++++++++++++---
ofproto/ofproto.c | 242 +++++++++++++++++++++------------------------
2 files changed, 211 insertions(+), 143 deletions(-)
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index 86b5062..ebc0ee3 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -450,23 +450,107 @@ void rule_actions_destroy(const struct rule_actions *);
bool ofproto_rule_has_out_port(const struct rule *, ofp_port_t port)
OVS_REQUIRES(ofproto_mutex);
-/* A set of rules to which an OpenFlow operation applies. */
-struct rule_collection {
- struct rule **rules; /* The rules. */
- size_t n; /* Number of rules collected. */
+/* A set of ofproto objects to which an OpenFlow operation applies. */
+struct ofproto_collection {
+ void **objs; /* Objects. */
+ size_t n; /* Number of objects collected. */
- size_t capacity; /* Number of rules that will fit in 'rules'. */
- struct rule *stub[5]; /* Preallocated rules to avoid malloc(). */
+ size_t capacity; /* Number of objects that fit in 'objs'. */
+ void *stub[5]; /* Preallocated array to avoid malloc(). */
};
-void rule_collection_init(struct rule_collection *);
-void rule_collection_add(struct rule_collection *, struct rule *);
-void rule_collection_remove(struct rule_collection *, struct rule *);
-void rule_collection_move(struct rule_collection *to,
- struct rule_collection *from);
-void rule_collection_ref(struct rule_collection *) OVS_REQUIRES(ofproto_mutex);
-void rule_collection_unref(struct rule_collection *);
-void rule_collection_destroy(struct rule_collection *);
+void ofproto_collection_init(struct ofproto_collection *);
+void ofproto_collection_add(struct ofproto_collection *, void *);
+void ofproto_collection_remove(struct ofproto_collection *, void *);
+void ofproto_collection_move(struct ofproto_collection *to,
+ struct ofproto_collection *from);
+void *ofproto_collection_detach(struct ofproto_collection *);
+void ofproto_collection_destroy(struct ofproto_collection *);
+
+
+#define DECL_COLLECTION(TYPE, NAME) \
+struct NAME##_collection { \
+ struct ofproto_collection collection; \
+}; \
+ \
+static inline void NAME##_collection_init(struct NAME##_collection *coll) \
+{ \
+ ofproto_collection_init(&coll->collection); \
+} \
+ \
+static inline void NAME##_collection_add(struct NAME##_collection *coll, \
+ TYPE obj) \
+{ \
+ ofproto_collection_add(&coll->collection, obj); \
+} \
+ \
+static inline void NAME##_collection_remove(struct NAME##_collection *coll, \
+ TYPE obj) \
+{ \
+ ofproto_collection_remove(&coll->collection, obj); \
+} \
+ \
+static inline void NAME##_collection_move(struct NAME##_collection *to, \
+ struct NAME##_collection *from) \
+{ \
+ ofproto_collection_move(&to->collection, &from->collection); \
+} \
+ \
+static inline void NAME##_collection_destroy(struct NAME##_collection *coll) \
+{ \
+ ofproto_collection_destroy(&coll->collection); \
+} \
+ \
+static inline TYPE* NAME##_collection_##NAME##s(const struct NAME##_collection *coll) \
+{ \
+ return (TYPE*)coll->collection.objs; \
+} \
+ \
+static inline TYPE* NAME##_collection_stub(struct NAME##_collection *coll) \
+{ \
+ return (TYPE*)coll->collection.stub; \
+} \
+ \
+static inline size_t NAME##_collection_n(const struct NAME##_collection *coll) \
+{ \
+ return coll->collection.n; \
+} \
+ \
+static inline void NAME##_collection_ref(struct NAME##_collection *coll) \
+ OVS_REQUIRES(ofproto_mutex) \
+{ \
+ for (size_t i = 0; i < coll->collection.n; i++) { \
+ ofproto_##NAME##_ref((TYPE)coll->collection.objs[i]); \
+ } \
+} \
+ \
+static inline void NAME##_collection_unref(struct NAME##_collection *coll) \
+{ \
+ for (size_t i = 0; i < coll->collection.n; i++) { \
+ ofproto_##NAME##_unref((TYPE)coll->collection.objs[i]); \
+ } \
+} \
+ \
+static inline TYPE* NAME##_collection_detach(struct NAME##_collection *coll) \
+{ \
+ return (TYPE*)ofproto_collection_detach(&coll->collection); \
+} \
+
+DECL_COLLECTION(struct rule *, rule)
+
+#define RULE_COLLECTION_FOR_EACH(RULE, RULES) \
+ for (size_t i__ = 0; \
+ i__ < rule_collection_n(RULES) \
+ ? (RULE = rule_collection_rules(RULES)[i__]) : false; \
+ i__++)
+
+#define RULE_COLLECTIONS_FOR_EACH(RULE1, RULE2, RULES1, RULES2) \
+ for (size_t i__ = 0; \
+ i__ < rule_collection_n(RULES1) \
+ ? ((RULE1 = rule_collection_rules(RULES1)[i__]), \
+ (RULE2 = rule_collection_rules(RULES2)[i__])) \
+ : false; \
+ i__++)
/* Limits the number of flows allowed in the datapath. Only affects the
* ofproto-dpif implementation. */
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 302161b..ac587f7 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -2964,7 +2964,7 @@ ofproto_group_unref(struct ofgroup *group)
OVS_NO_THREAD_SAFETY_ANALYSIS
{
if (group && ovs_refcount_unref_relaxed(&group->ref_count) == 1) {
- ovs_assert(group->rules.n == 0);
+ ovs_assert(rule_collection_n(&group->rules) == 0);
ovsrcu_postpone(group_destroy_cb, group);
}
}
@@ -4025,133 +4025,114 @@ rule_criteria_destroy(struct rule_criteria *criteria)
}
void
-rule_collection_init(struct rule_collection *rules)
+ofproto_collection_init(struct ofproto_collection *coll)
{
- rules->rules = rules->stub;
- rules->n = 0;
- rules->capacity = ARRAY_SIZE(rules->stub);
+ coll->objs = coll->stub;
+ coll->n = 0;
+ coll->capacity = ARRAY_SIZE(coll->stub);
}
void
-rule_collection_add(struct rule_collection *rules, struct rule *rule)
+ofproto_collection_add(struct ofproto_collection *coll, void *obj)
{
- if (rules->n >= rules->capacity) {
+ if (coll->n >= coll->capacity) {
size_t old_size, new_size;
- old_size = rules->capacity * sizeof *rules->rules;
- rules->capacity *= 2;
- new_size = rules->capacity * sizeof *rules->rules;
+ old_size = coll->capacity * sizeof *coll->objs;
+ coll->capacity *= 2;
+ new_size = coll->capacity * sizeof *coll->objs;
- if (rules->rules == rules->stub) {
- rules->rules = xmalloc(new_size);
- memcpy(rules->rules, rules->stub, old_size);
+ if (coll->objs == coll->stub) {
+ coll->objs = xmalloc(new_size);
+ memcpy(coll->objs, coll->stub, old_size);
} else {
- rules->rules = xrealloc(rules->rules, new_size);
+ coll->objs = xrealloc(coll->objs, new_size);
}
}
- rules->rules[rules->n++] = rule;
+ coll->objs[coll->n++] = obj;
}
void
-rule_collection_remove(struct rule_collection *rules, struct rule *rule)
+ofproto_collection_remove(struct ofproto_collection *coll, void *obj)
{
size_t i;
- for (i = 0; i < rules->n; i++) {
- if (rules->rules[i] == rule) {
+ for (i = 0; i < coll->n; i++) {
+ if (coll->objs[i] == obj) {
break;
}
}
- if (i == rules->n) {
+ if (i == coll->n) {
return;
}
- rules->n--;
+ coll->n--;
/* Swap the last item in if needed. */
- if (i != rules->n) {
- rules->rules[i] = rules->rules[rules->n];
+ if (i != coll->n) {
+ coll->objs[i] = coll->objs[coll->n];
}
- /* Shrink? */
- if (rules->rules != rules->stub && rules->n <= rules->capacity / 2) {
- size_t old_size, new_size;
+ /* Shrink? Watermark at '/ 4' to avoid hysteresis, but leave spare
+ * capacity. */
+ if (coll->objs != coll->stub && coll->n <= coll->capacity / 4) {
+ size_t actual_size, new_size;
- old_size = rules->capacity * sizeof *rules->rules;
- rules->capacity /= 2;
- new_size = rules->capacity * sizeof *rules->rules;
+ actual_size = coll->n * sizeof *coll->objs;
+ coll->capacity /= 2;
+ new_size = coll->capacity * sizeof *coll->objs;
- if (new_size <= sizeof(rules->stub)) {
- memcpy(rules->stub, rules->rules, new_size);
- free(rules->rules);
- rules->rules = rules->stub;
+ if (new_size <= sizeof(coll->stub)) {
+ memcpy(coll->stub, coll->objs, actual_size);
+ free(coll->objs);
+ coll->objs = coll->stub;
} else {
- rules->rules = xrealloc(rules->rules, new_size);
+ coll->objs = xrealloc(coll->objs, new_size);
}
}
}
void
-rule_collection_move(struct rule_collection *to, struct rule_collection *from)
+ofproto_collection_move(struct ofproto_collection *to,
+ struct ofproto_collection *from)
{
ovs_assert(to->n == 0);
*to = *from;
- if (from->rules == from->stub) {
- to->rules = to->stub;
- }
- rule_collection_init(from);
-}
-
-void
-rule_collection_ref(struct rule_collection *rules)
- OVS_REQUIRES(ofproto_mutex)
-{
- size_t i;
-
- for (i = 0; i < rules->n; i++) {
- ofproto_rule_ref(rules->rules[i]);
+ if (from->objs == from->stub) {
+ to->objs = to->stub;
}
+ ofproto_collection_init(from);
}
-void
-rule_collection_unref(struct rule_collection *rules)
-{
- size_t i;
-
- for (i = 0; i < rules->n; i++) {
- ofproto_rule_unref(rules->rules[i]);
- }
-}
-
-/* Returns a NULL-terminated array of rule pointers,
+/* Returns a NULL-terminated array of object pointers,
* destroys 'rules'. */
-static struct rule **
-rule_collection_detach(struct rule_collection *rules)
+void *
+ofproto_collection_detach(struct ofproto_collection *coll)
{
- struct rule **rule_array;
+ void **array;
- rule_collection_add(rules, NULL);
+ ofproto_collection_add(coll, NULL);
- if (rules->rules == rules->stub) {
- rules->rules = xmemdup(rules->rules, rules->n * sizeof *rules->rules);
+ if (coll->objs == coll->stub) {
+ coll->objs = xmemdup(coll->objs, coll->n * sizeof *coll->objs);
}
- rule_array = rules->rules;
- rule_collection_init(rules);
+ array = coll->objs;
+ ofproto_collection_init(coll);
- return rule_array;
+ return array;
}
void
-rule_collection_destroy(struct rule_collection *rules)
+ofproto_collection_destroy(struct ofproto_collection *coll)
{
- if (rules->rules != rules->stub) {
- free(rules->rules);
+ if (coll->objs != coll->stub) {
+ free(coll->objs);
}
/* Make repeated destruction harmless. */
- rule_collection_init(rules);
+ ofproto_collection_init(coll);
}
/* Schedules postponed removal of rules, destroys 'rules'. */
@@ -4159,10 +4140,10 @@ static void
rule_collection_remove_postponed(struct rule_collection *rules)
OVS_REQUIRES(ofproto_mutex)
{
- if (rules->n > 0) {
- if (rules->n == 1) {
- ovsrcu_postpone(remove_rule_rcu, rules->rules[0]);
- rules->n = 0;
+ if (rule_collection_n(rules) > 0) {
+ if (rule_collection_n(rules) == 1) {
+ ovsrcu_postpone(remove_rule_rcu, rule_collection_rules(rules)[0]);
+ rule_collection_init(rules);
} else {
ovsrcu_postpone(remove_rules_rcu, rule_collection_detach(rules));
}
@@ -4244,7 +4225,7 @@ collect_rules_loose(struct ofproto *ofproto,
}
exit:
- if (!error && !rules->n && n_readonly) {
+ if (!error && !rule_collection_n(rules) && n_readonly) {
/* We didn't find any rules to modify. We did find some read-only
* rules that we're not allowed to modify, so report that. */
error = OFPERR_OFPBRC_EPERM;
@@ -4302,7 +4283,7 @@ collect_rules_strict(struct ofproto *ofproto,
}
exit:
- if (!error && !rules->n && n_readonly) {
+ if (!error && !rule_collection_n(rules) && n_readonly) {
/* We didn't find any rules to modify. We did find some read-only
* rules that we're not allowed to modify, so report that. */
error = OFPERR_OFPBRC_EPERM;
@@ -4334,7 +4315,6 @@ handle_flow_stats_request(struct ofconn *ofconn,
struct rule_collection rules;
struct ovs_list replies;
enum ofperr error;
- size_t i;
error = ofputil_decode_flow_stats_request(&fsr, request);
if (error) {
@@ -4358,8 +4338,8 @@ handle_flow_stats_request(struct ofconn *ofconn,
}
ofpmp_init(&replies, request);
- for (i = 0; i < rules.n; i++) {
- struct rule *rule = rules.rules[i];
+ struct rule *rule;
+ RULE_COLLECTION_FOR_EACH (rule, &rules) {
long long int now = time_msec();
struct ofputil_flow_stats fs;
long long int created, used, modified;
@@ -4498,7 +4478,6 @@ handle_aggregate_stats_request(struct ofconn *ofconn,
struct rule_collection rules;
struct ofpbuf *reply;
enum ofperr error;
- size_t i;
error = ofputil_decode_flow_stats_request(&request, oh);
if (error) {
@@ -4523,8 +4502,9 @@ handle_aggregate_stats_request(struct ofconn *ofconn,
memset(&stats, 0, sizeof stats);
unknown_packets = unknown_bytes = false;
- for (i = 0; i < rules.n; i++) {
- struct rule *rule = rules.rules[i];
+
+ struct rule *rule;
+ RULE_COLLECTION_FOR_EACH (rule, &rules) {
uint64_t packet_count;
uint64_t byte_count;
long long int used;
@@ -4741,8 +4721,8 @@ add_flow_start(struct ofproto *ofproto, struct ofproto_flow_mod *ofm)
OVS_REQUIRES(ofproto_mutex)
{
struct ofputil_flow_mod *fm = &ofm->fm;
- struct rule **old_rule = &ofm->old_rules.stub[0];
- struct rule **new_rule = &ofm->new_rules.stub[0];
+ struct rule **old_rule = rule_collection_stub(&ofm->old_rules);
+ struct rule **new_rule = rule_collection_stub(&ofm->new_rules);
struct oftable *table;
struct cls_rule cr;
struct rule *rule;
@@ -4840,8 +4820,8 @@ add_flow_revert(struct ofproto *ofproto, struct ofproto_flow_mod *ofm)
OVS_REQUIRES(ofproto_mutex)
{
struct ofputil_flow_mod *fm = &ofm->fm;
- struct rule *old_rule = ofm->old_rules.stub[0];
- struct rule *new_rule = ofm->new_rules.stub[0];
+ struct rule *old_rule = rule_collection_stub(&ofm->old_rules)[0];
+ struct rule *new_rule = rule_collection_stub(&ofm->new_rules)[0];
if (old_rule && fm->delete_reason == OFPRR_EVICTION) {
/* Revert the eviction. */
@@ -4858,8 +4838,8 @@ add_flow_finish(struct ofproto *ofproto, struct ofproto_flow_mod *ofm,
OVS_REQUIRES(ofproto_mutex)
{
struct ofputil_flow_mod *fm = &ofm->fm;
- struct rule *old_rule = ofm->old_rules.stub[0];
- struct rule *new_rule = ofm->new_rules.stub[0];
+ struct rule *old_rule = rule_collection_stub(&ofm->old_rules)[0];
+ struct rule *new_rule = rule_collection_stub(&ofm->new_rules)[0];
struct ovs_list dead_cookies = OVS_LIST_INITIALIZER(&dead_cookies);
replace_rule_finish(ofproto, fm, req, old_rule, new_rule, &dead_cookies);
@@ -5081,14 +5061,13 @@ modify_flows_start__(struct ofproto *ofproto, struct ofproto_flow_mod *ofm)
rule_collection_init(new_rules);
- if (old_rules->n > 0) {
+ if (rule_collection_n(old_rules) > 0) {
struct cls_conjunction *conjs;
size_t n_conjs;
- size_t i;
/* Create a new 'modified' rule for each old rule. */
- for (i = 0; i < old_rules->n; i++) {
- struct rule *old_rule = old_rules->rules[i];
+ struct rule *old_rule;
+ RULE_COLLECTION_FOR_EACH (old_rule, old_rules) {
struct rule *new_rule;
struct cls_rule cr;
@@ -5103,12 +5082,14 @@ modify_flows_start__(struct ofproto *ofproto, struct ofproto_flow_mod *ofm)
return error;
}
}
- ovs_assert(new_rules->n == old_rules->n);
+ ovs_assert(rule_collection_n(new_rules)
+ == rule_collection_n(old_rules));
get_conjunctions(fm, &conjs, &n_conjs);
- for (i = 0; i < old_rules->n; i++) {
- replace_rule_start(ofproto, ofm->version, old_rules->rules[i],
- new_rules->rules[i], conjs, n_conjs);
+ struct rule *new_rule;
+ RULE_COLLECTIONS_FOR_EACH (old_rule, new_rule, old_rules, new_rules) {
+ replace_rule_start(ofproto, ofm->version, old_rule, new_rule,
+ conjs, n_conjs);
}
free(conjs);
} else if (!(fm->cookie_mask != htonll(0)
@@ -5117,9 +5098,9 @@ modify_flows_start__(struct ofproto *ofproto, struct ofproto_flow_mod *ofm)
error = add_flow_start(ofproto, ofm);
if (!error) {
ovs_assert(fm->delete_reason == OFPRR_EVICTION
- || !old_rules->rules[0]);
+ || !rule_collection_rules(old_rules)[0]);
}
- new_rules->n = 1;
+ new_rules->collection.n = 1;
} else {
error = 0;
}
@@ -5166,12 +5147,13 @@ modify_flows_revert(struct ofproto *ofproto, struct ofproto_flow_mod *ofm)
struct rule_collection *new_rules = &ofm->new_rules;
/* Old rules were not changed yet, only need to revert new rules. */
- if (old_rules->n == 0 && new_rules->n == 1) {
+ if (rule_collection_n(old_rules) == 0
+ && rule_collection_n(new_rules) == 1) {
add_flow_revert(ofproto, ofm);
- } else if (old_rules->n > 0) {
- for (size_t i = 0; i < old_rules->n; i++) {
- replace_rule_revert(ofproto, old_rules->rules[i],
- new_rules->rules[i]);
+ } else if (rule_collection_n(old_rules) > 0) {
+ struct rule *old_rule, *new_rule;
+ RULE_COLLECTIONS_FOR_EACH (old_rule, new_rule, old_rules, new_rules) {
+ replace_rule_revert(ofproto, old_rule, new_rule);
}
rule_collection_destroy(new_rules);
rule_collection_destroy(old_rules);
@@ -5187,21 +5169,25 @@ modify_flows_finish(struct ofproto *ofproto, struct ofproto_flow_mod *ofm,
struct rule_collection *old_rules = &ofm->old_rules;
struct rule_collection *new_rules = &ofm->new_rules;
- if (old_rules->n == 0 && new_rules->n == 1) {
+ if (rule_collection_n(old_rules) == 0
+ && rule_collection_n(new_rules) == 1) {
add_flow_finish(ofproto, ofm, req);
- } else if (old_rules->n > 0) {
+ } else if (rule_collection_n(old_rules) > 0) {
struct ovs_list dead_cookies = OVS_LIST_INITIALIZER(&dead_cookies);
- ovs_assert(new_rules->n == old_rules->n);
+ ovs_assert(rule_collection_n(new_rules)
+ == rule_collection_n(old_rules));
- for (size_t i = 0; i < old_rules->n; i++) {
- replace_rule_finish(ofproto, fm, req, old_rules->rules[i],
- new_rules->rules[i], &dead_cookies);
+ struct rule *old_rule, *new_rule;
+ RULE_COLLECTIONS_FOR_EACH (old_rule, new_rule, old_rules, new_rules) {
+ replace_rule_finish(ofproto, fm, req, old_rule, new_rule,
+ &dead_cookies);
}
learned_cookies_flush(ofproto, &dead_cookies);
rule_collection_remove_postponed(old_rules);
- send_buffered_packet(req, fm->buffer_id, new_rules->rules[0]);
+ send_buffered_packet(req, fm->buffer_id,
+ rule_collection_rules(new_rules)[0]);
rule_collection_destroy(new_rules);
}
}
@@ -5243,8 +5229,9 @@ delete_flows_start__(struct ofproto *ofproto, ovs_version_t version,
const struct rule_collection *rules)
OVS_REQUIRES(ofproto_mutex)
{
- for (size_t i = 0; i < rules->n; i++) {
- struct rule *rule = rules->rules[i];
+ struct rule *rule;
+
+ RULE_COLLECTION_FOR_EACH (rule, rules) {
struct oftable *table = &ofproto->tables[rule->table_id];
table->n_flows--;
@@ -5262,12 +5249,11 @@ delete_flows_finish__(struct ofproto *ofproto,
const struct flow_mod_requester *req)
OVS_REQUIRES(ofproto_mutex)
{
- if (rules->n) {
+ if (rule_collection_n(rules)) {
struct ovs_list dead_cookies = OVS_LIST_INITIALIZER(&dead_cookies);
+ struct rule *rule;
- for (size_t i = 0; i < rules->n; i++) {
- struct rule *rule = rules->rules[i];
-
+ RULE_COLLECTION_FOR_EACH (rule, rules) {
/* This value will be used to send the flow removed message right
* before the rule is actually destroyed. */
rule->removed_reason = reason;
@@ -5297,8 +5283,8 @@ delete_flows__(struct rule_collection *rules,
const struct flow_mod_requester *req)
OVS_REQUIRES(ofproto_mutex)
{
- if (rules->n) {
- struct ofproto *ofproto = rules->rules[0]->ofproto;
+ if (rule_collection_n(rules)) {
+ struct ofproto *ofproto = rule_collection_rules(rules)[0]->ofproto;
delete_flows_start__(ofproto, ofproto->tables_version + 1, rules);
ofproto_bump_tables_version(ofproto);
@@ -5337,9 +5323,9 @@ delete_flows_revert(struct ofproto *ofproto, struct ofproto_flow_mod *ofm)
OVS_REQUIRES(ofproto_mutex)
{
struct rule_collection *rules = &ofm->old_rules;
+ struct rule *rule;
- for (size_t i = 0; i < rules->n; i++) {
- struct rule *rule = rules->rules[i];
+ RULE_COLLECTION_FOR_EACH (rule, rules) {
struct oftable *table = &ofproto->tables[rule->table_id];
/* Add rule back to ofproto data structures. */
@@ -5429,9 +5415,8 @@ ofproto_rule_expire(struct rule *rule, uint8_t reason)
{
struct rule_collection rules;
- rules.rules = rules.stub;
- rules.n = 1;
- rules.stub[0] = rule;
+ rule_collection_init(&rules);
+ rule_collection_add(&rules, rule);
delete_flows__(&rules, reason, NULL);
}
@@ -5706,10 +5691,9 @@ ofmonitor_compose_refresh_updates(struct rule_collection *rules,
struct ovs_list *msgs)
OVS_REQUIRES(ofproto_mutex)
{
- size_t i;
+ struct rule *rule;
- for (i = 0; i < rules->n; i++) {
- struct rule *rule = rules->rules[i];
+ RULE_COLLECTION_FOR_EACH (rule, rules) {
enum nx_flow_monitor_flags flags = rule->monitor_flags;
rule->monitor_flags = 0;
@@ -6301,7 +6285,7 @@ append_group_stats(struct ofgroup *group, struct ovs_list *replies)
ogs.bucket_stats = xmalloc(group->n_buckets * sizeof *ogs.bucket_stats);
/* Provider sets the packet and byte counts, we do the rest. */
- ogs.ref_count = group->rules.n;
+ ogs.ref_count = rule_collection_n(&group->rules);
ogs.n_buckets = group->n_buckets;
error = (ofproto->ofproto_class->group_get_stats
--
2.1.4
More information about the dev
mailing list