* [PATCH v3 0/3] Introduce in_range_incl() inclusive range check macro
@ 2026-08-31 16:26 Guru Das Srinagesh
2026-08-31 16:26 ` [PATCH v3 1/3] minmax: Add in_range_inclusive() for inclusive range checks Guru Das Srinagesh
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Guru Das Srinagesh @ 2026-08-31 16:26 UTC (permalink / raw)
To: Alex Lanzano, Jonathan Cameron, David Lechner, Nuno Sá,
Andy Shevchenko, Matthew Wilcox, Andrew Morton, Gustavo Silva
Cc: linux-kernel, linux-iio, Guru Das Srinagesh
Extend the existing in_range() API to provide a user-friendly inclusive
range check and add one illustrative in-tree example of its use.
The in_range() API lends itself easily to callers who care about a
half-open range, but has no straightforward equivalent for callers that
want to check for an inclusive range. Such callers have to resort to
this pattern:
in_range(val, start, (end - start + 1))
which is quite clunky and easy to mess up. Examples of such callers:
- fs/btrfs/extent-io-tree.c hand-computes the inclusive check by
passing "state->end - state->start + 1" as in_range()'s len argument.
- drivers/md/dm-raid.c has its own file-local __within_range(v, min, max)
helper, independently invented, with almost a dozen call sites in
that one file.
- drivers/iio/imu/bmi270/bmi270_core.c has three "in_range(val, 0,
MAX + 1)" checks in bmi270_write_event_value(), computing the +1 by
hand for the same reason.
This series converts only bmi270_core.c as a first step, as an exemplar
for the usage of the new API. Note that similar conversions in other
call sites are not drop-in replacements and have to be reviewed
carefully to ensure the behaviour of the check does not change.
Signed-off-by: Guru Das Srinagesh <linux@gurudas.dev>
---
Changes in v3:
- (Matthew) Go back to in_range() approach which was earlier incorrectly
abandoned in v2 to avoid the overflow problem with v1's plain wrapper of
in_range() with len = (max - min + 1)
- (Andy) Add Kunit test for in_range_inclusive() covering both u32 and
u64 test cases. Tested with:
./tools/testing/kunit/kunit.py run --arch=x86_64 "in_range*"
- (Andy) Added return value kernel-doc description
- in_range_inclusive() deliberately does not add a BUILD_BUG_ON for
@min > @max nor a signedness check in keeping with the in_range() example.
- AI assistance (Claude, sashiko using Claude CLI as backend) was used
to improve precision in kernel-doc description, refining test cases
and adding more test case classes.
- Rebased to v7.2
- Link to v2: https://patch.msgid.link/20260816-minmax-in-range-incl-v2-0-766f737dd6bf@gurudas.dev
Changes in v2:
- (Andrew) Rename in_range_incl() to in_range_inclusive()
- Wholly incorrect approach abandoning in_range()
Link to v1: https://patch.msgid.link/20260815-minmax-in-range-incl-v1-0-a75f7d9ae92e@gurudas.dev
To: Alex Lanzano <lanzano.alex@gmail.com>
To: Jonathan Cameron <jic23@kernel.org>
To: David Lechner <dlechner@baylibre.com>
To: Nuno Sá <nuno.sa@analog.com>
To: Andy Shevchenko <andy@kernel.org>
Cc: linux-kernel@vger.kernel.org
Cc: linux-iio@vger.kernel.org
---
Guru Das Srinagesh (3):
minmax: Add in_range_inclusive() for inclusive range checks
lib/tests: Add in_range_inclusive() KUnit test
iio: imu: bmi270: Use in_range_inclusive() in bmi270_write_event_value()
drivers/iio/imu/bmi270/bmi270_core.c | 6 +-
include/linux/minmax.h | 39 ++++++
lib/Kconfig.debug | 18 +++
lib/tests/Makefile | 1 +
lib/tests/in_range_inclusive_kunit.c | 249 +++++++++++++++++++++++++++++++++++
5 files changed, 310 insertions(+), 3 deletions(-)
---
base-commit: 8d3ae59288f1e7d58d76558a6ee96d533bc5019f
change-id: 20260815-minmax-in-range-incl-a25c242be676
Best regards,
--
Guru Das Srinagesh <linux@gurudas.dev>
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v3 1/3] minmax: Add in_range_inclusive() for inclusive range checks
2026-08-31 16:26 [PATCH v3 0/3] Introduce in_range_incl() inclusive range check macro Guru Das Srinagesh
@ 2026-08-31 16:26 ` Guru Das Srinagesh
2026-09-01 7:26 ` Andy Shevchenko
2026-08-31 16:26 ` [PATCH v3 2/3] lib/tests: Add in_range_inclusive() KUnit test Guru Das Srinagesh
2026-08-31 16:26 ` [PATCH v3 3/3] iio: imu: bmi270: Use in_range_inclusive() in bmi270_write_event_value() Guru Das Srinagesh
2 siblings, 1 reply; 7+ messages in thread
From: Guru Das Srinagesh @ 2026-08-31 16:26 UTC (permalink / raw)
To: Alex Lanzano, Jonathan Cameron, David Lechner, Nuno Sá,
Andy Shevchenko, Matthew Wilcox, Andrew Morton, Gustavo Silva
Cc: linux-kernel, linux-iio, Guru Das Srinagesh
Extend the logic in in_range() to support checking for an inclusive
range [min, max].
The condition in the check is derived as follows, starting from the
in_range() macro with len = (max - min + 1):
(val - min) < (max - min + 1) // overflows for [0, U32/U64_MAX]
(val - min) <= (max - min) // no overflow
The behaviour of the macro from the signedness perspective is documented
in the kernel-doc and in the in_range_inclusive KUnit test suite.
Assisted-by: Claude-Code:claude-opus-5
Signed-off-by: Guru Das Srinagesh <linux@gurudas.dev>
---
include/linux/minmax.h | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/include/linux/minmax.h b/include/linux/minmax.h
index a0158db54a04..dce3a8bea9e2 100644
--- a/include/linux/minmax.h
+++ b/include/linux/minmax.h
@@ -299,6 +299,45 @@ static inline bool in_range32(u32 val, u32 start, u32 len)
((sizeof(start) | sizeof(len) | sizeof(val)) <= sizeof(u32) ? \
in_range32(val, start, len) : in_range64(val, start, len))
+static inline bool in_range_inclusive64(u64 val, u64 min, u64 max)
+{
+ return (val - min) <= (max - min);
+}
+
+static inline bool in_range_inclusive32(u32 val, u32 min, u32 max)
+{
+ return (val - min) <= (max - min);
+}
+
+/**
+ * in_range_inclusive - Determine if a value lies within an inclusive range.
+ * @val: Value to test.
+ * @min: First value in range.
+ * @max: Last value in range.
+ *
+ * This checks if a value lies within the closed range of [@min, @max]. Note that
+ * "range" refers to values counting up from @min with wraparound at
+ * unsigned-datatype max if encountered, continuing on till @max is reached.
+ *
+ * This macro is not a drop-in replacement for "if (val >= min && val <= max)".
+ * Unsigned arithmetic determines what the 'true' range exactly is depending on
+ * whether @min <= @max holds, and in which reading (signed vs unsigned) as follows::
+ *
+ * Valid in reading Example 'True' range is
+ * Both readings [5, 10] interval as written in either reading
+ * Signed only [-10, 5] signed interval
+ * Unsigned only [5, -10] unsigned interval
+ * Neither reading [-5, -10] neither; all values except [unsigned(-9), unsigned(-6)]
+ *
+ * The last two cases provide "surprising" results and are to be used carefully, if
+ * at all. Further, if @max = @min - 1, every @val is in range.
+ *
+ * Return: true or false as described above.
+ */
+#define in_range_inclusive(val, min, max) \
+ ((sizeof(val) | sizeof(min) | sizeof(max)) <= sizeof(u32) ? \
+ in_range_inclusive32(val, min, max) : in_range_inclusive64(val, min, max))
+
/**
* swap - swap values of @a and @b
* @a: first value
--
2.55.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v3 2/3] lib/tests: Add in_range_inclusive() KUnit test
2026-08-31 16:26 [PATCH v3 0/3] Introduce in_range_incl() inclusive range check macro Guru Das Srinagesh
2026-08-31 16:26 ` [PATCH v3 1/3] minmax: Add in_range_inclusive() for inclusive range checks Guru Das Srinagesh
@ 2026-08-31 16:26 ` Guru Das Srinagesh
2026-09-01 7:38 ` Andy Shevchenko
2026-08-31 16:26 ` [PATCH v3 3/3] iio: imu: bmi270: Use in_range_inclusive() in bmi270_write_event_value() Guru Das Srinagesh
2 siblings, 1 reply; 7+ messages in thread
From: Guru Das Srinagesh @ 2026-08-31 16:26 UTC (permalink / raw)
To: Alex Lanzano, Jonathan Cameron, David Lechner, Nuno Sá,
Andy Shevchenko, Matthew Wilcox, Andrew Morton, Gustavo Silva
Cc: linux-kernel, linux-iio, Guru Das Srinagesh
Add a KUnit test for the minmax.h in_range_inclusive() logic.
Assisted-by: Claude-Code:claude-opus-5
Signed-off-by: Guru Das Srinagesh <linux@gurudas.dev>
---
lib/Kconfig.debug | 18 +++
lib/tests/Makefile | 1 +
lib/tests/in_range_inclusive_kunit.c | 249 +++++++++++++++++++++++++++++++++++
3 files changed, 268 insertions(+)
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 1244dcac2294..88f5abc70266 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -3031,6 +3031,24 @@ config MIN_HEAP_KUNIT_TEST
If unsure, say N
+config IN_RANGE_INCLUSIVE_KUNIT_TEST
+ tristate "Test minmax in_range_inclusive() logic" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ Enable this option to build the in_range_inclusive KUnit module.
+ It tests the in_range_inclusive() logic.
+
+ KUnit tests run during boot and output the results to the debug log
+ in TAP format (http://testanything.org/). Only useful for kernel devs
+ running the KUnit test harness, and not intended for inclusion into a
+ production build.
+
+ For more information on KUnit and unit tests in general please refer
+ to the KUnit documentation in Documentation/dev-tools/kunit/.
+
+ If unsure, say N.
+
config IS_SIGNED_TYPE_KUNIT_TEST
tristate "Test is_signed_type() macro" if !KUNIT_ALL_TESTS
depends on KUNIT
diff --git a/lib/tests/Makefile b/lib/tests/Makefile
index 4ead57602eac..866b6006413d 100644
--- a/lib/tests/Makefile
+++ b/lib/tests/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_GLOB_KUNIT_TEST) += glob_kunit.o
obj-$(CONFIG_HASHTABLE_KUNIT_TEST) += hashtable_test.o
obj-$(CONFIG_HASH_KUNIT_TEST) += test_hash.o
obj-$(CONFIG_TEST_IOV_ITER) += kunit_iov_iter.o
+obj-$(CONFIG_IN_RANGE_INCLUSIVE_KUNIT_TEST) += in_range_inclusive_kunit.o
obj-$(CONFIG_IS_SIGNED_TYPE_KUNIT_TEST) += is_signed_type_kunit.o
obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o
obj-$(CONFIG_LIST_KUNIT_TEST) += list-test.o
diff --git a/lib/tests/in_range_inclusive_kunit.c b/lib/tests/in_range_inclusive_kunit.c
new file mode 100644
index 000000000000..83e8d48684f2
--- /dev/null
+++ b/lib/tests/in_range_inclusive_kunit.c
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test cases for minmax in_range_inclusive() helpers.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <kunit/test.h>
+#include <linux/minmax.h>
+
+/*
+ * When @min <= @max is valid in:
+ * Valid in reading Example 'True' range is
+ * Case 1 Both readings [5, 10] interval as written in either reading
+ * Case 2 Signed only [-10, 5] signed interval
+ * Case 3 Unsigned only [5, -10] unsigned interval
+ * Case 4 Neither reading [-5, -10] neither; all values except [unsigned(-9), unsigned(-6)]
+ *
+ * Visualizing the wraparound-continuous unsigned number scale as a full circle with
+ * 0 at 12 o'clock and S32_MAX/S64_MAX at 6 o'clock, with ranges being calculated
+ * only clockwise starting from @min until @max might help greatly in understanding
+ * the following true and false ranges.
+ */
+static void u32_tests(struct kunit *test)
+{
+ /* Case 1 first true value */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(5, 5, 10));
+ /* Case 1 last true value */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(10, 5, 10));
+ /* Case 1 first false value */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(11, 5, 10));
+ /* Case 1 last false value */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(4, 5, 10));
+ /* Case 1 0 not in range */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(0, 5, 10));
+ /* Case 1 U32_MAX not in range */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(U32_MAX, 5, 10));
+
+ /* Case 2 first true value */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(-10, -10, 5));
+ /* Case 2 last true value */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(5, -10, 5));
+ /* Case 2 first false value */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(6, -10, 5));
+ /* Case 2 last false value */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(-11, -10, 5));
+ /* Case 2 0 in range */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(0, -10, 5));
+ /* Case 2 U32_MAX in range */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(U32_MAX, -10, 5));
+
+ /* Case 3 first true value */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(5, 5, -10));
+ /* Case 3 last true value */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(-10, 5, -10));
+ /* Case 3 first false value */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(-9, 5, -10));
+ /* Case 3 last false value */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(4, 5, -10));
+ /* Case 3 0 not in range */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(0, 5, -10));
+ /* Case 3 U32_MAX not in range */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(U32_MAX, 5, -10));
+
+ /* Case 4 first true value */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(-5, -5, -10));
+ /* Case 4 last true value */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(-10, -5, -10));
+ /* Case 4 first false value */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(-9, -5, -10));
+ /* Case 4 last false value */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(-6, -5, -10));
+ /* Case 4 0 in range */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(0, -5, -10));
+ /* Case 4 U32_MAX in range */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(U32_MAX, -5, -10));
+}
+
+static void u64_tests(struct kunit *test)
+{
+ /* Case 1 first true value */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(5ULL, 5ULL, 10ULL));
+ /* Case 1 last true value */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(10ULL, 5ULL, 10ULL));
+ /* Case 1 first false value */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(11ULL, 5ULL, 10ULL));
+ /* Case 1 last false value */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(4ULL, 5ULL, 10ULL));
+ /* Case 1 0 not in range */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(0ULL, 5ULL, 10ULL));
+ /* Case 1 U64_MAX not in range */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(U64_MAX, 5ULL, 10ULL));
+
+ /* Case 2 first true value */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(-10ULL, -10ULL, 5ULL));
+ /* Case 2 last true value */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(5ULL, -10ULL, 5ULL));
+ /* Case 2 first false value */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(6ULL, -10ULL, 5ULL));
+ /* Case 2 last false value */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(-11ULL, -10ULL, 5ULL));
+ /* Case 2 0 in range */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(0ULL, -10ULL, 5ULL));
+ /* Case 2 U64_MAX in range */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(U64_MAX, -10ULL, 5ULL));
+
+ /* Case 3 first true value */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(5ULL, 5ULL, -10ULL));
+ /* Case 3 last true value */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(-10ULL, 5ULL, -10ULL));
+ /* Case 3 first false value */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(-9ULL, 5ULL, -10ULL));
+ /* Case 3 last false value */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(4ULL, 5ULL, -10ULL));
+ /* Case 3 0 not in range */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(0ULL, 5ULL, -10ULL));
+ /* Case 3 U64_MAX not in range */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(U64_MAX, 5ULL, -10ULL));
+
+ /* Case 4 first true value */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(-5ULL, -5ULL, -10ULL));
+ /* Case 4 last true value */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(-10ULL, -5ULL, -10ULL));
+ /* Case 4 first false value */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(-9ULL, -5ULL, -10ULL));
+ /* Case 4 last false value */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(-6ULL, -5ULL, -10ULL));
+ /* Case 4 0 in range */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(0ULL, -5ULL, -10ULL));
+ /* Case 4 U64_MAX in range */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(U64_MAX, -5ULL, -10ULL));
+}
+
+/* Check whether sizeof logic to select 32- vs 64-bit comparisons works */
+static void sizeof_logic_tests(struct kunit *test)
+{
+ u64 u64_val = BIT_ULL(32) | 7;
+ u64 u64_minval = BIT_ULL(32) | 5;
+ u64 u64_maxval = BIT_ULL(32) | 10;
+ u32 u32_minval = 5, u32_maxval = 10, u32_val = 100;
+
+ /* If sizeof trick does not work:
+ * - u64_val will get truncated to 7
+ * - 7 is in [5, 10] so test should pass.
+ */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(u64_val, u32_minval, u32_maxval));
+
+ /*
+ * If sizeof trick does not work:
+ * - u64_maxval will get truncated to 10
+ * - 100 is not in [5, 10], so test should fail.
+ */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(u32_val, u32_minval, u64_maxval));
+
+ /*
+ * If sizeof trick does not work:
+ * - u64_minval gets truncated to 5
+ * - 0 is not in [5, 10], so test should fail.
+ * Otherwise:
+ * - @min > @max here, so this is Case 4
+ * - 0 should be in range and test should pass.
+ */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(0, u64_minval, u32_maxval));
+}
+
+/*
+ * Each argument is widened to the selected width according to its own type:
+ * signed arguments sign-extend, unsigned ones zero-extend. The same written
+ * values can therefore give opposite answers depending on the declared types.
+ */
+static void sign_extension_tests(struct kunit *test)
+{
+ u32 u32_val = -9;
+ s32 s32_val = -9;
+ s32 s32_minval = 5, s32_maxval = -10;
+ s64 s64_minval = 5, s64_maxval = -10;
+
+ /* Case 3 range. -9 is out of range in both readings */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(u32_val, s32_minval, s32_maxval));
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(s32_val, s32_minval, s32_maxval));
+
+ /*
+ * Sign extension kicks in due to sizeof logic. minval "stays in place"
+ * because it is positive and maxval "moves" because it is negative and
+ * signed, thereby increasing the True range.
+ * Negative unsigned -9 "stays in place", so it becomes in range.
+ * Negative signed -9 "moves", so it still remains out of range.
+ */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(u32_val, s64_minval, s64_maxval));
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(s32_val, s64_minval, s64_maxval));
+}
+
+#define TEST_RANGE_MIN 0
+#define TEST_RANGE_MAX 100
+static void misc_tests(struct kunit *test)
+{
+ s32 s32_val = -5;
+
+ /*
+ * Test common device driver use case of rejecting negative input from
+ * userspace.
+ */
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(s32_val, TEST_RANGE_MIN, TEST_RANGE_MAX));
+
+ /* If min == max, only one true solution exists, val = min */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(5, 5, 5));
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(0, 5, 5));
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(U32_MAX, 5, 5));
+ KUNIT_EXPECT_FALSE(test, in_range_inclusive(U64_MAX, 5, 5));
+
+ /*
+ * When max = (min - 1), Case 4 kicks in, and there is no 'false' range,
+ * i.e. all values of val result in 'true'.
+ */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(5, 5, 4));
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(4, 5, 4));
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(0, 5, 4));
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(U32_MAX, 5, 4));
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(U64_MAX, 5, 4));
+
+ /*
+ * [0, U32_MAX] and [0, U64_MAX] are both the same case as above with
+ * min = 0 and max = (min - 1)
+ */
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(0, 0, U32_MAX));
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(U32_MAX, 0, U32_MAX));
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(0, 0, U64_MAX));
+ KUNIT_EXPECT_TRUE(test, in_range_inclusive(U64_MAX, 0, U64_MAX));
+}
+
+static struct kunit_case in_range_incl_test_cases[] = {
+ KUNIT_CASE(u32_tests),
+ KUNIT_CASE(u64_tests),
+ KUNIT_CASE(sizeof_logic_tests),
+ KUNIT_CASE(sign_extension_tests),
+ KUNIT_CASE(misc_tests),
+ {}
+};
+
+static struct kunit_suite in_range_incl_test_suite = {
+ .name = "in_range_inclusive",
+ .test_cases = in_range_incl_test_cases,
+};
+
+kunit_test_suites(&in_range_incl_test_suite);
+
+MODULE_AUTHOR("Guru Das Srinagesh <linux@gurudas.dev>");
+MODULE_DESCRIPTION("Test cases for in_range_inclusive()");
+MODULE_LICENSE("GPL");
--
2.55.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v3 3/3] iio: imu: bmi270: Use in_range_inclusive() in bmi270_write_event_value()
2026-08-31 16:26 [PATCH v3 0/3] Introduce in_range_incl() inclusive range check macro Guru Das Srinagesh
2026-08-31 16:26 ` [PATCH v3 1/3] minmax: Add in_range_inclusive() for inclusive range checks Guru Das Srinagesh
2026-08-31 16:26 ` [PATCH v3 2/3] lib/tests: Add in_range_inclusive() KUnit test Guru Das Srinagesh
@ 2026-08-31 16:26 ` Guru Das Srinagesh
2026-09-01 7:47 ` Andy Shevchenko
2 siblings, 1 reply; 7+ messages in thread
From: Guru Das Srinagesh @ 2026-08-31 16:26 UTC (permalink / raw)
To: Alex Lanzano, Jonathan Cameron, David Lechner, Nuno Sá,
Andy Shevchenko, Matthew Wilcox, Andrew Morton, Gustavo Silva
Cc: linux-kernel, linux-iio, Guru Das Srinagesh
Replace the three "in_range(val, 0, MAX + 1)" checks with the new
in_range_inclusive() helper, expressing each as the inclusive [0, MAX]
range it actually validates.
No functional changes are introduced by this change because @min <= @max
in the inclusive range in both signed and unsigned readings and hence
the check correctly continues to hold and is unchanged in behaviour.
Assisted-by: Claude-Code:claude-sonnet-5
Signed-off-by: Guru Das Srinagesh <linux@gurudas.dev>
---
drivers/iio/imu/bmi270/bmi270_core.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/iio/imu/bmi270/bmi270_core.c b/drivers/iio/imu/bmi270/bmi270_core.c
index 2ad230788532..7f386a615b39 100644
--- a/drivers/iio/imu/bmi270/bmi270_core.c
+++ b/drivers/iio/imu/bmi270/bmi270_core.c
@@ -1132,7 +1132,7 @@ static int bmi270_write_event_value(struct iio_dev *indio_dev,
guard(mutex)(&data->mutex);
if (type == IIO_EV_TYPE_CHANGE) {
- if (!in_range(val, 0, BMI270_STEP_COUNTER_MAX + 1))
+ if (!in_range_inclusive(val, 0, BMI270_STEP_COUNTER_MAX))
return -EINVAL;
raw = val / BMI270_STEP_COUNTER_FACTOR;
@@ -1152,7 +1152,7 @@ static int bmi270_write_event_value(struct iio_dev *indio_dev,
if (ret)
return ret;
- if (!in_range(val, 0, (BMI270_G_MICRO_M_S_2 / uscale) + 1))
+ if (!in_range_inclusive(val, 0, BMI270_G_MICRO_M_S_2 / uscale))
return -EINVAL;
tmp = (u64)val * BMI270_MOTION_THRES_FULL_SCALE * uscale;
@@ -1161,7 +1161,7 @@ static int bmi270_write_event_value(struct iio_dev *indio_dev,
regval = FIELD_PREP(BMI270_FEAT_MOTION_THRESHOLD_MSK, raw);
return bmi270_update_feature_reg(data, reg, mask, regval);
case IIO_EV_INFO_PERIOD:
- if (!in_range(val, 0, BMI270_MOTION_DURAT_MAX + 1))
+ if (!in_range_inclusive(val, 0, BMI270_MOTION_DURAT_MAX))
return -EINVAL;
raw = BMI270_INT_MICRO_TO_RAW(val, val2,
--
2.55.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v3 1/3] minmax: Add in_range_inclusive() for inclusive range checks
2026-08-31 16:26 ` [PATCH v3 1/3] minmax: Add in_range_inclusive() for inclusive range checks Guru Das Srinagesh
@ 2026-09-01 7:26 ` Andy Shevchenko
0 siblings, 0 replies; 7+ messages in thread
From: Andy Shevchenko @ 2026-09-01 7:26 UTC (permalink / raw)
To: Guru Das Srinagesh
Cc: Alex Lanzano, Jonathan Cameron, David Lechner, Nuno Sá,
Andy Shevchenko, Matthew Wilcox, Andrew Morton, Gustavo Silva,
linux-kernel, linux-iio
On Mon, Aug 31, 2026 at 09:26:28AM -0700, Guru Das Srinagesh wrote:
> Extend the logic in in_range() to support checking for an inclusive
> range [min, max].
>
> The condition in the check is derived as follows, starting from the
> in_range() macro with len = (max - min + 1):
>
> (val - min) < (max - min + 1) // overflows for [0, U32/U64_MAX]
> (val - min) <= (max - min) // no overflow
>
> The behaviour of the macro from the signedness perspective is documented
> in the kernel-doc and in the in_range_inclusive KUnit test suite.
...
> +/**
> + * in_range_inclusive - Determine if a value lies within an inclusive range.
> + * @val: Value to test.
> + * @min: First value in range.
> + * @max: Last value in range.
We have macros named min() and max(), since this is a macro as well it might
give an interesting outcome when two collide. Suggestion is to rename the
parameters to avoid potential collisions.
> + * This checks if a value lies within the closed range of [@min, @max]. Note that
> + * "range" refers to values counting up from @min with wraparound at
> + * unsigned-datatype max if encountered, continuing on till @max is reached.
> + *
> + * This macro is not a drop-in replacement for "if (val >= min && val <= max)".
> + * Unsigned arithmetic determines what the 'true' range exactly is depending on
> + * whether @min <= @max holds, and in which reading (signed vs unsigned) as follows::
> + *
> + * Valid in reading Example 'True' range is
> + * Both readings [5, 10] interval as written in either reading
> + * Signed only [-10, 5] signed interval
> + * Unsigned only [5, -10] unsigned interval
> + * Neither reading [-5, -10] neither; all values except [unsigned(-9), unsigned(-6)]
> + *
> + * The last two cases provide "surprising" results and are to be used carefully, if
> + * at all. Further, if @max = @min - 1, every @val is in range.
> + *
> + * Return: true or false as described above.
> + */
Imagine something like in_range_inclusive(value, min(A, B), max(C, D)) case
which I consider plausible to happen (in some form).
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v3 2/3] lib/tests: Add in_range_inclusive() KUnit test
2026-08-31 16:26 ` [PATCH v3 2/3] lib/tests: Add in_range_inclusive() KUnit test Guru Das Srinagesh
@ 2026-09-01 7:38 ` Andy Shevchenko
0 siblings, 0 replies; 7+ messages in thread
From: Andy Shevchenko @ 2026-09-01 7:38 UTC (permalink / raw)
To: Guru Das Srinagesh
Cc: Alex Lanzano, Jonathan Cameron, David Lechner, Nuno Sá,
Andy Shevchenko, Matthew Wilcox, Andrew Morton, Gustavo Silva,
linux-kernel, linux-iio
On Mon, Aug 31, 2026 at 09:26:29AM -0700, Guru Das Srinagesh wrote:
> Add a KUnit test for the minmax.h in_range_inclusive() logic.
Please, name it after the whole header, id est minmax_kunit.c.
...
Also would be nice to have a couple of tests that combine this macro with
others from minmax.h (see previous reply on the possible name collision).
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v3 3/3] iio: imu: bmi270: Use in_range_inclusive() in bmi270_write_event_value()
2026-08-31 16:26 ` [PATCH v3 3/3] iio: imu: bmi270: Use in_range_inclusive() in bmi270_write_event_value() Guru Das Srinagesh
@ 2026-09-01 7:47 ` Andy Shevchenko
0 siblings, 0 replies; 7+ messages in thread
From: Andy Shevchenko @ 2026-09-01 7:47 UTC (permalink / raw)
To: Guru Das Srinagesh
Cc: Alex Lanzano, Jonathan Cameron, David Lechner, Nuno Sá,
Andy Shevchenko, Matthew Wilcox, Andrew Morton, Gustavo Silva,
linux-kernel, linux-iio
On Mon, Aug 31, 2026 at 09:26:30AM -0700, Guru Das Srinagesh wrote:
> Replace the three "in_range(val, 0, MAX + 1)" checks with the new
> in_range_inclusive() helper, expressing each as the inclusive [0, MAX]
> range it actually validates.
>
> No functional changes are introduced by this change because @min <= @max
> in the inclusive range in both signed and unsigned readings and hence
> the check correctly continues to hold and is unchanged in behaviour.
Yes, this change makes code better (not only shorter, it makes it robust
against theoretical wrap-around).
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-09-01 7:48 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-31 16:26 [PATCH v3 0/3] Introduce in_range_incl() inclusive range check macro Guru Das Srinagesh
2026-08-31 16:26 ` [PATCH v3 1/3] minmax: Add in_range_inclusive() for inclusive range checks Guru Das Srinagesh
2026-09-01 7:26 ` Andy Shevchenko
2026-08-31 16:26 ` [PATCH v3 2/3] lib/tests: Add in_range_inclusive() KUnit test Guru Das Srinagesh
2026-09-01 7:38 ` Andy Shevchenko
2026-08-31 16:26 ` [PATCH v3 3/3] iio: imu: bmi270: Use in_range_inclusive() in bmi270_write_event_value() Guru Das Srinagesh
2026-09-01 7:47 ` Andy Shevchenko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox