const struct ofp_header *);
static enum ofperr modify_flows__(struct ofproto *, struct ofconn *,
struct ofputil_flow_mod *,
- const struct ofp_header *, struct list *);
+ const struct ofp_header *,
+ const struct rule_collection *);
static void delete_flow__(struct rule *rule, struct ofopgroup *,
enum ofp_flow_removed_reason)
OVS_RELEASES(rule->rwlock);
/* Deletes 'rule' from 'cls' within 'ofproto'.
*
+ * Within an ofproto implementation, this function allows an ofproto
+ * implementation to destroy any rules that remain when its ->destruct()
+ * function is called. This function is not suitable for use elsewhere in an
+ * ofproto implementation.
+ *
+ * This function is also used internally in ofproto.c.
+ *
+ * This function implements steps 4.4 and 4.5 in the section titled "Rule Life
+ * Cycle" in ofproto-provider.h.
+
* The 'cls' argument is redundant (it is &ofproto->tables[rule->table_id].cls)
* but it allows Clang to do better checking. */
-static void
-ofproto_delete_rule(struct ofproto *ofproto, struct classifier *cls,
+void
+ofproto_rule_delete(struct ofproto *ofproto, struct classifier *cls,
struct rule *rule)
OVS_REQ_WRLOCK(cls->rwlock)
{
cls_cursor_init(&cursor, &table->cls, NULL);
CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) {
if (!rule->pending) {
- ofproto_delete_rule(ofproto, &table->cls, rule);
+ ofproto_rule_delete(ofproto, &table->cls, rule);
}
}
ovs_rwlock_unlock(&table->cls.rwlock);
return error;
}
+static int
+simple_flow_mod(struct ofproto *ofproto,
+ const struct match *match, unsigned int priority,
+ const struct ofpact *ofpacts, size_t ofpacts_len,
+ enum ofp_flow_mod_command command)
+{
+ struct ofputil_flow_mod fm;
+
+ memset(&fm, 0, sizeof fm);
+ fm.match = *match;
+ fm.priority = priority;
+ fm.cookie = 0;
+ fm.new_cookie = 0;
+ fm.modify_cookie = false;
+ fm.table_id = 0;
+ fm.command = command;
+ fm.idle_timeout = 0;
+ fm.hard_timeout = 0;
+ fm.buffer_id = UINT32_MAX;
+ fm.out_port = OFPP_ANY;
+ fm.out_group = OFPG_ANY;
+ fm.flags = 0;
+ fm.ofpacts = CONST_CAST(struct ofpact *, ofpacts);
+ fm.ofpacts_len = ofpacts_len;
+ return handle_flow_mod__(ofproto, NULL, &fm, NULL);
+}
+
/* Adds a flow to OpenFlow flow table 0 in 'p' that matches 'cls_rule' and
* performs the 'n_actions' actions in 'actions'. The new flow will not
* timeout.
{
const struct rule *rule;
+ /* First do a cheap check whether the rule we're looking for already exists
+ * with the actions that we want. If it does, then we're done. */
ovs_rwlock_rdlock(&ofproto->tables[0].cls.rwlock);
rule = rule_from_cls_rule(classifier_find_match_exactly(
&ofproto->tables[0].cls, match, priority));
ovs_rwlock_unlock(&ofproto->tables[0].cls.rwlock);
+
+ /* If there's no such rule or the rule doesn't have the actions we want,
+ * fall back to a executing a full flow mod. We can't optimize this at
+ * all because we didn't take enough locks above to ensure that the flow
+ * table didn't already change beneath us. */
if (!rule || !ofpacts_equal(rule->ofpacts, rule->ofpacts_len,
ofpacts, ofpacts_len)) {
- struct ofputil_flow_mod fm;
-
- memset(&fm, 0, sizeof fm);
- fm.match = *match;
- fm.priority = priority;
- fm.buffer_id = UINT32_MAX;
- fm.ofpacts = xmemdup(ofpacts, ofpacts_len);
- fm.ofpacts_len = ofpacts_len;
- add_flow(ofproto, NULL, &fm, NULL);
- free(fm.ofpacts);
+ simple_flow_mod(ofproto, match, priority, ofpacts, ofpacts_len,
+ OFPFC_MODIFY_STRICT);
}
}
struct classifier *cls = &ofproto->tables[0].cls;
struct rule *rule;
+ /* First do a cheap check whether the rule we're looking for has already
+ * been deleted. If so, then we're done. */
ovs_rwlock_rdlock(&cls->rwlock);
rule = rule_from_cls_rule(classifier_find_match_exactly(cls, target,
priority));
ovs_rwlock_unlock(&cls->rwlock);
if (!rule) {
- /* No such rule -> success. */
- return true;
- } else if (rule->pending) {
- /* An operation on the rule is already pending -> failure.
- * Caller must retry later if it's important. */
- return false;
- } else {
- /* Initiate deletion -> success. */
- ovs_rwlock_wrlock(&cls->rwlock);
- ofproto_delete_rule(ofproto, cls, rule);
- ovs_rwlock_unlock(&cls->rwlock);
-
return true;
}
+ /* Fall back to a executing a full flow mod. We can't optimize this at all
+ * because we didn't take enough locks above to ensure that the flow table
+ * didn't already change beneath us. */
+ return simple_flow_mod(ofproto, target, priority, NULL, 0,
+ OFPFC_DELETE_STRICT) != OFPROTO_POSTPONE;
}
/* Starts the process of deleting all of the flows from all of ofproto's flow
rule->ofproto->ofproto_class->rule_dealloc(rule);
}
-/* This function allows an ofproto implementation to destroy any rules that
- * remain when its ->destruct() function is called.. This function implements
- * steps 4.4 and 4.5 in the section titled "Rule Life Cycle" in
- * ofproto-provider.h.
- *
- * This function should only be called from an ofproto implementation's
- * ->destruct() function. It is not suitable elsewhere. */
-void
-ofproto_rule_delete(struct ofproto *ofproto, struct classifier *cls,
- struct rule *rule)
- OVS_REQ_WRLOCK(cls->rwlock)
-{
- ofproto_delete_rule(ofproto, cls, rule);
-}
-
/* Returns true if 'rule' has an OpenFlow OFPAT_OUTPUT or OFPAT_ENQUEUE action
* that outputs to 'port' (output to OFPP_FLOOD and OFPP_ALL doesn't count). */
bool
}
}
-/* Finds the OFPACT_METER action, if any, in the 'ofpacts_len' bytes of
- * 'ofpacts'. If found, returns its meter ID; if not, returns 0.
- *
- * This function relies on the order of 'ofpacts' being correct (as checked by
- * ofpacts_verify()). */
-static uint32_t
-find_meter(const struct ofpact ofpacts[], size_t ofpacts_len)
-{
- const struct ofpact *a;
-
- OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
- enum ovs_instruction_type inst;
-
- inst = ovs_instruction_type_from_ofpact_type(a->type);
- if (a->type == OFPACT_METER) {
- return ofpact_get_METER(a)->meter_id;
- } else if (inst > OVSINST_OFPIT13_METER) {
- break;
- }
- }
-
- return 0;
-}
-
/* Checks that the 'ofpacts_len' bytes of actions in 'ofpacts' are appropriate
* for a packet with the prerequisites satisfied by 'flow' in table 'table_id'.
* 'flow' may be temporarily modified, but is restored at return.
return error;
}
- mid = find_meter(ofpacts, ofpacts_len);
+ mid = ofpacts_get_meter(ofpacts, ofpacts_len);
if (mid && ofproto_get_provider_meter_id(ofproto, mid) == UINT32_MAX) {
return OFPERR_OFPMMFC_INVALID_METER;
}
cls_rule_destroy(&criteria->cr);
}
+void
+rule_collection_init(struct rule_collection *rules)
+{
+ rules->rules = rules->stub;
+ rules->n = 0;
+ rules->capacity = ARRAY_SIZE(rules->stub);
+}
+
+void
+rule_collection_add(struct rule_collection *rules, struct rule *rule)
+{
+ if (rules->n >= rules->capacity) {
+ size_t old_size, new_size;
+
+ old_size = rules->capacity * sizeof *rules->rules;
+ rules->capacity *= 2;
+ new_size = rules->capacity * sizeof *rules->rules;
+
+ if (rules->rules == rules->stub) {
+ rules->rules = xmalloc(new_size);
+ memcpy(rules->rules, rules->stub, old_size);
+ } else {
+ rules->rules = xrealloc(rules->rules, new_size);
+ }
+ }
+
+ rules->rules[rules->n++] = rule;
+}
+
+void
+rule_collection_destroy(struct rule_collection *rules)
+{
+ if (rules->rules != rules->stub) {
+ free(rules->rules);
+ }
+}
+
static enum ofperr
collect_rule(struct rule *rule, const struct rule_criteria *c,
- struct list *rules)
+ struct rule_collection *rules)
{
if (ofproto_rule_is_hidden(rule)) {
return 0;
&& ofproto_rule_has_out_port(rule, c->out_port)
&& ofproto_rule_has_out_group(rule, c->out_group)
&& !((rule->flow_cookie ^ c->cookie) & c->cookie_mask)) {
- list_push_back(rules, &rule->ofproto_node);
+ rule_collection_add(rules, rule);
}
return 0;
}
* Returns 0 on success, otherwise an OpenFlow error code. */
static enum ofperr
collect_rules_loose(struct ofproto *ofproto,
- const struct rule_criteria *criteria, struct list *rules)
+ const struct rule_criteria *criteria,
+ struct rule_collection *rules)
{
struct oftable *table;
enum ofperr error;
+ rule_collection_init(rules);
+
error = check_table_id(ofproto, criteria->table_id);
if (error) {
- return error;
+ goto exit;
}
- list_init(rules);
-
if (criteria->cookie_mask == htonll(UINT64_MAX)) {
struct rule *rule;
}
}
+exit:
+ if (error) {
+ rule_collection_destroy(rules);
+ }
return error;
}
* Returns 0 on success, otherwise an OpenFlow error code. */
static enum ofperr
collect_rules_strict(struct ofproto *ofproto,
- const struct rule_criteria *criteria, struct list *rules)
+ const struct rule_criteria *criteria,
+ struct rule_collection *rules)
{
struct oftable *table;
int error;
+ rule_collection_init(rules);
+
error = check_table_id(ofproto, criteria->table_id);
if (error) {
- return error;
+ goto exit;
}
- list_init(rules);
-
if (criteria->cookie_mask == htonll(UINT64_MAX)) {
struct rule *rule;
}
}
+exit:
+ if (error) {
+ rule_collection_destroy(rules);
+ }
return error;
}
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
struct ofputil_flow_stats_request fsr;
struct rule_criteria criteria;
+ struct rule_collection rules;
struct list replies;
- struct list rules;
- struct rule *rule;
enum ofperr error;
+ size_t i;
error = ofputil_decode_flow_stats_request(&fsr, request);
if (error) {
}
ofpmp_init(&replies, request);
- LIST_FOR_EACH (rule, ofproto_node, &rules) {
+ for (i = 0; i < rules.n; i++) {
+ struct rule *rule = rules.rules[i];
long long int now = time_msec();
struct ofputil_flow_stats fs;
ofputil_append_flow_stats_reply(&fs, &replies);
}
+ rule_collection_destroy(&rules);
+
ofconn_send_replies(ofconn, &replies);
return 0;
struct ofputil_aggregate_stats stats;
bool unknown_packets, unknown_bytes;
struct rule_criteria criteria;
+ struct rule_collection rules;
struct ofpbuf *reply;
- struct list rules;
- struct rule *rule;
enum ofperr error;
+ size_t i;
error = ofputil_decode_flow_stats_request(&request, oh);
if (error) {
memset(&stats, 0, sizeof stats);
unknown_packets = unknown_bytes = false;
- LIST_FOR_EACH (rule, ofproto_node, &rules) {
+ for (i = 0; i < rules.n; i++) {
+ struct rule *rule = rules.rules[i];
uint64_t packet_count;
uint64_t byte_count;
stats.byte_count = UINT64_MAX;
}
+ rule_collection_destroy(&rules);
+
reply = ofputil_encode_aggregate_stats_reply(&stats, oh);
ofconn_send_reply(ofconn, reply);
* error code on failure, or OFPROTO_POSTPONE if the operation cannot be
* initiated now but may be retried later.
*
- * Upon successful return, takes ownership of 'fm->ofpacts'. On failure,
- * ownership remains with the caller.
+ * The caller retains ownership of 'fm->ofpacts'.
*
* 'ofconn' is used to retrieve the packet buffer specified in ofm->buffer_id,
* if any. */
} else if (rule->pending) {
return OFPROTO_POSTPONE;
} else {
- struct list rules;
+ struct rule_collection rules;
- list_init(&rules);
- list_push_back(&rules, &rule->ofproto_node);
+ rule_collection_init(&rules);
+ rule_collection_add(&rules, rule);
fm->modify_cookie = true;
- return modify_flows__(ofproto, ofconn, fm, request, &rules);
+ error = modify_flows__(ofproto, ofconn, fm, request, &rules);
+ rule_collection_destroy(&rules);
+
+ return error;
}
}
rule->ofpacts = xmemdup(fm->ofpacts, fm->ofpacts_len);
rule->ofpacts_len = fm->ofpacts_len;
- rule->meter_id = find_meter(rule->ofpacts, rule->ofpacts_len);
+ rule->meter_id = ofpacts_get_meter(rule->ofpacts, rule->ofpacts_len);
list_init(&rule->meter_list_node);
rule->eviction_group = NULL;
list_init(&rule->expirable);
static enum ofperr
modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
struct ofputil_flow_mod *fm, const struct ofp_header *request,
- struct list *rules)
+ const struct rule_collection *rules)
{
enum ofoperation_type type;
struct ofopgroup *group;
- struct rule *rule;
enum ofperr error;
+ size_t i;
type = fm->command == OFPFC_ADD ? OFOPERATION_REPLACE : OFOPERATION_MODIFY;
group = ofopgroup_create(ofproto, ofconn, request, fm->buffer_id);
error = OFPERR_OFPBRC_EPERM;
- LIST_FOR_EACH (rule, ofproto_node, rules) {
+ for (i = 0; i < rules->n; i++) {
+ struct rule *rule = rules->rules[i];
struct ofoperation *op;
bool actions_changed;
bool reset_counters;
rule->ofpacts_len = fm->ofpacts_len;
ovs_rwlock_unlock(&rule->rwlock);
- rule->meter_id = find_meter(rule->ofpacts, rule->ofpacts_len);
+ rule->meter_id = ofpacts_get_meter(rule->ofpacts,
+ rule->ofpacts_len);
rule->ofproto->ofproto_class->rule_modify_actions(rule,
reset_counters);
} else {
const struct ofp_header *request)
{
struct rule_criteria criteria;
- struct list rules;
+ struct rule_collection rules;
int error;
rule_criteria_init(&criteria, fm->table_id, &fm->match, 0,
error = collect_rules_loose(ofproto, &criteria, &rules);
rule_criteria_destroy(&criteria);
- if (error) {
- return error;
- } else if (list_is_empty(&rules)) {
- return modify_flows_add(ofproto, ofconn, fm, request);
- } else {
- return modify_flows__(ofproto, ofconn, fm, request, &rules);
+ if (!error) {
+ error = (rules.n > 0
+ ? modify_flows__(ofproto, ofconn, fm, request, &rules)
+ : modify_flows_add(ofproto, ofconn, fm, request));
}
+
+ rule_collection_destroy(&rules);
+
+ return error;
}
/* Implements OFPFC_MODIFY_STRICT. Returns 0 on success or an OpenFlow error
const struct ofp_header *request)
{
struct rule_criteria criteria;
- struct list rules;
+ struct rule_collection rules;
int error;
rule_criteria_init(&criteria, fm->table_id, &fm->match, fm->priority,
error = collect_rules_strict(ofproto, &criteria, &rules);
rule_criteria_destroy(&criteria);
- if (error) {
- return error;
- } else if (list_is_empty(&rules)) {
- return modify_flows_add(ofproto, ofconn, fm, request);
- } else {
- return list_is_singleton(&rules) ? modify_flows__(ofproto, ofconn,
- fm, request, &rules)
- : 0;
+ if (!error) {
+ if (rules.n == 0) {
+ error = modify_flows_add(ofproto, ofconn, fm, request);
+ } else if (rules.n == 1) {
+ error = modify_flows__(ofproto, ofconn, fm, request, &rules);
+ }
}
+
+ rule_collection_destroy(&rules);
+
+ return error;
}
\f
/* OFPFC_DELETE implementation. */
* Returns 0 on success, otherwise an OpenFlow error code. */
static enum ofperr
delete_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
- const struct ofp_header *request, struct list *rules,
+ const struct ofp_header *request,
+ const struct rule_collection *rules,
enum ofp_flow_removed_reason reason)
{
- struct rule *rule, *next;
struct ofopgroup *group;
+ size_t i;
group = ofopgroup_create(ofproto, ofconn, request, UINT32_MAX);
- LIST_FOR_EACH_SAFE (rule, next, ofproto_node, rules) {
+ for (i = 0; i < rules->n; i++) {
+ struct rule *rule = rules->rules[i];
ovs_rwlock_wrlock(&rule->rwlock);
delete_flow__(rule, group, reason);
}
const struct ofp_header *request)
{
struct rule_criteria criteria;
- struct list rules;
+ struct rule_collection rules;
enum ofperr error;
rule_criteria_init(&criteria, fm->table_id, &fm->match, 0,
error = collect_rules_loose(ofproto, &criteria, &rules);
rule_criteria_destroy(&criteria);
- return (error ? error
- : !list_is_empty(&rules) ? delete_flows__(ofproto, ofconn, request,
- &rules, OFPRR_DELETE)
- : 0);
+ if (!error && rules.n > 0) {
+ error = delete_flows__(ofproto, ofconn, request, &rules, OFPRR_DELETE);
+ }
+ rule_collection_destroy(&rules);
+
+ return error;
}
/* Implements OFPFC_DELETE_STRICT. */
const struct ofp_header *request)
{
struct rule_criteria criteria;
- struct list rules;
+ struct rule_collection rules;
enum ofperr error;
rule_criteria_init(&criteria, fm->table_id, &fm->match, fm->priority,
error = collect_rules_strict(ofproto, &criteria, &rules);
rule_criteria_destroy(&criteria);
- return (error ? error
- : list_is_singleton(&rules) ? delete_flows__(ofproto, ofconn,
- request, &rules,
- OFPRR_DELETE)
- : 0);
+ if (!error && rules.n > 0) {
+ error = delete_flows__(ofproto, ofconn, request, &rules, OFPRR_DELETE);
+ }
+ rule_collection_destroy(&rules);
+
+ return error;
}
static void
ofproto_rule_send_removed(rule, reason);
ovs_rwlock_wrlock(&cls->rwlock);
- ofproto_delete_rule(ofproto, cls, rule);
+ ofproto_rule_delete(ofproto, cls, rule);
ovs_rwlock_unlock(&cls->rwlock);
}
}
void
-ofmonitor_compose_refresh_updates(struct list *rules, struct list *msgs)
+ofmonitor_compose_refresh_updates(struct rule_collection *rules,
+ struct list *msgs)
{
- struct rule *rule;
+ size_t i;
- LIST_FOR_EACH (rule, ofproto_node, rules) {
+ for (i = 0; i < rules->n; i++) {
+ struct rule *rule = rules->rules[i];
enum nx_flow_monitor_flags flags = rule->monitor_flags;
rule->monitor_flags = 0;
static void
ofproto_collect_ofmonitor_refresh_rule(const struct ofmonitor *m,
struct rule *rule, uint64_t seqno,
- struct list *rules)
+ struct rule_collection *rules)
{
enum nx_flow_monitor_flags update;
}
if (!rule->monitor_flags) {
- list_push_back(rules, &rule->ofproto_node);
+ rule_collection_add(rules, rule);
}
rule->monitor_flags |= update | (m->flags & NXFMF_ACTIONS);
}
static void
ofproto_collect_ofmonitor_refresh_rules(const struct ofmonitor *m,
uint64_t seqno,
- struct list *rules)
+ struct rule_collection *rules)
{
const struct ofproto *ofproto = ofconn_get_ofproto(m->ofconn);
const struct ofoperation *op;
static void
ofproto_collect_ofmonitor_initial_rules(struct ofmonitor *m,
- struct list *rules)
+ struct rule_collection *rules)
{
if (m->flags & NXFMF_INITIAL) {
ofproto_collect_ofmonitor_refresh_rules(m, 0, rules);
void
ofmonitor_collect_resume_rules(struct ofmonitor *m,
- uint64_t seqno, struct list *rules)
+ uint64_t seqno, struct rule_collection *rules)
{
ofproto_collect_ofmonitor_refresh_rules(m, seqno, rules);
}
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
struct ofmonitor **monitors;
size_t n_monitors, allocated_monitors;
+ struct rule_collection rules;
struct list replies;
enum ofperr error;
- struct list rules;
struct ofpbuf b;
size_t i;
monitors[n_monitors++] = m;
}
- list_init(&rules);
+ rule_collection_init(&rules);
for (i = 0; i < n_monitors; i++) {
ofproto_collect_ofmonitor_initial_rules(monitors[i], &rules);
}
ofpmp_init(&replies, oh);
ofmonitor_compose_refresh_updates(&rules, &replies);
+ rule_collection_destroy(&rules);
+
ofconn_send_replies(ofconn, &replies);
free(monitors);
{
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
uint32_t meter_id = mm->meter.meter_id;
+ struct rule_collection rules;
+ enum ofperr error = 0;
uint32_t first, last;
- struct list rules;
if (meter_id == OFPM13_ALL) {
first = 1;
/* First delete the rules that use this meter. If any of those rules are
* currently being modified, postpone the whole operation until later. */
- list_init(&rules);
+ rule_collection_init(&rules);
for (meter_id = first; meter_id <= last; ++meter_id) {
struct meter *meter = ofproto->meters[meter_id];
if (meter && !list_is_empty(&meter->rules)) {
LIST_FOR_EACH (rule, meter_list_node, &meter->rules) {
if (rule->pending) {
- return OFPROTO_POSTPONE;
+ error = OFPROTO_POSTPONE;
+ goto exit;
}
- list_push_back(&rules, &rule->ofproto_node);
+ rule_collection_add(&rules, rule);
}
}
}
- if (!list_is_empty(&rules)) {
+ if (rules.n > 0) {
delete_flows__(ofproto, ofconn, oh, &rules, OFPRR_METER_DELETE);
}
/* Delete the meters. */
meter_delete(ofproto, first, last);
- return 0;
+exit:
+ rule_collection_destroy(&rules);
+
+ return error;
}
static enum ofperr