X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=ofproto%2Fofproto.c;h=6195b75bea605ba250806bd6b46256cd422e41bc;hb=dc723c447a797e555d400594133a35b9841eb1de;hp=884e63ef8377b987fb4760736385a3b94fc328a4;hpb=8917f72cbb04fb32dc29f93475bcd2de3011d442;p=sliver-openvswitch.git diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 884e63ef8..6195b75be 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -43,6 +43,7 @@ #include "ofproto-provider.h" #include "openflow/nicira-ext.h" #include "openflow/openflow.h" +#include "ovs-rcu.h" #include "packets.h" #include "pinsched.h" #include "pktbuf.h" @@ -124,7 +125,7 @@ struct ofoperation { /* OFOPERATION_MODIFY, OFOPERATION_REPLACE: The old actions, if the actions * are changing. */ - struct rule_actions *actions; + const struct rule_actions *actions; /* OFOPERATION_DELETE. */ enum ofp_flow_removed_reason reason; /* Reason flow was removed. */ @@ -258,14 +259,17 @@ struct ofport_usage { }; /* rule. */ -static void ofproto_rule_destroy__(struct rule *); static void ofproto_rule_send_removed(struct rule *, uint8_t reason); -static bool rule_is_modifiable(const struct rule *); +static bool rule_is_modifiable(const struct rule *rule, + enum ofputil_flow_mod_flags flag); /* OpenFlow. */ static enum ofperr add_flow(struct ofproto *, struct ofconn *, struct ofputil_flow_mod *, const struct ofp_header *); +static void do_add_flow(struct ofproto *, struct ofconn *, + const struct ofp_header *request, uint32_t buffer_id, + struct rule *); static enum ofperr modify_flows__(struct ofproto *, struct ofconn *, struct ofputil_flow_mod *, const struct ofp_header *, @@ -1014,10 +1018,12 @@ ofproto_port_set_bfd(struct ofproto *ofproto, ofp_port_t ofp_port, } } -/* Populates 'status' with key value pairs indicating the status of the BFD - * session on 'ofp_port'. This information is intended to be populated in the - * OVS database. Has no effect if 'ofp_port' is not na OpenFlow port in - * 'ofproto'. */ +/* Populates 'status' with the status of BFD on 'ofport'. Returns 0 on + * success. Returns a negative number if there is no status change since + * last update. Returns a positive errno otherwise. Has no effect if + * 'ofp_port' is not an OpenFlow port in 'ofproto'. + * + * The caller must provide and own '*status'. */ int ofproto_port_get_bfd_status(struct ofproto *ofproto, ofp_port_t ofp_port, struct smap *status) @@ -1142,6 +1148,24 @@ ofproto_get_n_tables(const struct ofproto *ofproto) return ofproto->n_tables; } +/* Returns the number of Controller visible OpenFlow tables + * in 'ofproto'. This number will exclude Hidden tables. + * This funtion's return value should be less or equal to that of + * ofproto_get_n_tables() . */ +uint8_t +ofproto_get_n_visible_tables(const struct ofproto *ofproto) +{ + uint8_t n = ofproto->n_tables; + + /* Count only non-hidden tables in the number of tables. (Hidden tables, + * if present, are always at the end.) */ + while(n && (ofproto->tables[n - 1].flags & OFTABLE_HIDDEN)) { + n--; + } + + return n; +} + /* Configures the OpenFlow table in 'ofproto' with id 'table_id' with the * settings from 's'. 'table_id' must be in the range 0 through the number of * OpenFlow tables in 'ofproto' minus 1, inclusive. @@ -1287,9 +1311,9 @@ ofproto_destroy__(struct ofproto *ofproto) ovs_assert(list_is_empty(&ofproto->pending)); destroy_rule_executes(ofproto); - guarded_list_destroy(&ofproto->rule_executes); - delete_group(ofproto, OFPG_ALL); + + guarded_list_destroy(&ofproto->rule_executes); ovs_rwlock_destroy(&ofproto->groups_rwlock); hmap_destroy(&ofproto->groups); @@ -1349,7 +1373,8 @@ ofproto_destroy(struct ofproto *p) } p->ofproto_class->destruct(p); - ofproto_destroy__(p); + /* Destroying rules is deferred, must have 'ofproto' around for them. */ + ovsrcu_postpone(ofproto_destroy__, p); } /* Destroys the datapath with the respective 'name' and 'type'. With the Linux @@ -1494,7 +1519,13 @@ ofproto_run(struct ofproto *p) * need this two-phase approach. */ sset_init(&devnames); HMAP_FOR_EACH (ofport, hmap_node, &p->ports) { - sset_add(&devnames, netdev_get_name(ofport->netdev)); + uint64_t port_change_seq; + + port_change_seq = netdev_get_change_seq(ofport->netdev); + if (ofport->change_seq != port_change_seq) { + ofport->change_seq = port_change_seq; + sset_add(&devnames, netdev_get_name(ofport->netdev)); + } } SSET_FOR_EACH (devname, &devnames) { update_port(p, devname); @@ -1910,11 +1941,9 @@ ofproto_add_flow(struct ofproto *ofproto, const struct match *match, rule = rule_from_cls_rule(classifier_find_match_exactly( &ofproto->tables[0].cls, match, priority)); if (rule) { - ovs_mutex_lock(&rule->mutex); - must_add = !ofpacts_equal(rule->actions->ofpacts, - rule->actions->ofpacts_len, + const struct rule_actions *actions = rule_get_actions(rule); + must_add = !ofpacts_equal(actions->ofpacts, actions->ofpacts_len, ofpacts, ofpacts_len); - ovs_mutex_unlock(&rule->mutex); } else { must_add = true; } @@ -1958,14 +1987,16 @@ ofproto_flow_mod(struct ofproto *ofproto, struct ofputil_flow_mod *fm) /* Reading many of the rule fields and writing on 'modified' * requires the rule->mutex. Also, rule->actions may change * if rule->mutex is not held. */ + const struct rule_actions *actions; + ovs_mutex_lock(&rule->mutex); + actions = rule_get_actions(rule); if (rule->idle_timeout == fm->idle_timeout && rule->hard_timeout == fm->hard_timeout && rule->flags == (fm->flags & OFPUTIL_FF_STATE) && (!fm->modify_cookie || (fm->new_cookie == rule->flow_cookie)) && ofpacts_equal(fm->ofpacts, fm->ofpacts_len, - rule->actions->ofpacts, - rule->actions->ofpacts_len)) { + actions->ofpacts, actions->ofpacts_len)) { /* Rule already exists and need not change, only update the modified timestamp. */ rule->modified = time_msec(); @@ -1983,6 +2014,47 @@ ofproto_flow_mod(struct ofproto *ofproto, struct ofputil_flow_mod *fm) return handle_flow_mod__(ofproto, NULL, fm, NULL); } +/* Resets the modified time for 'rule' or an equivalent rule. If 'rule' is not + * in the classifier, but an equivalent rule is, unref 'rule' and ref the new + * rule. Otherwise if 'rule' is no longer installed in the classifier, + * reinstall it. + * + * Returns the rule whose modified time has been reset. */ +struct rule * +ofproto_refresh_rule(struct rule *rule) +{ + const struct oftable *table = &rule->ofproto->tables[rule->table_id]; + const struct cls_rule *cr = &rule->cr; + struct rule *r; + + /* do_add_flow() requires that the rule is not installed. We lock the + * ofproto_mutex here so that another thread cannot add the flow before + * we get a chance to add it.*/ + ovs_mutex_lock(&ofproto_mutex); + + fat_rwlock_rdlock(&table->cls.rwlock); + r = rule_from_cls_rule(classifier_find_rule_exactly(&table->cls, cr)); + if (r != rule) { + ofproto_rule_ref(r); + } + fat_rwlock_unlock(&table->cls.rwlock); + + if (!r) { + do_add_flow(rule->ofproto, NULL, NULL, 0, rule); + } else if (r != rule) { + ofproto_rule_unref(rule); + rule = r; + } + ovs_mutex_unlock(&ofproto_mutex); + + /* Refresh the modified time for the rule. */ + ovs_mutex_lock(&rule->mutex); + rule->modified = MAX(rule->modified, time_msec()); + ovs_mutex_unlock(&rule->mutex); + + return rule; +} + /* Searches for a rule with matching criteria exactly equal to 'target' in * ofproto's table 0 and, if it finds one, deletes it. * @@ -2190,6 +2262,7 @@ ofport_install(struct ofproto *p, } ofport->ofproto = p; ofport->netdev = netdev; + ofport->change_seq = netdev_get_change_seq(netdev); ofport->pp = *pp; ofport->ofp_port = pp->port_no; ofport->created = time_msec(); @@ -2426,6 +2499,7 @@ update_port(struct ofproto *ofproto, const char *name) * Don't close the old netdev yet in case port_modified has to * remove a retained reference to it.*/ port->netdev = netdev; + port->change_seq = netdev_get_change_seq(netdev); if (port->ofproto->ofproto_class->port_modified) { port->ofproto->ofproto_class->port_modified(port); @@ -2570,6 +2644,23 @@ update_mtu(struct ofproto *p, struct ofport *port) } } +static void +ofproto_rule_destroy__(struct rule *rule) + OVS_NO_THREAD_SAFETY_ANALYSIS +{ + cls_rule_destroy(CONST_CAST(struct cls_rule *, &rule->cr)); + rule_actions_destroy(rule_get_actions(rule)); + ovs_mutex_destroy(&rule->mutex); + rule->ofproto->ofproto_class->rule_dealloc(rule); +} + +static void +rule_destroy_cb(struct rule *rule) +{ + rule->ofproto->ofproto_class->rule_destruct(rule); + ofproto_rule_destroy__(rule); +} + void ofproto_rule_ref(struct rule *rule) { @@ -2578,85 +2669,47 @@ ofproto_rule_ref(struct rule *rule) } } +/* Decrements 'rule''s ref_count and schedules 'rule' to be destroyed if the + * ref_count reaches 0. + * + * Use of RCU allows short term use (between RCU quiescent periods) without + * keeping a reference. A reference must be taken if the rule needs to + * stay around accross the RCU quiescent periods. */ void ofproto_rule_unref(struct rule *rule) { if (rule && ovs_refcount_unref(&rule->ref_count) == 1) { - rule->ofproto->ofproto_class->rule_destruct(rule); - ofproto_rule_destroy__(rule); + ovsrcu_postpone(rule_destroy_cb, rule); } } -struct rule_actions * -rule_get_actions(const struct rule *rule) - OVS_EXCLUDED(rule->mutex) -{ - struct rule_actions *actions; - - ovs_mutex_lock(&rule->mutex); - actions = rule_get_actions__(rule); - ovs_mutex_unlock(&rule->mutex); - - return actions; -} - -struct rule_actions * -rule_get_actions__(const struct rule *rule) - OVS_REQUIRES(rule->mutex) -{ - rule_actions_ref(rule->actions); - return rule->actions; -} - -static void -ofproto_rule_destroy__(struct rule *rule) - OVS_NO_THREAD_SAFETY_ANALYSIS -{ - cls_rule_destroy(CONST_CAST(struct cls_rule *, &rule->cr)); - rule_actions_unref(rule->actions); - ovs_mutex_destroy(&rule->mutex); - rule->ofproto->ofproto_class->rule_dealloc(rule); -} - static uint32_t get_provider_meter_id(const struct ofproto *, uint32_t of_meter_id); -/* Creates and returns a new 'struct rule_actions', with a ref_count of 1, - * whose actions are a copy of from the 'ofpacts_len' bytes of 'ofpacts'. */ -struct rule_actions * +/* Creates and returns a new 'struct rule_actions', whose actions are a copy + * of from the 'ofpacts_len' bytes of 'ofpacts'. */ +const struct rule_actions * rule_actions_create(const struct ofproto *ofproto, const struct ofpact *ofpacts, size_t ofpacts_len) { struct rule_actions *actions; - actions = xmalloc(sizeof *actions); - ovs_refcount_init(&actions->ref_count); - actions->ofpacts = xmemdup(ofpacts, ofpacts_len); + actions = xmalloc(sizeof *actions + ofpacts_len); actions->ofpacts_len = ofpacts_len; actions->provider_meter_id = get_provider_meter_id(ofproto, ofpacts_get_meter(ofpacts, ofpacts_len)); + memcpy(actions->ofpacts, ofpacts, ofpacts_len); return actions; } -/* Increments 'actions''s ref_count. */ +/* Free the actions after the RCU quiescent period is reached. */ void -rule_actions_ref(struct rule_actions *actions) +rule_actions_destroy(const struct rule_actions *actions) { if (actions) { - ovs_refcount_ref(&actions->ref_count); - } -} - -/* Decrements 'actions''s ref_count and frees 'actions' if the ref_count - * reaches 0. */ -void -rule_actions_unref(struct rule_actions *actions) -{ - if (actions && ovs_refcount_unref(&actions->ref_count) == 1) { - free(actions->ofpacts); - free(actions); + ovsrcu_postpone(free, CONST_CAST(struct rule_actions *, actions)); } } @@ -2666,9 +2719,13 @@ static bool ofproto_rule_has_out_port(const struct rule *rule, ofp_port_t port) OVS_REQUIRES(ofproto_mutex) { - return (port == OFPP_ANY - || ofpacts_output_to_port(rule->actions->ofpacts, - rule->actions->ofpacts_len, port)); + if (port == OFPP_ANY) { + return true; + } else { + const struct rule_actions *actions = rule_get_actions(rule); + return ofpacts_output_to_port(actions->ofpacts, + actions->ofpacts_len, port); + } } /* Returns true if 'rule' has group and equals group_id. */ @@ -2676,9 +2733,13 @@ static bool ofproto_rule_has_out_group(const struct rule *rule, uint32_t group_id) OVS_REQUIRES(ofproto_mutex) { - return (group_id == OFPG11_ANY - || ofpacts_output_to_group(rule->actions->ofpacts, - rule->actions->ofpacts_len, group_id)); + if (group_id == OFPG_ANY) { + return true; + } else { + const struct rule_actions *actions = rule_get_actions(rule); + return ofpacts_output_to_group(actions->ofpacts, + actions->ofpacts_len, group_id); + } } /* Returns true if a rule related to 'op' has an OpenFlow OFPAT_OUTPUT or @@ -2757,19 +2818,27 @@ destroy_rule_executes(struct ofproto *ofproto) static bool ofproto_rule_is_hidden(const struct rule *rule) { - return rule->cr.priority > UINT16_MAX; + return (rule->cr.priority > UINT16_MAX); } -static enum oftable_flags -rule_get_flags(const struct rule *rule) +static bool +oftable_is_modifiable(const struct oftable *table, + enum ofputil_flow_mod_flags flags) { - return rule->ofproto->tables[rule->table_id].flags; + if (flags & OFPUTIL_FF_NO_READONLY) { + return true; + } + + return !(table->flags & OFTABLE_READONLY); } static bool -rule_is_modifiable(const struct rule *rule) +rule_is_modifiable(const struct rule *rule, enum ofputil_flow_mod_flags flags) { - return !(rule_get_flags(rule) & OFTABLE_READONLY); + const struct oftable *rule_table; + + rule_table = &rule->ofproto->tables[rule->table_id]; + return oftable_is_modifiable(rule_table, flags); } static enum ofperr @@ -2787,26 +2856,14 @@ handle_features_request(struct ofconn *ofconn, const struct ofp_header *oh) struct ofport *port; bool arp_match_ip; struct ofpbuf *b; - int n_tables; - int i; ofproto->ofproto_class->get_features(ofproto, &arp_match_ip, &features.actions); ovs_assert(features.actions & OFPUTIL_A_OUTPUT); /* sanity check */ - /* Count only non-hidden tables in the number of tables. (Hidden tables, - * if present, are always at the end.) */ - n_tables = ofproto->n_tables; - for (i = 0; i < ofproto->n_tables; i++) { - if (ofproto->tables[i].flags & OFTABLE_HIDDEN) { - n_tables = i; - break; - } - } - features.datapath_id = ofproto->datapath_id; features.n_buffers = pktbuf_capacity(); - features.n_tables = n_tables; + features.n_tables = ofproto_get_n_visible_tables(ofproto); features.capabilities = (OFPUTIL_C_FLOW_STATS | OFPUTIL_C_TABLE_STATS | OFPUTIL_C_PORT_STATS | OFPUTIL_C_QUEUE_STATS); if (arp_match_ip) { @@ -3208,8 +3265,7 @@ handle_port_desc_stats_request(struct ofconn *ofconn, static uint32_t hash_cookie(ovs_be64 cookie) { - return hash_2words((OVS_FORCE uint64_t)cookie >> 32, - (OVS_FORCE uint64_t)cookie); + return hash_uint64((OVS_FORCE uint64_t)cookie); } static void @@ -3597,7 +3653,7 @@ handle_flow_stats_request(struct ofconn *ofconn, long long int now = time_msec(); struct ofputil_flow_stats fs; long long int created, used, modified; - struct rule_actions *actions; + const struct rule_actions *actions; enum ofputil_flow_mod_flags flags; ovs_mutex_lock(&rule->mutex); @@ -3606,7 +3662,7 @@ handle_flow_stats_request(struct ofconn *ofconn, fs.hard_timeout = rule->hard_timeout; created = rule->created; modified = rule->modified; - actions = rule_get_actions__(rule); + actions = rule_get_actions(rule); flags = rule->flags; ovs_mutex_unlock(&rule->mutex); @@ -3624,8 +3680,6 @@ handle_flow_stats_request(struct ofconn *ofconn, fs.flags = flags; ofputil_append_flow_stats_reply(&fs, &replies); - - rule_actions_unref(actions); } rule_collection_unref(&rules); @@ -3640,14 +3694,14 @@ static void flow_stats_ds(struct rule *rule, struct ds *results) { uint64_t packet_count, byte_count; - struct rule_actions *actions; + const struct rule_actions *actions; long long int created, used; rule->ofproto->ofproto_class->rule_get_stats(rule, &packet_count, &byte_count, &used); ovs_mutex_lock(&rule->mutex); - actions = rule_get_actions__(rule); + actions = rule_get_actions(rule); created = rule->created; ovs_mutex_unlock(&rule->mutex); @@ -3664,8 +3718,6 @@ flow_stats_ds(struct rule *rule, struct ds *results) ofpacts_format(actions->ofpacts, actions->ofpacts_len, results); ds_put_cstr(results, "\n"); - - rule_actions_unref(actions); } /* Adds a pretty-printed description of all flows to 'results', including @@ -3697,20 +3749,22 @@ ofproto_get_netflow_ids(const struct ofproto *ofproto, ofproto->ofproto_class->get_netflow_ids(ofproto, engine_type, engine_id); } -/* Checks the status of CFM configured on 'ofp_port' within 'ofproto'. Returns - * true if the port's CFM status was successfully stored into '*status'. - * Returns false if the port did not have CFM configured, in which case - * '*status' is indeterminate. +/* Checks the status of CFM configured on 'ofp_port' within 'ofproto'. + * Returns 0 if the port's CFM status was successfully stored into + * '*status'. Returns positive errno if the port did not have CFM + * configured. Returns negative number if there is no status change + * since last update. * - * The caller must provide and owns '*status', and must free 'status->rmps'. */ -bool + * The caller must provide and own '*status', and must free 'status->rmps'. + * '*status' is indeterminate if the return value is non-zero. */ +int ofproto_port_get_cfm_status(const struct ofproto *ofproto, ofp_port_t ofp_port, struct ofproto_cfm_status *status) { struct ofport *ofport = ofproto_get_port(ofproto, ofp_port); - return (ofport - && ofproto->ofproto_class->get_cfm_status - && ofproto->ofproto_class->get_cfm_status(ofport, status)); + return (ofport && ofproto->ofproto_class->get_cfm_status + ? ofproto->ofproto_class->get_cfm_status(ofport, status) + : EOPNOTSUPP); } static enum ofperr @@ -3957,7 +4011,6 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn, OVS_REQUIRES(ofproto_mutex) { struct oftable *table; - struct ofopgroup *group; struct cls_rule cr; struct rule *rule; uint8_t table_id; @@ -3989,10 +4042,18 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn, table = &ofproto->tables[table_id]; - if (table->flags & OFTABLE_READONLY) { + if (!oftable_is_modifiable(table, fm->flags)) { return OFPERR_OFPBRC_EPERM; } + if (!(fm->flags & OFPUTIL_FF_HIDDEN_FIELDS)) { + if (!match_has_default_hidden_fields(&fm->match)) { + VLOG_WARN_RL(&rl, "%s: (add_flow) only internal flows can set " + "non-default values to hidden fields", ofproto->name); + return OFPERR_OFPBRC_EPERM; + } + } + cls_rule_init(&cr, &fm->match, fm->priority); /* Transform "add" into "modify" if there's an existing identical flow. */ @@ -4001,7 +4062,7 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn, fat_rwlock_unlock(&table->cls.rwlock); if (rule) { cls_rule_destroy(&cr); - if (!rule_is_modifiable(rule)) { + if (!rule_is_modifiable(rule, fm->flags)) { return OFPERR_OFPBRC_EPERM; } else if (rule->pending) { return OFPROTO_POSTPONE; @@ -4070,7 +4131,8 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn, *CONST_CAST(uint8_t *, &rule->table_id) = table - ofproto->tables; rule->flags = fm->flags & OFPUTIL_FF_STATE; - rule->actions = rule_actions_create(ofproto, fm->ofpacts, fm->ofpacts_len); + ovsrcu_set(&rule->actions, + rule_actions_create(ofproto, fm->ofpacts, fm->ofpacts_len)); list_init(&rule->meter_list_node); rule->eviction_group = NULL; list_init(&rule->expirable); @@ -4086,14 +4148,25 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn, } /* Insert rule. */ + do_add_flow(ofproto, ofconn, request, fm->buffer_id, rule); + + return error; +} + +static void +do_add_flow(struct ofproto *ofproto, struct ofconn *ofconn, + const struct ofp_header *request, uint32_t buffer_id, + struct rule *rule) + OVS_REQUIRES(ofproto_mutex) +{ + struct ofopgroup *group; + oftable_insert_rule(rule); - group = ofopgroup_create(ofproto, ofconn, request, fm->buffer_id); + group = ofopgroup_create(ofproto, ofconn, request, buffer_id); ofoperation_create(group, rule, OFOPERATION_ADD, 0); ofproto->ofproto_class->rule_insert(rule); ofopgroup_submit(group); - - return error; } /* OFPFC_MODIFY and OFPFC_MODIFY_STRICT. */ @@ -4121,22 +4194,24 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn, error = OFPERR_OFPBRC_EPERM; for (i = 0; i < rules->n; i++) { struct rule *rule = rules->rules[i]; + const struct rule_actions *actions; struct ofoperation *op; bool actions_changed; bool reset_counters; /* FIXME: Implement OFPFUTIL_FF_RESET_COUNTS */ - if (rule_is_modifiable(rule)) { + if (rule_is_modifiable(rule, fm->flags)) { /* At least one rule is modifiable, don't report EPERM error. */ error = 0; } else { continue; } + actions = rule_get_actions(rule); actions_changed = !ofpacts_equal(fm->ofpacts, fm->ofpacts_len, - rule->actions->ofpacts, - rule->actions->ofpacts_len); + actions->ofpacts, + actions->ofpacts_len); op = ofoperation_create(group, rule, type, 0); @@ -4161,15 +4236,13 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn, reset_counters = (fm->flags & OFPUTIL_FF_RESET_COUNTS) != 0; if (actions_changed || reset_counters) { - struct rule_actions *new_actions; + const struct rule_actions *new_actions; - op->actions = rule->actions; + op->actions = rule_get_actions(rule); new_actions = rule_actions_create(ofproto, fm->ofpacts, fm->ofpacts_len); - ovs_mutex_lock(&rule->mutex); - rule->actions = new_actions; - ovs_mutex_unlock(&rule->mutex); + ovsrcu_set(&rule->actions, new_actions); rule->ofproto->ofproto_class->rule_modify_actions(rule, reset_counters); @@ -4750,7 +4823,7 @@ ofproto_compose_flow_refresh_update(const struct rule *rule, if (!(flags & NXFMF_ACTIONS)) { actions = NULL; } else if (!op) { - actions = rule->actions; + actions = rule_get_actions(rule); } else { /* An operation is in progress. Use the previous version of the flow's * actions, so that when the operation commits we report the change. */ @@ -4760,11 +4833,11 @@ ofproto_compose_flow_refresh_update(const struct rule *rule, case OFOPERATION_MODIFY: case OFOPERATION_REPLACE: - actions = op->actions ? op->actions : rule->actions; + actions = op->actions ? op->actions : rule_get_actions(rule); break; case OFOPERATION_DELETE: - actions = rule->actions; + actions = rule_get_actions(rule); break; default: @@ -5806,11 +5879,21 @@ handle_group_mod(struct ofconn *ofconn, const struct ofp_header *oh) } } +enum ofproto_table_config +ofproto_table_get_config(const struct ofproto *ofproto, uint8_t table_id) +{ + unsigned int value; + atomic_read(&ofproto->tables[table_id].config, &value); + return (enum ofproto_table_config)value; +} + static enum ofperr table_mod(struct ofproto *ofproto, const struct ofputil_table_mod *tm) { - /* XXX Reject all configurations because none are currently supported */ - return OFPERR_OFPTMFC_BAD_CONFIG; + /* Only accept currently supported configurations */ + if (tm->config & ~OFPTC11_TABLE_MISS_MASK) { + return OFPERR_OFPTMFC_BAD_CONFIG; + } if (tm->table_id == OFPTT_ALL) { int i; @@ -5852,7 +5935,7 @@ static enum ofperr handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg) OVS_EXCLUDED(ofproto_mutex) { - const struct ofp_header *oh = msg->data; + const struct ofp_header *oh = ofpbuf_data(msg); enum ofptype type; enum ofperr error; @@ -6025,7 +6108,7 @@ handle_openflow(struct ofconn *ofconn, const struct ofpbuf *ofp_msg) { int error = handle_openflow__(ofconn, ofp_msg); if (error && error != OFPROTO_POSTPONE) { - ofconn_send_error(ofconn, ofp_msg->data, error); + ofconn_send_error(ofconn, ofpbuf_data(ofp_msg), error); } COVERAGE_INC(ofproto_recv_openflow); return error != OFPROTO_POSTPONE; @@ -6251,15 +6334,15 @@ ofopgroup_complete(struct ofopgroup *group) rule->hard_timeout = op->hard_timeout; ovs_mutex_unlock(&rule->mutex); if (op->actions) { - struct rule_actions *old_actions; + const struct rule_actions *old_actions; ovs_mutex_lock(&rule->mutex); - old_actions = rule->actions; - rule->actions = op->actions; + old_actions = rule_get_actions(rule); + ovsrcu_set(&rule->actions, op->actions); ovs_mutex_unlock(&rule->mutex); op->actions = NULL; - rule_actions_unref(old_actions); + rule_actions_destroy(old_actions); } rule->flags = op->flags; } @@ -6345,7 +6428,7 @@ ofoperation_destroy(struct ofoperation *op) hmap_remove(&group->ofproto->deletions, &op->hmap_node); } list_remove(&op->group_node); - rule_actions_unref(op->actions); + rule_actions_destroy(op->actions); free(op); } @@ -6688,7 +6771,7 @@ oftable_init(struct oftable *table) memset(table, 0, sizeof *table); classifier_init(&table->cls, flow_segment_u32s); table->max_flows = UINT_MAX; - atomic_init(&table->config, (unsigned int)OFPTC11_TABLE_MISS_CONTROLLER); + atomic_init(&table->config, (unsigned int)OFPROTO_TABLE_MISS_DEFAULT); } /* Destroys 'table', including its classifier and eviction groups. @@ -6827,6 +6910,7 @@ oftable_insert_rule(struct rule *rule) { struct ofproto *ofproto = rule->ofproto; struct oftable *table = &ofproto->tables[rule->table_id]; + const struct rule_actions *actions; bool may_expire; ovs_mutex_lock(&rule->mutex); @@ -6839,9 +6923,10 @@ oftable_insert_rule(struct rule *rule) cookies_insert(ofproto, rule); - if (rule->actions->provider_meter_id != UINT32_MAX) { - uint32_t meter_id = ofpacts_get_meter(rule->actions->ofpacts, - rule->actions->ofpacts_len); + actions = rule_get_actions(rule); + if (actions->provider_meter_id != UINT32_MAX) { + uint32_t meter_id = ofpacts_get_meter(actions->ofpacts, + actions->ofpacts_len); struct meter *meter = ofproto->meters[meter_id]; list_insert(&meter->rules, &rule->meter_list_node); }