- /* Insert new rule. */
- victim = oftable_replace_rule(rule);
- if (victim && !rule_is_modifiable(victim)) {
- error = OFPERR_OFPBRC_EPERM;
- } else if (victim && victim->pending) {
- error = OFPROTO_POSTPONE;
- } else {
- struct ofoperation *op;
- struct rule *evict;
- size_t n_rules;
-
- ovs_rwlock_rdlock(&table->cls.rwlock);
- n_rules = classifier_count(&table->cls);
- ovs_rwlock_unlock(&table->cls.rwlock);
- if (n_rules > table->max_flows) {
- ovs_rwlock_rdlock(&rule->evict);
- if (choose_rule_to_evict(table, &evict)) {
- ovs_rwlock_unlock(&rule->evict);
- ovs_rwlock_unlock(&evict->evict);
- if (evict->pending) {
- error = OFPROTO_POSTPONE;
- goto exit;
- }
- } else {
- ovs_rwlock_unlock(&rule->evict);
- error = OFPERR_OFPFMFC_TABLE_FULL;
- goto exit;
- }
- } else {
- evict = NULL;
- }
-
- group = ofopgroup_create(ofproto, ofconn, request, fm->buffer_id);
- op = ofoperation_create(group, rule, OFOPERATION_ADD, 0);
- op->victim = victim;
-
- error = ofproto->ofproto_class->rule_construct(rule);
- if (error) {
- op->group->n_running--;
- ofoperation_destroy(rule->pending);
- } else if (evict) {
- /* It would be better if we maintained the lock we took in
- * choose_rule_to_evict() earlier, but that confuses the thread
- * safety analysis, and this code is fragile enough that we really
- * need it. In the worst case, we'll have to block a little while
- * before we perform the eviction, which doesn't seem like a big
- * problem. */
- ovs_rwlock_wrlock(&evict->evict);
- delete_flow__(evict, group, OFPRR_EVICTION);
- }
- ofopgroup_submit(group);
- }
-
-exit:
- /* Back out if an error occurred. */