@@ -3481,6 +3481,99 @@ struct arg_netdev_event {
3481
3481
};
3482
3482
};
3483
3483
3484
+ static struct rt6_info * rt6_multipath_first_sibling (const struct rt6_info * rt )
3485
+ {
3486
+ struct rt6_info * iter ;
3487
+ struct fib6_node * fn ;
3488
+
3489
+ fn = rcu_dereference_protected (rt -> rt6i_node ,
3490
+ lockdep_is_held (& rt -> rt6i_table -> tb6_lock ));
3491
+ iter = rcu_dereference_protected (fn -> leaf ,
3492
+ lockdep_is_held (& rt -> rt6i_table -> tb6_lock ));
3493
+ while (iter ) {
3494
+ if (iter -> rt6i_metric == rt -> rt6i_metric &&
3495
+ rt6_qualify_for_ecmp (iter ))
3496
+ return iter ;
3497
+ iter = rcu_dereference_protected (iter -> rt6_next ,
3498
+ lockdep_is_held (& rt -> rt6i_table -> tb6_lock ));
3499
+ }
3500
+
3501
+ return NULL ;
3502
+ }
3503
+
3504
+ static bool rt6_is_dead (const struct rt6_info * rt )
3505
+ {
3506
+ if (rt -> rt6i_nh_flags & RTNH_F_DEAD ||
3507
+ (rt -> rt6i_nh_flags & RTNH_F_LINKDOWN &&
3508
+ rt -> rt6i_idev -> cnf .ignore_routes_with_linkdown ))
3509
+ return true;
3510
+
3511
+ return false;
3512
+ }
3513
+
3514
+ static int rt6_multipath_total_weight (const struct rt6_info * rt )
3515
+ {
3516
+ struct rt6_info * iter ;
3517
+ int total = 0 ;
3518
+
3519
+ if (!rt6_is_dead (rt ))
3520
+ total ++ ;
3521
+
3522
+ list_for_each_entry (iter , & rt -> rt6i_siblings , rt6i_siblings ) {
3523
+ if (!rt6_is_dead (iter ))
3524
+ total ++ ;
3525
+ }
3526
+
3527
+ return total ;
3528
+ }
3529
+
3530
+ static void rt6_upper_bound_set (struct rt6_info * rt , int * weight , int total )
3531
+ {
3532
+ int upper_bound = -1 ;
3533
+
3534
+ if (!rt6_is_dead (rt )) {
3535
+ (* weight )++ ;
3536
+ upper_bound = DIV_ROUND_CLOSEST_ULL ((u64 ) (* weight ) << 31 ,
3537
+ total ) - 1 ;
3538
+ }
3539
+ atomic_set (& rt -> rt6i_nh_upper_bound , upper_bound );
3540
+ }
3541
+
3542
+ static void rt6_multipath_upper_bound_set (struct rt6_info * rt , int total )
3543
+ {
3544
+ struct rt6_info * iter ;
3545
+ int weight = 0 ;
3546
+
3547
+ rt6_upper_bound_set (rt , & weight , total );
3548
+
3549
+ list_for_each_entry (iter , & rt -> rt6i_siblings , rt6i_siblings )
3550
+ rt6_upper_bound_set (iter , & weight , total );
3551
+ }
3552
+
3553
+ void rt6_multipath_rebalance (struct rt6_info * rt )
3554
+ {
3555
+ struct rt6_info * first ;
3556
+ int total ;
3557
+
3558
+ /* In case the entire multipath route was marked for flushing,
3559
+ * then there is no need to rebalance upon the removal of every
3560
+ * sibling route.
3561
+ */
3562
+ if (!rt -> rt6i_nsiblings || rt -> should_flush )
3563
+ return ;
3564
+
3565
+ /* During lookup routes are evaluated in order, so we need to
3566
+ * make sure upper bounds are assigned from the first sibling
3567
+ * onwards.
3568
+ */
3569
+ first = rt6_multipath_first_sibling (rt );
3570
+ if (WARN_ON_ONCE (!first ))
3571
+ return ;
3572
+
3573
+ total = rt6_multipath_total_weight (first );
3574
+ rt6_multipath_upper_bound_set (first , total );
3575
+ }
3576
+
3484
3577
static int fib6_ifup (struct rt6_info * rt , void * p_arg )
3485
3578
{
3486
3579
const struct arg_netdev_event * arg = p_arg ;
@@ -3489,6 +3582,7 @@ static int fib6_ifup(struct rt6_info *rt, void *p_arg)
3489
3582
if (rt != net -> ipv6 .ip6_null_entry && rt -> dst .dev == arg -> dev ) {
3490
3583
rt -> rt6i_nh_flags &= ~arg -> nh_flags ;
3491
3584
fib6_update_sernum_upto_root (dev_net (rt -> dst .dev ), rt );
3585
+ rt6_multipath_rebalance (rt );
3492
3586
}
3493
3587
3494
3588
return 0 ;
@@ -3588,13 +3682,15 @@ static int fib6_ifdown(struct rt6_info *rt, void *p_arg)
3588
3682
rt6_multipath_nh_flags_set (rt , dev , RTNH_F_DEAD |
3589
3683
RTNH_F_LINKDOWN );
3590
3684
fib6_update_sernum (rt );
3685
+ rt6_multipath_rebalance (rt );
3591
3686
}
3592
3687
return -2 ;
3593
3688
case NETDEV_CHANGE :
3594
3689
if (rt -> dst .dev != dev ||
3595
3690
rt -> rt6i_flags & (RTF_LOCAL | RTF_ANYCAST ))
3596
3691
break ;
3597
3692
rt -> rt6i_nh_flags |= RTNH_F_LINKDOWN ;
3693
+ rt6_multipath_rebalance (rt );
3598
3694
break ;
3599
3695
}
3600
3696
0 commit comments