net: hsr: Add KUnit test for PRP
authorJaakko Karrenpalo <jkarrenpalo@gmail.com>
Fri, 7 Mar 2025 16:17:00 +0000 (18:17 +0200)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 13 Mar 2025 09:04:22 +0000 (10:04 +0100)
Add unit tests for the PRP duplicate detection

Signed-off-by: Jaakko Karrenpalo <jkarrenpalo@gmail.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20250307161700.1045-2-jkarrenpalo@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
net/hsr/Kconfig
net/hsr/Makefile
net/hsr/hsr_framereg.c
net/hsr/prp_dup_discard_test.c [new file with mode: 0644]

index 1b048c1..fcacdf4 100644 (file)
@@ -38,3 +38,21 @@ config HSR
          relying on this code in a safety critical system!
 
          If unsure, say N.
+
+if HSR
+
+config PRP_DUP_DISCARD_KUNIT_TEST
+       tristate "PRP duplicate discard KUnit tests" if !KUNIT_ALL_TESTS
+       depends on KUNIT
+       default KUNIT_ALL_TESTS
+       help
+         Covers the PRP duplicate discard algorithm.
+         Only useful for kernel devs running KUnit test harness and are not
+         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.
+
+endif
index 75df90d..34e581d 100644 (file)
@@ -8,3 +8,5 @@ obj-$(CONFIG_HSR)       += hsr.o
 hsr-y                  := hsr_main.o hsr_framereg.o hsr_device.o \
                           hsr_netlink.o hsr_slave.o hsr_forward.o
 hsr-$(CONFIG_DEBUG_FS) += hsr_debugfs.o
+
+obj-$(CONFIG_PRP_DUP_DISCARD_KUNIT_TEST) += prp_dup_discard_test.o
index 85991fa..4ce471a 100644 (file)
@@ -588,6 +588,10 @@ int prp_register_frame_out(struct hsr_port *port, struct hsr_frame_info *frame)
        return 0;
 }
 
+#if IS_MODULE(CONFIG_PRP_DUP_DISCARD_KUNIT_TEST)
+EXPORT_SYMBOL(prp_register_frame_out);
+#endif
+
 static struct hsr_port *get_late_port(struct hsr_priv *hsr,
                                      struct hsr_node *node)
 {
diff --git a/net/hsr/prp_dup_discard_test.c b/net/hsr/prp_dup_discard_test.c
new file mode 100644 (file)
index 0000000..e86b7b6
--- /dev/null
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <kunit/test.h>
+
+#include "hsr_main.h"
+#include "hsr_framereg.h"
+
+struct prp_test_data {
+       struct hsr_port port;
+       struct hsr_port port_rcv;
+       struct hsr_frame_info frame;
+       struct hsr_node node;
+};
+
+static struct prp_test_data *build_prp_test_data(struct kunit *test)
+{
+       struct prp_test_data *data = kunit_kzalloc(test,
+               sizeof(struct prp_test_data), GFP_USER);
+       KUNIT_EXPECT_NOT_ERR_OR_NULL(test, data);
+
+       data->frame.node_src = &data->node;
+       data->frame.port_rcv = &data->port_rcv;
+       data->port_rcv.type = HSR_PT_SLAVE_A;
+       data->node.seq_start[HSR_PT_SLAVE_A] = 1;
+       data->node.seq_expected[HSR_PT_SLAVE_A] = 1;
+       data->node.seq_start[HSR_PT_SLAVE_B] = 1;
+       data->node.seq_expected[HSR_PT_SLAVE_B] = 1;
+       data->node.seq_out[HSR_PT_MASTER] = 0;
+       data->node.time_out[HSR_PT_MASTER] = jiffies;
+       data->port.type = HSR_PT_MASTER;
+
+       return data;
+}
+
+static void check_prp_counters(struct kunit *test,
+                              struct prp_test_data *data,
+                              u16 seq_start_a, u16 seq_expected_a,
+                              u16 seq_start_b, u16 seq_expected_b)
+{
+       KUNIT_EXPECT_EQ(test, data->node.seq_start[HSR_PT_SLAVE_A],
+                       seq_start_a);
+       KUNIT_EXPECT_EQ(test, data->node.seq_start[HSR_PT_SLAVE_B],
+                       seq_start_b);
+       KUNIT_EXPECT_EQ(test, data->node.seq_expected[HSR_PT_SLAVE_A],
+                       seq_expected_a);
+       KUNIT_EXPECT_EQ(test, data->node.seq_expected[HSR_PT_SLAVE_B],
+                       seq_expected_b);
+}
+
+static void prp_dup_discard_forward(struct kunit *test)
+{
+       /* Normal situation, both LANs in sync. Next frame is forwarded */
+       struct prp_test_data *data = build_prp_test_data(test);
+
+       data->frame.sequence_nr = 2;
+       KUNIT_EXPECT_EQ(test, 0,
+                       prp_register_frame_out(&data->port, &data->frame));
+       KUNIT_EXPECT_EQ(test, data->frame.sequence_nr,
+                       data->node.seq_out[HSR_PT_MASTER]);
+       KUNIT_EXPECT_EQ(test, jiffies, data->node.time_out[HSR_PT_MASTER]);
+       check_prp_counters(test, data, data->frame.sequence_nr,
+                          data->frame.sequence_nr + 1, 1, 1);
+}
+
+static void prp_dup_discard_inside_dropwindow(struct kunit *test)
+{
+       /* Normal situation, other LAN ahead by one. Frame is dropped */
+       struct prp_test_data *data = build_prp_test_data(test);
+       unsigned long time = jiffies - 10;
+
+       data->frame.sequence_nr = 1;
+       data->node.seq_expected[HSR_PT_SLAVE_B] = 3;
+       data->node.seq_out[HSR_PT_MASTER] = 2;
+       data->node.time_out[HSR_PT_MASTER] = time;
+
+       KUNIT_EXPECT_EQ(test, 1,
+                       prp_register_frame_out(&data->port, &data->frame));
+       KUNIT_EXPECT_EQ(test, 2, data->node.seq_out[HSR_PT_MASTER]);
+       KUNIT_EXPECT_EQ(test, time, data->node.time_out[HSR_PT_MASTER]);
+       check_prp_counters(test, data, 2, 2, 2, 3);
+}
+
+static void prp_dup_discard_node_timeout(struct kunit *test)
+{
+       /* Timeout situation, node hasn't sent anything for a while */
+       struct prp_test_data *data = build_prp_test_data(test);
+
+       data->frame.sequence_nr = 7;
+       data->node.seq_start[HSR_PT_SLAVE_A] = 1234;
+       data->node.seq_expected[HSR_PT_SLAVE_A] = 1235;
+       data->node.seq_start[HSR_PT_SLAVE_B] = 1234;
+       data->node.seq_expected[HSR_PT_SLAVE_B] = 1234;
+       data->node.seq_out[HSR_PT_MASTER] = 1234;
+       data->node.time_out[HSR_PT_MASTER] =
+               jiffies - msecs_to_jiffies(HSR_ENTRY_FORGET_TIME) - 1;
+
+       KUNIT_EXPECT_EQ(test, 0,
+                       prp_register_frame_out(&data->port, &data->frame));
+       KUNIT_EXPECT_EQ(test, data->frame.sequence_nr,
+                       data->node.seq_out[HSR_PT_MASTER]);
+       KUNIT_EXPECT_EQ(test, jiffies, data->node.time_out[HSR_PT_MASTER]);
+       check_prp_counters(test, data, data->frame.sequence_nr,
+                          data->frame.sequence_nr + 1, 1234, 1234);
+}
+
+static void prp_dup_discard_out_of_sequence(struct kunit *test)
+{
+       /* One frame is received out of sequence on both LANs */
+       struct prp_test_data *data = build_prp_test_data(test);
+
+       data->node.seq_start[HSR_PT_SLAVE_A] = 10;
+       data->node.seq_expected[HSR_PT_SLAVE_A] = 10;
+       data->node.seq_start[HSR_PT_SLAVE_B] = 10;
+       data->node.seq_expected[HSR_PT_SLAVE_B] = 10;
+       data->node.seq_out[HSR_PT_MASTER] = 9;
+
+       /* 1st old frame, should be accepted */
+       data->frame.sequence_nr = 8;
+       KUNIT_EXPECT_EQ(test, 0,
+                       prp_register_frame_out(&data->port, &data->frame));
+       KUNIT_EXPECT_EQ(test, data->frame.sequence_nr,
+                       data->node.seq_out[HSR_PT_MASTER]);
+       check_prp_counters(test, data, data->frame.sequence_nr,
+                          data->frame.sequence_nr + 1, 10, 10);
+
+       /* 2nd frame should be dropped */
+       data->frame.sequence_nr = 8;
+       data->port_rcv.type = HSR_PT_SLAVE_B;
+       KUNIT_EXPECT_EQ(test, 1,
+                       prp_register_frame_out(&data->port, &data->frame));
+       check_prp_counters(test, data, data->frame.sequence_nr + 1,
+                          data->frame.sequence_nr + 1,
+                          data->frame.sequence_nr + 1,
+                          data->frame.sequence_nr + 1);
+
+       /* Next frame, this is forwarded */
+       data->frame.sequence_nr = 10;
+       data->port_rcv.type = HSR_PT_SLAVE_A;
+       KUNIT_EXPECT_EQ(test, 0,
+                       prp_register_frame_out(&data->port, &data->frame));
+       KUNIT_EXPECT_EQ(test, data->frame.sequence_nr,
+                       data->node.seq_out[HSR_PT_MASTER]);
+       check_prp_counters(test, data, data->frame.sequence_nr,
+                          data->frame.sequence_nr + 1, 9, 9);
+
+       /* and next one is dropped */
+       data->frame.sequence_nr = 10;
+       data->port_rcv.type = HSR_PT_SLAVE_B;
+       KUNIT_EXPECT_EQ(test, 1,
+                       prp_register_frame_out(&data->port, &data->frame));
+       check_prp_counters(test, data, data->frame.sequence_nr + 1,
+                          data->frame.sequence_nr + 1,
+                          data->frame.sequence_nr + 1,
+                          data->frame.sequence_nr + 1);
+}
+
+static void prp_dup_discard_lan_b_late(struct kunit *test)
+{
+       /* LAN B is behind */
+       struct prp_test_data *data = build_prp_test_data(test);
+
+       data->node.seq_start[HSR_PT_SLAVE_A] = 9;
+       data->node.seq_expected[HSR_PT_SLAVE_A] = 9;
+       data->node.seq_start[HSR_PT_SLAVE_B] = 9;
+       data->node.seq_expected[HSR_PT_SLAVE_B] = 9;
+       data->node.seq_out[HSR_PT_MASTER] = 8;
+
+       data->frame.sequence_nr = 9;
+       KUNIT_EXPECT_EQ(test, 0,
+                       prp_register_frame_out(&data->port, &data->frame));
+       KUNIT_EXPECT_EQ(test, data->frame.sequence_nr,
+                       data->node.seq_out[HSR_PT_MASTER]);
+       check_prp_counters(test, data, 9, 10, 9, 9);
+
+       data->frame.sequence_nr = 10;
+       KUNIT_EXPECT_EQ(test, 0,
+                       prp_register_frame_out(&data->port, &data->frame));
+       KUNIT_EXPECT_EQ(test, data->frame.sequence_nr,
+                       data->node.seq_out[HSR_PT_MASTER]);
+       check_prp_counters(test, data, 9, 11, 9, 9);
+
+       data->frame.sequence_nr = 9;
+       data->port_rcv.type = HSR_PT_SLAVE_B;
+       KUNIT_EXPECT_EQ(test, 1,
+                       prp_register_frame_out(&data->port, &data->frame));
+       check_prp_counters(test, data, 10, 11, 10, 10);
+
+       data->frame.sequence_nr = 10;
+       data->port_rcv.type = HSR_PT_SLAVE_B;
+       KUNIT_EXPECT_EQ(test, 1,
+                       prp_register_frame_out(&data->port, &data->frame));
+       check_prp_counters(test, data,  11, 11, 11, 11);
+}
+
+static struct kunit_case prp_dup_discard_test_cases[] = {
+       KUNIT_CASE(prp_dup_discard_forward),
+       KUNIT_CASE(prp_dup_discard_inside_dropwindow),
+       KUNIT_CASE(prp_dup_discard_node_timeout),
+       KUNIT_CASE(prp_dup_discard_out_of_sequence),
+       KUNIT_CASE(prp_dup_discard_lan_b_late),
+       {}
+};
+
+static struct kunit_suite prp_dup_discard_suite = {
+       .name = "prp_duplicate_discard",
+       .test_cases = prp_dup_discard_test_cases,
+};
+
+kunit_test_suite(prp_dup_discard_suite);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("KUnit tests for PRP duplicate discard");
+MODULE_AUTHOR("Jaakko Karrenpalo <jkarrenpalo@gmail.com>");