Re: [PATCH net v4 2/2] bonding: fix u32 overflow in compute_gap()

Nikolay Aleksandrov <[email protected]>
Newsgroups org.kernel.vger.netdev,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
On 21/08/2026 15:58, Hangbin Liu wrote:
> On Fri, Aug 21, 2026 at 02:33:39PM +0300, Nikolay Aleksandrov wrote:
>>>> I think Sashiko's review has a point here:
>>>> "Does clamping the gap to 0 completely break load balancing when all interfaces
>>>> are overloaded?
>>>> When all slaves are overloaded, compute_gap() returns 0 for all of them. Since
>>>> max_gap is initialized to 0, max_gap <= gap will evaluate to 0 <= 0, which is
>>>> true.
>>>> This means tlb_get_least_loaded_slave() will continually update least_loaded to
>>>> the current slave, ultimately routing all traffic to the last slave in the list
>>>> instead of distributing it across the least overloaded interfaces."
>>>
>>> Yes, I have thought about this question. Previous code set max_gap LLONG_MIN,
>>> so there always has a slave assigned. Now we use u64. If we use (max_gap < gap),
>>> there may return NULL pointer.
>>>
>>>>
>>>> That is, compute_gap makes multiple different scenarios look the same:
>>>>    if speed is unknown           = 0
>>>>    if exactly equal capacity     = 0
>>>>    if overloaded by *any* amount = 0
>>>
>>> Yes, if there is are 2 NICs with 1 Gbps and 10 Gbps, but both shows as
>>> unknown. There is no meaning to compare the gaps. Because they use the same
>>> speed value (u32)-1 << 20.
>>>
>>> If two NICs both overloaded or equal capacity. There is also no mean to select
>>> any devices.
>>
>> Well that is debatable, to be correct you'd like to choose the NIC that is
>> least overloaded, one could be at capacity and the other could be 10Gbps above
>> capacity and you can still choose the second with this.
>>
>> If you make it a signed comparison then you can choose the least loaded, you'd
> 
> Oh, do you want to fallback to use s64 (long long) in compute_gap? Then
> all the counters need to using s64. The same with unbalanced_load, and we
> can't using the "delta" anymore. Do we need to change back to using spin_lock
> to protect the unbalanced_load writing.
> 

why? see more below

>> have to mark unknown speed with S64_MIN but it will compute the correct numbers
> 
> Here do you mean
> 	if (raw_speed == (u32)SPEED_UNKNOWN)
> 		s64 speed = S64_MIN
> 
> ? Then the 's64 gap = speed - load' will overflow, which means a 1Gbps NIC
> (shown as unknown) will have more gaps then 10Gbps NIC (correctly shown speed)
> 

oh that is easily fixed, it should not be a problem

>> and you can choose the least overloaded NIC, which the current code actually
>> does correctly.
>>
>> And most importantly - you definitely want to differentiate between unknown speed
>> and overload, these should not be the same.
> 
> If we use s64 and all slaves are overloaded, we can compute the difference.
> But once there is an unknown speed NIC, we lose visibility into the real difference.
> Such a NIC could be 1G, 10G, or 100G, yet we set its speed to `(u32)-1`.

no, we use signed and set it at S64_MIN, it is never chosen.

> 
> That is why I believe comparing gaps for NICs with unknown speed is meaningless.
> 

Right and they shouldn't be considered or rather should be last.

> Regarding overload scenarios: do you think this is a common‑case situation?
> Because in practice, we rarely hit the theoretical maximum link speed.
> For example, a 10Gbps NIC typically peaks at around ~950 Mbps.
> 
> Thanks
> Hangbin

Completely untested, but something like:

-static u64 compute_gap(struct slave *slave)
+static s64 compute_gap(struct slave *slave)
  {
         u64 slave_load = SLAVE_TLB_INFO(slave).load << 3;
         u32 raw_speed = READ_ONCE(slave->speed);
         u64 speed = (u64)raw_speed << 20;

         if (raw_speed == (u32)SPEED_UNKNOWN)
-               return 0;
-
-       if (speed <= slave_load)
-               return 0;
+               return S64_MIN;

-       return speed - slave_load;
+       return (s64)speed - (s64)slave_load;
  }

Then change the selection variables and comparison:

-       u64 max_gap = 0;
+       s64 max_gap = S64_MIN;

...

-                       u64 gap = compute_gap(slave);
+                       s64 gap = compute_gap(slave);

-                       if (max_gap <= gap) {
+                       if (!least_loaded || max_gap < gap) {

This should choose the slave with smallest gap and put the unknown speed behind all
slaves with known speeds.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.