ofproto: Eliminate use of unset error code.
[cascardo/ovs.git] / ofproto / ofproto.c
index eb1f3be..6c6dc2d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
  * Copyright (c) 2010 Jean Tourrilhes - HP-Labs.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -58,6 +58,7 @@
 #include "unaligned.h"
 #include "unixctl.h"
 #include "vlog.h"
+#include "bundles.h"
 
 VLOG_DEFINE_THIS_MODULE(ofproto);
 
@@ -529,16 +530,13 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
     ofproto->n_pending = 0;
     hmap_init(&ofproto->deletions);
     guarded_list_init(&ofproto->rule_executes);
-    ofproto->n_add = ofproto->n_delete = ofproto->n_modify = 0;
-    ofproto->first_op = ofproto->last_op = LLONG_MIN;
-    ofproto->next_op_report = LLONG_MAX;
-    ofproto->op_backoff = LLONG_MIN;
     ofproto->vlan_bitmap = NULL;
     ofproto->vlans_changed = false;
     ofproto->min_mtu = INT_MAX;
     ovs_rwlock_init(&ofproto->groups_rwlock);
     hmap_init(&ofproto->groups);
     ovs_mutex_unlock(&ofproto_mutex);
+    ofproto->ogf.types = 0xf;
     ofproto->ogf.capabilities = OFPGFC_CHAINING | OFPGFC_SELECT_LIVENESS |
                                 OFPGFC_SELECT_WEIGHT;
     ofproto->ogf.max_groups[OFPGT11_ALL] = OFPG_MAX;
@@ -1018,19 +1016,22 @@ ofproto_port_set_bfd(struct ofproto *ofproto, ofp_port_t ofp_port,
     }
 }
 
-/* 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'.
+/* Populates 'status' with the status of BFD on 'ofport'.  If 'force' is set to
+ * true, status will be returned even if there is no status change since last
+ * update.
+ *
+ * Returns 0 on success.  Returns a negative number if there is no status change
+ * since last update and 'force' is set to false.  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)
+                            bool force, struct smap *status)
 {
     struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
     return (ofport && ofproto->ofproto_class->get_bfd_status
-            ? ofproto->ofproto_class->get_bfd_status(ofport, status)
+            ? ofproto->ofproto_class->get_bfd_status(ofport, force, status)
             : EOPNOTSUPP);
 }
 
@@ -1339,6 +1340,9 @@ ofproto_destroy__(struct ofproto *ofproto)
 
     hmap_destroy(&ofproto->deletions);
 
+    ovs_assert(hindex_is_empty(&ofproto->cookies));
+    hindex_destroy(&ofproto->cookies);
+
     free(ofproto->vlan_bitmap);
 
     ofproto->ofproto_class->dealloc(ofproto);
@@ -1477,6 +1481,15 @@ ofproto_run(struct ofproto *p)
 
             ovs_mutex_lock(&ofproto_mutex);
             fat_rwlock_rdlock(&table->cls.rwlock);
+
+            if (classifier_count(&table->cls) > 100000) {
+                static struct vlog_rate_limit count_rl =
+                    VLOG_RATE_LIMIT_INIT(1, 1);
+                VLOG_WARN_RL(&count_rl, "Table %"PRIuSIZE" has an excessive"
+                             " number of rules: %d", i,
+                             classifier_count(&table->cls));
+            }
+
             cls_cursor_init(&cursor, &table->cls, NULL);
             CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
                 if (rule->idle_timeout || rule->hard_timeout) {
@@ -1561,43 +1574,6 @@ ofproto_run(struct ofproto *p)
         OVS_NOT_REACHED();
     }
 
-    if (time_msec() >= p->next_op_report) {
-        long long int ago = (time_msec() - p->first_op) / 1000;
-        long long int interval = (p->last_op - p->first_op) / 1000;
-        struct ds s;
-
-        ds_init(&s);
-        ds_put_format(&s, "%d flow_mods ",
-                      p->n_add + p->n_delete + p->n_modify);
-        if (interval == ago) {
-            ds_put_format(&s, "in the last %lld s", ago);
-        } else if (interval) {
-            ds_put_format(&s, "in the %lld s starting %lld s ago",
-                          interval, ago);
-        } else {
-            ds_put_format(&s, "%lld s ago", ago);
-        }
-
-        ds_put_cstr(&s, " (");
-        if (p->n_add) {
-            ds_put_format(&s, "%d adds, ", p->n_add);
-        }
-        if (p->n_delete) {
-            ds_put_format(&s, "%d deletes, ", p->n_delete);
-        }
-        if (p->n_modify) {
-            ds_put_format(&s, "%d modifications, ", p->n_modify);
-        }
-        s.length -= 2;
-        ds_put_char(&s, ')');
-
-        VLOG_INFO("%s: %s", p->name, ds_cstr(&s));
-        ds_destroy(&s);
-
-        p->n_add = p->n_delete = p->n_modify = 0;
-        p->next_op_report = LLONG_MAX;
-    }
-
     return error;
 }
 
@@ -2013,47 +1989,6 @@ 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.
  *
@@ -3078,7 +3013,7 @@ handle_port_mod(struct ofconn *ofconn, const struct ofp_header *oh)
         return error;
     }
 
-    error = ofputil_decode_port_mod(oh, &pm);
+    error = ofputil_decode_port_mod(oh, &pm, false);
     if (error) {
         return error;
     }
@@ -3210,55 +3145,62 @@ append_port_stat(struct ofport *port, struct list *replies)
     ofputil_append_port_stat(replies, &ops);
 }
 
-static enum ofperr
-handle_port_stats_request(struct ofconn *ofconn,
-                          const struct ofp_header *request)
+static void
+handle_port_request(struct ofconn *ofconn,
+                    const struct ofp_header *request, ofp_port_t port_no,
+                    void (*cb)(struct ofport *, struct list *replies))
 {
-    struct ofproto *p = ofconn_get_ofproto(ofconn);
+    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
     struct ofport *port;
     struct list replies;
-    ofp_port_t port_no;
-    enum ofperr error;
-
-    error = ofputil_decode_port_stats_request(request, &port_no);
-    if (error) {
-        return error;
-    }
 
     ofpmp_init(&replies, request);
     if (port_no != OFPP_ANY) {
-        port = ofproto_get_port(p, port_no);
+        port = ofproto_get_port(ofproto, port_no);
         if (port) {
-            append_port_stat(port, &replies);
+            cb(port, &replies);
         }
     } else {
-        HMAP_FOR_EACH (port, hmap_node, &p->ports) {
-            append_port_stat(port, &replies);
+        HMAP_FOR_EACH (port, hmap_node, &ofproto->ports) {
+            cb(port, &replies);
         }
     }
 
     ofconn_send_replies(ofconn, &replies);
-    return 0;
+}
+
+static enum ofperr
+handle_port_stats_request(struct ofconn *ofconn,
+                          const struct ofp_header *request)
+{
+    ofp_port_t port_no;
+    enum ofperr error;
+
+    error = ofputil_decode_port_stats_request(request, &port_no);
+    if (!error) {
+        handle_port_request(ofconn, request, port_no, append_port_stat);
+    }
+    return error;
+}
+
+static void
+append_port_desc(struct ofport *port, struct list *replies)
+{
+    ofputil_append_port_desc_stats_reply(&port->pp, replies);
 }
 
 static enum ofperr
 handle_port_desc_stats_request(struct ofconn *ofconn,
                                const struct ofp_header *request)
 {
-    struct ofproto *p = ofconn_get_ofproto(ofconn);
-    enum ofp_version version;
-    struct ofport *port;
-    struct list replies;
-
-    ofpmp_init(&replies, request);
+    ofp_port_t port_no;
+    enum ofperr error;
 
-    version = ofputil_protocol_to_ofp_version(ofconn_get_protocol(ofconn));
-    HMAP_FOR_EACH (port, hmap_node, &p->ports) {
-        ofputil_append_port_desc_stats_reply(version, &port->pp, &replies);
+    error = ofputil_decode_port_desc_stats_request(request, &port_no);
+    if (!error) {
+        handle_port_request(ofconn, request, port_no, append_port_desc);
     }
-
-    ofconn_send_replies(ofconn, &replies);
-    return 0;
+    return error;
 }
 
 static uint32_t
@@ -3748,21 +3690,23 @@ 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 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.
+/* Checks the status of CFM configured on 'ofp_port' within 'ofproto' and stores
+ * the port's CFM status in '*status'.  If 'force' is set to true, status will
+ * be returned even if there is no status change since last update.
+ *
+ * Returns 0 on success.  Returns a negative number if there is no status
+ * change since last update and 'force' is set to false.  Returns positive errno
+ * if the port did not have CFM configured.
  *
  * 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)
+                            bool force, 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)
+            ? ofproto->ofproto_class->get_cfm_status(ofport, force, status)
             : EOPNOTSUPP);
 }
 
@@ -4110,8 +4054,7 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
     rule = ofproto->ofproto_class->rule_alloc();
     if (!rule) {
         cls_rule_destroy(&cr);
-        VLOG_WARN_RL(&rl, "%s: failed to create rule (%s)",
-                     ofproto->name, ovs_strerror(error));
+        VLOG_WARN_RL(&rl, "%s: failed to allocate a rule.", ofproto->name);
         return ENOMEM;
     }
 
@@ -4351,21 +4294,30 @@ delete_flow__(struct rule *rule, struct ofopgroup *group,
  * Returns 0 on success, otherwise an OpenFlow error code. */
 static enum ofperr
 delete_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
+               enum ofputil_flow_mod_flags flags,
                const struct ofp_header *request,
                const struct rule_collection *rules,
                enum ofp_flow_removed_reason reason)
     OVS_REQUIRES(ofproto_mutex)
 {
     struct ofopgroup *group;
+    enum ofperr error;
     size_t i;
 
+    error = OFPERR_OFPBRC_EPERM;
     group = ofopgroup_create(ofproto, ofconn, request, UINT32_MAX);
     for (i = 0; i < rules->n; i++) {
-        delete_flow__(rules->rules[i], group, reason);
+        struct rule *rule = rules->rules[i];
+
+        if (rule_is_modifiable(rule, flags)) {
+            /* At least one rule is modifiable, don't report EPERM error. */
+            error = 0;
+            delete_flow__(rule, group, reason);
+        }
     }
     ofopgroup_submit(group);
 
-    return 0;
+    return error;
 }
 
 /* Implements OFPFC_DELETE. */
@@ -4386,7 +4338,8 @@ delete_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn,
     rule_criteria_destroy(&criteria);
 
     if (!error && rules.n > 0) {
-        error = delete_flows__(ofproto, ofconn, request, &rules, OFPRR_DELETE);
+        error = delete_flows__(ofproto, ofconn, fm->flags, request,
+                               &rules, OFPRR_DELETE);
     }
     rule_collection_destroy(&rules);
 
@@ -4411,7 +4364,8 @@ delete_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn,
     rule_criteria_destroy(&criteria);
 
     if (!error && rules.n > 0) {
-        error = delete_flows__(ofproto, ofconn, request, &rules, OFPRR_DELETE);
+        error = delete_flows__(ofproto, ofconn, fm->flags, request,
+                               &rules, OFPRR_DELETE);
     }
     rule_collection_destroy(&rules);
 
@@ -4513,7 +4467,6 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
     uint64_t ofpacts_stub[1024 / 8];
     struct ofpbuf ofpacts;
     enum ofperr error;
-    long long int now;
 
     error = reject_slave_controller(ofconn);
     if (error) {
@@ -4535,31 +4488,7 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
         goto exit_free_ofpacts;
     }
 
-    /* Record the operation for logging a summary report. */
-    switch (fm.command) {
-    case OFPFC_ADD:
-        ofproto->n_add++;
-        break;
-
-    case OFPFC_MODIFY:
-    case OFPFC_MODIFY_STRICT:
-        ofproto->n_modify++;
-        break;
-
-    case OFPFC_DELETE:
-    case OFPFC_DELETE_STRICT:
-        ofproto->n_delete++;
-        break;
-    }
-
-    now = time_msec();
-    if (ofproto->next_op_report == LLONG_MAX) {
-        ofproto->first_op = now;
-        ofproto->next_op_report = MAX(now + 10 * 1000,
-                                      ofproto->op_backoff);
-        ofproto->op_backoff = ofproto->next_op_report + 60 * 1000;
-    }
-    ofproto->last_op = now;
+    ofconn_report_flow_mod(ofconn, fm.command);
 
 exit_free_ofpacts:
     ofpbuf_uninit(&ofpacts);
@@ -5226,7 +5155,8 @@ handle_delete_meter(struct ofconn *ofconn, const struct ofp_header *oh,
         }
     }
     if (rules.n > 0) {
-        delete_flows__(ofproto, ofconn, oh, &rules, OFPRR_METER_DELETE);
+        delete_flows__(ofproto, ofconn, OFPUTIL_FF_NO_READONLY,
+                       oh, &rules, OFPRR_METER_DELETE);
     }
 
     /* Delete the meters. */
@@ -5516,64 +5446,66 @@ append_group_stats(struct ofgroup *group, struct list *replies)
     free(ogs.bucket_stats);
 }
 
-static enum ofperr
-handle_group_stats_request(struct ofconn *ofconn,
-                           const struct ofp_header *request)
+static void
+handle_group_request(struct ofconn *ofconn,
+                     const struct ofp_header *request, uint32_t group_id,
+                     void (*cb)(struct ofgroup *, struct list *replies))
 {
     struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
-    struct list replies;
-    enum ofperr error;
     struct ofgroup *group;
-    uint32_t group_id;
-
-    error = ofputil_decode_group_stats_request(request, &group_id);
-    if (error) {
-        return error;
-    }
+    struct list replies;
 
     ofpmp_init(&replies, request);
-
     if (group_id == OFPG_ALL) {
         ovs_rwlock_rdlock(&ofproto->groups_rwlock);
         HMAP_FOR_EACH (group, hmap_node, &ofproto->groups) {
             ovs_rwlock_rdlock(&group->rwlock);
-            append_group_stats(group, &replies);
+            cb(group, &replies);
             ovs_rwlock_unlock(&group->rwlock);
         }
         ovs_rwlock_unlock(&ofproto->groups_rwlock);
     } else {
         if (ofproto_group_lookup(ofproto, group_id, &group)) {
-            append_group_stats(group, &replies);
+            cb(group, &replies);
             ofproto_group_release(group);
         }
     }
-
     ofconn_send_replies(ofconn, &replies);
-
-    return 0;
 }
 
 static enum ofperr
-handle_group_desc_stats_request(struct ofconn *ofconn,
-                                const struct ofp_header *request)
+handle_group_stats_request(struct ofconn *ofconn,
+                           const struct ofp_header *request)
 {
-    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
-    struct list replies;
-    struct ofputil_group_desc gds;
-    struct ofgroup *group;
-
-    ofpmp_init(&replies, request);
+    uint32_t group_id;
+    enum ofperr error;
 
-    ovs_rwlock_rdlock(&ofproto->groups_rwlock);
-    HMAP_FOR_EACH (group, hmap_node, &ofproto->groups) {
-        gds.group_id = group->group_id;
-        gds.type = group->type;
-        ofputil_append_group_desc_reply(&gds, &group->buckets, &replies);
+    error = ofputil_decode_group_stats_request(request, &group_id);
+    if (error) {
+        return error;
     }
-    ovs_rwlock_unlock(&ofproto->groups_rwlock);
 
-    ofconn_send_replies(ofconn, &replies);
+    handle_group_request(ofconn, request, group_id, append_group_stats);
+    return 0;
+}
 
+static void
+append_group_desc(struct ofgroup *group, struct list *replies)
+{
+    struct ofputil_group_desc gds;
+
+    gds.group_id = group->group_id;
+    gds.type = group->type;
+    ofputil_append_group_desc_reply(&gds, &group->buckets, replies);
+}
+
+static enum ofperr
+handle_group_desc_stats_request(struct ofconn *ofconn,
+                                const struct ofp_header *request)
+{
+    handle_group_request(ofconn, request,
+                         ofputil_decode_group_desc_request(request),
+                         append_group_desc);
     return 0;
 }
 
@@ -5931,6 +5863,69 @@ handle_table_mod(struct ofconn *ofconn, const struct ofp_header *oh)
     return table_mod(ofproto, &tm);
 }
 
+static enum ofperr
+handle_bundle_control(struct ofconn *ofconn, const struct ofp_header *oh)
+{
+    enum ofperr error;
+    struct ofputil_bundle_ctrl_msg bctrl;
+    struct ofpbuf *buf;
+    struct ofputil_bundle_ctrl_msg reply;
+
+    error = ofputil_decode_bundle_ctrl(oh, &bctrl);
+    if (error) {
+        return error;
+    }
+    reply.flags = 0;
+    reply.bundle_id = bctrl.bundle_id;
+
+    switch (bctrl.type) {
+        case OFPBCT_OPEN_REQUEST:
+        error = ofp_bundle_open(ofconn, bctrl.bundle_id, bctrl.flags);
+        reply.type = OFPBCT_OPEN_REPLY;
+        break;
+    case OFPBCT_CLOSE_REQUEST:
+        error = ofp_bundle_close(ofconn, bctrl.bundle_id, bctrl.flags);
+        reply.type = OFPBCT_CLOSE_REPLY;;
+        break;
+    case OFPBCT_COMMIT_REQUEST:
+        error = ofp_bundle_commit(ofconn, bctrl.bundle_id, bctrl.flags);
+        reply.type = OFPBCT_COMMIT_REPLY;
+        break;
+    case OFPBCT_DISCARD_REQUEST:
+        error = ofp_bundle_discard(ofconn, bctrl.bundle_id);
+        reply.type = OFPBCT_DISCARD_REPLY;
+        break;
+
+    case OFPBCT_OPEN_REPLY:
+    case OFPBCT_CLOSE_REPLY:
+    case OFPBCT_COMMIT_REPLY:
+    case OFPBCT_DISCARD_REPLY:
+        return OFPERR_OFPBFC_BAD_TYPE;
+        break;
+    }
+
+    if (!error) {
+        buf = ofputil_encode_bundle_ctrl_reply(oh, &reply);
+        ofconn_send_reply(ofconn, buf);
+    }
+    return error;
+}
+
+
+static enum ofperr
+handle_bundle_add(struct ofconn *ofconn, const struct ofp_header *oh)
+{
+    enum ofperr error;
+    struct ofputil_bundle_add_msg badd;
+
+    error = ofputil_decode_bundle_add(oh, &badd);
+    if (error) {
+        return error;
+    }
+
+    return ofp_bundle_add_message(ofconn, &badd);
+}
+
 static enum ofperr
 handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     OVS_EXCLUDED(ofproto_mutex)
@@ -6063,6 +6058,12 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     case OFPTYPE_QUEUE_GET_CONFIG_REQUEST:
         return handle_queue_get_config_request(ofconn, oh);
 
+    case OFPTYPE_BUNDLE_CONTROL:
+        return handle_bundle_control(ofconn, oh);
+
+    case OFPTYPE_BUNDLE_ADD_MESSAGE:
+        return handle_bundle_add(ofconn, oh);
+
     case OFPTYPE_HELLO:
     case OFPTYPE_ERROR:
     case OFPTYPE_FEATURES_REPLY:
@@ -6257,7 +6258,7 @@ ofopgroup_complete(struct ofopgroup *group)
         if (!(op->error
               || ofproto_rule_is_hidden(rule)
               || (op->type == OFOPERATION_MODIFY
-                  && op->actions
+                  && !op->actions
                   && rule->flow_cookie == op->flow_cookie))) {
             /* Check that we can just cast from ofoperation_type to
              * nx_flow_update_event. */