All of lore.kernel.org
 help / color / mirror / Atom feed
From: Alexander Potapenko <glider@google.com>
To: glider@google.com, catalin.marinas@arm.com, will@kernel.org,
	 pcc@google.com, andreyknvl@gmail.com
Cc: linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,  eugenis@google.com,
	yury.norov@gmail.com
Subject: [Resend v1 2/5] linux/bitqueue.h: add a KUnit test for bitqueue.h
Date: Tue, 11 Jul 2023 16:42:30 +0200	[thread overview]
Message-ID: <20230711144233.3129207-3-glider@google.com> (raw)
In-Reply-To: <20230711144233.3129207-1-glider@google.com>

Add tests checking that struct bitq correctly handles sub-byte values.

Signed-off-by: Alexander Potapenko <glider@google.com>
---
 lib/Kconfig.debug   |   8 ++
 lib/Makefile        |   1 +
 lib/test_bitqueue.c | 244 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 253 insertions(+)
 create mode 100644 lib/test_bitqueue.c

diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index ce51d4dc6803e..a6598b2c250d5 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -2678,6 +2678,14 @@ config SIPHASH_KUNIT_TEST
 	  This is intended to help people writing architecture-specific
 	  optimized versions.  If unsure, say N.
 
+config BITQUEUE_KUNIT_TEST
+	tristate "Test <linux/bitqueue.h>" if !KUNIT_ALL_TESTS
+	depends on KUNIT
+	default KUNIT_ALL_TESTS
+	help
+	  Enable this option to test the kernel's bit queue implementation
+	  (<linux/bitqueue.h>).
+
 config TEST_UDELAY
 	tristate "udelay test driver"
 	help
diff --git a/lib/Makefile b/lib/Makefile
index 876fcdeae34ec..7efb6aba31cf9 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -394,6 +394,7 @@ CFLAGS_fortify_kunit.o += $(DISABLE_STRUCTLEAK_PLUGIN)
 obj-$(CONFIG_FORTIFY_KUNIT_TEST) += fortify_kunit.o
 obj-$(CONFIG_STRSCPY_KUNIT_TEST) += strscpy_kunit.o
 obj-$(CONFIG_SIPHASH_KUNIT_TEST) += siphash_kunit.o
+obj-$(CONFIG_BITQUEUE_KUNIT_TEST) += test_bitqueue.o
 
 obj-$(CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED) += devmem_is_allowed.o
 
diff --git a/lib/test_bitqueue.c b/lib/test_bitqueue.c
new file mode 100644
index 0000000000000..aec04b3a5f068
--- /dev/null
+++ b/lib/test_bitqueue.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Test cases for struct bitq, a simple bit queue.
+ */
+
+#include <kunit/test.h>
+#include <linux/bitqueue.h>
+#include <linux/slab.h>
+
+/* Set up a bit queue containing @size bytes. */
+static void bitq_setup(struct bitq *it, size_t size)
+{
+	u8 *data = kmalloc(size, GFP_KERNEL);
+
+	bitq_init(it, data, size);
+}
+
+/* Tear down the bit queue. */
+static void bitq_teardown(struct bitq *it)
+{
+	kfree(it->data);
+	memset(it, 0, sizeof(*it));
+}
+
+/* Test that nothing can be popped from an empty queue. */
+static void test_empty(struct kunit *test)
+{
+	struct bitq it;
+	u8 val = 0;
+
+	/* Allocate a two-byte queue. */
+	bitq_setup(&it, 2);
+
+	/* Queue is empty. */
+	KUNIT_EXPECT_EQ(test, bitq_dequeue(&it, &val, 8), -1);
+	bitq_teardown(&it);
+}
+
+/* Test that simple byte-granular enqueue/dequeue operations work. */
+static void test_basic_enqueue_dequeue(struct kunit *test)
+{
+	struct bitq it;
+	u8 val = 0;
+
+	/* Allocate a two-byte queue. */
+	bitq_setup(&it, 2);
+	/* Enqueue two 8-bit values. */
+	KUNIT_EXPECT_EQ(test, bitq_enqueue(&it, 0xaa, 8), 8);
+	KUNIT_EXPECT_EQ(test, bitq_enqueue(&it, 0xbb, 8), 8);
+	/* Cannot enqueue the third byte. */
+	KUNIT_EXPECT_EQ(test, bitq_enqueue(&it, 1, 8), -1);
+	/* Dequeue two bytes. */
+	KUNIT_EXPECT_EQ(test, bitq_dequeue(&it, &val, 8), 8);
+	KUNIT_EXPECT_EQ(test, val, 0xaa);
+	KUNIT_EXPECT_EQ(test, bitq_dequeue(&it, &val, 8), 8);
+	KUNIT_EXPECT_EQ(test, val, 0xbb);
+
+	/* Queue is empty. */
+	KUNIT_EXPECT_EQ(test, bitq_dequeue(&it, &val, 8), -1);
+	bitq_teardown(&it);
+}
+
+/* Test that values shorter than 8 bits can be enqueued and dequeued. */
+static void test_shorter_than_byte(struct kunit *test)
+{
+	struct bitq it;
+	u8 val = 0;
+
+	/* Allocate a two-byte queue. */
+	bitq_setup(&it, 2);
+	/* Enqueue two 0b101 values. */
+	KUNIT_EXPECT_EQ(test, bitq_enqueue(&it, 0b101, 3), 3);
+	KUNIT_EXPECT_EQ(test, bitq_enqueue(&it, 0b101, 3), 3);
+	/* The first byte of the queue is now 0b10110100. */
+
+	/* Now dequeue three 2-bit values: 0b10, 0b11, 0b01. */
+	KUNIT_EXPECT_EQ(test, bitq_dequeue(&it, &val, 2), 2);
+	KUNIT_EXPECT_EQ(test, val, 0b10);
+	KUNIT_EXPECT_EQ(test, bitq_dequeue(&it, &val, 2), 2);
+	KUNIT_EXPECT_EQ(test, val, 0b11);
+	KUNIT_EXPECT_EQ(test, bitq_dequeue(&it, &val, 2), 2);
+	KUNIT_EXPECT_EQ(test, val, 0b01);
+
+	/* Queue is empty. */
+	KUNIT_EXPECT_EQ(test, bitq_dequeue(&it, &val, 1), -1);
+	bitq_teardown(&it);
+}
+
+/* Test that bits are carried over correctly if they do not fit. */
+static void test_carryover(struct kunit *test)
+{
+	struct bitq it;
+	u8 val = 0;
+	int i;
+
+	/* Allocate a three-byte queue. */
+	bitq_setup(&it, 3);
+	/* Enqueue 0b100 seven times. */
+	for (i = 0; i < 7; i++)
+		KUNIT_EXPECT_EQ(test, bitq_enqueue(&it, 0b100, 3), 3);
+	/* Now dequeue three 7-bit values: 0b1001001, 0b0010010, 0b0100100. */
+	KUNIT_EXPECT_EQ(test, bitq_dequeue(&it, &val, 7), 7);
+	KUNIT_EXPECT_EQ(test, val, 0b1001001);
+	KUNIT_EXPECT_EQ(test, bitq_dequeue(&it, &val, 7), 7);
+	KUNIT_EXPECT_EQ(test, val, 0b0010010);
+	KUNIT_EXPECT_EQ(test, bitq_dequeue(&it, &val, 7), 7);
+	KUNIT_EXPECT_EQ(test, val, 0b0100100);
+
+	/* Queue is empty. */
+	KUNIT_EXPECT_EQ(test, bitq_dequeue(&it, &val, 1), -1);
+	bitq_teardown(&it);
+}
+
+/*
+ * Test case extracted from the EA0 tag compression algorithm, where
+ * carried over bits were accidentally written into the previous byte.
+ */
+static void test_carryover_ea0(struct kunit *test)
+{
+	struct bitq it;
+	u8 val = 0;
+
+	/* Allocate a three-byte queue. */
+	bitq_setup(&it, 3);
+	KUNIT_EXPECT_EQ(test, bitq_enqueue(&it, 0b100, 3), 3);
+	KUNIT_EXPECT_EQ(test, bitq_enqueue(&it, 0b1010, 4), 4);
+	KUNIT_EXPECT_EQ(test, bitq_enqueue(&it, 0b0000, 4), 4);
+	KUNIT_EXPECT_EQ(test, bitq_enqueue(&it, 0b1010, 4), 4);
+	KUNIT_EXPECT_EQ(test, bitq_enqueue(&it, 0b1011, 4), 4);
+
+	/* Now dequeue two byte values: 0b10010100, 0b00010101. */
+	KUNIT_EXPECT_EQ(test, bitq_dequeue(&it, &val, 8), 8);
+	KUNIT_EXPECT_EQ(test, val, 0b10010100);
+	KUNIT_EXPECT_EQ(test, bitq_dequeue(&it, &val, 8), 8);
+	KUNIT_EXPECT_EQ(test, val, 0b00010101);
+	/* And the remaining 0b011. */
+	KUNIT_EXPECT_EQ(test, bitq_dequeue(&it, &val, 3), 3);
+	KUNIT_EXPECT_EQ(test, val, 0b011);
+
+	/* Queue is empty. */
+	KUNIT_EXPECT_EQ(test, bitq_dequeue(&it, &val, 1), -1);
+	bitq_teardown(&it);
+}
+
+/* Test that upper bits of the pushed value are discarded. */
+static void test_trim_upper_bits(struct kunit *test)
+{
+	struct bitq it;
+	u8 val = 0;
+
+	/* Allocate a two-byte queue. */
+	bitq_setup(&it, 2);
+	/* Enqueue two values that do not fit into 4 bits. */
+	KUNIT_EXPECT_EQ(test, bitq_enqueue(&it, 0xab, 4), 4);
+	KUNIT_EXPECT_EQ(test, bitq_enqueue(&it, 0xab, 4), 4);
+	/* The first byte of the queue is now 0xbb. */
+	KUNIT_EXPECT_EQ(test, bitq_dequeue(&it, &val, 8), 8);
+	KUNIT_EXPECT_EQ(test, val, 0xbb);
+
+	/* Queue is empty. */
+	KUNIT_EXPECT_EQ(test, bitq_dequeue(&it, &val, 1), -1);
+	bitq_teardown(&it);
+}
+
+/* Another test for discarding the upper bits. */
+static void test_trim_upper_bits2(struct kunit *test)
+{
+	struct bitq it;
+	u8 val = 0;
+
+	/* Allocate a two-byte queue. */
+	bitq_setup(&it, 2);
+	/* Push seven zero bits. */
+	KUNIT_EXPECT_EQ(test, bitq_enqueue(&it, 0, 7), 7);
+	/* Push a single 1 bit, but pass a bigger value to bitq_enqueue(). */
+	KUNIT_EXPECT_EQ(test, bitq_enqueue(&it, 0xff, 1), 1);
+	/* The first byte of the queue is now 0x01. */
+	KUNIT_EXPECT_EQ(test, bitq_dequeue(&it, &val, 8), 8);
+	KUNIT_EXPECT_EQ(test, val, 0x01);
+
+	/* Queue is empty. */
+	KUNIT_EXPECT_EQ(test, bitq_dequeue(&it, &val, 1), -1);
+	bitq_teardown(&it);
+}
+
+/* Test that a NULL value can be used as output of bitq_dequeue() */
+static void test_dequeue_to_null(struct kunit *test)
+{
+	struct bitq it;
+
+	/* Allocate a two-byte queue. */
+	bitq_setup(&it, 2);
+	/* Enqueue a byte value. */
+	KUNIT_EXPECT_EQ(test, bitq_enqueue(&it, 0xab, 8), 8);
+	/* Dequeue the byte, but discard its value. */
+	KUNIT_EXPECT_EQ(test, bitq_dequeue(&it, NULL, 8), 8);
+
+	/* Queue is empty. */
+	KUNIT_EXPECT_EQ(test, bitq_dequeue(&it, NULL, 1), -1);
+	bitq_teardown(&it);
+}
+
+/* Test that bitq_init_full works. */
+static void test_init_full(struct kunit *test)
+{
+	struct bitq it;
+	u8 data[2] = { 0xaa, 0xbb };
+	u8 val = 0;
+
+	/* Initialize a queue with the contents of @data */
+	bitq_init_full(&it, data, 2);
+	/* Cannot enqueue anything else. */
+	KUNIT_EXPECT_EQ(test, bitq_enqueue(&it, 1, 8), -1);
+	/* Dequeue two bytes. */
+	KUNIT_EXPECT_EQ(test, bitq_dequeue(&it, &val, 8), 8);
+	KUNIT_EXPECT_EQ(test, val, 0xaa);
+	KUNIT_EXPECT_EQ(test, bitq_dequeue(&it, &val, 8), 8);
+	KUNIT_EXPECT_EQ(test, val, 0xbb);
+
+	/* Queue is empty. */
+	KUNIT_EXPECT_EQ(test, bitq_dequeue(&it, NULL, 1), -1);
+}
+
+static struct kunit_case bitq_test_cases[] = {
+	KUNIT_CASE(test_empty),
+	KUNIT_CASE(test_basic_enqueue_dequeue),
+	KUNIT_CASE(test_shorter_than_byte),
+	KUNIT_CASE(test_carryover),
+	KUNIT_CASE(test_carryover_ea0),
+	KUNIT_CASE(test_trim_upper_bits),
+	KUNIT_CASE(test_trim_upper_bits2),
+	KUNIT_CASE(test_dequeue_to_null),
+	KUNIT_CASE(test_init_full),
+	{}
+};
+
+static struct kunit_suite bitq_test_suite = {
+	.name = "bitq",
+	.test_cases = bitq_test_cases,
+};
+kunit_test_suites(&bitq_test_suite);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alexander Potapenko <glider@google.com>");
-- 
2.41.0.255.g8b1d071c50-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

  parent reply	other threads:[~2023-07-11 14:43 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-07-11 14:42 [Resend v1 0/5] Implement MTE tag compression for swapped pages Alexander Potapenko
2023-07-11 14:42 ` [Resend v1 1/5] linux/bitqueue.h: add the bit queue implementation Alexander Potapenko
2023-07-11 19:20   ` Yury Norov
2023-07-12 10:24     ` Alexander Potapenko
2023-07-11 14:42 ` Alexander Potapenko [this message]
2023-07-11 14:42 ` [Resend v1 3/5] arm64: mte: implement CONFIG_ARM64_MTE_COMP Alexander Potapenko
2023-07-11 14:42 ` [Resend v1 4/5] arm64: mte: add a test for MTE tags compression Alexander Potapenko
2023-07-11 14:42 ` [Resend v1 5/5] arm64: mte: add compression support to mteswap.c Alexander Potapenko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230711144233.3129207-3-glider@google.com \
    --to=glider@google.com \
    --cc=andreyknvl@gmail.com \
    --cc=catalin.marinas@arm.com \
    --cc=eugenis@google.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=pcc@google.com \
    --cc=will@kernel.org \
    --cc=yury.norov@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.