+static bool
+odp_port_is_alive(const struct xlate_ctx *ctx, ofp_port_t ofp_port)
+{
+ struct xport *xport;
+
+ xport = get_ofp_port(ctx->xbridge, ofp_port);
+ if (!xport || xport->config & OFPUTIL_PC_PORT_DOWN ||
+ xport->state & OFPUTIL_PS_LINK_DOWN) {
+ return false;
+ }
+
+ return true;
+}
+
+static const struct ofputil_bucket *
+group_first_live_bucket(const struct xlate_ctx *, const struct group_dpif *,
+ int depth);
+
+static bool
+group_is_alive(const struct xlate_ctx *ctx, uint32_t group_id, int depth)
+{
+ struct group_dpif *group;
+ bool hit;
+
+ hit = group_dpif_lookup(ctx->xbridge->ofproto, group_id, &group);
+ if (!hit) {
+ return false;
+ }
+
+ hit = group_first_live_bucket(ctx, group, depth) != NULL;
+
+ group_dpif_release(group);
+ return hit;
+}
+
+#define MAX_LIVENESS_RECURSION 128 /* Arbitrary limit */
+
+static bool
+bucket_is_alive(const struct xlate_ctx *ctx,
+ const struct ofputil_bucket *bucket, int depth)
+{
+ if (depth >= MAX_LIVENESS_RECURSION) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+
+ VLOG_WARN_RL(&rl, "bucket chaining exceeded %d links",
+ MAX_LIVENESS_RECURSION);
+ return false;
+ }
+
+ return !ofputil_bucket_has_liveness(bucket) ||
+ (bucket->watch_port != OFPP_ANY &&
+ odp_port_is_alive(ctx, bucket->watch_port)) ||
+ (bucket->watch_group != OFPG_ANY &&
+ group_is_alive(ctx, bucket->watch_group, depth + 1));
+}
+
+static const struct ofputil_bucket *
+group_first_live_bucket(const struct xlate_ctx *ctx,
+ const struct group_dpif *group, int depth)
+{
+ struct ofputil_bucket *bucket;
+ const struct list *buckets;
+
+ group_dpif_get_buckets(group, &buckets);
+ LIST_FOR_EACH (bucket, list_node, buckets) {
+ if (bucket_is_alive(ctx, bucket, depth)) {
+ return bucket;
+ }
+ }
+
+ return NULL;
+}
+
+static const struct ofputil_bucket *
+group_best_live_bucket(const struct xlate_ctx *ctx,
+ const struct group_dpif *group,
+ uint32_t basis)
+{
+ const struct ofputil_bucket *best_bucket = NULL;
+ uint32_t best_score = 0;
+ int i = 0;
+
+ const struct ofputil_bucket *bucket;
+ const struct list *buckets;
+
+ group_dpif_get_buckets(group, &buckets);
+ LIST_FOR_EACH (bucket, list_node, buckets) {
+ if (bucket_is_alive(ctx, bucket, 0)) {
+ uint32_t score = (hash_int(i, basis) & 0xffff) * bucket->weight;
+ if (score >= best_score) {
+ best_bucket = bucket;
+ best_score = score;
+ }
+ }
+ i++;
+ }
+
+ return best_bucket;
+}
+