From patchwork Wed Feb 19 12:48:30 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jaakko Karrenpalo X-Patchwork-Id: 13982086 X-Patchwork-Delegate: kuba@kernel.org Received: from mail-lf1-f44.google.com (mail-lf1-f44.google.com [209.85.167.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B31341E04BB; Wed, 19 Feb 2025 12:48:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739969325; cv=none; b=fLpoN8c86QhvPKpu4VHnY38TWBCwj6mQENEb8UpUHUDJJqEG5+6CRpL2NcsjiRlwLJjHAdH683llatabuzi2gjaVvDuJTWwJuC4v84xDfzf2a8xdfHFYFZH0HwxXBwqKAIZnQP9fPgK1PSP+ixi69lAC9WbFj9BXUarPsPfX1zE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739969325; c=relaxed/simple; bh=EB/CsZcesxrImhw+3coc+JVc+7O/1dmsVRnO6y0/uTw=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=DTr/4X+Lqn0sUwRd8EZm18TaUM2VSz6U3xYShAEgzuqY5toCm7oL/1po/bMTUmZXFrxxcj14/YH39Vh8yAVrizSTTYvj1X7fVITHDN+ebYY1FDUv+Ees8Nhnn6KqS1xHOdJ+yn1vh3jfklJsaq6ZUJ10Xmju6/rqMtNtYSpXjNo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=VKp075yJ; arc=none smtp.client-ip=209.85.167.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="VKp075yJ" Received: by mail-lf1-f44.google.com with SMTP id 2adb3069b0e04-5439e331cceso7654463e87.1; Wed, 19 Feb 2025 04:48:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1739969322; x=1740574122; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=hzNTZfP7n5BOt7dybu51ip23e0L/CZr2o/aX08a8GfY=; b=VKp075yJwnK+JzyhReodNCFKPZNBwcLxfjfzXrzCvuMBcpZzldg9qJirzGHchSllGc vbrftxjeIPsFLcd2sArFBLICsZpBpEqML0i6HemU5gUwUpaH0bqMGetAoSwmP2u6ZfSv 1u5V2DKLTWV3DqUs+svioE50G4nVNcZdvntPJ3LbDP/0pOLVBmJ+WEpybGuS9UBg2kno oF9ESgaFvakzmx/52nbdn/2B4tt10yQcLC0Q0B66zwGkek3ZtvRQAFdqDn6BS75nLEE+ HzPHX5c38RTiomVijkWI9amZoJFy5Ac83ll4m9+woB0X9SnoDNe0PVoJ+eU6UX0e+4QO Vexg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1739969322; x=1740574122; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=hzNTZfP7n5BOt7dybu51ip23e0L/CZr2o/aX08a8GfY=; b=qGQT+ZgEVtMUOIUwRWWWJW/Fr39tbCMMx/JLmGHTZo5cSyZDW8d/Lgsmx/RMtx/pdA 2ChHbaIDIeP98Z/0j01uJtukR8xv1fC+Ah0C/XFX8A1leHVjdEyRhD3qUpfCPhXMO7rC V1PlqBXfb2gt7NgT0/WAFKjbULrMQqauvjLTii02G7UV7M9X2bPpA4Sn1bjZn5GnbBl3 9flo5YFY4Rbm2C5dw4YEAkMVrixiXbrBDRP4HelO+hQq5ML8U0+3MuyUfe7HfNsUAEHC Amf6OtmqbDlWpqZdxCoEqQ9iVJdt0fvm1kkYgWQfoDHhgqi8ijiEJw5qUZl6Rkf5ofJo 7vag== X-Forwarded-Encrypted: i=1; AJvYcCUyB5z1Au8Wn4qR/tN1I26mQTuTXoFcIQK/t2sy1NtuGli2w2vYuT/gxhHRWMeNL1UmP2B2Lgk=@vger.kernel.org X-Gm-Message-State: AOJu0YwcWPgUvgONpgGdyMU/zNWsGT0qX9Lv+K7NBFjZIe/BB4yKC+Hq NHiNwx4HmUWAUrKnlS2GmYZ7BB04Q7W8Indji0WMMvKYOj11+UHF X-Gm-Gg: ASbGncvBd3i0ypuGDeuETjSujRjz84ZB1F/yevHlFLTGRKMPaSo8KOIuCigzTPBpKTL QQjLaPKgV8dlkP470pjR2xHi/YfvF9Whh7dfknUbAehdKsGcIZCdsnWkEI9RArkFDNTQwb/fEWQ 6lBQFZmxBXDXKj4l7/kStwc3mZgkgHD61FwPssOCpqWPFihHazVP0aGcPAI2otawOlFFoHiUDwc o6fLFq0J5dTjgpch02hMQ3Ed2BYZ5XGpbEZ7UYilK/h3/paOzQvmPr8vqSetgPO497QdzPaiZpw PdBFwiny/jV8Zo8/nrxhgxpF6v7XhqMleCGdse8nj6aV+w== X-Google-Smtp-Source: AGHT+IHLWuOt3h0wCc9yU77xsZA0VgXiNIgcTgfCXZxglDJL+YGVTAmSzxTd8Ceuqtmw2oHaAN6Tsw== X-Received: by 2002:a05:6512:b1f:b0:545:ee3:f3c5 with SMTP id 2adb3069b0e04-5462eee23ebmr1421610e87.17.1739969321411; Wed, 19 Feb 2025 04:48:41 -0800 (PST) Received: from FI-L-7486715.nmea.abb.com ([147.161.186.89]) by smtp.gmail.com with ESMTPSA id 2adb3069b0e04-5452c937745sm1732697e87.243.2025.02.19.04.48.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Feb 2025 04:48:40 -0800 (PST) From: Jaakko Karrenpalo X-Google-Original-From: Jaakko Karrenpalo To: "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman Cc: linux-kernel@vger.kernel.org, netdev@vger.kernel.org, Jaakko Karrenpalo Subject: [PATCH net-next 1/2] net: hsr: Fix PRP duplicate detection Date: Wed, 19 Feb 2025 14:48:30 +0200 Message-ID: <20250219124831.544318-1-jaakko.karrenpalo@fi.abb.com> X-Mailer: git-send-email 2.43.0 Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: kuba@kernel.org Add PRP specific function for handling duplicate packets. This is needed because of potential L2 802.1p prioritization done by network switches. Signed-off-by: Jaakko Karrenpalo --- net/hsr/hsr_device.c | 2 + net/hsr/hsr_forward.c | 4 +- net/hsr/hsr_framereg.c | 93 ++++++++++++++++++++++++++++++++++++++++-- net/hsr/hsr_framereg.h | 8 +++- net/hsr/hsr_main.h | 2 + 5 files changed, 102 insertions(+), 7 deletions(-) diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c index b6fb18469439..2c43776b7c4f 100644 --- a/net/hsr/hsr_device.c +++ b/net/hsr/hsr_device.c @@ -616,6 +616,7 @@ static struct hsr_proto_ops hsr_ops = { .drop_frame = hsr_drop_frame, .fill_frame_info = hsr_fill_frame_info, .invalid_dan_ingress_frame = hsr_invalid_dan_ingress_frame, + .register_frame_out = hsr_register_frame_out, }; static struct hsr_proto_ops prp_ops = { @@ -626,6 +627,7 @@ static struct hsr_proto_ops prp_ops = { .fill_frame_info = prp_fill_frame_info, .handle_san_frame = prp_handle_san_frame, .update_san_info = prp_update_san_info, + .register_frame_out = prp_register_frame_out, }; void hsr_dev_setup(struct net_device *dev) diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c index a4bacf198555..aebeced10ad8 100644 --- a/net/hsr/hsr_forward.c +++ b/net/hsr/hsr_forward.c @@ -536,8 +536,8 @@ static void hsr_forward_do(struct hsr_frame_info *frame) * Also for SAN, this shouldn't be done. */ if (!frame->is_from_san && - hsr_register_frame_out(port, frame->node_src, - frame->sequence_nr)) + hsr->proto_ops->register_frame_out && + hsr->proto_ops->register_frame_out(port, frame)) continue; if (frame->is_supervision && port->type == HSR_PT_MASTER && diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c index 73bc6f659812..98898f05df6a 100644 --- a/net/hsr/hsr_framereg.c +++ b/net/hsr/hsr_framereg.c @@ -35,6 +35,7 @@ static bool seq_nr_after(u16 a, u16 b) #define seq_nr_before(a, b) seq_nr_after((b), (a)) #define seq_nr_before_or_eq(a, b) (!seq_nr_after((a), (b))) +#define PRP_DROP_WINDOW_LEN 32768 bool hsr_addr_is_redbox(struct hsr_priv *hsr, unsigned char *addr) { @@ -176,8 +177,11 @@ static struct hsr_node *hsr_add_node(struct hsr_priv *hsr, new_node->time_in[i] = now; new_node->time_out[i] = now; } - for (i = 0; i < HSR_PT_PORTS; i++) + for (i = 0; i < HSR_PT_PORTS; i++) { new_node->seq_out[i] = seq_out; + new_node->seq_expected[i] = seq_out + 1; + new_node->seq_start[i] = seq_out + 1; + } if (san && hsr->proto_ops->handle_san_frame) hsr->proto_ops->handle_san_frame(san, rx_port, new_node); @@ -482,9 +486,11 @@ void hsr_register_frame_in(struct hsr_node *node, struct hsr_port *port, * 0 otherwise, or * negative error code on error */ -int hsr_register_frame_out(struct hsr_port *port, struct hsr_node *node, - u16 sequence_nr) +int hsr_register_frame_out(struct hsr_port *port, struct hsr_frame_info *frame) { + struct hsr_node *node = frame->node_src; + u16 sequence_nr = frame->sequence_nr; + spin_lock_bh(&node->seq_out_lock); if (seq_nr_before_or_eq(sequence_nr, node->seq_out[port->type]) && time_is_after_jiffies(node->time_out[port->type] + @@ -499,6 +505,87 @@ int hsr_register_frame_out(struct hsr_port *port, struct hsr_node *node, return 0; } +/* Adaptation of the PRP duplicate discard algorithm described in wireshark + * wiki (https://wiki.wireshark.org/PRP) + * + * A drop window is maintained for both LANs with start sequence set to the + * first sequence accepted on the LAN that has not been seen on the other LAN, + * and expected sequence set to the latest received sequence number plus one. + * + * When a frame is received on either LAN it is compared against the received + * frames on the other LAN. If it is outside the drop window of the other LAN + * the frame is accepted and the drop window is updated. + * The drop window for the other LAN is reset. + * + * 'port' is the outgoing interface + * 'frame' is the frame to be sent + * + * Return: + * 1 if frame can be shown to have been sent recently on this interface, + * 0 otherwise + */ +int prp_register_frame_out(struct hsr_port *port, struct hsr_frame_info *frame) +{ + enum hsr_port_type other_port; + enum hsr_port_type rcv_port; + struct hsr_node *node; + u16 sequence_nr; + + /* out-going frames are always in order + *and can be checked the same way as for HSR + */ + if (frame->port_rcv->type == HSR_PT_MASTER) + return hsr_register_frame_out(port, frame); + + /* for PRP we should only forward frames from the slave ports + * to the master port + */ + if (port->type != HSR_PT_MASTER) + return 1; + + node = frame->node_src; + sequence_nr = frame->sequence_nr; + rcv_port = frame->port_rcv->type; + other_port = + rcv_port == HSR_PT_SLAVE_A ? HSR_PT_SLAVE_B : HSR_PT_SLAVE_A; + + spin_lock_bh(&node->seq_out_lock); + if (time_is_before_jiffies(node->time_out[port->type] + + msecs_to_jiffies(HSR_ENTRY_FORGET_TIME)) || + (node->seq_start[rcv_port] == node->seq_expected[rcv_port] && + node->seq_start[other_port] == node->seq_expected[other_port])) { + /* the node hasn't been sending for a while + * or both drop windows are empty, forward the frame + */ + node->seq_start[rcv_port] = sequence_nr; + } else if (seq_nr_before(sequence_nr, node->seq_expected[other_port]) && + seq_nr_before_or_eq(node->seq_start[other_port], sequence_nr)) { + /* drop the frame, update the drop window for the other port + * and reset our drop window + */ + node->seq_start[other_port] = sequence_nr + 1; + node->seq_expected[rcv_port] = sequence_nr + 1; + node->seq_start[rcv_port] = node->seq_expected[rcv_port]; + spin_unlock_bh(&node->seq_out_lock); + return 1; + } + + /* update the drop window for the port where this frame was received + * and clear the drop window for the other port + */ + node->seq_start[other_port] = node->seq_expected[other_port]; + node->seq_expected[rcv_port] = sequence_nr + 1; + if ((u16)(node->seq_expected[rcv_port] - node->seq_start[rcv_port]) + > PRP_DROP_WINDOW_LEN) + node->seq_start[rcv_port] = + node->seq_expected[rcv_port] - PRP_DROP_WINDOW_LEN; + + node->time_out[port->type] = jiffies; + node->seq_out[port->type] = sequence_nr; + spin_unlock_bh(&node->seq_out_lock); + return 0; +} + static struct hsr_port *get_late_port(struct hsr_priv *hsr, struct hsr_node *node) { diff --git a/net/hsr/hsr_framereg.h b/net/hsr/hsr_framereg.h index 993fa950d814..b04948659d84 100644 --- a/net/hsr/hsr_framereg.h +++ b/net/hsr/hsr_framereg.h @@ -44,8 +44,7 @@ void hsr_addr_subst_dest(struct hsr_node *node_src, struct sk_buff *skb, void hsr_register_frame_in(struct hsr_node *node, struct hsr_port *port, u16 sequence_nr); -int hsr_register_frame_out(struct hsr_port *port, struct hsr_node *node, - u16 sequence_nr); +int hsr_register_frame_out(struct hsr_port *port, struct hsr_frame_info *frame); void hsr_prune_nodes(struct timer_list *t); void hsr_prune_proxy_nodes(struct timer_list *t); @@ -73,6 +72,8 @@ void prp_update_san_info(struct hsr_node *node, bool is_sup); bool hsr_is_node_in_db(struct list_head *node_db, const unsigned char addr[ETH_ALEN]); +int prp_register_frame_out(struct hsr_port *port, struct hsr_frame_info *frame); + struct hsr_node { struct list_head mac_list; /* Protect R/W access to seq_out */ @@ -89,6 +90,9 @@ struct hsr_node { bool san_b; u16 seq_out[HSR_PT_PORTS]; bool removed; + /* PRP specific duplicate handling */ + u16 seq_expected[HSR_PT_PORTS]; + u16 seq_start[HSR_PT_PORTS]; struct rcu_head rcu_head; }; diff --git a/net/hsr/hsr_main.h b/net/hsr/hsr_main.h index 7561845b8bf6..1bc47b17a296 100644 --- a/net/hsr/hsr_main.h +++ b/net/hsr/hsr_main.h @@ -175,6 +175,8 @@ struct hsr_proto_ops { struct hsr_frame_info *frame); bool (*invalid_dan_ingress_frame)(__be16 protocol); void (*update_san_info)(struct hsr_node *node, bool is_sup); + int (*register_frame_out)(struct hsr_port *port, + struct hsr_frame_info *frame); }; struct hsr_self_node { From patchwork Wed Feb 19 12:48:31 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jaakko Karrenpalo X-Patchwork-Id: 13982087 X-Patchwork-Delegate: kuba@kernel.org Received: from mail-lf1-f53.google.com (mail-lf1-f53.google.com [209.85.167.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 39F5C1E25EB; Wed, 19 Feb 2025 12:48:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739969329; cv=none; b=aaWOKNTbDH6rFSjxafFvuBhoyaH0KFtP349JLBwnnHrteKIZNk8PVcrEL/NrRSiN303bSSq6yWJqQJE88IjfEe5MmfbvucRHahYtYMnC3gCH0z0uFqKkZDEc6PQRyGJdLeDN6hInSUCzYnwMwJOAqCushzgbG+TS60pypCxqHjY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739969329; c=relaxed/simple; bh=0k00O3dedaXSkq8X71dtr6Gs0IkPdXHgP3eokPEhazU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=sTKNbwnObMAb0wXww1IY8KoiXsfvI2SHFjhchHCtrRrhNpzay0KXEAEuKvYKv+yok2i/d5WuFgRASnhHaHESr0vh5jybOcflhoouQ8SJ9rCgacTgydSrNj9tKdg4Ga5dwUNuGJmyvvPG7/0bn88d4Naim/LTj2fTqqaMe6UNceM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Mc7U4Ef+; arc=none smtp.client-ip=209.85.167.53 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Mc7U4Ef+" Received: by mail-lf1-f53.google.com with SMTP id 2adb3069b0e04-54622940ef7so3368365e87.3; Wed, 19 Feb 2025 04:48:46 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1739969325; x=1740574125; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=UPUJe4F7NO0QNwPr6L/FNnvAtDpJ4vGmW7Xwi/oxtGU=; b=Mc7U4Ef+Tu6kkJyT5moBxXNbHUB5XTx2nyRc72GD0zcTCQ/Irr+qjB1PrUu9xmVQrX Ia+3UepzTk/LxsV5bS8B9je1wGasQs+49CFDQn6SKG4m6R+0e0+Th8RoSTlIUbTeLSTN Ra/NK0p7Ci5mN1mFxytlvBA/gJQTaBGUSYl3lXParA+0/c9Dc5S34UBGlu+6Cq4Q9SPe xQMIQRWsyPJEypdcvHTwpRW1WPQRDp0F/IMvm7aEAIzKPzQXnzBWpCNxnmmmRlDqr8+j UEPHgtIoFZFSk8c56SLWYzXzKvXHhEKrpm/17WCL53RBsQ8pskFOgmy2Y3VHTeRvm1dv PwDg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1739969325; x=1740574125; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=UPUJe4F7NO0QNwPr6L/FNnvAtDpJ4vGmW7Xwi/oxtGU=; b=UkD7RkFaQFoqKXQ6ipOsGWkl3ZhRpIkucHfowJQRk0b6R54gl5Nclh/0kppusd6WaF gBJUOVho7vOYHq17YGdXCYBOsCFLqGs1jkup58WwRVs09edQoj2BgwQqHRsxbz0YPfHW sM4fVLSuNCmUxy1K5jjobzM5+IAYOleVgKTzuhKw/S6p82ryGddPz2ki0M5aTvhHvafN 6WDNn06RGyJSwiNA1jX+0kdnVPyvlKsda7i4FMTJKPoiZzjcYp7UUi7it/23UlROnite QLGrJ5lUzkJaUw1CFsBoDqy+E9EAF7GEQbd1lp9sqPa1Oc0KfjLjVeIGRie/AujKeNu8 AsVA== X-Forwarded-Encrypted: i=1; AJvYcCVVyNU22NrBAa/3q4yWjXAlv0Q3XB2HaC4dRzEyoTczxjsa/uPn9e+YFIZgvzBJ00H4bsJ16ro=@vger.kernel.org X-Gm-Message-State: AOJu0YwpDzPLXAQqbiIdmJVzqu8P3SjVoFZI9sC+BtjP1fqO65xmYHjj acBKyEut+Me7w1IlX1tSpVJxVuBoSC/Z3Ciw203+BGVfNlOziw7o X-Gm-Gg: ASbGncvKRkqOtTd3mUdEh0/OOlbM0KuXS5h61mvPuct1wtx8xpxRsTyxaiqaB6HwkM7 2SrxxYLb2vXU4qtui1PCPYzLsu1d4zFy2gCe31hSxsVNBxXbMc4HKNId+lTAaAj6HcKdf5mJHEu q8QKoHxhKgw8RCT/5pGVezTe0qlrdefCTiQD/SV+TUn3f5mX7YKVF+Tu/fSz8kVuqqVpqLp7wUq WQlnCAHqfM83K7Y9IEbkRVU3n+LM4GzOrA1AoyBY9FG9incqVqwgLnqppXmQF1s5fMvsAvqsVD3 JbvFKlHT3AgxGHVThuxn3vlOENPscAVEPgbzEjmIpTCTsw== X-Google-Smtp-Source: AGHT+IHtr0c6t4RY88x3YXdqbbjC9xNV9Tr8g0PeaxFCG9JzCy606WUl+z3oDN5IGfyHK3EdI+HuzA== X-Received: by 2002:a05:6512:10d6:b0:545:1104:616a with SMTP id 2adb3069b0e04-5462eed85d5mr1225312e87.2.1739969324969; Wed, 19 Feb 2025 04:48:44 -0800 (PST) Received: from FI-L-7486715.nmea.abb.com ([147.161.186.89]) by smtp.gmail.com with ESMTPSA id 2adb3069b0e04-5452c937745sm1732697e87.243.2025.02.19.04.48.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Feb 2025 04:48:44 -0800 (PST) From: Jaakko Karrenpalo X-Google-Original-From: Jaakko Karrenpalo To: "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman Cc: linux-kernel@vger.kernel.org, netdev@vger.kernel.org, Jaakko Karrenpalo Subject: [PATCH net-next 2/2] net: hsr: Add KUnit test for PRP Date: Wed, 19 Feb 2025 14:48:31 +0200 Message-ID: <20250219124831.544318-2-jaakko.karrenpalo@fi.abb.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250219124831.544318-1-jaakko.karrenpalo@fi.abb.com> References: <20250219124831.544318-1-jaakko.karrenpalo@fi.abb.com> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: kuba@kernel.org Add unit tests for the PRP duplicate detection Signed-off-by: Jaakko Karrenpalo --- net/hsr/Kconfig | 18 +++ net/hsr/Makefile | 2 + net/hsr/prp_dup_discard_test.c | 210 +++++++++++++++++++++++++++++++++ 3 files changed, 230 insertions(+) create mode 100644 net/hsr/prp_dup_discard_test.c diff --git a/net/hsr/Kconfig b/net/hsr/Kconfig index 1b048c17b6c8..fcacdf4f0ffc 100644 --- a/net/hsr/Kconfig +++ b/net/hsr/Kconfig @@ -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 diff --git a/net/hsr/Makefile b/net/hsr/Makefile index 75df90d3b416..34e581db5c41 100644 --- a/net/hsr/Makefile +++ b/net/hsr/Makefile @@ -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 diff --git a/net/hsr/prp_dup_discard_test.c b/net/hsr/prp_dup_discard_test.c new file mode 100644 index 000000000000..e212bdf24720 --- /dev/null +++ b/net/hsr/prp_dup_discard_test.c @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: GPL-2.0 +#include + +#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");