+static int
+weight_cmp (normalized_sub_tunnel_weights_t *a,
+ normalized_sub_tunnel_weights_t *b)
+{
+ int cmp = a->weight - b->weight;
+ return (cmp == 0
+ ? a->sub_tunnel_index - b->sub_tunnel_index
+ : (cmp > 0 ? -1 : 1));
+}
+
+/** Computes sub tunnel load balancing vector.
+ * Algorithm is identical to that used for building unequal-cost multipath
+ * adjacencies */
+static void
+compute_sub_tunnels_balancing_vector (lisp_gpe_tunnel_t * t)
+{
+ uword n_sts, i, n_nsts, n_nsts_left;
+ f64 sum_weight, norm, error, tolerance;
+ normalized_sub_tunnel_weights_t * nsts = 0, * stp;
+ lisp_gpe_sub_tunnel_t * sts = t->sub_tunnels;
+ u32 * st_lbv = 0;
+
+ /* Accept 1% error */
+ tolerance = .01;
+
+ n_sts = vec_len (sts);
+ vec_validate(nsts, 2 * n_sts - 1);
+
+ sum_weight = 0;
+ for (i = 0; i < n_sts; i++)
+ {
+ /* Find total weight to normalize weights. */
+ sum_weight += sts[i].weight;
+
+ /* build normalized sub tunnels vector */
+ nsts[i].weight = sts[i].weight;
+ nsts[i].sub_tunnel_index = i;
+ }
+
+ n_nsts = n_sts;
+ if (n_sts == 1)
+ {
+ nsts[0].weight = 1;
+ _vec_len(nsts) = 1;
+ goto build_lbv;
+ }
+
+ /* Sort sub-tunnels by weight */
+ qsort (nsts, n_nsts, sizeof(u32), (void * )weight_cmp);
+
+ /* Save copies of all next hop weights to avoid being overwritten in loop below. */
+ for (i = 0; i < n_nsts; i++)
+ nsts[n_nsts + i].weight = nsts[i].weight;
+
+ /* Try larger and larger power of 2 sized blocks until we
+ find one where traffic flows to within 1% of specified weights. */
+ for (n_nsts = max_pow2 (n_sts); ; n_nsts *= 2)
+ {
+ error = 0;
+
+ norm = n_nsts / sum_weight;
+ n_nsts_left = n_nsts;
+ for (i = 0; i < n_sts; i++)
+ {
+ f64 nf = nsts[n_sts + i].weight * norm;
+ word n = flt_round_nearest (nf);
+
+ n = n > n_nsts_left ? n_nsts_left : n;
+ n_nsts_left -= n;
+ error += fabs (nf - n);
+ nsts[i].weight = n;
+ }
+
+ nsts[0].weight += n_nsts_left;
+
+ /* Less than 5% average error per adjacency with this size adjacency block? */
+ if (error <= tolerance * n_nsts)
+ {
+ /* Truncate any next hops with zero weight. */
+ _vec_len (nsts) = i;
+ break;
+ }
+ }
+
+ build_lbv:
+
+ /* build load balancing vector */
+ vec_foreach (stp, nsts)
+ {
+ for (i = 0; i < stp[0].weight; i++)
+ vec_add1(st_lbv, stp[0].sub_tunnel_index);
+ }
+
+ t->sub_tunnels_lbv = st_lbv;
+ t->sub_tunnels_lbv_count = n_nsts;
+ t->norm_sub_tunnel_weights = nsts;
+}
+
+static void
+create_sub_tunnels (lisp_gpe_main_t * lgm, lisp_gpe_tunnel_t * t)
+{
+ lisp_gpe_sub_tunnel_t st;
+ locator_pair_t * lp = 0;
+ int i;
+
+ /* create sub-tunnels for all locator pairs */
+ for (i = 0; i < vec_len(t->locator_pairs); i++)
+ {
+ lp = &t->locator_pairs[i];
+ st.locator_pair_index = i;
+ st.parent_index = t - lgm->tunnels;
+ st.weight = lp->weight;
+
+ /* compute rewrite for sub-tunnel */
+ lisp_gpe_rewrite (t, &st, lp);
+ vec_add1(t->sub_tunnels, st);
+ }
+
+ /* normalize weights and compute sub-tunnel load balancing vector */
+ compute_sub_tunnels_balancing_vector(t);
+}
+