linux/include
Alexandru Ardelean 3e5fbcbc38 util_macros.h: fix/rework find_closest() macros
commit bc73b41867 upstream.

A bug was found in the find_closest() (find_closest_descending() is also
affected after some testing), where for certain values with small
progressions, the rounding (done by averaging 2 values) causes an
incorrect index to be returned.  The rounding issues occur for
progressions of 1, 2 and 3.  It goes away when the progression/interval
between two values is 4 or larger.

It's particularly bad for progressions of 1.  For example if there's an
array of 'a = { 1, 2, 3 }', using 'find_closest(2, a ...)' would return 0
(the index of '1'), rather than returning 1 (the index of '2').  This
means that for exact values (with a progression of 1), find_closest() will
misbehave and return the index of the value smaller than the one we're
searching for.

For progressions of 2 and 3, the exact values are obtained correctly; but
values aren't approximated correctly (as one would expect).  Starting with
progressions of 4, all seems to be good (one gets what one would expect).

While one could argue that 'find_closest()' should not be used for arrays
with progressions of 1 (i.e. '{1, 2, 3, ...}', the macro should still
behave correctly.

The bug was found while testing the 'drivers/iio/adc/ad7606.c',
specifically the oversampling feature.
For reference, the oversampling values are listed as:
   static const unsigned int ad7606_oversampling_avail[7] = {
          1, 2, 4, 8, 16, 32, 64,
   };

When doing:
  1. $ echo 1 > /sys/bus/iio/devices/iio\:device0/oversampling_ratio
     $ cat /sys/bus/iio/devices/iio\:device0/oversampling_ratio
     1  # this is fine
  2. $ echo 2 > /sys/bus/iio/devices/iio\:device0/oversampling_ratio
     $ cat /sys/bus/iio/devices/iio\:device0/oversampling_ratio
     1  # this is wrong; 2 should be returned here
  3. $ echo 3 > /sys/bus/iio/devices/iio\:device0/oversampling_ratio
     $ cat /sys/bus/iio/devices/iio\:device0/oversampling_ratio
     2  # this is fine
  4. $ echo 4 > /sys/bus/iio/devices/iio\:device0/oversampling_ratio
     $ cat /sys/bus/iio/devices/iio\:device0/oversampling_ratio
     4  # this is fine
And from here-on, the values are as correct (one gets what one would
expect.)

While writing a kunit test for this bug, a peculiar issue was found for the
array in the 'drivers/hwmon/ina2xx.c' & 'drivers/iio/adc/ina2xx-adc.c'
drivers. While running the kunit test (for 'ina226_avg_tab' from these
drivers):
  * idx = find_closest([-1 to 2], ina226_avg_tab, ARRAY_SIZE(ina226_avg_tab));
    This returns idx == 0, so value.
  * idx = find_closest(3, ina226_avg_tab, ARRAY_SIZE(ina226_avg_tab));
    This returns idx == 0, value 1; and now one could argue whether 3 is
    closer to 4 or to 1. This quirk only appears for value '3' in this
    array, but it seems to be a another rounding issue.
  * And from 4 onwards the 'find_closest'() works fine (one gets what one
    would expect).

This change reworks the find_closest() macros to also check the difference
between the left and right elements when 'x'. If the distance to the right
is smaller (than the distance to the left), the index is incremented by 1.
This also makes redundant the need for using the DIV_ROUND_CLOSEST() macro.

In order to accommodate for any mix of negative + positive values, the
internal variables '__fc_x', '__fc_mid_x', '__fc_left' & '__fc_right' are
forced to 'long' type. This also addresses any potential bugs/issues with
'x' being of an unsigned type. In those situations any comparison between
signed & unsigned would be promoted to a comparison between 2 unsigned
numbers; this is especially annoying when '__fc_left' & '__fc_right'
underflow.

The find_closest_descending() macro was also reworked and duplicated from
the find_closest(), and it is being iterated in reverse. The main reason
for this is to get the same indices as 'find_closest()' (but in reverse).
The comparison for '__fc_right < __fc_left' favors going the array in
ascending order.
For example for array '{ 1024, 512, 256, 128, 64, 16, 4, 1 }' and x = 3, we
get:
    __fc_mid_x = 2
    __fc_left = -1
    __fc_right = -2
    Then '__fc_right < __fc_left' evaluates to true and '__fc_i++' becomes 7
    which is not quite incorrect, but 3 is closer to 4 than to 1.

This change has been validated with the kunit from the next patch.

Link: https://lkml.kernel.org/r/20241105145406.554365-1-aardelean@baylibre.com
Fixes: 95d119528b ("util_macros.h: add find_closest() macro")
Signed-off-by: Alexandru Ardelean <aardelean@baylibre.com>
Cc: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
History 2024-12-09 10:41:06 +01:00
..
acpi ACPI: processor: Move arch_init_invariance_cppc() call later 2024-11-06 21:31:36 +01:00
asm-generic Rename .data.once to .data..once to fix resetting WARN*_ONCE 2024-12-05 14:03:08 +01:00
clocksource pwm: xilinx: Fix u32 overflow issue in 32-bit width PWM mode. 2024-06-03 13:07:56 +02:00
crypto move asm/unaligned.h to linux/unaligned.h 2024-10-02 17:23:23 -04:00
cxl cxl: Move mailbox related bits to the same context 2024-09-12 08:38:01 -07:00
drm drm/i915/gsc: ARL-H and ARL-U need a newer GSC FW. 2024-11-12 09:44:55 +02:00
dt-bindings soc: convert ep93xx to devicetree 2024-09-26 12:00:25 -07:00
keys KEYS: Remove unused declarations 2024-09-20 18:28:26 +03:00
kunit kunit: skb: use "gfp" variable instead of hardcoding GFP_KERNEL 2024-12-05 14:02:13 +01:00
kvm KVM: arm64: Refine PMU defines for number of counters 2024-08-16 13:09:12 +01:00
linux util_macros.h: fix/rework find_closest() macros 2024-12-09 10:41:06 +01:00
math-emu powerpc/math-emu: Remove -w build flag and fix warnings 2022-09-05 17:28:25 +10:00
media media: v4l2-core: v4l2-dv-timings: check cvt/gtf result 2024-12-05 14:02:57 +01:00
memory memory: renesas-rpc-if: Remove Runtime PM wrappers 2023-01-23 11:48:00 +01:00
misc cxl: fix typo in comment 2022-11-24 23:12:19 +11:00
net Rename .data.once to .data..once to fix resetting WARN*_ONCE 2024-12-05 14:03:08 +01:00
pcmcia ARM: pxa/sa1100: move I/O space to PCI_IOBASE 2022-05-07 22:56:17 +02:00
ras mm/memory-failure: remove obsolete MF_MSG_DIFFERENT_COMPOUND 2024-07-12 15:52:22 -07:00
rdma RDMA/core: Implement RoCE GID port rescan and export delete function 2024-12-05 14:02:08 +01:00
rv rv: Set variable 'da_mon_##name' to static 2023-09-01 21:00:00 -04:00
scsi move asm/unaligned.h to linux/unaligned.h 2024-10-02 17:23:23 -04:00
soc soc: driver updates for 6.12 2024-09-17 10:48:09 +02:00
sound ALSA: hda: fix trigger_tstamp_latched 2024-10-02 12:50:24 +02:00
target move asm/unaligned.h to linux/unaligned.h 2024-10-02 17:23:23 -04:00
trace Including fixes from can and netfilter. 2024-11-07 11:07:57 -10:00
uapi netkit: Add option for scrubbing skb meta data 2024-12-09 10:40:56 +01:00
ufs Many singleton patches - please see the various changelogs for details. 2024-09-21 08:20:50 -07:00
vdso random: vDSO: add a __vdso_getrandom prototype for all architectures 2024-09-13 17:28:35 +02:00
video fbdev: da8xx: remove the driver 2024-10-15 10:08:23 +02:00
xen xen: Remove dependency between pciback and privcmd 2024-10-18 11:59:04 +02:00