Skip to content

Commit

Permalink
net: hsr: Add KUnit test for PRP
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Jaakko Karrenpalo authored and Paolo Abeni committed Mar 13, 2025
1 parent 05fd00e commit 814dbf4
Show file tree
Hide file tree
Showing 4 changed files with 236 additions and 0 deletions.
18 changes: 18 additions & 0 deletions net/hsr/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 2 additions & 0 deletions net/hsr/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
4 changes: 4 additions & 0 deletions net/hsr/hsr_framereg.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
212 changes: 212 additions & 0 deletions net/hsr/prp_dup_discard_test.c
Original file line number Diff line number Diff line change
@@ -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>");

0 comments on commit 814dbf4

Please sign in to comment.