uint32_t buffer_id, struct rule *)
OVS_REQUIRES(ofproto_mutex);
-static bool ofproto_group_exists__(const struct ofproto *ofproto,
- uint32_t group_id)
- OVS_REQ_RDLOCK(ofproto->groups_rwlock);
-static bool ofproto_group_exists(const struct ofproto *ofproto,
- uint32_t group_id)
- OVS_EXCLUDED(ofproto->groups_rwlock);
+static bool ofproto_group_exists(const struct ofproto *, uint32_t group_id);
static enum ofperr add_group(struct ofproto *,
const struct ofputil_group_mod *);
static void handle_openflow(struct ofconn *, const struct ofpbuf *);
guarded_list_init(&ofproto->rule_executes);
ofproto->min_mtu = INT_MAX;
ovs_rwlock_init(&ofproto->groups_rwlock);
- hmap_init(&ofproto->groups);
+ cmap_init(&ofproto->groups);
ovs_mutex_unlock(&ofproto_mutex);
ofproto->ogf.types = 0xf;
ofproto->ogf.capabilities = OFPGFC_CHAINING | OFPGFC_SELECT_LIVENESS |
guarded_list_destroy(&ofproto->rule_executes);
ovs_rwlock_destroy(&ofproto->groups_rwlock);
- hmap_destroy(&ofproto->groups);
+ cmap_destroy(&ofproto->groups);
hmap_remove(&all_ofprotos, &ofproto->hmap_node);
free(ofproto->name);
}
}
+bool
+ofproto_group_try_ref(struct ofgroup *group)
+{
+ if (group) {
+ return ovs_refcount_try_ref_rcu(&group->ref_count);
+ }
+ return false;
+}
+
+static void
+group_destroy_cb(struct ofgroup *group)
+{
+ group->ofproto->ofproto_class->group_destruct(group);
+ ofputil_bucket_list_destroy(CONST_CAST(struct ovs_list *,
+ &group->buckets));
+ group->ofproto->ofproto_class->group_dealloc(group);
+}
+
void
ofproto_group_unref(struct ofgroup *group)
{
- if (group && ovs_refcount_unref(&group->ref_count) == 1) {
- group->ofproto->ofproto_class->group_destruct(group);
- ofputil_bucket_list_destroy(&group->buckets);
- group->ofproto->ofproto_class->group_dealloc(group);
+ if (group && ovs_refcount_unref_relaxed(&group->ref_count) == 1) {
+ ovsrcu_postpone(group_destroy_cb, group);
}
}
return 0;
}
-static bool
-ofproto_group_lookup__(const struct ofproto *ofproto, uint32_t group_id,
- struct ofgroup **group)
- OVS_REQ_RDLOCK(ofproto->groups_rwlock)
+/* Returned group is RCU protected. */
+static struct ofgroup *
+ofproto_group_lookup__(const struct ofproto *ofproto, uint32_t group_id)
{
- HMAP_FOR_EACH_IN_BUCKET (*group, hmap_node,
- hash_int(group_id, 0), &ofproto->groups) {
- if ((*group)->group_id == group_id) {
- return true;
+ struct ofgroup *group;
+
+ CMAP_FOR_EACH_WITH_HASH (group, cmap_node, hash_int(group_id, 0),
+ &ofproto->groups) {
+ if (group->group_id == group_id) {
+ return group;
}
}
- return false;
+ return NULL;
}
/* If the group exists, this function increments the groups's reference count.
*
* Make sure to call ofproto_group_unref() after no longer needing to maintain
* a reference to the group. */
-bool
-ofproto_group_lookup(const struct ofproto *ofproto, uint32_t group_id,
- struct ofgroup **group)
+struct ofgroup *
+ofproto_group_lookup(const struct ofproto *ofproto, uint32_t group_id)
{
- bool found;
-
- ovs_rwlock_rdlock(&ofproto->groups_rwlock);
- found = ofproto_group_lookup__(ofproto, group_id, group);
- if (found) {
- ofproto_group_ref(*group);
- }
- ovs_rwlock_unlock(&ofproto->groups_rwlock);
- return found;
-}
-
-static bool
-ofproto_group_exists__(const struct ofproto *ofproto, uint32_t group_id)
- OVS_REQ_RDLOCK(ofproto->groups_rwlock)
-{
- struct ofgroup *grp;
+ struct ofgroup *group;
- HMAP_FOR_EACH_IN_BUCKET (grp, hmap_node,
- hash_int(group_id, 0), &ofproto->groups) {
- if (grp->group_id == group_id) {
- return true;
- }
+ group = ofproto_group_lookup__(ofproto, group_id);
+ if (group) {
+ /* Not holding a lock, so it is possible that another thread releases
+ * the last reference just before we manage to get one. */
+ return ofproto_group_try_ref(group) ? group : NULL;
}
- return false;
+ return NULL;
}
+/* Caller should hold 'ofproto->groups_rwlock' if it is important that the
+ * group is not removed by someone else. */
static bool
ofproto_group_exists(const struct ofproto *ofproto, uint32_t group_id)
- OVS_EXCLUDED(ofproto->groups_rwlock)
{
- bool exists;
-
- ovs_rwlock_rdlock(&ofproto->groups_rwlock);
- exists = ofproto_group_exists__(ofproto, group_id);
- ovs_rwlock_unlock(&ofproto->groups_rwlock);
-
- return exists;
+ return ofproto_group_lookup__(ofproto, group_id) != NULL;
}
+/* XXX: This is potentially very expensive for large flow tables, and may be
+ * called in a loop. Maybe explicitly maintain the count of rules referring to
+ * the group instead?. */
static uint32_t
group_get_ref_count(struct ofgroup *group)
OVS_EXCLUDED(ofproto_mutex)
ofpmp_init(&replies, request);
if (group_id == OFPG_ALL) {
+ /* Must exclude modifications to guarantee iterating all groups. */
ovs_rwlock_rdlock(&ofproto->groups_rwlock);
- HMAP_FOR_EACH (group, hmap_node, &ofproto->groups) {
+ CMAP_FOR_EACH (group, cmap_node, &ofproto->groups) {
cb(group, &replies);
}
ovs_rwlock_unlock(&ofproto->groups_rwlock);
} else {
- if (ofproto_group_lookup(ofproto, group_id, &group)) {
+ group = ofproto_group_lookup__(ofproto, group_id);
+ if (group) {
cb(group, &replies);
- ofproto_group_unref(group);
}
}
ofconn_send_replies(ofconn, &replies);
*CONST_CAST(long long int *, &((*ofgroup)->modified)) = now;
ovs_refcount_init(&(*ofgroup)->ref_count);
- ovs_list_init(&(*ofgroup)->buckets);
- ofputil_bucket_clone_list(&(*ofgroup)->buckets, &gm->buckets, NULL);
+ ovs_list_init(CONST_CAST(struct ovs_list *, &(*ofgroup)->buckets));
+ ofputil_bucket_clone_list(CONST_CAST(struct ovs_list *,
+ &(*ofgroup)->buckets),
+ &gm->buckets, NULL);
*CONST_CAST(uint32_t *, &(*ofgroup)->n_buckets) =
ovs_list_size(&(*ofgroup)->buckets);
/* Construct called BEFORE any locks are held. */
error = ofproto->ofproto_class->group_construct(*ofgroup);
if (error) {
- ofputil_bucket_list_destroy(&(*ofgroup)->buckets);
+ ofputil_bucket_list_destroy(CONST_CAST(struct ovs_list *,
+ &(*ofgroup)->buckets));
ofproto->ofproto_class->group_dealloc(*ofgroup);
}
return error;
return error;
}
- /* We wrlock as late as possible to minimize the time we jam any other
- * threads: No visible state changes before acquiring the lock. */
+ /* We take the mutex as late as possible to minimize the time we jam any
+ * other threads: No visible state changes before acquiring the lock. */
ovs_rwlock_wrlock(&ofproto->groups_rwlock);
if (ofproto->n_groups[gm->type] >= ofproto->ogf.max_groups[gm->type]) {
goto unlock_out;
}
- if (ofproto_group_exists__(ofproto, gm->group_id)) {
+ if (ofproto_group_exists(ofproto, gm->group_id)) {
error = OFPERR_OFPGMFC_GROUP_EXISTS;
goto unlock_out;
}
- if (!error) {
- /* Insert new group. */
- hmap_insert(&ofproto->groups, &ofgroup->hmap_node,
- hash_int(ofgroup->group_id, 0));
- ofproto->n_groups[ofgroup->type]++;
+ /* Insert new group. */
+ cmap_insert(&ofproto->groups, &ofgroup->cmap_node,
+ hash_int(ofgroup->group_id, 0));
+ ofproto->n_groups[ofgroup->type]++;
- ovs_rwlock_unlock(&ofproto->groups_rwlock);
- return error;
- }
+ ovs_rwlock_unlock(&ofproto->groups_rwlock);
+ return 0;
unlock_out:
ovs_rwlock_unlock(&ofproto->groups_rwlock);
- ofproto->ofproto_class->group_destruct(ofgroup);
- ofputil_bucket_list_destroy(&ofgroup->buckets);
- ofproto->ofproto_class->group_dealloc(ofgroup);
-
+ group_destroy_cb(ofgroup);
return error;
}
}
}
- ofputil_bucket_clone_list(&new_ofgroup->buckets, &ofgroup->buckets, NULL);
+ ofputil_bucket_clone_list(CONST_CAST(struct ovs_list *,
+ &new_ofgroup->buckets),
+ &ofgroup->buckets, NULL);
if (ofputil_bucket_check_duplicate_id(&new_ofgroup->buckets)) {
VLOG_INFO_RL(&rl, "Duplicate bucket id");
first->bucket_id);
ovs_list_splice(new_ofgroup->buckets.next, &new_first->list_node,
- &new_ofgroup->buckets);
+ CONST_CAST(struct ovs_list *,
+ &new_ofgroup->buckets));
}
} else if (command_bucket_id <= OFPG15_BUCKET_MAX && last) {
struct ofputil_bucket *after;
}
}
- ofputil_bucket_clone_list(&new_ofgroup->buckets, &ofgroup->buckets, skip);
+ ofputil_bucket_clone_list(CONST_CAST(struct ovs_list *,
+ &new_ofgroup->buckets),
+ &ofgroup->buckets, skip);
return 0;
}
retiring = new_ofgroup;
ovs_rwlock_wrlock(&ofproto->groups_rwlock);
- if (!ofproto_group_lookup__(ofproto, gm->group_id, &ofgroup)) {
+ ofgroup = ofproto_group_lookup__(ofproto, gm->group_id);
+ if (!ofgroup) {
error = OFPERR_OFPGMFC_UNKNOWN_GROUP;
goto out;
}
*CONST_CAST(long long int *, &(new_ofgroup->created)) = ofgroup->created;
*CONST_CAST(long long int *, &(new_ofgroup->modified)) = time_msec();
+ /* XXX: OK to lose old group's stats? */
+
error = ofproto->ofproto_class->group_modify(new_ofgroup);
if (error) {
goto out;
retiring = ofgroup;
/* Replace ofgroup in ofproto's groups hash map with new_ofgroup. */
- hmap_remove(&ofproto->groups, &ofgroup->hmap_node);
- hmap_insert(&ofproto->groups, &new_ofgroup->hmap_node,
- hash_int(new_ofgroup->group_id, 0));
+ cmap_replace(&ofproto->groups, &ofgroup->cmap_node,
+ &new_ofgroup->cmap_node, hash_int(ofgroup->group_id, 0));
+
if (ofgroup->type != new_ofgroup->type) {
ofproto->n_groups[ofgroup->type]--;
ofproto->n_groups[new_ofgroup->type]++;
static enum ofperr
add_or_modify_group(struct ofproto *ofproto, const struct ofputil_group_mod *gm)
{
- struct ofgroup *ofgroup;
enum ofperr error;
bool exists;
- ovs_rwlock_rdlock(&ofproto->groups_rwlock);
- exists = ofproto_group_lookup__(ofproto, gm->group_id, &ofgroup);
- ovs_rwlock_unlock(&ofproto->groups_rwlock);
+ exists = ofproto_group_exists(ofproto, gm->group_id);
+
+ /* XXX: Race: Should hold groups_rwlock here. */
if (!exists) {
error = add_group(ofproto, gm);
ofm.fm.table_id = OFPTT_ALL;
handle_flow_mod__(ofproto, &ofm, NULL);
- hmap_remove(&ofproto->groups, &ofgroup->hmap_node);
- /* No-one can find this group any more. */
+ cmap_remove(&ofproto->groups, &ofgroup->cmap_node,
+ hash_int(ofgroup->group_id, 0));
+ /* No-one can find this group any more, but current users may continue to
+ * use it. */
ofproto->n_groups[ofgroup->type]--;
ovs_rwlock_unlock(&ofproto->groups_rwlock);
ofproto_group_unref(ofgroup);
ovs_rwlock_wrlock(&ofproto->groups_rwlock);
if (group_id == OFPG_ALL) {
for (;;) {
- struct hmap_node *node = hmap_first(&ofproto->groups);
+ struct cmap_node *node = cmap_first(&ofproto->groups);
if (!node) {
break;
}
- ofgroup = CONTAINER_OF(node, struct ofgroup, hmap_node);
- delete_group__(ofproto, ofgroup);
+ ofgroup = CONTAINER_OF(node, struct ofgroup, cmap_node);
+ delete_group__(ofproto, ofgroup); /* Releases the mutex. */
/* Lock for each node separately, so that we will not jam the
* other threads for too long time. */
ovs_rwlock_wrlock(&ofproto->groups_rwlock);
}
} else {
- HMAP_FOR_EACH_IN_BUCKET (ofgroup, hmap_node,
+ CMAP_FOR_EACH_WITH_HASH (ofgroup, cmap_node,
hash_int(group_id, 0), &ofproto->groups) {
if (ofgroup->group_id == group_id) {
delete_group__(ofproto, ofgroup);
])
AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-groups br0 groups.txt])
AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn dump-groups br0 ], [0], [stdout])
-AT_CHECK([strip_xids < stdout], [0], [dnl
-OFPST_GROUP_DESC reply (OF1.1):
+AT_CHECK([strip_xids < stdout | sort], [0], [dnl
group_id=1234,type=all,bucket=actions=output:10
group_id=1235,type=all,bucket=actions=output:10
+OFPST_GROUP_DESC reply (OF1.1):
])
AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn del-groups br0 group_id=1234])
AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn dump-groups br0], [0], [stdout])
])
AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn del-groups br0 group_id=1234])
AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout])
-AT_CHECK([strip_xids < stdout], [0], [dnl
-OFPST_GROUP_DESC reply (OF1.5):
- group_id=1235,type=all,bucket=bucket_id:2,actions=output:12,bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11
+AT_CHECK([strip_xids < stdout | sort], [0], [dnl
group_id=1233,type=select,selection_method=hash,bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11
+ group_id=1235,type=all,bucket=bucket_id:2,actions=output:12,bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11
+OFPST_GROUP_DESC reply (OF1.5):
])
AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn del-groups br0 group_id=1234])
AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout])
-AT_CHECK([strip_xids < stdout], [0], [dnl
-OFPST_GROUP_DESC reply (OF1.5):
- group_id=1235,type=all,bucket=bucket_id:2,actions=output:12,bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11
+AT_CHECK([strip_xids < stdout | sort], [0], [dnl
group_id=1233,type=select,selection_method=hash,bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11
+ group_id=1235,type=all,bucket=bucket_id:2,actions=output:12,bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11
+OFPST_GROUP_DESC reply (OF1.5):
])
AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn del-groups br0], [0])
AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout])