X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=ofproto%2Fofproto.c;h=90cf343bb40c8c4f0af3a0eacc6b886dbd81fc8e;hb=080437614b40799853a42806fa29e7c71f42210d;hp=709713d02e39b28fbcc08fedd854c1bb5091eaae;hpb=e825ace22c8f863e806ff2ac9a6f58a4e112ee5c;p=openvswitch diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 709713d0..90cf343b 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -87,6 +87,7 @@ struct ofopgroup { struct ofproto *ofproto; /* Owning ofproto. */ struct list ofproto_node; /* In ofproto's "pending" list. */ struct list ops; /* List of "struct ofoperation"s. */ + int n_running; /* Number of ops still pending. */ /* Data needed to send OpenFlow reply on failure or to send a buffered * packet on success. @@ -101,7 +102,6 @@ struct ofopgroup { struct ofconn *ofconn; /* ofconn for reply (but see note above). */ struct ofp_header *request; /* Original request (truncated at 64 bytes). */ uint32_t buffer_id; /* Buffer id from original request. */ - int error; /* 0 if no error yet, otherwise error code. */ }; static struct ofopgroup *ofopgroup_create_unattached(struct ofproto *); @@ -109,7 +109,7 @@ static struct ofopgroup *ofopgroup_create(struct ofproto *, struct ofconn *, const struct ofp_header *, uint32_t buffer_id); static void ofopgroup_submit(struct ofopgroup *); -static void ofopgroup_destroy(struct ofopgroup *); +static void ofopgroup_complete(struct ofopgroup *); /* A single flow table operation. */ struct ofoperation { @@ -118,14 +118,21 @@ struct ofoperation { struct hmap_node hmap_node; /* In ofproto's "deletions" hmap. */ struct rule *rule; /* Rule being operated upon. */ enum ofoperation_type type; /* Type of operation. */ - struct rule *victim; /* OFOPERATION_ADDING: Replaced rule. */ - struct ofpact *ofpacts; /* OFOPERATION_MODIFYING: Replaced actions. */ - size_t ofpacts_len; /* OFOPERATION_MODIFYING: Bytes of ofpacts. */ + + /* OFOPERATION_ADD. */ + struct rule *victim; /* Rule being replaced, if any.. */ + + /* OFOPERATION_MODIFY: The old actions, if the actions are changing. */ + struct ofpact *ofpacts; + size_t ofpacts_len; + ovs_be64 flow_cookie; /* Rule's old flow cookie. */ + enum ofperr error; /* 0 if no error. */ }; -static void ofoperation_create(struct ofopgroup *, struct rule *, - enum ofoperation_type); +static struct ofoperation *ofoperation_create(struct ofopgroup *, + struct rule *, + enum ofoperation_type); static void ofoperation_destroy(struct ofoperation *); /* oftable. */ @@ -2295,7 +2302,7 @@ check_table_id(const struct ofproto *ofproto, uint8_t table_id) } static struct oftable * -next_visible_table(struct ofproto *ofproto, uint8_t table_id) +next_visible_table(const struct ofproto *ofproto, uint8_t table_id) { struct oftable *table; @@ -2311,7 +2318,7 @@ next_visible_table(struct ofproto *ofproto, uint8_t table_id) } static struct oftable * -first_matching_table(struct ofproto *ofproto, uint8_t table_id) +first_matching_table(const struct ofproto *ofproto, uint8_t table_id) { if (table_id == 0xff) { return next_visible_table(ofproto, 0); @@ -2323,8 +2330,8 @@ first_matching_table(struct ofproto *ofproto, uint8_t table_id) } static struct oftable * -next_matching_table(struct ofproto *ofproto, - struct oftable *table, uint8_t table_id) +next_matching_table(const struct ofproto *ofproto, + const struct oftable *table, uint8_t table_id) { return (table_id == 0xff ? next_visible_table(ofproto, (table - ofproto->tables) + 1) @@ -2856,6 +2863,7 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn, } else if (victim && victim->pending) { error = OFPROTO_POSTPONE; } else { + struct ofoperation *op; struct rule *evict; if (classifier_count(&table->cls) > table->max_flows) { @@ -2878,11 +2886,12 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn, } group = ofopgroup_create(ofproto, ofconn, request, fm->buffer_id); - ofoperation_create(group, rule, OFOPERATION_ADD); - rule->pending->victim = victim; + op = ofoperation_create(group, rule, OFOPERATION_ADD); + op->victim = victim; error = ofproto->ofproto_class->rule_construct(rule); if (error) { + op->group->n_running--; ofoperation_destroy(rule->pending); } else if (evict) { delete_flow__(evict, group); @@ -2920,6 +2929,10 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn, group = ofopgroup_create(ofproto, ofconn, request, fm->buffer_id); error = OFPERR_OFPBRC_EPERM; LIST_FOR_EACH (rule, ofproto_node, rules) { + struct ofoperation *op; + bool actions_changed; + ovs_be64 new_cookie; + if (rule_is_modifiable(rule)) { /* At least one rule is modifiable, don't report EPERM error. */ error = 0; @@ -2927,19 +2940,26 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn, continue; } - if (!ofpacts_equal(fm->ofpacts, fm->ofpacts_len, - rule->ofpacts, rule->ofpacts_len)) { - ofoperation_create(group, rule, OFOPERATION_MODIFY); - rule->pending->ofpacts = rule->ofpacts; - rule->pending->ofpacts_len = rule->ofpacts_len; + actions_changed = !ofpacts_equal(fm->ofpacts, fm->ofpacts_len, + rule->ofpacts, rule->ofpacts_len); + new_cookie = (fm->new_cookie != htonll(UINT64_MAX) + ? fm->new_cookie + : rule->flow_cookie); + if (!actions_changed && new_cookie == rule->flow_cookie) { + /* No change at all. */ + continue; + } + + op = ofoperation_create(group, rule, OFOPERATION_MODIFY); + rule->flow_cookie = new_cookie; + if (actions_changed) { + op->ofpacts = rule->ofpacts; + op->ofpacts_len = rule->ofpacts_len; rule->ofpacts = xmemdup(fm->ofpacts, fm->ofpacts_len); rule->ofpacts_len = fm->ofpacts_len; rule->ofproto->ofproto_class->rule_modify_actions(rule); } else { - rule->modified = time_msec(); - } - if (fm->new_cookie != htonll(UINT64_MAX)) { - rule->flow_cookie = fm->new_cookie; + ofoperation_complete(op, 0); } } ofopgroup_submit(group); @@ -3565,8 +3585,8 @@ ofopgroup_create(struct ofproto *ofproto, struct ofconn *ofconn, static void ofopgroup_submit(struct ofopgroup *group) { - if (list_is_empty(&group->ops)) { - ofopgroup_destroy(group); + if (!group->n_running) { + ofopgroup_complete(group); } else { list_push_back(&group->ofproto->pending, &group->ofproto_node); group->ofproto->n_pending++; @@ -3574,28 +3594,116 @@ ofopgroup_submit(struct ofopgroup *group) } static void -ofopgroup_destroy(struct ofopgroup *group) +ofopgroup_complete(struct ofopgroup *group) { - assert(list_is_empty(&group->ops)); + struct ofproto *ofproto = group->ofproto; + struct ofoperation *op, *next_op; + int error; + + assert(!group->n_running); + + error = 0; + LIST_FOR_EACH (op, group_node, &group->ops) { + if (op->error) { + error = op->error; + break; + } + } + + if (!error && group->ofconn && group->buffer_id != UINT32_MAX) { + LIST_FOR_EACH (op, group_node, &group->ops) { + if (op->type != OFOPERATION_DELETE) { + struct ofpbuf *packet; + uint16_t in_port; + + error = ofconn_pktbuf_retrieve(group->ofconn, group->buffer_id, + &packet, &in_port); + if (packet) { + assert(!error); + error = rule_execute(op->rule, in_port, packet); + } + break; + } + } + } + + LIST_FOR_EACH_SAFE (op, next_op, group_node, &group->ops) { + struct rule *rule = op->rule; + rule->pending = NULL; + + switch (op->type) { + case OFOPERATION_ADD: + if (!op->error) { + ofproto_rule_destroy__(op->victim); + if ((rule->cr.wc.vlan_tci_mask & htons(VLAN_VID_MASK)) + == htons(VLAN_VID_MASK)) { + if (ofproto->vlan_bitmap) { + uint16_t vid = vlan_tci_to_vid(rule->cr.flow.vlan_tci); + + if (!bitmap_is_set(ofproto->vlan_bitmap, vid)) { + bitmap_set1(ofproto->vlan_bitmap, vid); + ofproto->vlans_changed = true; + } + } else { + ofproto->vlans_changed = true; + } + } + } else { + oftable_substitute_rule(rule, op->victim); + ofproto_rule_destroy__(rule); + } + break; + + case OFOPERATION_DELETE: + assert(!op->error); + ofproto_rule_destroy__(rule); + op->rule = NULL; + break; + + case OFOPERATION_MODIFY: + if (!op->error) { + rule->modified = time_msec(); + } else { + rule->flow_cookie = op->flow_cookie; + if (op->ofpacts) { + free(rule->ofpacts); + rule->ofpacts = op->ofpacts; + rule->ofpacts_len = op->ofpacts_len; + op->ofpacts = NULL; + op->ofpacts_len = 0; + } + } + break; + + default: + NOT_REACHED(); + } + + ofoperation_destroy(op); + } + if (!list_is_empty(&group->ofproto_node)) { - assert(group->ofproto->n_pending > 0); - group->ofproto->n_pending--; + assert(ofproto->n_pending > 0); + ofproto->n_pending--; list_remove(&group->ofproto_node); } if (!list_is_empty(&group->ofconn_node)) { list_remove(&group->ofconn_node); - if (group->error) { - ofconn_send_error(group->ofconn, group->request, group->error); + if (error) { + ofconn_send_error(group->ofconn, group->request, error); } - connmgr_retry(group->ofproto->connmgr); + connmgr_retry(ofproto->connmgr); } free(group->request); free(group); } /* Initiates a new operation on 'rule', of the specified 'type', within - * 'group'. Prior to calling, 'rule' must not have any pending operation. */ -static void + * 'group'. Prior to calling, 'rule' must not have any pending operation. + * + * Returns the newly created ofoperation (which is also available as + * rule->pending). */ +static struct ofoperation * ofoperation_create(struct ofopgroup *group, struct rule *rule, enum ofoperation_type type) { @@ -3611,10 +3719,14 @@ ofoperation_create(struct ofopgroup *group, struct rule *rule, op->type = type; op->flow_cookie = rule->flow_cookie; + group->n_running++; + if (type == OFOPERATION_DELETE) { hmap_insert(&ofproto->deletions, &op->hmap_node, cls_rule_hash(&rule->cr, rule->table_id)); } + + return op; } static void @@ -3631,10 +3743,6 @@ ofoperation_destroy(struct ofoperation *op) list_remove(&op->group_node); free(op->ofpacts); free(op); - - if (list_is_empty(&group->ops) && !list_is_empty(&group->ofproto_node)) { - ofopgroup_destroy(group); - } } /* Indicates that 'op' completed with status 'error', which is either 0 to @@ -3670,76 +3778,15 @@ void ofoperation_complete(struct ofoperation *op, enum ofperr error) { struct ofopgroup *group = op->group; - struct rule *rule = op->rule; - struct ofproto *ofproto = rule->ofproto; - - assert(rule->pending == op); - - if (!error - && !group->error - && op->type != OFOPERATION_DELETE - && group->ofconn - && group->buffer_id != UINT32_MAX - && list_is_singleton(&op->group_node)) { - struct ofpbuf *packet; - uint16_t in_port; - - error = ofconn_pktbuf_retrieve(group->ofconn, group->buffer_id, - &packet, &in_port); - if (packet) { - assert(!error); - error = rule_execute(rule, in_port, packet); - } - } - if (!group->error) { - group->error = error; - } - switch (op->type) { - case OFOPERATION_ADD: - if (!error) { - ofproto_rule_destroy__(op->victim); - if ((rule->cr.wc.vlan_tci_mask & htons(VLAN_VID_MASK)) - == htons(VLAN_VID_MASK)) { - if (ofproto->vlan_bitmap) { - uint16_t vid = vlan_tci_to_vid(rule->cr.flow.vlan_tci); + assert(op->rule->pending == op); + assert(group->n_running > 0); + assert(!error || op->type != OFOPERATION_DELETE); - if (!bitmap_is_set(ofproto->vlan_bitmap, vid)) { - bitmap_set1(ofproto->vlan_bitmap, vid); - ofproto->vlans_changed = true; - } - } else { - ofproto->vlans_changed = true; - } - } - } else { - oftable_substitute_rule(rule, op->victim); - ofproto_rule_destroy__(rule); - op->rule = NULL; - } - break; - - case OFOPERATION_DELETE: - assert(!error); - ofproto_rule_destroy__(rule); - op->rule = NULL; - break; - - case OFOPERATION_MODIFY: - if (!error) { - rule->modified = time_msec(); - } else { - free(rule->ofpacts); - rule->ofpacts = op->ofpacts; - rule->ofpacts_len = op->ofpacts_len; - op->ofpacts = NULL; - } - break; - - default: - NOT_REACHED(); + op->error = error; + if (!--group->n_running && !list_is_empty(&group->ofproto_node)) { + ofopgroup_complete(group); } - ofoperation_destroy(op); } struct rule *