+
+ bundle = ofconn_get_bundle(ofconn, id);
+
+ if (!bundle) {
+ return OFPERR_OFPBFC_BAD_ID;
+ }
+ if (bundle->flags != flags) {
+ error = OFPERR_OFPBFC_BAD_FLAGS;
+ } else {
+ bool prev_is_port_mod = false;
+
+ error = 0;
+ ovs_mutex_lock(&ofproto_mutex);
+
+ /* 1. Begin. */
+ LIST_FOR_EACH (be, node, &bundle->msg_list) {
+ if (be->type == OFPTYPE_PORT_MOD) {
+ /* Our port mods are not atomic. */
+ if (flags & OFPBF_ATOMIC) {
+ error = OFPERR_OFPBFC_MSG_FAILED;
+ } else {
+ prev_is_port_mod = true;
+ error = port_mod_start(ofconn, &be->opm.pm, &be->opm.port);
+ }
+ } else if (be->type == OFPTYPE_FLOW_MOD) {
+ /* Flow mods between port mods are applied as a single
+ * version, but the versions are published only after
+ * we know the commit is successful. */
+ if (prev_is_port_mod) {
+ ++version;
+ }
+ prev_is_port_mod = false;
+ /* Store the version in which the changes should take
+ * effect. */
+ be->ofm.version = version;
+ error = ofproto_flow_mod_start(ofproto, &be->ofm);
+ } else {
+ OVS_NOT_REACHED();
+ }
+ if (error) {
+ break;
+ }
+ }
+
+ if (error) {
+ /* Send error referring to the original message. */
+ if (error) {
+ ofconn_send_error(ofconn, be->ofp_msg, error);
+ error = OFPERR_OFPBFC_MSG_FAILED;
+ }
+
+ /* 2. Revert. Undo all the changes made above. */
+ LIST_FOR_EACH_REVERSE_CONTINUE(be, node, &bundle->msg_list) {
+ if (be->type == OFPTYPE_FLOW_MOD) {
+ ofproto_flow_mod_revert(ofproto, &be->ofm);
+ }
+ /* Nothing needs to be reverted for a port mod. */
+ }
+ } else {
+ /* 4. Finish. */
+ LIST_FOR_EACH (be, node, &bundle->msg_list) {
+ if (be->type == OFPTYPE_FLOW_MOD) {
+ struct flow_mod_requester req = { ofconn, be->ofp_msg };
+
+ /* Bump the lookup version to the one of the current
+ * message. This makes all the changes in the bundle at
+ * this version visible to lookups at once. */
+ if (ofproto->tables_version < be->ofm.version) {
+ ofproto->tables_version = be->ofm.version;
+ ofproto->ofproto_class->set_tables_version(
+ ofproto, ofproto->tables_version);
+ }
+
+ ofproto_flow_mod_finish(ofproto, &be->ofm, &req);
+ } else if (be->type == OFPTYPE_PORT_MOD) {
+ /* Perform the actual port mod. This is not atomic, i.e.,
+ * the effects will be immediately seen by upcall
+ * processing regardless of the lookup version. It should
+ * be noted that port configuration changes can originate
+ * also from OVSDB changes asynchronously to all upcall
+ * processing. */
+ port_mod_finish(ofconn, &be->opm.pm, be->opm.port);
+ }
+ }
+ }
+
+ ofmonitor_flush(ofproto->connmgr);
+ ovs_mutex_unlock(&ofproto_mutex);
+
+ run_rule_executes(ofproto);
+ }
+
+ /* The bundle is discarded regardless the outcome. */
+ ofp_bundle_remove__(ofconn, bundle, !error);
+ return error;
+}
+
+static enum ofperr
+handle_bundle_control(struct ofconn *ofconn, const struct ofp_header *oh)
+{