X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=ofproto%2Fofproto.c;h=f10d3ae6812ff75910df56347df572fa75e68493;hb=f416c8d616012b0369cd53d43960fc9da166229b;hp=b2d6526c6b76b6d590edf020e1d25af443c7bd68;hpb=965607c850bb14e24905c46845d050f14105d923;p=sliver-openvswitch.git diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index b2d6526c6..f10d3ae68 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -259,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 *, @@ -1143,6 +1146,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. @@ -1350,7 +1371,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 @@ -1495,7 +1517,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); @@ -1984,6 +2012,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. * @@ -2191,6 +2260,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(); @@ -2427,6 +2497,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); @@ -2571,6 +2642,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) { @@ -2579,25 +2667,20 @@ 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); } } -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 uint32_t get_provider_meter_id(const struct ofproto *, uint32_t of_meter_id); @@ -2741,19 +2824,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 @@ -2771,26 +2862,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) { @@ -3936,7 +4015,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; @@ -3968,10 +4046,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. */ @@ -3980,7 +4066,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; @@ -4066,14 +4152,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. */ @@ -4108,7 +4205,7 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn, /* 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 { @@ -5786,12 +5883,12 @@ handle_group_mod(struct ofconn *ofconn, const struct ofp_header *oh) } } -enum ofp_table_config -table_get_config(const struct ofproto *ofproto, uint8_t table_id) +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 ofp_table_config)value; + return (enum ofproto_table_config)value; } static enum ofperr @@ -5842,7 +5939,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; @@ -6015,7 +6112,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; @@ -6678,7 +6775,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.