Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'hsr-implement-more-robust-duplicate-discard-algorithm'

Felix Maurer says:

====================
hsr: Implement more robust duplicate discard algorithm

The duplicate discard algorithms for PRP and HSR do not work reliably
with certain link faults. Especially with packet loss on one link, the
duplicate discard algorithms drop valid packets. For a more thorough
description see patches 4 (for PRP) and 6 (for HSR).

This patchset replaces the current algorithms (based on a drop window
for PRP and highest seen sequence number for HSR) with a single new one
that tracks the received sequence numbers individually (descriptions
again in patches 4 and 6).

The changes will lead to higher memory usage and more work to do for
each packet. But I argue that this is an acceptable trade-off to make
for a more robust PRP and HSR behavior with faulty links. After all,
both protocols are to be used in environments where redundancy is needed
and people are willing to setup special network topologies to achieve
that.

Some more reasoning on the overhead and expected scale of the deployment
from the RFC discussion:

> As for the expected scale, there are two dimensions: the number of nodes
> in the network and the data rate with which they send.
>
> The number of nodes in the network affect the memory usage because each
> node now has the block buffer. For PRP that's 64 blocks * 32 byte =
> 2kbyte for each node in the node table. A PRP network doesn't have an
> explicit limit for the number of nodes. However, the whole network is a
> single layer-2 segment which shouldn't grow too large anyways. Even if
> one really tries to put 1000 nodes into the PRP network, the memory
> overhead (2Mbyte) is acceptable in my opinion.
>
> For HSR, the blocks would be larger because we need to track the
> sequence numbers per port. I expect 64 blocks * 80 byte = 5kbyte per
> node in the node table. There is no explicit limit for the size of an
> HSR ring either. But I expect them to be of limited size because the
> forwarding delays add up throughout the ring. I've seen vendors limiting
> the ring size to 50 nodes with 100Mbit/s links and 300 with 1Gbit/s
> links. In both cases I consider the memory overhead acceptable.
>
> The data rates are harder to reason about. In general, the data rates
> for HSR and PRP are limited because too high packet rates would lead to
> very fast re-use of the 16bit sequence numbers. The IEC 62439-3:2021
> mentions 100Mbit/s links and 1Gbit/s links. I don't expect HSR or PRP
> networks to scale out to, e.g., 10Gbit/s links with the current
> specification as this would mean that sequence numbers could repeat as
> often as every ~4ms. The default constants in the IEC standard, which we
> also use, are oriented at a 100Mbit/s network.
>
> In my tests with veth pairs, the CPU overhead didn't lead to
> significantly lower data rates. The main factor limiting the data rate
> at the moment, I assume, is the per-node spinlock that is taken for each
> received packet. IMHO, there is a lot more to gain in terms of CPU
> overhead from making this lock smaller or getting rid of it, than we
> loose with the more accurate duplicate discard algorithm in this patchset.
>
> The CPU overhead of the algorithm benefits from the fact that in high
> packet rate scenarios (where it really matters) many packets will have
> sequence numbers in already initialized blocks. These packets just have
> additionally: one xarray lookup, one comparison, and one bit setting. If
> a block needs to be initialized (once every 128 packets plus their 128
> duplicates if all sequence numbers are seen), we will have: one
> xa_erase, a bunch of memory writes, and one xa_store.
>
> In theory, all packets could end up in the slow path if a node sends
> every 128th packet to us. If this is sent from a well behaving node, the
> packet rate wouldn't be an issue anymore, though.

Signed-off-by: Felix Maurer <fmaurer@redhat.com>
====================

Link: https://patch.msgid.link/cover.1770299429.git.fmaurer@redhat.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

+918 -386
+1
MAINTAINERS
··· 11592 11592 L: netdev@vger.kernel.org 11593 11593 S: Orphan 11594 11594 F: net/hsr/ 11595 + F: tools/testing/selftests/net/hsr/ 11595 11596 11596 11597 HT16K33 LED CONTROLLER DRIVER 11597 11598 M: Robin van der Gracht <robin@protonic.nl>
+223 -153
net/hsr/hsr_framereg.c
··· 11 11 * Same code handles filtering of duplicates for PRP as well. 12 12 */ 13 13 14 + #include <kunit/visibility.h> 14 15 #include <linux/if_ether.h> 15 16 #include <linux/etherdevice.h> 16 17 #include <linux/slab.h> ··· 19 18 #include "hsr_main.h" 20 19 #include "hsr_framereg.h" 21 20 #include "hsr_netlink.h" 22 - 23 - /* seq_nr_after(a, b) - return true if a is after (higher in sequence than) b, 24 - * false otherwise. 25 - */ 26 - static bool seq_nr_after(u16 a, u16 b) 27 - { 28 - /* Remove inconsistency where 29 - * seq_nr_after(a, b) == seq_nr_before(a, b) 30 - */ 31 - if ((int)b - a == 32768) 32 - return false; 33 - 34 - return (((s16)(b - a)) < 0); 35 - } 36 - 37 - #define seq_nr_before(a, b) seq_nr_after((b), (a)) 38 - #define seq_nr_before_or_eq(a, b) (!seq_nr_after((a), (b))) 39 - #define PRP_DROP_WINDOW_LEN 32768 40 21 41 22 bool hsr_addr_is_redbox(struct hsr_priv *hsr, unsigned char *addr) 42 23 { ··· 109 126 kfree_rcu(old, rcu_head); 110 127 } 111 128 129 + static void hsr_free_node(struct hsr_node *node) 130 + { 131 + xa_destroy(&node->seq_blocks); 132 + kfree(node->block_buf); 133 + kfree(node); 134 + } 135 + 136 + static void hsr_free_node_rcu(struct rcu_head *rn) 137 + { 138 + struct hsr_node *node = container_of(rn, struct hsr_node, rcu_head); 139 + 140 + hsr_free_node(node); 141 + } 142 + 112 143 void hsr_del_nodes(struct list_head *node_db) 113 144 { 114 145 struct hsr_node *node; 115 146 struct hsr_node *tmp; 116 147 117 - list_for_each_entry_safe(node, tmp, node_db, mac_list) 118 - kfree(node); 148 + list_for_each_entry_safe(node, tmp, node_db, mac_list) { 149 + list_del(&node->mac_list); 150 + hsr_free_node(node); 151 + } 119 152 } 120 153 121 154 void prp_handle_san_frame(bool san, enum hsr_port_type port, ··· 147 148 node->san_b = true; 148 149 } 149 150 150 - /* Allocate an hsr_node and add it to node_db. 'addr' is the node's address_A; 151 - * seq_out is used to initialize filtering of outgoing duplicate frames 152 - * originating from the newly added node. 151 + /* Allocate an hsr_node and add it to node_db. 'addr' is the node's address_A. 153 152 */ 154 153 static struct hsr_node *hsr_add_node(struct hsr_priv *hsr, 155 154 struct list_head *node_db, 156 - unsigned char addr[], 157 - u16 seq_out, bool san, 155 + unsigned char addr[], bool san, 158 156 enum hsr_port_type rx_port) 159 157 { 160 - struct hsr_node *new_node, *node; 158 + struct hsr_node *new_node, *node = NULL; 161 159 unsigned long now; 160 + size_t block_sz; 162 161 int i; 163 162 164 163 new_node = kzalloc(sizeof(*new_node), GFP_ATOMIC); ··· 166 169 ether_addr_copy(new_node->macaddress_A, addr); 167 170 spin_lock_init(&new_node->seq_out_lock); 168 171 172 + if (hsr->prot_version == PRP_V1) 173 + new_node->seq_port_cnt = 1; 174 + else 175 + new_node->seq_port_cnt = HSR_PT_PORTS - 1; 176 + 177 + block_sz = hsr_seq_block_size(new_node); 178 + new_node->block_buf = kcalloc(HSR_MAX_SEQ_BLOCKS, block_sz, GFP_ATOMIC); 179 + if (!new_node->block_buf) 180 + goto free; 181 + 182 + xa_init(&new_node->seq_blocks); 183 + 169 184 /* We are only interested in time diffs here, so use current jiffies 170 185 * as initialization. (0 could trigger an spurious ring error warning). 171 186 */ 172 187 now = jiffies; 173 188 for (i = 0; i < HSR_PT_PORTS; i++) { 174 189 new_node->time_in[i] = now; 175 - new_node->time_out[i] = now; 176 - } 177 - for (i = 0; i < HSR_PT_PORTS; i++) { 178 - new_node->seq_out[i] = seq_out; 179 - new_node->seq_expected[i] = seq_out + 1; 180 - new_node->seq_start[i] = seq_out + 1; 181 190 } 182 191 183 192 if (san && hsr->proto_ops->handle_san_frame) ··· 202 199 return new_node; 203 200 out: 204 201 spin_unlock_bh(&hsr->list_lock); 202 + kfree(new_node->block_buf); 203 + free: 205 204 kfree(new_node); 206 205 return node; 207 206 } ··· 228 223 struct ethhdr *ethhdr; 229 224 struct prp_rct *rct; 230 225 bool san = false; 231 - u16 seq_out; 232 226 233 227 if (!skb_mac_header_was_set(skb)) 234 228 return NULL; ··· 264 260 /* Check if skb contains hsr_ethhdr */ 265 261 if (skb->mac_len < sizeof(struct hsr_ethhdr)) 266 262 return NULL; 267 - 268 - /* Use the existing sequence_nr from the tag as starting point 269 - * for filtering duplicate frames. 270 - */ 271 - seq_out = hsr_get_skb_sequence_nr(skb) - 1; 272 263 } else { 273 264 rct = skb_get_PRP_rct(skb); 274 - if (rct && prp_check_lsdu_size(skb, rct, is_sup)) { 275 - seq_out = prp_get_skb_sequence_nr(rct); 276 - } else { 277 - if (rx_port != HSR_PT_MASTER) 278 - san = true; 279 - seq_out = HSR_SEQNR_START; 280 - } 265 + if (!rct && rx_port != HSR_PT_MASTER) 266 + san = true; 281 267 } 282 268 283 - return hsr_add_node(hsr, node_db, ethhdr->h_source, seq_out, 284 - san, rx_port); 269 + return hsr_add_node(hsr, node_db, ethhdr->h_source, san, rx_port); 285 270 } 271 + 272 + static bool hsr_seq_block_is_old(struct hsr_seq_block *block) 273 + { 274 + unsigned long expiry = msecs_to_jiffies(HSR_ENTRY_FORGET_TIME); 275 + 276 + return time_is_before_jiffies(block->time + expiry); 277 + } 278 + 279 + static void hsr_forget_seq_block(struct hsr_node *node, 280 + struct hsr_seq_block *block) 281 + { 282 + if (block->time) 283 + xa_erase(&node->seq_blocks, block->block_idx); 284 + block->time = 0; 285 + } 286 + 287 + /* Get the currently active sequence number block. If there is no block yet, or 288 + * the existing one is expired, a new block is created. The idea is to maintain 289 + * a "sparse bitmap" where a bitmap for the whole sequence number space is 290 + * split into blocks and not all blocks exist all the time. The blocks can 291 + * expire after time (in low traffic situations) or when they are replaced in 292 + * the backing fixed size buffer (in high traffic situations). 293 + */ 294 + VISIBLE_IF_KUNIT struct hsr_seq_block *hsr_get_seq_block(struct hsr_node *node, 295 + u16 block_idx) 296 + { 297 + struct hsr_seq_block *block, *res; 298 + size_t block_sz; 299 + 300 + block = xa_load(&node->seq_blocks, block_idx); 301 + 302 + if (block && hsr_seq_block_is_old(block)) { 303 + hsr_forget_seq_block(node, block); 304 + block = NULL; 305 + } 306 + 307 + if (!block) { 308 + block_sz = hsr_seq_block_size(node); 309 + block = node->block_buf + node->next_block * block_sz; 310 + hsr_forget_seq_block(node, block); 311 + 312 + memset(block, 0, block_sz); 313 + block->time = jiffies; 314 + block->block_idx = block_idx; 315 + 316 + res = xa_store(&node->seq_blocks, block_idx, block, GFP_ATOMIC); 317 + if (xa_is_err(res)) { 318 + block->time = 0; 319 + return NULL; 320 + } 321 + 322 + node->next_block = 323 + (node->next_block + 1) & (HSR_MAX_SEQ_BLOCKS - 1); 324 + } 325 + 326 + return block; 327 + } 328 + EXPORT_SYMBOL_IF_KUNIT(hsr_get_seq_block); 286 329 287 330 /* Use the Supervision frame's info about an eventual macaddress_B for merging 288 331 * nodes that has previously had their macaddress_B registered as a separate ··· 339 288 { 340 289 struct hsr_node *node_curr = frame->node_src; 341 290 struct hsr_port *port_rcv = frame->port_rcv; 291 + struct hsr_seq_block *src_blk, *merge_blk; 342 292 struct hsr_priv *hsr = port_rcv->hsr; 343 - struct hsr_sup_payload *hsr_sp; 344 293 struct hsr_sup_tlv *hsr_sup_tlv; 294 + struct hsr_sup_payload *hsr_sp; 345 295 struct hsr_node *node_real; 346 296 struct sk_buff *skb = NULL; 347 297 struct list_head *node_db; 348 298 struct ethhdr *ethhdr; 349 - int i; 350 - unsigned int pull_size = 0; 351 299 unsigned int total_pull_size = 0; 300 + unsigned int pull_size = 0; 301 + unsigned long idx; 302 + int i; 352 303 353 304 /* Here either frame->skb_hsr or frame->skb_prp should be 354 305 * valid as supervision frame always will have protocol ··· 393 340 if (!node_real) 394 341 /* No frame received from AddrA of this node yet */ 395 342 node_real = hsr_add_node(hsr, node_db, hsr_sp->macaddress_A, 396 - HSR_SEQNR_START - 1, true, 397 - port_rcv->type); 343 + true, port_rcv->type); 398 344 if (!node_real) 399 345 goto done; /* No mem */ 400 346 if (node_real == node_curr) ··· 440 388 node_real->time_in_stale[i] = 441 389 node_curr->time_in_stale[i]; 442 390 } 443 - if (seq_nr_after(node_curr->seq_out[i], node_real->seq_out[i])) 444 - node_real->seq_out[i] = node_curr->seq_out[i]; 391 + } 392 + 393 + xa_for_each(&node_curr->seq_blocks, idx, src_blk) { 394 + if (hsr_seq_block_is_old(src_blk)) 395 + continue; 396 + 397 + merge_blk = hsr_get_seq_block(node_real, src_blk->block_idx); 398 + if (!merge_blk) 399 + continue; 400 + merge_blk->time = min(merge_blk->time, src_blk->time); 401 + for (i = 0; i < node_real->seq_port_cnt; i++) { 402 + bitmap_or(merge_blk->seq_nrs[i], merge_blk->seq_nrs[i], 403 + src_blk->seq_nrs[i], HSR_SEQ_BLOCK_SIZE); 404 + } 445 405 } 446 406 spin_unlock_bh(&node_real->seq_out_lock); 447 407 node_real->addr_B_port = port_rcv->type; ··· 462 398 if (!node_curr->removed) { 463 399 list_del_rcu(&node_curr->mac_list); 464 400 node_curr->removed = true; 465 - kfree_rcu(node_curr, rcu_head); 401 + call_rcu(&node_curr->rcu_head, hsr_free_node_rcu); 466 402 } 467 403 spin_unlock_bh(&hsr->list_lock); 468 404 ··· 530 466 void hsr_register_frame_in(struct hsr_node *node, struct hsr_port *port, 531 467 u16 sequence_nr) 532 468 { 533 - /* Don't register incoming frames without a valid sequence number. This 534 - * ensures entries of restarted nodes gets pruned so that they can 535 - * re-register and resume communications. 536 - */ 537 - if (!(port->dev->features & NETIF_F_HW_HSR_TAG_RM) && 538 - seq_nr_before(sequence_nr, node->seq_out[port->type])) 539 - return; 540 - 541 469 node->time_in[port->type] = jiffies; 542 470 node->time_in_stale[port->type] = false; 543 471 } 544 472 545 - /* 'skb' is a HSR Ethernet frame (with a HSR tag inserted), with a valid 546 - * ethhdr->h_source address and skb->mac_header set. 473 + /* Duplicate discard algorithm: we maintain a bitmap where we set a bit for 474 + * every seen sequence number. The bitmap is split into blocks and the block 475 + * management is detailed in hsr_get_seq_block(). In any case, we err on the 476 + * side of accepting a packet, as the specification requires the algorithm to 477 + * be "designed such that it never rejects a legitimate frame, while occasional 478 + * acceptance of a duplicate can be tolerated." (IEC 62439-3:2021, 4.1.10.3). 479 + * While this requirement is explicit for PRP, applying it to HSR does no harm 480 + * either. 481 + * 482 + * 'frame' is the frame to be sent 483 + * 'port_type' is the type of the outgoing interface 547 484 * 548 485 * Return: 549 486 * 1 if frame can be shown to have been sent recently on this interface, 550 - * 0 otherwise, or 551 - * negative error code on error 487 + * 0 otherwise 488 + */ 489 + static int hsr_check_duplicate(struct hsr_frame_info *frame, 490 + unsigned int port_type) 491 + { 492 + u16 sequence_nr, seq_bit, block_idx; 493 + struct hsr_seq_block *block; 494 + struct hsr_node *node; 495 + 496 + node = frame->node_src; 497 + sequence_nr = frame->sequence_nr; 498 + 499 + if (WARN_ON_ONCE(port_type >= node->seq_port_cnt)) 500 + return 0; 501 + 502 + spin_lock_bh(&node->seq_out_lock); 503 + 504 + block_idx = hsr_seq_block_index(sequence_nr); 505 + block = hsr_get_seq_block(node, block_idx); 506 + if (!block) 507 + goto out_new; 508 + 509 + seq_bit = hsr_seq_block_bit(sequence_nr); 510 + if (__test_and_set_bit(seq_bit, block->seq_nrs[port_type])) 511 + goto out_seen; 512 + 513 + out_new: 514 + spin_unlock_bh(&node->seq_out_lock); 515 + return 0; 516 + 517 + out_seen: 518 + spin_unlock_bh(&node->seq_out_lock); 519 + return 1; 520 + } 521 + 522 + /* HSR duplicate discard: we check if the same frame has already been sent on 523 + * this outgoing interface. The check follows the general duplicate discard 524 + * algorithm. 525 + * 526 + * 'port' is the outgoing interface 527 + * 'frame' is the frame to be sent 528 + * 529 + * Return: 530 + * 1 if frame can be shown to have been sent recently on this interface, 531 + * 0 otherwise 552 532 */ 553 533 int hsr_register_frame_out(struct hsr_port *port, struct hsr_frame_info *frame) 554 534 { 555 - struct hsr_node *node = frame->node_src; 556 - u16 sequence_nr = frame->sequence_nr; 557 - 558 - spin_lock_bh(&node->seq_out_lock); 559 - if (seq_nr_before_or_eq(sequence_nr, node->seq_out[port->type]) && 560 - time_is_after_jiffies(node->time_out[port->type] + 561 - msecs_to_jiffies(HSR_ENTRY_FORGET_TIME))) { 562 - spin_unlock_bh(&node->seq_out_lock); 563 - return 1; 564 - } 565 - 566 - node->time_out[port->type] = jiffies; 567 - node->seq_out[port->type] = sequence_nr; 568 - spin_unlock_bh(&node->seq_out_lock); 569 - return 0; 535 + return hsr_check_duplicate(frame, port->type - 1); 570 536 } 571 537 572 - /* Adaptation of the PRP duplicate discard algorithm described in wireshark 573 - * wiki (https://wiki.wireshark.org/PRP) 574 - * 575 - * A drop window is maintained for both LANs with start sequence set to the 576 - * first sequence accepted on the LAN that has not been seen on the other LAN, 577 - * and expected sequence set to the latest received sequence number plus one. 578 - * 579 - * When a frame is received on either LAN it is compared against the received 580 - * frames on the other LAN. If it is outside the drop window of the other LAN 581 - * the frame is accepted and the drop window is updated. 582 - * The drop window for the other LAN is reset. 538 + /* PRP duplicate discard: we only consider frames that are received on port A 539 + * or port B and should go to the master port. For those, we check if they have 540 + * already been received by the host, i.e., master port. The check uses the 541 + * general duplicate discard algorithm, but without tracking multiple ports. 583 542 * 584 543 * 'port' is the outgoing interface 585 544 * 'frame' is the frame to be sent ··· 613 526 */ 614 527 int prp_register_frame_out(struct hsr_port *port, struct hsr_frame_info *frame) 615 528 { 616 - enum hsr_port_type other_port; 617 - enum hsr_port_type rcv_port; 618 - struct hsr_node *node; 619 - u16 sequence_diff; 620 - u16 sequence_exp; 621 - u16 sequence_nr; 622 - 623 - /* out-going frames are always in order 624 - * and can be checked the same way as for HSR 625 - */ 529 + /* out-going frames are always in order */ 626 530 if (frame->port_rcv->type == HSR_PT_MASTER) 627 - return hsr_register_frame_out(port, frame); 531 + return 0; 628 532 629 533 /* for PRP we should only forward frames from the slave ports 630 534 * to the master port ··· 623 545 if (port->type != HSR_PT_MASTER) 624 546 return 1; 625 547 626 - node = frame->node_src; 627 - sequence_nr = frame->sequence_nr; 628 - sequence_exp = sequence_nr + 1; 629 - rcv_port = frame->port_rcv->type; 630 - other_port = rcv_port == HSR_PT_SLAVE_A ? HSR_PT_SLAVE_B : 631 - HSR_PT_SLAVE_A; 632 - 633 - spin_lock_bh(&node->seq_out_lock); 634 - if (time_is_before_jiffies(node->time_out[port->type] + 635 - msecs_to_jiffies(HSR_ENTRY_FORGET_TIME)) || 636 - (node->seq_start[rcv_port] == node->seq_expected[rcv_port] && 637 - node->seq_start[other_port] == node->seq_expected[other_port])) { 638 - /* the node hasn't been sending for a while 639 - * or both drop windows are empty, forward the frame 640 - */ 641 - node->seq_start[rcv_port] = sequence_nr; 642 - } else if (seq_nr_before(sequence_nr, node->seq_expected[other_port]) && 643 - seq_nr_before_or_eq(node->seq_start[other_port], sequence_nr)) { 644 - /* drop the frame, update the drop window for the other port 645 - * and reset our drop window 646 - */ 647 - node->seq_start[other_port] = sequence_exp; 648 - node->seq_expected[rcv_port] = sequence_exp; 649 - node->seq_start[rcv_port] = node->seq_expected[rcv_port]; 650 - spin_unlock_bh(&node->seq_out_lock); 651 - return 1; 652 - } 653 - 654 - /* update the drop window for the port where this frame was received 655 - * and clear the drop window for the other port 656 - */ 657 - node->seq_start[other_port] = node->seq_expected[other_port]; 658 - node->seq_expected[rcv_port] = sequence_exp; 659 - sequence_diff = sequence_exp - node->seq_start[rcv_port]; 660 - if (sequence_diff > PRP_DROP_WINDOW_LEN) 661 - node->seq_start[rcv_port] = sequence_exp - PRP_DROP_WINDOW_LEN; 662 - 663 - node->time_out[port->type] = jiffies; 664 - node->seq_out[port->type] = sequence_nr; 665 - spin_unlock_bh(&node->seq_out_lock); 666 - return 0; 548 + return hsr_check_duplicate(frame, 0); 667 549 } 668 - 669 - #if IS_MODULE(CONFIG_PRP_DUP_DISCARD_KUNIT_TEST) 670 - EXPORT_SYMBOL(prp_register_frame_out); 671 - #endif 550 + EXPORT_SYMBOL_IF_KUNIT(prp_register_frame_out); 672 551 673 552 static struct hsr_port *get_late_port(struct hsr_priv *hsr, 674 553 struct hsr_node *node) ··· 707 672 list_del_rcu(&node->mac_list); 708 673 node->removed = true; 709 674 /* Note that we need to free this entry later: */ 710 - kfree_rcu(node, rcu_head); 675 + call_rcu(&node->rcu_head, hsr_free_node_rcu); 711 676 } 712 677 } 713 678 } ··· 741 706 list_del_rcu(&node->mac_list); 742 707 node->removed = true; 743 708 /* Note that we need to free this entry later: */ 744 - kfree_rcu(node, rcu_head); 709 + call_rcu(&node->rcu_head, hsr_free_node_rcu); 745 710 } 746 711 } 747 712 } ··· 773 738 } 774 739 775 740 return NULL; 741 + } 742 + 743 + /* Fill the last sequence number that has been received from node on if1 by 744 + * finding the last sequence number sent on port B; accordingly get the last 745 + * received sequence number for if2 using sent sequence numbers on port A. 746 + */ 747 + static void fill_last_seq_nrs(struct hsr_node *node, u16 *if1_seq, u16 *if2_seq) 748 + { 749 + struct hsr_seq_block *block; 750 + unsigned int block_off; 751 + size_t block_sz; 752 + u16 seq_bit; 753 + 754 + spin_lock_bh(&node->seq_out_lock); 755 + 756 + /* Get last inserted block */ 757 + block_off = (node->next_block - 1) & (HSR_MAX_SEQ_BLOCKS - 1); 758 + block_sz = hsr_seq_block_size(node); 759 + block = node->block_buf + block_off * block_sz; 760 + 761 + if (!bitmap_empty(block->seq_nrs[HSR_PT_SLAVE_B - 1], 762 + HSR_SEQ_BLOCK_SIZE)) { 763 + seq_bit = find_last_bit(block->seq_nrs[HSR_PT_SLAVE_B - 1], 764 + HSR_SEQ_BLOCK_SIZE); 765 + *if1_seq = (block->block_idx << HSR_SEQ_BLOCK_SHIFT) | seq_bit; 766 + } 767 + if (!bitmap_empty(block->seq_nrs[HSR_PT_SLAVE_A - 1], 768 + HSR_SEQ_BLOCK_SIZE)) { 769 + seq_bit = find_last_bit(block->seq_nrs[HSR_PT_SLAVE_A - 1], 770 + HSR_SEQ_BLOCK_SIZE); 771 + *if2_seq = (block->block_idx << HSR_SEQ_BLOCK_SHIFT) | seq_bit; 772 + } 773 + spin_unlock_bh(&node->seq_out_lock); 776 774 } 777 775 778 776 int hsr_get_node_data(struct hsr_priv *hsr, ··· 848 780 *if2_age = jiffies_to_msecs(tdiff); 849 781 850 782 /* Present sequence numbers as if they were incoming on interface */ 851 - *if1_seq = node->seq_out[HSR_PT_SLAVE_B]; 852 - *if2_seq = node->seq_out[HSR_PT_SLAVE_A]; 783 + *if1_seq = 0; 784 + *if2_seq = 0; 785 + if (hsr->prot_version != PRP_V1) 786 + fill_last_seq_nrs(node, if1_seq, if2_seq); 853 787 854 788 if (node->addr_B_port != HSR_PT_NONE) { 855 789 port = hsr_port_get_hsr(hsr, node->addr_B_port);
+33 -6
net/hsr/hsr_framereg.h
··· 74 74 75 75 int prp_register_frame_out(struct hsr_port *port, struct hsr_frame_info *frame); 76 76 77 + #if IS_ENABLED(CONFIG_KUNIT) 78 + struct hsr_seq_block *hsr_get_seq_block(struct hsr_node *node, u16 block_idx); 79 + #endif 80 + 81 + #define HSR_SEQ_BLOCK_SHIFT 7 /* 128 bits */ 82 + #define HSR_SEQ_BLOCK_SIZE (1 << HSR_SEQ_BLOCK_SHIFT) 83 + #define HSR_SEQ_BLOCK_MASK (HSR_SEQ_BLOCK_SIZE - 1) 84 + #define HSR_MAX_SEQ_BLOCKS 64 85 + 86 + #define hsr_seq_block_index(sequence_nr) ((sequence_nr) >> HSR_SEQ_BLOCK_SHIFT) 87 + #define hsr_seq_block_bit(sequence_nr) ((sequence_nr) & HSR_SEQ_BLOCK_MASK) 88 + 89 + struct hsr_seq_block { 90 + unsigned long time; 91 + u16 block_idx; 92 + /* Should be a flexible array member of what DECLARE_BITMAP() would 93 + * produce. 94 + */ 95 + unsigned long seq_nrs[][BITS_TO_LONGS(HSR_SEQ_BLOCK_SIZE)]; 96 + }; 97 + 77 98 struct hsr_node { 78 99 struct list_head mac_list; 79 - /* Protect R/W access to seq_out */ 100 + /* Protect R/W access seq_blocks */ 80 101 spinlock_t seq_out_lock; 81 102 unsigned char macaddress_A[ETH_ALEN]; 82 103 unsigned char macaddress_B[ETH_ALEN]; ··· 105 84 enum hsr_port_type addr_B_port; 106 85 unsigned long time_in[HSR_PT_PORTS]; 107 86 bool time_in_stale[HSR_PT_PORTS]; 108 - unsigned long time_out[HSR_PT_PORTS]; 109 87 /* if the node is a SAN */ 110 88 bool san_a; 111 89 bool san_b; 112 - u16 seq_out[HSR_PT_PORTS]; 113 90 bool removed; 114 - /* PRP specific duplicate handling */ 115 - u16 seq_expected[HSR_PT_PORTS]; 116 - u16 seq_start[HSR_PT_PORTS]; 91 + /* Duplicate detection */ 92 + struct xarray seq_blocks; 93 + void *block_buf; 94 + unsigned int next_block; 95 + unsigned int seq_port_cnt; 117 96 struct rcu_head rcu_head; 118 97 }; 98 + 99 + static inline size_t hsr_seq_block_size(struct hsr_node *node) 100 + { 101 + WARN_ON_ONCE(node->seq_port_cnt == 0); 102 + return struct_size_t(struct hsr_seq_block, seq_nrs, node->seq_port_cnt); 103 + } 119 104 120 105 #endif /* __HSR_FRAMEREG_H */
+73 -83
net/hsr/prp_dup_discard_test.c
··· 4 4 #include "hsr_main.h" 5 5 #include "hsr_framereg.h" 6 6 7 + MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); 8 + 7 9 struct prp_test_data { 8 10 struct hsr_port port; 9 11 struct hsr_port port_rcv; ··· 15 13 16 14 static struct prp_test_data *build_prp_test_data(struct kunit *test) 17 15 { 16 + size_t block_sz; 17 + 18 18 struct prp_test_data *data = kunit_kzalloc(test, 19 19 sizeof(struct prp_test_data), GFP_USER); 20 20 KUNIT_EXPECT_NOT_ERR_OR_NULL(test, data); 21 21 22 + data->node.seq_port_cnt = 1; 23 + block_sz = hsr_seq_block_size(&data->node); 24 + data->node.block_buf = kunit_kcalloc(test, HSR_MAX_SEQ_BLOCKS, block_sz, 25 + GFP_ATOMIC); 26 + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, data->node.block_buf); 27 + 28 + xa_init(&data->node.seq_blocks); 29 + spin_lock_init(&data->node.seq_out_lock); 30 + 22 31 data->frame.node_src = &data->node; 23 32 data->frame.port_rcv = &data->port_rcv; 24 33 data->port_rcv.type = HSR_PT_SLAVE_A; 25 - data->node.seq_start[HSR_PT_SLAVE_A] = 1; 26 - data->node.seq_expected[HSR_PT_SLAVE_A] = 1; 27 - data->node.seq_start[HSR_PT_SLAVE_B] = 1; 28 - data->node.seq_expected[HSR_PT_SLAVE_B] = 1; 29 - data->node.seq_out[HSR_PT_MASTER] = 0; 30 - data->node.time_out[HSR_PT_MASTER] = jiffies; 31 34 data->port.type = HSR_PT_MASTER; 32 35 33 36 return data; 34 37 } 35 38 36 - static void check_prp_counters(struct kunit *test, 37 - struct prp_test_data *data, 38 - u16 seq_start_a, u16 seq_expected_a, 39 - u16 seq_start_b, u16 seq_expected_b) 39 + static void check_prp_frame_seen(struct kunit *test, struct prp_test_data *data, 40 + u16 sequence_nr) 40 41 { 41 - KUNIT_EXPECT_EQ(test, data->node.seq_start[HSR_PT_SLAVE_A], 42 - seq_start_a); 43 - KUNIT_EXPECT_EQ(test, data->node.seq_start[HSR_PT_SLAVE_B], 44 - seq_start_b); 45 - KUNIT_EXPECT_EQ(test, data->node.seq_expected[HSR_PT_SLAVE_A], 46 - seq_expected_a); 47 - KUNIT_EXPECT_EQ(test, data->node.seq_expected[HSR_PT_SLAVE_B], 48 - seq_expected_b); 42 + u16 block_idx, seq_bit; 43 + struct hsr_seq_block *block; 44 + 45 + block_idx = sequence_nr >> HSR_SEQ_BLOCK_SHIFT; 46 + block = xa_load(&data->node.seq_blocks, block_idx); 47 + KUNIT_EXPECT_NOT_NULL(test, block); 48 + 49 + seq_bit = sequence_nr & HSR_SEQ_BLOCK_MASK; 50 + KUNIT_EXPECT_TRUE(test, test_bit(seq_bit, block->seq_nrs[0])); 51 + } 52 + 53 + static void check_prp_frame_unseen(struct kunit *test, 54 + struct prp_test_data *data, u16 sequence_nr) 55 + { 56 + u16 block_idx, seq_bit; 57 + struct hsr_seq_block *block; 58 + 59 + block_idx = sequence_nr >> HSR_SEQ_BLOCK_SHIFT; 60 + block = hsr_get_seq_block(&data->node, block_idx); 61 + KUNIT_EXPECT_NOT_NULL(test, block); 62 + 63 + seq_bit = sequence_nr & HSR_SEQ_BLOCK_MASK; 64 + KUNIT_EXPECT_FALSE(test, test_bit(seq_bit, block->seq_nrs[0])); 49 65 } 50 66 51 67 static void prp_dup_discard_forward(struct kunit *test) ··· 74 54 data->frame.sequence_nr = 2; 75 55 KUNIT_EXPECT_EQ(test, 0, 76 56 prp_register_frame_out(&data->port, &data->frame)); 77 - KUNIT_EXPECT_EQ(test, data->frame.sequence_nr, 78 - data->node.seq_out[HSR_PT_MASTER]); 79 - KUNIT_EXPECT_EQ(test, jiffies, data->node.time_out[HSR_PT_MASTER]); 80 - check_prp_counters(test, data, data->frame.sequence_nr, 81 - data->frame.sequence_nr + 1, 1, 1); 57 + check_prp_frame_seen(test, data, data->frame.sequence_nr); 82 58 } 83 59 84 - static void prp_dup_discard_inside_dropwindow(struct kunit *test) 60 + static void prp_dup_discard_drop_duplicate(struct kunit *test) 85 61 { 86 - /* Normal situation, other LAN ahead by one. Frame is dropped */ 87 62 struct prp_test_data *data = build_prp_test_data(test); 88 - unsigned long time = jiffies - 10; 89 63 90 - data->frame.sequence_nr = 1; 91 - data->node.seq_expected[HSR_PT_SLAVE_B] = 3; 92 - data->node.seq_out[HSR_PT_MASTER] = 2; 93 - data->node.time_out[HSR_PT_MASTER] = time; 64 + data->frame.sequence_nr = 2; 65 + KUNIT_EXPECT_EQ(test, 0, 66 + prp_register_frame_out(&data->port, &data->frame)); 67 + check_prp_frame_seen(test, data, data->frame.sequence_nr); 94 68 95 69 KUNIT_EXPECT_EQ(test, 1, 96 70 prp_register_frame_out(&data->port, &data->frame)); 97 - KUNIT_EXPECT_EQ(test, 2, data->node.seq_out[HSR_PT_MASTER]); 98 - KUNIT_EXPECT_EQ(test, time, data->node.time_out[HSR_PT_MASTER]); 99 - check_prp_counters(test, data, 2, 2, 2, 3); 71 + check_prp_frame_seen(test, data, data->frame.sequence_nr); 100 72 } 101 73 102 - static void prp_dup_discard_node_timeout(struct kunit *test) 74 + static void prp_dup_discard_entry_timeout(struct kunit *test) 103 75 { 104 76 /* Timeout situation, node hasn't sent anything for a while */ 105 77 struct prp_test_data *data = build_prp_test_data(test); 78 + struct hsr_seq_block *block; 79 + u16 block_idx; 106 80 107 81 data->frame.sequence_nr = 7; 108 - data->node.seq_start[HSR_PT_SLAVE_A] = 1234; 109 - data->node.seq_expected[HSR_PT_SLAVE_A] = 1235; 110 - data->node.seq_start[HSR_PT_SLAVE_B] = 1234; 111 - data->node.seq_expected[HSR_PT_SLAVE_B] = 1234; 112 - data->node.seq_out[HSR_PT_MASTER] = 1234; 113 - data->node.time_out[HSR_PT_MASTER] = 114 - jiffies - msecs_to_jiffies(HSR_ENTRY_FORGET_TIME) - 1; 82 + KUNIT_EXPECT_EQ(test, 0, 83 + prp_register_frame_out(&data->port, &data->frame)); 84 + check_prp_frame_seen(test, data, data->frame.sequence_nr); 85 + 86 + data->frame.sequence_nr = 11; 87 + KUNIT_EXPECT_EQ(test, 0, 88 + prp_register_frame_out(&data->port, &data->frame)); 89 + check_prp_frame_seen(test, data, data->frame.sequence_nr); 90 + 91 + block_idx = data->frame.sequence_nr >> HSR_SEQ_BLOCK_SHIFT; 92 + block = hsr_get_seq_block(&data->node, block_idx); 93 + block->time = jiffies - msecs_to_jiffies(HSR_ENTRY_FORGET_TIME) - 1; 115 94 116 95 KUNIT_EXPECT_EQ(test, 0, 117 96 prp_register_frame_out(&data->port, &data->frame)); 118 - KUNIT_EXPECT_EQ(test, data->frame.sequence_nr, 119 - data->node.seq_out[HSR_PT_MASTER]); 120 - KUNIT_EXPECT_EQ(test, jiffies, data->node.time_out[HSR_PT_MASTER]); 121 - check_prp_counters(test, data, data->frame.sequence_nr, 122 - data->frame.sequence_nr + 1, 1234, 1234); 97 + check_prp_frame_seen(test, data, data->frame.sequence_nr); 98 + check_prp_frame_unseen(test, data, 7); 123 99 } 124 100 125 101 static void prp_dup_discard_out_of_sequence(struct kunit *test) ··· 123 107 /* One frame is received out of sequence on both LANs */ 124 108 struct prp_test_data *data = build_prp_test_data(test); 125 109 126 - data->node.seq_start[HSR_PT_SLAVE_A] = 10; 127 - data->node.seq_expected[HSR_PT_SLAVE_A] = 10; 128 - data->node.seq_start[HSR_PT_SLAVE_B] = 10; 129 - data->node.seq_expected[HSR_PT_SLAVE_B] = 10; 130 - data->node.seq_out[HSR_PT_MASTER] = 9; 110 + /* initial frame, should be accepted */ 111 + data->frame.sequence_nr = 9; 112 + KUNIT_EXPECT_EQ(test, 0, 113 + prp_register_frame_out(&data->port, &data->frame)); 114 + check_prp_frame_seen(test, data, data->frame.sequence_nr); 131 115 132 116 /* 1st old frame, should be accepted */ 133 117 data->frame.sequence_nr = 8; 134 118 KUNIT_EXPECT_EQ(test, 0, 135 119 prp_register_frame_out(&data->port, &data->frame)); 136 - KUNIT_EXPECT_EQ(test, data->frame.sequence_nr, 137 - data->node.seq_out[HSR_PT_MASTER]); 138 - check_prp_counters(test, data, data->frame.sequence_nr, 139 - data->frame.sequence_nr + 1, 10, 10); 120 + check_prp_frame_seen(test, data, data->frame.sequence_nr); 140 121 141 122 /* 2nd frame should be dropped */ 142 123 data->frame.sequence_nr = 8; 143 124 data->port_rcv.type = HSR_PT_SLAVE_B; 144 125 KUNIT_EXPECT_EQ(test, 1, 145 126 prp_register_frame_out(&data->port, &data->frame)); 146 - check_prp_counters(test, data, data->frame.sequence_nr + 1, 147 - data->frame.sequence_nr + 1, 148 - data->frame.sequence_nr + 1, 149 - data->frame.sequence_nr + 1); 150 127 151 128 /* Next frame, this is forwarded */ 152 129 data->frame.sequence_nr = 10; 153 130 data->port_rcv.type = HSR_PT_SLAVE_A; 154 131 KUNIT_EXPECT_EQ(test, 0, 155 132 prp_register_frame_out(&data->port, &data->frame)); 156 - KUNIT_EXPECT_EQ(test, data->frame.sequence_nr, 157 - data->node.seq_out[HSR_PT_MASTER]); 158 - check_prp_counters(test, data, data->frame.sequence_nr, 159 - data->frame.sequence_nr + 1, 9, 9); 133 + check_prp_frame_seen(test, data, data->frame.sequence_nr); 160 134 161 135 /* and next one is dropped */ 162 136 data->frame.sequence_nr = 10; 163 137 data->port_rcv.type = HSR_PT_SLAVE_B; 164 138 KUNIT_EXPECT_EQ(test, 1, 165 139 prp_register_frame_out(&data->port, &data->frame)); 166 - check_prp_counters(test, data, data->frame.sequence_nr + 1, 167 - data->frame.sequence_nr + 1, 168 - data->frame.sequence_nr + 1, 169 - data->frame.sequence_nr + 1); 170 140 } 171 141 172 142 static void prp_dup_discard_lan_b_late(struct kunit *test) ··· 160 158 /* LAN B is behind */ 161 159 struct prp_test_data *data = build_prp_test_data(test); 162 160 163 - data->node.seq_start[HSR_PT_SLAVE_A] = 9; 164 - data->node.seq_expected[HSR_PT_SLAVE_A] = 9; 165 - data->node.seq_start[HSR_PT_SLAVE_B] = 9; 166 - data->node.seq_expected[HSR_PT_SLAVE_B] = 9; 167 - data->node.seq_out[HSR_PT_MASTER] = 8; 168 - 169 161 data->frame.sequence_nr = 9; 170 162 KUNIT_EXPECT_EQ(test, 0, 171 163 prp_register_frame_out(&data->port, &data->frame)); 172 - KUNIT_EXPECT_EQ(test, data->frame.sequence_nr, 173 - data->node.seq_out[HSR_PT_MASTER]); 174 - check_prp_counters(test, data, 9, 10, 9, 9); 164 + check_prp_frame_seen(test, data, data->frame.sequence_nr); 175 165 176 166 data->frame.sequence_nr = 10; 177 167 KUNIT_EXPECT_EQ(test, 0, 178 168 prp_register_frame_out(&data->port, &data->frame)); 179 - KUNIT_EXPECT_EQ(test, data->frame.sequence_nr, 180 - data->node.seq_out[HSR_PT_MASTER]); 181 - check_prp_counters(test, data, 9, 11, 9, 9); 169 + check_prp_frame_seen(test, data, data->frame.sequence_nr); 182 170 183 171 data->frame.sequence_nr = 9; 184 172 data->port_rcv.type = HSR_PT_SLAVE_B; 185 173 KUNIT_EXPECT_EQ(test, 1, 186 174 prp_register_frame_out(&data->port, &data->frame)); 187 - check_prp_counters(test, data, 10, 11, 10, 10); 188 175 189 176 data->frame.sequence_nr = 10; 190 177 data->port_rcv.type = HSR_PT_SLAVE_B; 191 178 KUNIT_EXPECT_EQ(test, 1, 192 179 prp_register_frame_out(&data->port, &data->frame)); 193 - check_prp_counters(test, data, 11, 11, 11, 11); 194 180 } 195 181 196 182 static struct kunit_case prp_dup_discard_test_cases[] = { 197 183 KUNIT_CASE(prp_dup_discard_forward), 198 - KUNIT_CASE(prp_dup_discard_inside_dropwindow), 199 - KUNIT_CASE(prp_dup_discard_node_timeout), 184 + KUNIT_CASE(prp_dup_discard_drop_duplicate), 185 + KUNIT_CASE(prp_dup_discard_entry_timeout), 200 186 KUNIT_CASE(prp_dup_discard_out_of_sequence), 201 187 KUNIT_CASE(prp_dup_discard_lan_b_late), 202 188 {}
+2
tools/testing/selftests/net/hsr/Makefile
··· 5 5 TEST_PROGS := \ 6 6 hsr_ping.sh \ 7 7 hsr_redbox.sh \ 8 + link_faults.sh \ 9 + prp_ping.sh \ 8 10 # end of TEST_PROGS 9 11 10 12 TEST_FILES += hsr_common.sh
+60 -143
tools/testing/selftests/net/hsr/hsr_ping.sh
··· 27 27 esac 28 28 done 29 29 30 - do_complete_ping_test() 30 + do_ping_tests() 31 31 { 32 + local netid="$1" 33 + 34 + echo "INFO: Running ping tests." 35 + 32 36 echo "INFO: Initial validation ping." 33 - # Each node has to be able each one. 34 - do_ping "$ns1" 100.64.0.2 35 - do_ping "$ns2" 100.64.0.1 36 - do_ping "$ns3" 100.64.0.1 37 - stop_if_error "Initial validation failed." 37 + # Each node has to be able to reach each one. 38 + do_ping "$ns1" "100.64.$netid.2" 39 + do_ping "$ns1" "100.64.$netid.3" 40 + do_ping "$ns2" "100.64.$netid.1" 41 + do_ping "$ns2" "100.64.$netid.3" 42 + do_ping "$ns3" "100.64.$netid.1" 43 + do_ping "$ns3" "100.64.$netid.2" 44 + stop_if_error "Initial validation failed on IPv4." 38 45 39 - do_ping "$ns1" 100.64.0.3 40 - do_ping "$ns2" 100.64.0.3 41 - do_ping "$ns3" 100.64.0.2 42 - 43 - do_ping "$ns1" dead:beef:1::2 44 - do_ping "$ns1" dead:beef:1::3 45 - do_ping "$ns2" dead:beef:1::1 46 - do_ping "$ns2" dead:beef:1::2 47 - do_ping "$ns3" dead:beef:1::1 48 - do_ping "$ns3" dead:beef:1::2 49 - 50 - stop_if_error "Initial validation failed." 46 + do_ping "$ns1" "dead:beef:$netid::2" 47 + do_ping "$ns1" "dead:beef:$netid::3" 48 + do_ping "$ns2" "dead:beef:$netid::1" 49 + do_ping "$ns2" "dead:beef:$netid::2" 50 + do_ping "$ns3" "dead:beef:$netid::1" 51 + do_ping "$ns3" "dead:beef:$netid::2" 52 + stop_if_error "Initial validation failed on IPv6." 51 53 52 54 # Wait until supervisor all supervision frames have been processed and the node 53 55 # entries have been merged. Otherwise duplicate frames will be observed which is 54 56 # valid at this stage. 57 + echo "INFO: Wait for node table entries to be merged." 55 58 WAIT=5 56 59 while [ ${WAIT} -gt 0 ] 57 60 do ··· 71 68 sleep 1 72 69 73 70 echo "INFO: Longer ping test." 74 - do_ping_long "$ns1" 100.64.0.2 75 - do_ping_long "$ns1" dead:beef:1::2 76 - do_ping_long "$ns1" 100.64.0.3 77 - do_ping_long "$ns1" dead:beef:1::3 71 + do_ping_long "$ns1" "100.64.$netid.2" 72 + do_ping_long "$ns1" "dead:beef:$netid::2" 73 + do_ping_long "$ns1" "100.64.$netid.3" 74 + do_ping_long "$ns1" "dead:beef:$netid::3" 75 + stop_if_error "Longer ping test failed (ns1)." 78 76 79 - stop_if_error "Longer ping test failed." 77 + do_ping_long "$ns2" "100.64.$netid.1" 78 + do_ping_long "$ns2" "dead:beef:$netid::1" 79 + do_ping_long "$ns2" "100.64.$netid.3" 80 + do_ping_long "$ns2" "dead:beef:$netid::3" 81 + stop_if_error "Longer ping test failed (ns2)." 80 82 81 - do_ping_long "$ns2" 100.64.0.1 82 - do_ping_long "$ns2" dead:beef:1::1 83 - do_ping_long "$ns2" 100.64.0.3 84 - do_ping_long "$ns2" dead:beef:1::2 85 - stop_if_error "Longer ping test failed." 86 - 87 - do_ping_long "$ns3" 100.64.0.1 88 - do_ping_long "$ns3" dead:beef:1::1 89 - do_ping_long "$ns3" 100.64.0.2 90 - do_ping_long "$ns3" dead:beef:1::2 91 - stop_if_error "Longer ping test failed." 92 - 93 - echo "INFO: Cutting one link." 94 - do_ping_long "$ns1" 100.64.0.3 & 95 - 96 - sleep 3 97 - ip -net "$ns3" link set ns3eth1 down 98 - wait 99 - 100 - ip -net "$ns3" link set ns3eth1 up 101 - 102 - stop_if_error "Failed with one link down." 103 - 104 - echo "INFO: Delay the link and drop a few packages." 105 - tc -net "$ns3" qdisc add dev ns3eth1 root netem delay 50ms 106 - tc -net "$ns2" qdisc add dev ns2eth1 root netem delay 5ms loss 25% 107 - 108 - do_ping_long "$ns1" 100.64.0.2 109 - do_ping_long "$ns1" 100.64.0.3 110 - 111 - stop_if_error "Failed with delay and packetloss." 112 - 113 - do_ping_long "$ns2" 100.64.0.1 114 - do_ping_long "$ns2" 100.64.0.3 115 - 116 - stop_if_error "Failed with delay and packetloss." 117 - 118 - do_ping_long "$ns3" 100.64.0.1 119 - do_ping_long "$ns3" 100.64.0.2 120 - stop_if_error "Failed with delay and packetloss." 121 - 122 - echo "INFO: All good." 83 + do_ping_long "$ns3" "100.64.$netid.1" 84 + do_ping_long "$ns3" "dead:beef:$netid::1" 85 + do_ping_long "$ns3" "100.64.$netid.2" 86 + do_ping_long "$ns3" "dead:beef:$netid::2" 87 + stop_if_error "Longer ping test failed (ns3)." 123 88 } 124 89 125 90 setup_hsr_interfaces() 126 91 { 127 92 local HSRv="$1" 128 93 129 - echo "INFO: preparing interfaces for HSRv${HSRv}." 94 + echo "INFO: Preparing interfaces for HSRv${HSRv}." 130 95 # Three HSR nodes. Each node has one link to each of its neighbour, two links in total. 131 96 # 132 97 # ns1eth1 ----- ns2eth1 ··· 111 140 ip link add ns3eth2 netns "$ns3" type veth peer name ns2eth2 netns "$ns2" 112 141 113 142 # HSRv0/1 114 - ip -net "$ns1" link add name hsr1 type hsr slave1 ns1eth1 slave2 ns1eth2 supervision 45 version $HSRv proto 0 115 - ip -net "$ns2" link add name hsr2 type hsr slave1 ns2eth1 slave2 ns2eth2 supervision 45 version $HSRv proto 0 116 - ip -net "$ns3" link add name hsr3 type hsr slave1 ns3eth1 slave2 ns3eth2 supervision 45 version $HSRv proto 0 143 + ip -net "$ns1" link add name hsr1 type hsr slave1 ns1eth1 \ 144 + slave2 ns1eth2 supervision 45 version "$HSRv" proto 0 145 + ip -net "$ns2" link add name hsr2 type hsr slave1 ns2eth1 \ 146 + slave2 ns2eth2 supervision 45 version "$HSRv" proto 0 147 + ip -net "$ns3" link add name hsr3 type hsr slave1 ns3eth1 \ 148 + slave2 ns3eth2 supervision 45 version "$HSRv" proto 0 117 149 118 150 # IP for HSR 119 151 ip -net "$ns1" addr add 100.64.0.1/24 dev hsr1 120 - ip -net "$ns1" addr add dead:beef:1::1/64 dev hsr1 nodad 152 + ip -net "$ns1" addr add dead:beef:0::1/64 dev hsr1 nodad 121 153 ip -net "$ns2" addr add 100.64.0.2/24 dev hsr2 122 - ip -net "$ns2" addr add dead:beef:1::2/64 dev hsr2 nodad 154 + ip -net "$ns2" addr add dead:beef:0::2/64 dev hsr2 nodad 123 155 ip -net "$ns3" addr add 100.64.0.3/24 dev hsr3 124 - ip -net "$ns3" addr add dead:beef:1::3/64 dev hsr3 nodad 156 + ip -net "$ns3" addr add dead:beef:0::3/64 dev hsr3 nodad 125 157 126 158 ip -net "$ns1" link set address 00:11:22:00:01:01 dev ns1eth1 127 159 ip -net "$ns1" link set address 00:11:22:00:01:02 dev ns1eth2 ··· 151 177 152 178 setup_vlan_interfaces() { 153 179 ip -net "$ns1" link add link hsr1 name hsr1.2 type vlan id 2 154 - ip -net "$ns1" link add link hsr1 name hsr1.3 type vlan id 3 155 - ip -net "$ns1" link add link hsr1 name hsr1.4 type vlan id 4 156 - ip -net "$ns1" link add link hsr1 name hsr1.5 type vlan id 5 157 - 158 180 ip -net "$ns2" link add link hsr2 name hsr2.2 type vlan id 2 159 - ip -net "$ns2" link add link hsr2 name hsr2.3 type vlan id 3 160 - ip -net "$ns2" link add link hsr2 name hsr2.4 type vlan id 4 161 - ip -net "$ns2" link add link hsr2 name hsr2.5 type vlan id 5 162 - 163 181 ip -net "$ns3" link add link hsr3 name hsr3.2 type vlan id 2 164 - ip -net "$ns3" link add link hsr3 name hsr3.3 type vlan id 3 165 - ip -net "$ns3" link add link hsr3 name hsr3.4 type vlan id 4 166 - ip -net "$ns3" link add link hsr3 name hsr3.5 type vlan id 5 167 182 168 183 ip -net "$ns1" addr add 100.64.2.1/24 dev hsr1.2 169 - ip -net "$ns1" addr add 100.64.3.1/24 dev hsr1.3 170 - ip -net "$ns1" addr add 100.64.4.1/24 dev hsr1.4 171 - ip -net "$ns1" addr add 100.64.5.1/24 dev hsr1.5 184 + ip -net "$ns1" addr add dead:beef:2::1/64 dev hsr1.2 nodad 172 185 173 186 ip -net "$ns2" addr add 100.64.2.2/24 dev hsr2.2 174 - ip -net "$ns2" addr add 100.64.3.2/24 dev hsr2.3 175 - ip -net "$ns2" addr add 100.64.4.2/24 dev hsr2.4 176 - ip -net "$ns2" addr add 100.64.5.2/24 dev hsr2.5 187 + ip -net "$ns2" addr add dead:beef:2::2/64 dev hsr2.2 nodad 177 188 178 189 ip -net "$ns3" addr add 100.64.2.3/24 dev hsr3.2 179 - ip -net "$ns3" addr add 100.64.3.3/24 dev hsr3.3 180 - ip -net "$ns3" addr add 100.64.4.3/24 dev hsr3.4 181 - ip -net "$ns3" addr add 100.64.5.3/24 dev hsr3.5 190 + ip -net "$ns3" addr add dead:beef:2::3/64 dev hsr3.2 nodad 182 191 183 192 ip -net "$ns1" link set dev hsr1.2 up 184 - ip -net "$ns1" link set dev hsr1.3 up 185 - ip -net "$ns1" link set dev hsr1.4 up 186 - ip -net "$ns1" link set dev hsr1.5 up 187 - 188 193 ip -net "$ns2" link set dev hsr2.2 up 189 - ip -net "$ns2" link set dev hsr2.3 up 190 - ip -net "$ns2" link set dev hsr2.4 up 191 - ip -net "$ns2" link set dev hsr2.5 up 192 - 193 194 ip -net "$ns3" link set dev hsr3.2 up 194 - ip -net "$ns3" link set dev hsr3.3 up 195 - ip -net "$ns3" link set dev hsr3.4 up 196 - ip -net "$ns3" link set dev hsr3.5 up 197 195 198 196 } 199 197 200 - hsr_vlan_ping() { 201 - do_ping "$ns1" 100.64.2.2 202 - do_ping "$ns1" 100.64.3.2 203 - do_ping "$ns1" 100.64.4.2 204 - do_ping "$ns1" 100.64.5.2 205 - 206 - do_ping "$ns1" 100.64.2.3 207 - do_ping "$ns1" 100.64.3.3 208 - do_ping "$ns1" 100.64.4.3 209 - do_ping "$ns1" 100.64.5.3 210 - 211 - do_ping "$ns2" 100.64.2.1 212 - do_ping "$ns2" 100.64.3.1 213 - do_ping "$ns2" 100.64.4.1 214 - do_ping "$ns2" 100.64.5.1 215 - 216 - do_ping "$ns2" 100.64.2.3 217 - do_ping "$ns2" 100.64.3.3 218 - do_ping "$ns2" 100.64.4.3 219 - do_ping "$ns2" 100.64.5.3 220 - 221 - do_ping "$ns3" 100.64.2.1 222 - do_ping "$ns3" 100.64.3.1 223 - do_ping "$ns3" 100.64.4.1 224 - do_ping "$ns3" 100.64.5.1 225 - 226 - do_ping "$ns3" 100.64.2.2 227 - do_ping "$ns3" 100.64.3.2 228 - do_ping "$ns3" 100.64.4.2 229 - do_ping "$ns3" 100.64.5.2 198 + run_ping_tests() 199 + { 200 + echo "INFO: Running ping tests." 201 + do_ping_tests 0 230 202 } 231 203 232 - run_vlan_tests() { 204 + run_vlan_tests() 205 + { 233 206 vlan_challenged_hsr1=$(ip net exec "$ns1" ethtool -k hsr1 | grep "vlan-challenged" | awk '{print $2}') 234 207 vlan_challenged_hsr2=$(ip net exec "$ns2" ethtool -k hsr2 | grep "vlan-challenged" | awk '{print $2}') 235 208 vlan_challenged_hsr3=$(ip net exec "$ns3" ethtool -k hsr3 | grep "vlan-challenged" | awk '{print $2}') 236 209 237 210 if [[ "$vlan_challenged_hsr1" = "off" || "$vlan_challenged_hsr2" = "off" || "$vlan_challenged_hsr3" = "off" ]]; then 238 - echo "INFO: Running VLAN tests" 211 + echo "INFO: Running VLAN ping tests" 239 212 setup_vlan_interfaces 240 - hsr_vlan_ping 213 + do_ping_tests 2 241 214 else 242 215 echo "INFO: Not Running VLAN tests as the device does not support VLAN" 243 216 fi 244 217 } 245 218 246 219 check_prerequisites 247 - setup_ns ns1 ns2 ns3 248 - 249 220 trap cleanup_all_ns EXIT 250 221 222 + setup_ns ns1 ns2 ns3 251 223 setup_hsr_interfaces 0 252 - do_complete_ping_test 253 - 224 + run_ping_tests 254 225 run_vlan_tests 255 226 256 227 setup_ns ns1 ns2 ns3 257 - 258 228 setup_hsr_interfaces 1 259 - do_complete_ping_test 260 - 229 + run_ping_tests 261 230 run_vlan_tests 262 231 263 232 exit $ret
+147
tools/testing/selftests/net/hsr/prp_ping.sh
··· 1 + #!/bin/bash 2 + # SPDX-License-Identifier: GPL-2.0 3 + 4 + ipv6=true 5 + 6 + source ./hsr_common.sh 7 + 8 + optstring="h4" 9 + usage() { 10 + echo "Usage: $0 [OPTION]" 11 + echo -e "\t-4: IPv4 only: disable IPv6 tests (default: test both IPv4 and IPv6)" 12 + } 13 + 14 + while getopts "$optstring" option;do 15 + case "$option" in 16 + "h") 17 + usage "$0" 18 + exit 0 19 + ;; 20 + "4") 21 + ipv6=false 22 + ;; 23 + "?") 24 + usage "$0" 25 + exit 1 26 + ;; 27 + esac 28 + done 29 + 30 + setup_prp_interfaces() 31 + { 32 + echo "INFO: Preparing interfaces for PRP" 33 + # Two PRP nodes, connected by two links (treated as LAN A and LAN B). 34 + # 35 + # vethA ----- vethA 36 + # prp1 prp2 37 + # vethB ----- vethB 38 + # 39 + # node1 node2 40 + 41 + # Interfaces 42 + # shellcheck disable=SC2154 # variables assigned by setup_ns 43 + ip link add vethA netns "$node1" type veth peer name vethA netns "$node2" 44 + ip link add vethB netns "$node1" type veth peer name vethB netns "$node2" 45 + 46 + # MAC addresses will be copied from LAN A interface 47 + ip -net "$node1" link set address 00:11:22:00:00:01 dev vethA 48 + ip -net "$node2" link set address 00:11:22:00:00:02 dev vethA 49 + 50 + # PRP 51 + ip -net "$node1" link add name prp1 type hsr \ 52 + slave1 vethA slave2 vethB supervision 45 proto 1 53 + ip -net "$node2" link add name prp2 type hsr \ 54 + slave1 vethA slave2 vethB supervision 45 proto 1 55 + 56 + # IP addresses 57 + ip -net "$node1" addr add 100.64.0.1/24 dev prp1 58 + ip -net "$node1" addr add dead:beef:0::1/64 dev prp1 nodad 59 + ip -net "$node2" addr add 100.64.0.2/24 dev prp2 60 + ip -net "$node2" addr add dead:beef:0::2/64 dev prp2 nodad 61 + 62 + # All links up 63 + ip -net "$node1" link set vethA up 64 + ip -net "$node1" link set vethB up 65 + ip -net "$node1" link set prp1 up 66 + 67 + ip -net "$node2" link set vethA up 68 + ip -net "$node2" link set vethB up 69 + ip -net "$node2" link set prp2 up 70 + } 71 + 72 + setup_vlan_interfaces() 73 + { 74 + # Interfaces 75 + ip -net "$node1" link add link prp1 name prp1.2 type vlan id 2 76 + ip -net "$node2" link add link prp2 name prp2.2 type vlan id 2 77 + 78 + # IP addresses 79 + ip -net "$node1" addr add 100.64.2.1/24 dev prp1.2 80 + ip -net "$node1" addr add dead:beef:2::1/64 dev prp1.2 nodad 81 + 82 + ip -net "$node2" addr add 100.64.2.2/24 dev prp2.2 83 + ip -net "$node2" addr add dead:beef:2::2/64 dev prp2.2 nodad 84 + 85 + # All links up 86 + ip -net "$node1" link set prp1.2 up 87 + ip -net "$node2" link set prp2.2 up 88 + } 89 + 90 + do_ping_tests() 91 + { 92 + local netid="$1" 93 + 94 + echo "INFO: Initial validation ping" 95 + 96 + do_ping "$node1" "100.64.$netid.2" 97 + do_ping "$node2" "100.64.$netid.1" 98 + stop_if_error "Initial validation failed on IPv4" 99 + 100 + do_ping "$node1" "dead:beef:$netid::2" 101 + do_ping "$node2" "dead:beef:$netid::1" 102 + stop_if_error "Initial validation failed on IPv6" 103 + 104 + echo "INFO: Longer ping test." 105 + 106 + do_ping_long "$node1" "100.64.$netid.2" 107 + do_ping_long "$node2" "100.64.$netid.1" 108 + stop_if_error "Longer ping test failed on IPv4." 109 + 110 + do_ping_long "$node1" "dead:beef:$netid::2" 111 + do_ping_long "$node2" "dead:beef:$netid::1" 112 + stop_if_error "Longer ping test failed on IPv6." 113 + } 114 + 115 + run_ping_tests() 116 + { 117 + echo "INFO: Running ping tests" 118 + do_ping_tests 0 119 + } 120 + 121 + run_vlan_ping_tests() 122 + { 123 + vlan_challenged_prp1=$(ip net exec "$node1" ethtool -k prp1 | \ 124 + grep "vlan-challenged" | awk '{print $2}') 125 + vlan_challenged_prp2=$(ip net exec "$node2" ethtool -k prp2 | \ 126 + grep "vlan-challenged" | awk '{print $2}') 127 + 128 + if [[ "$vlan_challenged_prp1" = "off" || \ 129 + "$vlan_challenged_prp2" = "off" ]]; then 130 + echo "INFO: Running VLAN ping tests" 131 + setup_vlan_interfaces 132 + do_ping_tests 2 133 + else 134 + echo "INFO: Not Running VLAN tests as the device does not support VLAN" 135 + fi 136 + } 137 + 138 + check_prerequisites 139 + trap cleanup_all_ns EXIT 140 + 141 + setup_ns node1 node2 142 + setup_prp_interfaces 143 + 144 + run_ping_tests 145 + run_vlan_ping_tests 146 + 147 + exit $ret
+1 -1
tools/testing/selftests/net/hsr/settings
··· 1 - timeout=50 1 + timeout=180