Linux IIO development
 help / color / mirror / Atom feed
* [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