From 70ebe4a47185db15f3c55be9611a1a971237870b Mon Sep 17 00:00:00 2001 From: Arvid Brodin Date: Fri, 4 Jul 2014 23:34:38 +0200 Subject: net/hsr: Better variable names and update of contact info. Signed-off-by: Arvid Brodin Signed-off-by: David S. Miller --- net/hsr/hsr_main.c | 170 ++++++++++++++++++++++++++--------------------------- 1 file changed, 85 insertions(+), 85 deletions(-) (limited to 'net/hsr/hsr_main.c') diff --git a/net/hsr/hsr_main.c b/net/hsr/hsr_main.c index 3fee5218a691..99b8fc4eca6c 100644 --- a/net/hsr/hsr_main.c +++ b/net/hsr/hsr_main.c @@ -1,4 +1,4 @@ -/* Copyright 2011-2013 Autronica Fire and Security AS +/* Copyright 2011-2014 Autronica Fire and Security AS * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -6,7 +6,7 @@ * any later version. * * Author(s): - * 2011-2013 Arvid Brodin, arvid.brodin@xdin.com + * 2011-2014 Arvid Brodin, arvid.brodin@alten.se * * In addition to routines for registering and unregistering HSR support, this * file also contains the receive routine that handles all incoming frames with @@ -26,30 +26,30 @@ /* List of all registered virtual HSR devices */ static LIST_HEAD(hsr_list); -void register_hsr_master(struct hsr_priv *hsr_priv) +void register_hsr_master(struct hsr_priv *hsr) { - list_add_tail_rcu(&hsr_priv->hsr_list, &hsr_list); + list_add_tail_rcu(&hsr->hsr_list, &hsr_list); } -void unregister_hsr_master(struct hsr_priv *hsr_priv) +void unregister_hsr_master(struct hsr_priv *hsr) { - struct hsr_priv *hsr_priv_it; + struct hsr_priv *hsr_it; - list_for_each_entry(hsr_priv_it, &hsr_list, hsr_list) - if (hsr_priv_it == hsr_priv) { - list_del_rcu(&hsr_priv_it->hsr_list); + list_for_each_entry(hsr_it, &hsr_list, hsr_list) + if (hsr_it == hsr) { + list_del_rcu(&hsr_it->hsr_list); return; } } bool is_hsr_slave(struct net_device *dev) { - struct hsr_priv *hsr_priv_it; + struct hsr_priv *hsr_it; - list_for_each_entry_rcu(hsr_priv_it, &hsr_list, hsr_list) { - if (dev == hsr_priv_it->slave[0]) + list_for_each_entry_rcu(hsr_it, &hsr_list, hsr_list) { + if (dev == hsr_it->slave[0]) return true; - if (dev == hsr_priv_it->slave[1]) + if (dev == hsr_it->slave[1]) return true; } @@ -62,14 +62,14 @@ bool is_hsr_slave(struct net_device *dev) */ static struct hsr_priv *get_hsr_master(struct net_device *dev) { - struct hsr_priv *hsr_priv; + struct hsr_priv *hsr; rcu_read_lock(); - list_for_each_entry_rcu(hsr_priv, &hsr_list, hsr_list) - if ((dev == hsr_priv->slave[0]) || - (dev == hsr_priv->slave[1])) { + list_for_each_entry_rcu(hsr, &hsr_list, hsr_list) + if ((dev == hsr->slave[0]) || + (dev == hsr->slave[1])) { rcu_read_unlock(); - return hsr_priv; + return hsr; } rcu_read_unlock(); @@ -80,13 +80,13 @@ static struct hsr_priv *get_hsr_master(struct net_device *dev) /* If dev is a HSR slave device, return the other slave device. Return NULL * otherwise. */ -static struct net_device *get_other_slave(struct hsr_priv *hsr_priv, +static struct net_device *get_other_slave(struct hsr_priv *hsr, struct net_device *dev) { - if (dev == hsr_priv->slave[0]) - return hsr_priv->slave[1]; - if (dev == hsr_priv->slave[1]) - return hsr_priv->slave[0]; + if (dev == hsr->slave[0]) + return hsr->slave[1]; + if (dev == hsr->slave[1]) + return hsr->slave[0]; return NULL; } @@ -96,7 +96,7 @@ static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event, void *ptr) { struct net_device *slave, *other_slave; - struct hsr_priv *hsr_priv; + struct hsr_priv *hsr; int old_operstate; int mtu_max; int res; @@ -104,68 +104,68 @@ static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event, dev = netdev_notifier_info_to_dev(ptr); - hsr_priv = get_hsr_master(dev); - if (hsr_priv) { + hsr = get_hsr_master(dev); + if (hsr) { /* dev is a slave device */ slave = dev; - other_slave = get_other_slave(hsr_priv, slave); + other_slave = get_other_slave(hsr, slave); } else { if (!is_hsr_master(dev)) return NOTIFY_DONE; - hsr_priv = netdev_priv(dev); - slave = hsr_priv->slave[0]; - other_slave = hsr_priv->slave[1]; + hsr = netdev_priv(dev); + slave = hsr->slave[0]; + other_slave = hsr->slave[1]; } switch (event) { case NETDEV_UP: /* Administrative state DOWN */ case NETDEV_DOWN: /* Administrative state UP */ case NETDEV_CHANGE: /* Link (carrier) state changes */ - old_operstate = hsr_priv->dev->operstate; - hsr_set_carrier(hsr_priv->dev, slave, other_slave); + old_operstate = hsr->dev->operstate; + hsr_set_carrier(hsr->dev, slave, other_slave); /* netif_stacked_transfer_operstate() cannot be used here since * it doesn't set IF_OPER_LOWERLAYERDOWN (?) */ - hsr_set_operstate(hsr_priv->dev, slave, other_slave); - hsr_check_announce(hsr_priv->dev, old_operstate); + hsr_set_operstate(hsr->dev, slave, other_slave); + hsr_check_announce(hsr->dev, old_operstate); break; case NETDEV_CHANGEADDR: /* This should not happen since there's no ndo_set_mac_address() * for HSR devices - i.e. not supported. */ - if (dev == hsr_priv->dev) + if (dev == hsr->dev) break; - if (dev == hsr_priv->slave[0]) - ether_addr_copy(hsr_priv->dev->dev_addr, - hsr_priv->slave[0]->dev_addr); + if (dev == hsr->slave[0]) + ether_addr_copy(hsr->dev->dev_addr, + hsr->slave[0]->dev_addr); /* Make sure we recognize frames from ourselves in hsr_rcv() */ - res = hsr_create_self_node(&hsr_priv->self_node_db, - hsr_priv->dev->dev_addr, - hsr_priv->slave[1] ? - hsr_priv->slave[1]->dev_addr : - hsr_priv->dev->dev_addr); + res = hsr_create_self_node(&hsr->self_node_db, + hsr->dev->dev_addr, + hsr->slave[1] ? + hsr->slave[1]->dev_addr : + hsr->dev->dev_addr); if (res) - netdev_warn(hsr_priv->dev, + netdev_warn(hsr->dev, "Could not update HSR node address.\n"); - if (dev == hsr_priv->slave[0]) - call_netdevice_notifiers(NETDEV_CHANGEADDR, hsr_priv->dev); + if (dev == hsr->slave[0]) + call_netdevice_notifiers(NETDEV_CHANGEADDR, hsr->dev); break; case NETDEV_CHANGEMTU: - if (dev == hsr_priv->dev) + if (dev == hsr->dev) break; /* Handled in ndo_change_mtu() */ - mtu_max = hsr_get_max_mtu(hsr_priv); - if (hsr_priv->dev->mtu > mtu_max) - dev_set_mtu(hsr_priv->dev, mtu_max); + mtu_max = hsr_get_max_mtu(hsr); + if (hsr->dev->mtu > mtu_max) + dev_set_mtu(hsr->dev, mtu_max); break; case NETDEV_UNREGISTER: - if (dev == hsr_priv->slave[0]) - hsr_priv->slave[0] = NULL; - if (dev == hsr_priv->slave[1]) - hsr_priv->slave[1] = NULL; + if (dev == hsr->slave[0]) + hsr->slave[0] = NULL; + if (dev == hsr->slave[1]) + hsr->slave[1] = NULL; /* There should really be a way to set a new slave device... */ @@ -185,11 +185,11 @@ static struct timer_list prune_timer; static void prune_nodes_all(unsigned long data) { - struct hsr_priv *hsr_priv; + struct hsr_priv *hsr; rcu_read_lock(); - list_for_each_entry_rcu(hsr_priv, &hsr_list, hsr_list) - hsr_prune_nodes(hsr_priv); + list_for_each_entry_rcu(hsr, &hsr_list, hsr_list) + hsr_prune_nodes(hsr); rcu_read_unlock(); prune_timer.expires = jiffies + msecs_to_jiffies(PRUNE_PERIOD); @@ -207,12 +207,12 @@ static struct sk_buff *hsr_pull_tag(struct sk_buff *skb) goto err_free; skb = skb2; - if (unlikely(!pskb_may_pull(skb, HSR_TAGLEN))) + if (unlikely(!pskb_may_pull(skb, HSR_HLEN))) goto err_free; hsr_tag = (struct hsr_tag *) skb->data; skb->protocol = hsr_tag->encap_proto; - skb_pull(skb, HSR_TAGLEN); + skb_pull(skb, HSR_HLEN); return skb; @@ -237,12 +237,12 @@ err_free: * 3) Allow different MAC addresses for the two slave interfaces, using the * MacAddressA field. */ -static bool is_supervision_frame(struct hsr_priv *hsr_priv, struct sk_buff *skb) +static bool is_supervision_frame(struct hsr_priv *hsr, struct sk_buff *skb) { struct hsr_sup_tag *hsr_stag; if (!ether_addr_equal(eth_hdr(skb)->h_dest, - hsr_priv->sup_multicast_addr)) + hsr->sup_multicast_addr)) return false; hsr_stag = (struct hsr_sup_tag *) skb->data; @@ -263,25 +263,25 @@ static bool is_supervision_frame(struct hsr_priv *hsr_priv, struct sk_buff *skb) static int hsr_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { - struct hsr_priv *hsr_priv; + struct hsr_priv *hsr; struct net_device *other_slave; - struct node_entry *node; + struct hsr_node *node; bool deliver_to_self; struct sk_buff *skb_deliver; enum hsr_dev_idx dev_in_idx, dev_other_idx; bool dup_out; int ret; - hsr_priv = get_hsr_master(dev); + hsr = get_hsr_master(dev); - if (!hsr_priv) { + if (!hsr) { /* Non-HSR-slave device 'dev' is connected to a HSR network */ kfree_skb(skb); dev->stats.rx_errors++; return NET_RX_SUCCESS; } - if (dev == hsr_priv->slave[0]) { + if (dev == hsr->slave[0]) { dev_in_idx = HSR_DEV_SLAVE_A; dev_other_idx = HSR_DEV_SLAVE_B; } else { @@ -289,7 +289,7 @@ static int hsr_rcv(struct sk_buff *skb, struct net_device *dev, dev_other_idx = HSR_DEV_SLAVE_A; } - node = hsr_find_node(&hsr_priv->self_node_db, skb); + node = hsr_find_node(&hsr->self_node_db, skb); if (node) { /* Always kill frames sent by ourselves */ kfree_skb(skb); @@ -303,22 +303,22 @@ static int hsr_rcv(struct sk_buff *skb, struct net_device *dev, (skb->pkt_type == PACKET_BROADCAST)) deliver_to_self = true; else if (ether_addr_equal(eth_hdr(skb)->h_dest, - hsr_priv->dev->dev_addr)) { + hsr->dev->dev_addr)) { skb->pkt_type = PACKET_HOST; deliver_to_self = true; } rcu_read_lock(); /* node_db */ - node = hsr_find_node(&hsr_priv->node_db, skb); + node = hsr_find_node(&hsr->node_db, skb); - if (is_supervision_frame(hsr_priv, skb)) { + if (is_supervision_frame(hsr, skb)) { skb_pull(skb, sizeof(struct hsr_sup_tag)); - node = hsr_merge_node(hsr_priv, node, skb, dev_in_idx); + node = hsr_merge_node(hsr, node, skb, dev_in_idx); if (!node) { rcu_read_unlock(); /* node_db */ kfree_skb(skb); - hsr_priv->dev->stats.rx_dropped++; + hsr->dev->stats.rx_dropped++; return NET_RX_DROP; } skb_push(skb, sizeof(struct hsr_sup_tag)); @@ -345,7 +345,7 @@ static int hsr_rcv(struct sk_buff *skb, struct net_device *dev, /* Forward this frame? */ if (!dup_out && (skb->pkt_type != PACKET_HOST)) - other_slave = get_other_slave(hsr_priv, dev); + other_slave = get_other_slave(hsr, dev); else other_slave = NULL; @@ -368,7 +368,7 @@ static int hsr_rcv(struct sk_buff *skb, struct net_device *dev, skb_deliver = pskb_copy(skb, GFP_ATOMIC); if (!skb_deliver) { deliver_to_self = false; - hsr_priv->dev->stats.rx_dropped++; + hsr->dev->stats.rx_dropped++; } } @@ -377,7 +377,7 @@ static int hsr_rcv(struct sk_buff *skb, struct net_device *dev, skb_deliver = hsr_pull_tag(skb_deliver); if (!skb_deliver) { - hsr_priv->dev->stats.rx_dropped++; + hsr->dev->stats.rx_dropped++; goto forward; } #if !defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) @@ -386,7 +386,7 @@ static int hsr_rcv(struct sk_buff *skb, struct net_device *dev, * tag. In practice, this removes/overwrites the HSR tag in * the header and restores a "standard" packet. */ - memmove(skb_deliver->data - HSR_TAGLEN, skb_deliver->data, + memmove(skb_deliver->data - HSR_HLEN, skb_deliver->data, skb_headlen(skb_deliver)); /* Adjust skb members so they correspond with the move above. @@ -397,20 +397,20 @@ static int hsr_rcv(struct sk_buff *skb, struct net_device *dev, * the mac header nor the head. So we only need to adjust data * and tail: */ - skb_deliver->data -= HSR_TAGLEN; - skb_deliver->tail -= HSR_TAGLEN; + skb_deliver->data -= HSR_HLEN; + skb_deliver->tail -= HSR_HLEN; #endif - skb_deliver->dev = hsr_priv->dev; - hsr_addr_subst_source(hsr_priv, skb_deliver); + skb_deliver->dev = hsr->dev; + hsr_addr_subst_source(hsr, skb_deliver); multicast_frame = (skb_deliver->pkt_type == PACKET_MULTICAST); ret = netif_rx(skb_deliver); if (ret == NET_RX_DROP) { - hsr_priv->dev->stats.rx_dropped++; + hsr->dev->stats.rx_dropped++; } else { - hsr_priv->dev->stats.rx_packets++; - hsr_priv->dev->stats.rx_bytes += skb->len; + hsr->dev->stats.rx_packets++; + hsr->dev->stats.rx_bytes += skb->len; if (multicast_frame) - hsr_priv->dev->stats.multicast++; + hsr->dev->stats.multicast++; } } @@ -439,7 +439,7 @@ static int __init hsr_init(void) { int res; - BUILD_BUG_ON(sizeof(struct hsr_tag) != HSR_TAGLEN); + BUILD_BUG_ON(sizeof(struct hsr_tag) != HSR_HLEN); dev_add_pack(&hsr_pt); -- cgit v1.2.3 From 81ba6afd6e6443d2bf4bf40f16df1f1f91c603f8 Mon Sep 17 00:00:00 2001 From: Arvid Brodin Date: Fri, 4 Jul 2014 23:35:24 +0200 Subject: net/hsr: Switch from dev_add_pack() to netdev_rx_handler_register() Also move the frame receive handler to hsr_slave.c. Signed-off-by: Arvid Brodin Signed-off-by: David S. Miller --- net/hsr/Makefile | 3 +- net/hsr/hsr_device.c | 13 ++- net/hsr/hsr_main.c | 248 +-------------------------------------------------- net/hsr/hsr_main.h | 3 + net/hsr/hsr_slave.c | 243 +++++++++++++++++++++++++++++++++++++++++++++++++ net/hsr/hsr_slave.h | 20 +++++ 6 files changed, 283 insertions(+), 247 deletions(-) create mode 100644 net/hsr/hsr_slave.c create mode 100644 net/hsr/hsr_slave.h (limited to 'net/hsr/hsr_main.c') diff --git a/net/hsr/Makefile b/net/hsr/Makefile index b68359f181cc..b533b243a48b 100644 --- a/net/hsr/Makefile +++ b/net/hsr/Makefile @@ -4,4 +4,5 @@ obj-$(CONFIG_HSR) += hsr.o -hsr-y := hsr_main.o hsr_framereg.o hsr_device.o hsr_netlink.o +hsr-y := hsr_main.o hsr_framereg.o hsr_device.o hsr_netlink.o \ + hsr_slave.o diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c index 4dc2a4207ee2..4e5d92a8f079 100644 --- a/net/hsr/hsr_device.c +++ b/net/hsr/hsr_device.c @@ -19,6 +19,7 @@ #include #include #include "hsr_device.h" +#include "hsr_slave.h" #include "hsr_framereg.h" #include "hsr_main.h" @@ -393,7 +394,6 @@ static void restore_slaves(struct net_device *hsr_dev) rtnl_lock(); - /* Restore promiscuity */ for (i = 0; i < HSR_MAX_SLAVE; i++) { if (!hsr->slave[i]) continue; @@ -402,8 +402,12 @@ static void restore_slaves(struct net_device *hsr_dev) netdev_info(hsr_dev, "Cannot restore slave promiscuity (%s, %d)\n", hsr->slave[i]->name, res); + + if (hsr->slave[i]->rx_handler == hsr_handle_frame) + netdev_rx_handler_unregister(hsr->slave[i]); } + rtnl_unlock(); } @@ -575,6 +579,13 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2], } } + for (i = 0; i < HSR_MAX_SLAVE; i++) { + res = netdev_rx_handler_register(slave[i], hsr_handle_frame, + hsr); + if (res) + goto fail; + } + /* Make sure we recognize frames from ourselves in hsr_rcv() */ res = hsr_create_self_node(&hsr->self_node_db, hsr_dev->dev_addr, hsr->slave[1]->dev_addr); diff --git a/net/hsr/hsr_main.c b/net/hsr/hsr_main.c index 99b8fc4eca6c..bcda901437bc 100644 --- a/net/hsr/hsr_main.c +++ b/net/hsr/hsr_main.c @@ -7,10 +7,6 @@ * * Author(s): * 2011-2014 Arvid Brodin, arvid.brodin@alten.se - * - * In addition to routines for registering and unregistering HSR support, this - * file also contains the receive routine that handles all incoming frames with - * Ethertype (protocol) ETH_P_PRP (HSRv0), and network device event handling. */ #include @@ -56,11 +52,10 @@ bool is_hsr_slave(struct net_device *dev) return false; } - /* If dev is a HSR slave device, return the virtual master device. Return NULL * otherwise. */ -static struct hsr_priv *get_hsr_master(struct net_device *dev) +struct hsr_priv *get_hsr_master(struct net_device *dev) { struct hsr_priv *hsr; @@ -76,12 +71,11 @@ static struct hsr_priv *get_hsr_master(struct net_device *dev) return NULL; } - /* If dev is a HSR slave device, return the other slave device. Return NULL * otherwise. */ -static struct net_device *get_other_slave(struct hsr_priv *hsr, - struct net_device *dev) +struct net_device *get_other_slave(struct hsr_priv *hsr, + struct net_device *dev) { if (dev == hsr->slave[0]) return hsr->slave[1]; @@ -197,239 +191,6 @@ static void prune_nodes_all(unsigned long data) } -static struct sk_buff *hsr_pull_tag(struct sk_buff *skb) -{ - struct hsr_tag *hsr_tag; - struct sk_buff *skb2; - - skb2 = skb_share_check(skb, GFP_ATOMIC); - if (unlikely(!skb2)) - goto err_free; - skb = skb2; - - if (unlikely(!pskb_may_pull(skb, HSR_HLEN))) - goto err_free; - - hsr_tag = (struct hsr_tag *) skb->data; - skb->protocol = hsr_tag->encap_proto; - skb_pull(skb, HSR_HLEN); - - return skb; - -err_free: - kfree_skb(skb); - return NULL; -} - - -/* The uses I can see for these HSR supervision frames are: - * 1) Use the frames that are sent after node initialization ("HSR_TLV.Type = - * 22") to reset any sequence_nr counters belonging to that node. Useful if - * the other node's counter has been reset for some reason. - * -- - * Or not - resetting the counter and bridging the frame would create a - * loop, unfortunately. - * - * 2) Use the LifeCheck frames to detect ring breaks. I.e. if no LifeCheck - * frame is received from a particular node, we know something is wrong. - * We just register these (as with normal frames) and throw them away. - * - * 3) Allow different MAC addresses for the two slave interfaces, using the - * MacAddressA field. - */ -static bool is_supervision_frame(struct hsr_priv *hsr, struct sk_buff *skb) -{ - struct hsr_sup_tag *hsr_stag; - - if (!ether_addr_equal(eth_hdr(skb)->h_dest, - hsr->sup_multicast_addr)) - return false; - - hsr_stag = (struct hsr_sup_tag *) skb->data; - if (get_hsr_stag_path(hsr_stag) != 0x0f) - return false; - if ((hsr_stag->HSR_TLV_Type != HSR_TLV_ANNOUNCE) && - (hsr_stag->HSR_TLV_Type != HSR_TLV_LIFE_CHECK)) - return false; - if (hsr_stag->HSR_TLV_Length != 12) - return false; - - return true; -} - - -/* Implementation somewhat according to IEC-62439-3, p. 43 - */ -static int hsr_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev) -{ - struct hsr_priv *hsr; - struct net_device *other_slave; - struct hsr_node *node; - bool deliver_to_self; - struct sk_buff *skb_deliver; - enum hsr_dev_idx dev_in_idx, dev_other_idx; - bool dup_out; - int ret; - - hsr = get_hsr_master(dev); - - if (!hsr) { - /* Non-HSR-slave device 'dev' is connected to a HSR network */ - kfree_skb(skb); - dev->stats.rx_errors++; - return NET_RX_SUCCESS; - } - - if (dev == hsr->slave[0]) { - dev_in_idx = HSR_DEV_SLAVE_A; - dev_other_idx = HSR_DEV_SLAVE_B; - } else { - dev_in_idx = HSR_DEV_SLAVE_B; - dev_other_idx = HSR_DEV_SLAVE_A; - } - - node = hsr_find_node(&hsr->self_node_db, skb); - if (node) { - /* Always kill frames sent by ourselves */ - kfree_skb(skb); - return NET_RX_SUCCESS; - } - - /* Is this frame a candidate for local reception? */ - deliver_to_self = false; - if ((skb->pkt_type == PACKET_HOST) || - (skb->pkt_type == PACKET_MULTICAST) || - (skb->pkt_type == PACKET_BROADCAST)) - deliver_to_self = true; - else if (ether_addr_equal(eth_hdr(skb)->h_dest, - hsr->dev->dev_addr)) { - skb->pkt_type = PACKET_HOST; - deliver_to_self = true; - } - - - rcu_read_lock(); /* node_db */ - node = hsr_find_node(&hsr->node_db, skb); - - if (is_supervision_frame(hsr, skb)) { - skb_pull(skb, sizeof(struct hsr_sup_tag)); - node = hsr_merge_node(hsr, node, skb, dev_in_idx); - if (!node) { - rcu_read_unlock(); /* node_db */ - kfree_skb(skb); - hsr->dev->stats.rx_dropped++; - return NET_RX_DROP; - } - skb_push(skb, sizeof(struct hsr_sup_tag)); - deliver_to_self = false; - } - - if (!node) { - /* Source node unknown; this might be a HSR frame from - * another net (different multicast address). Ignore it. - */ - rcu_read_unlock(); /* node_db */ - kfree_skb(skb); - return NET_RX_SUCCESS; - } - - /* Register ALL incoming frames as outgoing through the other interface. - * This allows us to register frames as incoming only if they are valid - * for the receiving interface, without using a specific counter for - * incoming frames. - */ - dup_out = hsr_register_frame_out(node, dev_other_idx, skb); - if (!dup_out) - hsr_register_frame_in(node, dev_in_idx); - - /* Forward this frame? */ - if (!dup_out && (skb->pkt_type != PACKET_HOST)) - other_slave = get_other_slave(hsr, dev); - else - other_slave = NULL; - - if (hsr_register_frame_out(node, HSR_DEV_MASTER, skb)) - deliver_to_self = false; - - rcu_read_unlock(); /* node_db */ - - if (!deliver_to_self && !other_slave) { - kfree_skb(skb); - /* Circulated frame; silently remove it. */ - return NET_RX_SUCCESS; - } - - skb_deliver = skb; - if (deliver_to_self && other_slave) { - /* skb_clone() is not enough since we will strip the hsr tag - * and do address substitution below - */ - skb_deliver = pskb_copy(skb, GFP_ATOMIC); - if (!skb_deliver) { - deliver_to_self = false; - hsr->dev->stats.rx_dropped++; - } - } - - if (deliver_to_self) { - bool multicast_frame; - - skb_deliver = hsr_pull_tag(skb_deliver); - if (!skb_deliver) { - hsr->dev->stats.rx_dropped++; - goto forward; - } -#if !defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) - /* Move everything in the header that is after the HSR tag, - * to work around alignment problems caused by the 6-byte HSR - * tag. In practice, this removes/overwrites the HSR tag in - * the header and restores a "standard" packet. - */ - memmove(skb_deliver->data - HSR_HLEN, skb_deliver->data, - skb_headlen(skb_deliver)); - - /* Adjust skb members so they correspond with the move above. - * This cannot possibly underflow skb->data since hsr_pull_tag() - * above succeeded. - * At this point in the protocol stack, the transport and - * network headers have not been set yet, and we haven't touched - * the mac header nor the head. So we only need to adjust data - * and tail: - */ - skb_deliver->data -= HSR_HLEN; - skb_deliver->tail -= HSR_HLEN; -#endif - skb_deliver->dev = hsr->dev; - hsr_addr_subst_source(hsr, skb_deliver); - multicast_frame = (skb_deliver->pkt_type == PACKET_MULTICAST); - ret = netif_rx(skb_deliver); - if (ret == NET_RX_DROP) { - hsr->dev->stats.rx_dropped++; - } else { - hsr->dev->stats.rx_packets++; - hsr->dev->stats.rx_bytes += skb->len; - if (multicast_frame) - hsr->dev->stats.multicast++; - } - } - -forward: - if (other_slave) { - skb_push(skb, ETH_HLEN); - skb->dev = other_slave; - dev_queue_xmit(skb); - } - - return NET_RX_SUCCESS; -} - - -static struct packet_type hsr_pt __read_mostly = { - .type = htons(ETH_P_PRP), - .func = hsr_rcv, -}; - static struct notifier_block hsr_nb = { .notifier_call = hsr_netdev_notify, /* Slave event notifications */ }; @@ -441,8 +202,6 @@ static int __init hsr_init(void) BUILD_BUG_ON(sizeof(struct hsr_tag) != HSR_HLEN); - dev_add_pack(&hsr_pt); - init_timer(&prune_timer); prune_timer.function = prune_nodes_all; prune_timer.data = 0; @@ -461,7 +220,6 @@ static void __exit hsr_exit(void) unregister_netdevice_notifier(&hsr_nb); del_timer_sync(&prune_timer); hsr_netlink_exit(); - dev_remove_pack(&hsr_pt); } module_init(hsr_init); diff --git a/net/hsr/hsr_main.h b/net/hsr/hsr_main.h index 360a49af0c1b..d919321b0bdb 100644 --- a/net/hsr/hsr_main.h +++ b/net/hsr/hsr_main.h @@ -162,5 +162,8 @@ struct hsr_priv { void register_hsr_master(struct hsr_priv *hsr); void unregister_hsr_master(struct hsr_priv *hsr); bool is_hsr_slave(struct net_device *dev); +struct hsr_priv *get_hsr_master(struct net_device *dev); +struct net_device *get_other_slave(struct hsr_priv *hsr, + struct net_device *dev); #endif /* __HSR_PRIVATE_H */ diff --git a/net/hsr/hsr_slave.c b/net/hsr/hsr_slave.c new file mode 100644 index 000000000000..702814631ee1 --- /dev/null +++ b/net/hsr/hsr_slave.c @@ -0,0 +1,243 @@ +/* Copyright 2011-2014 Autronica Fire and Security AS + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Author(s): + * 2011-2014 Arvid Brodin, arvid.brodin@alten.se + */ + +#include "hsr_slave.h" +#include +#include "hsr_main.h" +#include "hsr_framereg.h" + + +static struct sk_buff *hsr_pull_tag(struct sk_buff *skb) +{ + struct hsr_tag *hsr_tag; + struct sk_buff *skb2; + + skb2 = skb_share_check(skb, GFP_ATOMIC); + if (unlikely(!skb2)) + goto err_free; + skb = skb2; + + if (unlikely(!pskb_may_pull(skb, HSR_HLEN))) + goto err_free; + + hsr_tag = (struct hsr_tag *) skb->data; + skb->protocol = hsr_tag->encap_proto; + skb_pull(skb, HSR_HLEN); + + return skb; + +err_free: + kfree_skb(skb); + return NULL; +} + + +/* The uses I can see for these HSR supervision frames are: + * 1) Use the frames that are sent after node initialization ("HSR_TLV.Type = + * 22") to reset any sequence_nr counters belonging to that node. Useful if + * the other node's counter has been reset for some reason. + * -- + * Or not - resetting the counter and bridging the frame would create a + * loop, unfortunately. + * + * 2) Use the LifeCheck frames to detect ring breaks. I.e. if no LifeCheck + * frame is received from a particular node, we know something is wrong. + * We just register these (as with normal frames) and throw them away. + * + * 3) Allow different MAC addresses for the two slave interfaces, using the + * MacAddressA field. + */ +static bool is_supervision_frame(struct hsr_priv *hsr, struct sk_buff *skb) +{ + struct hsr_sup_tag *hsr_stag; + + if (!ether_addr_equal(eth_hdr(skb)->h_dest, + hsr->sup_multicast_addr)) + return false; + + hsr_stag = (struct hsr_sup_tag *) skb->data; + if (get_hsr_stag_path(hsr_stag) != 0x0f) + return false; + if ((hsr_stag->HSR_TLV_Type != HSR_TLV_ANNOUNCE) && + (hsr_stag->HSR_TLV_Type != HSR_TLV_LIFE_CHECK)) + return false; + if (hsr_stag->HSR_TLV_Length != 12) + return false; + + return true; +} + + +/* Implementation somewhat according to IEC-62439-3, p. 43 + */ +rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb) +{ + struct sk_buff *skb = *pskb; + struct net_device *dev = skb->dev; + struct hsr_priv *hsr; + struct net_device *other_slave; + struct hsr_node *node; + bool deliver_to_self; + struct sk_buff *skb_deliver; + enum hsr_dev_idx dev_in_idx, dev_other_idx; + bool dup_out; + int ret; + + if (eth_hdr(skb)->h_proto != htons(ETH_P_PRP)) + return RX_HANDLER_PASS; + + hsr = get_hsr_master(dev); + if (!hsr) { + WARN_ON_ONCE(1); + return RX_HANDLER_PASS; + } + + if (dev == hsr->slave[0]) { + dev_in_idx = HSR_DEV_SLAVE_A; + dev_other_idx = HSR_DEV_SLAVE_B; + } else { + dev_in_idx = HSR_DEV_SLAVE_B; + dev_other_idx = HSR_DEV_SLAVE_A; + } + + node = hsr_find_node(&hsr->self_node_db, skb); + if (node) { + /* Always kill frames sent by ourselves */ + kfree_skb(skb); + return RX_HANDLER_CONSUMED; + } + + /* Is this frame a candidate for local reception? */ + deliver_to_self = false; + if ((skb->pkt_type == PACKET_HOST) || + (skb->pkt_type == PACKET_MULTICAST) || + (skb->pkt_type == PACKET_BROADCAST)) + deliver_to_self = true; + else if (ether_addr_equal(eth_hdr(skb)->h_dest, hsr->dev->dev_addr)) { + skb->pkt_type = PACKET_HOST; + deliver_to_self = true; + } + + + rcu_read_lock(); /* node_db */ + node = hsr_find_node(&hsr->node_db, skb); + + if (is_supervision_frame(hsr, skb)) { + skb_pull(skb, sizeof(struct hsr_sup_tag)); + node = hsr_merge_node(hsr, node, skb, dev_in_idx); + if (!node) { + rcu_read_unlock(); /* node_db */ + kfree_skb(skb); + hsr->dev->stats.rx_dropped++; + return RX_HANDLER_CONSUMED; + } + skb_push(skb, sizeof(struct hsr_sup_tag)); + deliver_to_self = false; + } + + if (!node) { + /* Source node unknown; this might be a HSR frame from + * another net (different multicast address). Ignore it. + */ + rcu_read_unlock(); /* node_db */ + kfree_skb(skb); + return RX_HANDLER_CONSUMED; + } + + /* Register ALL incoming frames as outgoing through the other interface. + * This allows us to register frames as incoming only if they are valid + * for the receiving interface, without using a specific counter for + * incoming frames. + */ + dup_out = hsr_register_frame_out(node, dev_other_idx, skb); + if (!dup_out) + hsr_register_frame_in(node, dev_in_idx); + + /* Forward this frame? */ + if (!dup_out && (skb->pkt_type != PACKET_HOST)) + other_slave = get_other_slave(hsr, dev); + else + other_slave = NULL; + + if (hsr_register_frame_out(node, HSR_DEV_MASTER, skb)) + deliver_to_self = false; + + rcu_read_unlock(); /* node_db */ + + if (!deliver_to_self && !other_slave) { + kfree_skb(skb); + /* Circulated frame; silently remove it. */ + return RX_HANDLER_CONSUMED; + } + + skb_deliver = skb; + if (deliver_to_self && other_slave) { + /* skb_clone() is not enough since we will strip the hsr tag + * and do address substitution below + */ + skb_deliver = pskb_copy(skb, GFP_ATOMIC); + if (!skb_deliver) { + deliver_to_self = false; + hsr->dev->stats.rx_dropped++; + } + } + + if (deliver_to_self) { + bool multicast_frame; + + skb_deliver = hsr_pull_tag(skb_deliver); + if (!skb_deliver) { + hsr->dev->stats.rx_dropped++; + goto forward; + } +#if !defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) + /* Move everything in the header that is after the HSR tag, + * to work around alignment problems caused by the 6-byte HSR + * tag. In practice, this removes/overwrites the HSR tag in + * the header and restores a "standard" packet. + */ + memmove(skb_deliver->data - HSR_HLEN, skb_deliver->data, + skb_headlen(skb_deliver)); + + /* Adjust skb members so they correspond with the move above. + * This cannot possibly underflow skb->data since hsr_pull_tag() + * above succeeded. + * At this point in the protocol stack, the transport and + * network headers have not been set yet, and we haven't touched + * the mac header nor the head. So we only need to adjust data + * and tail: + */ + skb_deliver->data -= HSR_HLEN; + skb_deliver->tail -= HSR_HLEN; +#endif + skb_deliver->dev = hsr->dev; + hsr_addr_subst_source(hsr, skb_deliver); + multicast_frame = (skb_deliver->pkt_type == PACKET_MULTICAST); + ret = netif_rx(skb_deliver); + if (ret == NET_RX_DROP) { + hsr->dev->stats.rx_dropped++; + } else { + hsr->dev->stats.rx_packets++; + hsr->dev->stats.rx_bytes += skb->len; + if (multicast_frame) + hsr->dev->stats.multicast++; + } + } + +forward: + if (other_slave) { + skb_push(skb, ETH_HLEN); + skb->dev = other_slave; + dev_queue_xmit(skb); + } + + return RX_HANDLER_CONSUMED; +} diff --git a/net/hsr/hsr_slave.h b/net/hsr/hsr_slave.h new file mode 100644 index 000000000000..ae90c8d0fde4 --- /dev/null +++ b/net/hsr/hsr_slave.h @@ -0,0 +1,20 @@ +/* Copyright 2011-2014 Autronica Fire and Security AS + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Author(s): + * 2011-2014 Arvid Brodin, arvid.brodin@alten.se + */ + +#ifndef __HSR_SLAVE_H +#define __HSR_SLAVE_H + +#include +#include + +rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb); + +#endif /* __HSR_SLAVE_H */ -- cgit v1.2.3 From abff7162765cd66ab109c97fd433ef1f39299120 Mon Sep 17 00:00:00 2001 From: Arvid Brodin Date: Fri, 4 Jul 2014 23:35:47 +0200 Subject: net/hsr: Move to per-hsr device prune timer. Signed-off-by: Arvid Brodin Signed-off-by: David S. Miller --- net/hsr/hsr_device.c | 10 +++++++++- net/hsr/hsr_framereg.c | 5 ++++- net/hsr/hsr_framereg.h | 2 +- net/hsr/hsr_main.c | 24 ------------------------ net/hsr/hsr_main.h | 1 + 5 files changed, 15 insertions(+), 27 deletions(-) (limited to 'net/hsr/hsr_main.c') diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c index 4e5d92a8f079..f224067153e4 100644 --- a/net/hsr/hsr_device.c +++ b/net/hsr/hsr_device.c @@ -429,7 +429,8 @@ static void hsr_dev_destroy(struct net_device *hsr_dev) hsr = netdev_priv(hsr_dev); - del_timer(&hsr->announce_timer); + del_timer_sync(&hsr->prune_timer); + del_timer_sync(&hsr->announce_timer); unregister_hsr_master(hsr); /* calls list_del_rcu on hsr */ restore_slaves(hsr_dev); call_rcu(&hsr->rcu_head, reclaim_hsr_dev); /* reclaim hsr */ @@ -523,6 +524,10 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2], hsr->announce_timer.function = hsr_announce; hsr->announce_timer.data = (unsigned long) hsr; + init_timer(&hsr->prune_timer); + hsr->prune_timer.function = hsr_prune_nodes; + hsr->prune_timer.data = (unsigned long) hsr; + ether_addr_copy(hsr->sup_multicast_addr, def_multicast_addr); hsr->sup_multicast_addr[ETH_ALEN - 1] = multicast_spec; @@ -596,6 +601,9 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2], if (res) goto fail; + hsr->prune_timer.expires = jiffies + msecs_to_jiffies(PRUNE_PERIOD); + add_timer(&hsr->prune_timer); + register_hsr_master(hsr); return 0; diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c index b419edbd7012..79e3f7ff6654 100644 --- a/net/hsr/hsr_framereg.c +++ b/net/hsr/hsr_framereg.c @@ -366,12 +366,15 @@ static bool is_late(struct hsr_node *node, enum hsr_dev_idx dev_idx) /* Remove stale sequence_nr records. Called by timer every * HSR_LIFE_CHECK_INTERVAL (two seconds or so). */ -void hsr_prune_nodes(struct hsr_priv *hsr) +void hsr_prune_nodes(unsigned long data) { + struct hsr_priv *hsr; struct hsr_node *node; unsigned long timestamp; unsigned long time_a, time_b; + hsr = (struct hsr_priv *) data; + rcu_read_lock(); list_for_each_entry_rcu(node, &hsr->node_db, mac_list) { /* Shorthand */ diff --git a/net/hsr/hsr_framereg.h b/net/hsr/hsr_framereg.h index 3675139df379..ccb09cf4ec5b 100644 --- a/net/hsr/hsr_framereg.h +++ b/net/hsr/hsr_framereg.h @@ -32,7 +32,7 @@ void hsr_register_frame_in(struct hsr_node *node, enum hsr_dev_idx dev_idx); int hsr_register_frame_out(struct hsr_node *node, enum hsr_dev_idx dev_idx, struct sk_buff *skb); -void hsr_prune_nodes(struct hsr_priv *hsr); +void hsr_prune_nodes(unsigned long data); int hsr_create_self_node(struct list_head *self_node_db, unsigned char addr_a[ETH_ALEN], diff --git a/net/hsr/hsr_main.c b/net/hsr/hsr_main.c index bcda901437bc..5f9cd7fdbd93 100644 --- a/net/hsr/hsr_main.c +++ b/net/hsr/hsr_main.c @@ -175,22 +175,6 @@ static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event, } -static struct timer_list prune_timer; - -static void prune_nodes_all(unsigned long data) -{ - struct hsr_priv *hsr; - - rcu_read_lock(); - list_for_each_entry_rcu(hsr, &hsr_list, hsr_list) - hsr_prune_nodes(hsr); - rcu_read_unlock(); - - prune_timer.expires = jiffies + msecs_to_jiffies(PRUNE_PERIOD); - add_timer(&prune_timer); -} - - static struct notifier_block hsr_nb = { .notifier_call = hsr_netdev_notify, /* Slave event notifications */ }; @@ -202,14 +186,7 @@ static int __init hsr_init(void) BUILD_BUG_ON(sizeof(struct hsr_tag) != HSR_HLEN); - init_timer(&prune_timer); - prune_timer.function = prune_nodes_all; - prune_timer.data = 0; - prune_timer.expires = jiffies + msecs_to_jiffies(PRUNE_PERIOD); - add_timer(&prune_timer); - register_netdevice_notifier(&hsr_nb); - res = hsr_netlink_init(); return res; @@ -218,7 +195,6 @@ static int __init hsr_init(void) static void __exit hsr_exit(void) { unregister_netdevice_notifier(&hsr_nb); - del_timer_sync(&prune_timer); hsr_netlink_exit(); } diff --git a/net/hsr/hsr_main.h b/net/hsr/hsr_main.h index d919321b0bdb..43689a6d731f 100644 --- a/net/hsr/hsr_main.h +++ b/net/hsr/hsr_main.h @@ -153,6 +153,7 @@ struct hsr_priv { struct list_head node_db; /* Other HSR nodes */ struct list_head self_node_db; /* MACs of slaves */ struct timer_list announce_timer; /* Supervision frame dispatch */ + struct timer_list prune_timer; int announce_count; u16 sequence_nr; spinlock_t seqnr_lock; /* locking for sequence_nr */ -- cgit v1.2.3 From e9aae56ea43ef4a32527b9d86c1f6b5eebfbd223 Mon Sep 17 00:00:00 2001 From: Arvid Brodin Date: Fri, 4 Jul 2014 23:36:40 +0200 Subject: net/hsr: Operstate handling cleanup. Signed-off-by: Arvid Brodin Signed-off-by: David S. Miller --- net/hsr/hsr_device.c | 37 ++++++++++++++++++++++++++++--------- net/hsr/hsr_device.h | 6 +----- net/hsr/hsr_main.c | 9 +-------- 3 files changed, 30 insertions(+), 22 deletions(-) (limited to 'net/hsr/hsr_main.c') diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c index f224067153e4..8936b3c2bb84 100644 --- a/net/hsr/hsr_device.c +++ b/net/hsr/hsr_device.c @@ -46,31 +46,36 @@ static void __hsr_set_operstate(struct net_device *dev, int transition) } } -void hsr_set_operstate(struct net_device *hsr_dev, struct net_device *slave1, - struct net_device *slave2) +static void hsr_set_operstate(struct net_device *hsr_dev, bool has_carrier) { if (!is_admin_up(hsr_dev)) { __hsr_set_operstate(hsr_dev, IF_OPER_DOWN); return; } - if (is_slave_up(slave1) || is_slave_up(slave2)) + if (has_carrier) __hsr_set_operstate(hsr_dev, IF_OPER_UP); else __hsr_set_operstate(hsr_dev, IF_OPER_LOWERLAYERDOWN); } -void hsr_set_carrier(struct net_device *hsr_dev, struct net_device *slave1, - struct net_device *slave2) +static bool hsr_check_carrier(struct hsr_priv *hsr) { - if (is_slave_up(slave1) || is_slave_up(slave2)) - netif_carrier_on(hsr_dev); + bool has_carrier; + + has_carrier = (is_slave_up(hsr->slave[0]) || is_slave_up(hsr->slave[1])); + + if (has_carrier) + netif_carrier_on(hsr->dev); else - netif_carrier_off(hsr_dev); + netif_carrier_off(hsr->dev); + + return has_carrier; } -void hsr_check_announce(struct net_device *hsr_dev, int old_operstate) +static void hsr_check_announce(struct net_device *hsr_dev, + unsigned char old_operstate) { struct hsr_priv *hsr; @@ -89,6 +94,20 @@ void hsr_check_announce(struct net_device *hsr_dev, int old_operstate) del_timer(&hsr->announce_timer); } +void hsr_check_carrier_and_operstate(struct hsr_priv *hsr) +{ + unsigned char old_operstate; + bool has_carrier; + + /* netif_stacked_transfer_operstate() cannot be used here since + * it doesn't set IF_OPER_LOWERLAYERDOWN (?) + */ + old_operstate = hsr->dev->operstate; + has_carrier = hsr_check_carrier(hsr); + hsr_set_operstate(hsr->dev, has_carrier); + hsr_check_announce(hsr->dev, old_operstate); +} + int hsr_get_max_mtu(struct hsr_priv *hsr) { diff --git a/net/hsr/hsr_device.h b/net/hsr/hsr_device.h index feb744f90f3d..108a5d59d2a6 100644 --- a/net/hsr/hsr_device.h +++ b/net/hsr/hsr_device.h @@ -18,11 +18,7 @@ void hsr_dev_setup(struct net_device *dev); int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2], unsigned char multicast_spec); -void hsr_set_operstate(struct net_device *hsr_dev, struct net_device *slave1, - struct net_device *slave2); -void hsr_set_carrier(struct net_device *hsr_dev, struct net_device *slave1, - struct net_device *slave2); -void hsr_check_announce(struct net_device *hsr_dev, int old_operstate); +void hsr_check_carrier_and_operstate(struct hsr_priv *hsr); bool is_hsr_master(struct net_device *dev); int hsr_get_max_mtu(struct hsr_priv *hsr); diff --git a/net/hsr/hsr_main.c b/net/hsr/hsr_main.c index 5f9cd7fdbd93..431b528c2447 100644 --- a/net/hsr/hsr_main.c +++ b/net/hsr/hsr_main.c @@ -91,7 +91,6 @@ static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event, { struct net_device *slave, *other_slave; struct hsr_priv *hsr; - int old_operstate; int mtu_max; int res; struct net_device *dev; @@ -115,13 +114,7 @@ static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event, case NETDEV_UP: /* Administrative state DOWN */ case NETDEV_DOWN: /* Administrative state UP */ case NETDEV_CHANGE: /* Link (carrier) state changes */ - old_operstate = hsr->dev->operstate; - hsr_set_carrier(hsr->dev, slave, other_slave); - /* netif_stacked_transfer_operstate() cannot be used here since - * it doesn't set IF_OPER_LOWERLAYERDOWN (?) - */ - hsr_set_operstate(hsr->dev, slave, other_slave); - hsr_check_announce(hsr->dev, old_operstate); + hsr_check_carrier_and_operstate(hsr); break; case NETDEV_CHANGEADDR: -- cgit v1.2.3 From 51f3c605318b056ac5deb9079bbef2a976558827 Mon Sep 17 00:00:00 2001 From: Arvid Brodin Date: Fri, 4 Jul 2014 23:37:27 +0200 Subject: net/hsr: Move slave init to hsr_slave.c. Also try to prevent some possible slave dereference race conditions. This is finalized in the next patch, which abandons the slave array in favour of a list_head list and list RCU. Signed-off-by: Arvid Brodin Signed-off-by: David S. Miller --- net/hsr/hsr_device.c | 192 ++++++++++++++----------------------------------- net/hsr/hsr_framereg.c | 6 +- net/hsr/hsr_main.c | 24 ++++--- net/hsr/hsr_netlink.c | 53 ++++++++++---- net/hsr/hsr_slave.c | 91 +++++++++++++++++++++++ net/hsr/hsr_slave.h | 3 + 6 files changed, 205 insertions(+), 164 deletions(-) (limited to 'net/hsr/hsr_main.c') diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c index 8936b3c2bb84..1f8869cb70ae 100644 --- a/net/hsr/hsr_device.c +++ b/net/hsr/hsr_device.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include "hsr_device.h" @@ -108,23 +107,27 @@ void hsr_check_carrier_and_operstate(struct hsr_priv *hsr) hsr_check_announce(hsr->dev, old_operstate); } - int hsr_get_max_mtu(struct hsr_priv *hsr) { - int mtu_max; - - if (hsr->slave[0] && hsr->slave[1]) - mtu_max = min(hsr->slave[0]->mtu, hsr->slave[1]->mtu); - else if (hsr->slave[0]) - mtu_max = hsr->slave[0]->mtu; - else if (hsr->slave[1]) - mtu_max = hsr->slave[1]->mtu; - else - mtu_max = HSR_HLEN; - + unsigned int mtu_max; + struct net_device *slave; + + mtu_max = ETH_DATA_LEN; + rcu_read_lock(); + slave = hsr->slave[0]; + if (slave) + mtu_max = min(slave->mtu, mtu_max); + slave = hsr->slave[1]; + if (slave) + mtu_max = min(slave->mtu, mtu_max); + rcu_read_unlock(); + + if (mtu_max < HSR_HLEN) + return 0; return mtu_max - HSR_HLEN; } + static int hsr_dev_change_mtu(struct net_device *dev, int new_mtu) { struct hsr_priv *hsr; @@ -145,18 +148,20 @@ static int hsr_dev_change_mtu(struct net_device *dev, int new_mtu) static int hsr_dev_open(struct net_device *dev) { struct hsr_priv *hsr; + struct net_device *slave; int i; char *slave_name; hsr = netdev_priv(dev); for (i = 0; i < HSR_MAX_SLAVE; i++) { - if (hsr->slave[i]) - slave_name = hsr->slave[i]->name; + slave = hsr->slave[i]; + if (slave) + slave_name = slave->name; else slave_name = "null"; - if (!is_slave_up(hsr->slave[i])) + if (!is_slave_up(slave)) netdev_warn(dev, "Slave %c (%s) is not up; please bring it up to get a working HSR network\n", 'A' + i, slave_name); } @@ -223,6 +228,8 @@ static int slave_xmit(struct sk_buff *skb, struct hsr_priv *hsr, hsr_ethhdr = (struct hsr_ethhdr *) skb->data; skb->dev = hsr->slave[dev_idx]; + if (unlikely(!skb->dev)) + return NET_XMIT_DROP; hsr_addr_subst_dest(hsr, &hsr_ethhdr->ethhdr, dev_idx); @@ -252,14 +259,8 @@ static int hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev) } skb2 = pskb_copy(skb, GFP_ATOMIC); - - res1 = NET_XMIT_DROP; - if (likely(hsr->slave[HSR_DEV_SLAVE_A])) - res1 = slave_xmit(skb, hsr, HSR_DEV_SLAVE_A); - - res2 = NET_XMIT_DROP; - if (likely(skb2 && hsr->slave[HSR_DEV_SLAVE_B])) - res2 = slave_xmit(skb2, hsr, HSR_DEV_SLAVE_B); + res1 = slave_xmit(skb, hsr, HSR_DEV_SLAVE_A); + res2 = slave_xmit(skb2, hsr, HSR_DEV_SLAVE_B); if (likely(res1 == NET_XMIT_SUCCESS || res1 == NET_XMIT_CN || res2 == NET_XMIT_SUCCESS || res2 == NET_XMIT_CN)) { @@ -406,28 +407,10 @@ static void hsr_announce(unsigned long data) static void restore_slaves(struct net_device *hsr_dev) { struct hsr_priv *hsr; - int i; - int res; hsr = netdev_priv(hsr_dev); - - rtnl_lock(); - - for (i = 0; i < HSR_MAX_SLAVE; i++) { - if (!hsr->slave[i]) - continue; - res = dev_set_promiscuity(hsr->slave[i], -1); - if (res) - netdev_info(hsr_dev, - "Cannot restore slave promiscuity (%s, %d)\n", - hsr->slave[i]->name, res); - - if (hsr->slave[i]->rx_handler == hsr_handle_frame) - netdev_rx_handler_unregister(hsr->slave[i]); - } - - - rtnl_unlock(); + hsr_del_slave(hsr, 1); + hsr_del_slave(hsr, 0); } static void reclaim_hsr_dev(struct rcu_head *rh) @@ -483,38 +466,6 @@ bool is_hsr_master(struct net_device *dev) return (dev->netdev_ops->ndo_start_xmit == hsr_dev_xmit); } -static int check_slave_ok(struct net_device *dev) -{ - /* Don't allow HSR on non-ethernet like devices */ - if ((dev->flags & IFF_LOOPBACK) || (dev->type != ARPHRD_ETHER) || - (dev->addr_len != ETH_ALEN)) { - netdev_info(dev, "Cannot use loopback or non-ethernet device as HSR slave.\n"); - return -EINVAL; - } - - /* Don't allow enslaving hsr devices */ - if (is_hsr_master(dev)) { - netdev_info(dev, "Cannot create trees of HSR devices.\n"); - return -EINVAL; - } - - if (is_hsr_slave(dev)) { - netdev_info(dev, "This device is already a HSR slave.\n"); - return -EINVAL; - } - - if (dev->priv_flags & IFF_802_1Q_VLAN) { - netdev_info(dev, "HSR on top of VLAN is not yet supported in this driver.\n"); - return -EINVAL; - } - - /* HSR over bonded devices has not been tested, but I'm not sure it - * won't work... - */ - - return 0; -} - /* Default multicast address for HSR Supervision frames */ static const unsigned char def_multicast_addr[ETH_ALEN] __aligned(2) = { @@ -525,15 +476,30 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2], unsigned char multicast_spec) { struct hsr_priv *hsr; - int i; int res; hsr = netdev_priv(hsr_dev); hsr->dev = hsr_dev; + hsr->slave[0] = NULL; + hsr->slave[1] = NULL; INIT_LIST_HEAD(&hsr->node_db); INIT_LIST_HEAD(&hsr->self_node_db); - for (i = 0; i < HSR_MAX_SLAVE; i++) - hsr->slave[i] = slave[i]; + + ether_addr_copy(hsr_dev->dev_addr, slave[0]->dev_addr); + + /* Make sure we recognize frames from ourselves in hsr_rcv() */ + res = hsr_create_self_node(&hsr->self_node_db, hsr_dev->dev_addr, + slave[1]->dev_addr); + if (res < 0) + return res; + + hsr_dev->features = slave[0]->features & slave[1]->features; + /* Prevent recursive tx locking */ + hsr_dev->features |= NETIF_F_LLTX; + /* VLAN on top of HSR needs testing and probably some work on + * hsr_header_create() etc. + */ + hsr_dev->features |= NETIF_F_VLAN_CHALLENGED; spin_lock_init(&hsr->seqnr_lock); /* Overflow soon to find bugs easier: */ @@ -560,65 +526,21 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2], * IFF_HSR_MASTER/SLAVE? */ - for (i = 0; i < HSR_MAX_SLAVE; i++) { - res = check_slave_ok(slave[i]); - if (res) - return res; - } - - hsr_dev->features = slave[0]->features & slave[1]->features; - /* Prevent recursive tx locking */ - hsr_dev->features |= NETIF_F_LLTX; - /* VLAN on top of HSR needs testing and probably some work on - * hsr_header_create() etc. - */ - hsr_dev->features |= NETIF_F_VLAN_CHALLENGED; - - /* Set hsr_dev's MAC address to that of mac_slave1 */ - ether_addr_copy(hsr_dev->dev_addr, hsr->slave[0]->dev_addr); - - /* Set required header length */ - for (i = 0; i < HSR_MAX_SLAVE; i++) { - if (slave[i]->hard_header_len + HSR_HLEN > - hsr_dev->hard_header_len) - hsr_dev->hard_header_len = - slave[i]->hard_header_len + HSR_HLEN; - } - - /* MTU */ - for (i = 0; i < HSR_MAX_SLAVE; i++) - if (slave[i]->mtu - HSR_HLEN < hsr_dev->mtu) - hsr_dev->mtu = slave[i]->mtu - HSR_HLEN; - /* Make sure the 1st call to netif_carrier_on() gets through */ netif_carrier_off(hsr_dev); - /* Promiscuity */ - for (i = 0; i < HSR_MAX_SLAVE; i++) { - res = dev_set_promiscuity(slave[i], 1); - if (res) { - netdev_info(hsr_dev, "Cannot set slave promiscuity (%s, %d)\n", - slave[i]->name, res); - goto fail; - } - } - - for (i = 0; i < HSR_MAX_SLAVE; i++) { - res = netdev_rx_handler_register(slave[i], hsr_handle_frame, - hsr); - if (res) - goto fail; - } - - /* Make sure we recognize frames from ourselves in hsr_rcv() */ - res = hsr_create_self_node(&hsr->self_node_db, hsr_dev->dev_addr, - hsr->slave[1]->dev_addr); - if (res < 0) - goto fail; - res = register_netdevice(hsr_dev); if (res) - goto fail; + return res; + + res = hsr_add_slave(hsr, slave[0], 0); + if (res) + return res; + res = hsr_add_slave(hsr, slave[1], 1); + if (res) { + hsr_del_slave(hsr, 0); + return res; + } hsr->prune_timer.expires = jiffies + msecs_to_jiffies(PRUNE_PERIOD); add_timer(&hsr->prune_timer); @@ -626,8 +548,4 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2], register_hsr_master(hsr); return 0; - -fail: - restore_slaves(hsr_dev); - return res; } diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c index 79e3f7ff6654..3666f94c526f 100644 --- a/net/hsr/hsr_framereg.c +++ b/net/hsr/hsr_framereg.c @@ -455,6 +455,7 @@ int hsr_get_node_data(struct hsr_priv *hsr, u16 *if2_seq) { struct hsr_node *node; + struct net_device *slave; unsigned long tdiff; @@ -491,8 +492,9 @@ int hsr_get_node_data(struct hsr_priv *hsr, *if1_seq = node->seq_out[HSR_DEV_SLAVE_B]; *if2_seq = node->seq_out[HSR_DEV_SLAVE_A]; - if ((node->AddrB_if != HSR_DEV_NONE) && hsr->slave[node->AddrB_if]) - *addr_b_ifindex = hsr->slave[node->AddrB_if]->ifindex; + slave = hsr->slave[node->AddrB_if]; + if ((node->AddrB_if != HSR_DEV_NONE) && slave) + *addr_b_ifindex = slave->ifindex; else *addr_b_ifindex = -1; diff --git a/net/hsr/hsr_main.c b/net/hsr/hsr_main.c index 431b528c2447..b5abe26f7b4c 100644 --- a/net/hsr/hsr_main.c +++ b/net/hsr/hsr_main.c @@ -17,6 +17,7 @@ #include "hsr_device.h" #include "hsr_netlink.h" #include "hsr_framereg.h" +#include "hsr_slave.h" /* List of all registered virtual HSR devices */ @@ -124,22 +125,21 @@ static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event, if (dev == hsr->dev) break; - if (dev == hsr->slave[0]) - ether_addr_copy(hsr->dev->dev_addr, - hsr->slave[0]->dev_addr); + if (dev == hsr->slave[0]) { + ether_addr_copy(hsr->dev->dev_addr, dev->dev_addr); + call_netdevice_notifiers(NETDEV_CHANGEADDR, hsr->dev); + } /* Make sure we recognize frames from ourselves in hsr_rcv() */ + other_slave = hsr->slave[1]; res = hsr_create_self_node(&hsr->self_node_db, hsr->dev->dev_addr, - hsr->slave[1] ? - hsr->slave[1]->dev_addr : + other_slave ? + other_slave->dev_addr : hsr->dev->dev_addr); if (res) netdev_warn(hsr->dev, "Could not update HSR node address.\n"); - - if (dev == hsr->slave[0]) - call_netdevice_notifiers(NETDEV_CHANGEADDR, hsr->dev); break; case NETDEV_CHANGEMTU: if (dev == hsr->dev) @@ -149,10 +149,14 @@ static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event, dev_set_mtu(hsr->dev, mtu_max); break; case NETDEV_UNREGISTER: - if (dev == hsr->slave[0]) + if (dev == hsr->slave[0]) { hsr->slave[0] = NULL; - if (dev == hsr->slave[1]) + hsr_del_slave(hsr, 0); + } + if (dev == hsr->slave[1]) { hsr->slave[1] = NULL; + hsr_del_slave(hsr, 1); + } /* There should really be a way to set a new slave device... */ diff --git a/net/hsr/hsr_netlink.c b/net/hsr/hsr_netlink.c index bea250ec3586..a2ce359774f3 100644 --- a/net/hsr/hsr_netlink.c +++ b/net/hsr/hsr_netlink.c @@ -64,16 +64,28 @@ static int hsr_newlink(struct net *src_net, struct net_device *dev, static int hsr_fill_info(struct sk_buff *skb, const struct net_device *dev) { struct hsr_priv *hsr; + struct net_device *slave; + int res; hsr = netdev_priv(dev); - if (hsr->slave[0]) - if (nla_put_u32(skb, IFLA_HSR_SLAVE1, hsr->slave[0]->ifindex)) - goto nla_put_failure; + res = 0; - if (hsr->slave[1]) - if (nla_put_u32(skb, IFLA_HSR_SLAVE2, hsr->slave[1]->ifindex)) - goto nla_put_failure; + rcu_read_lock(); + slave = hsr->slave[0]; + if (slave) + res = nla_put_u32(skb, IFLA_HSR_SLAVE1, slave->ifindex); + rcu_read_unlock(); + if (res) + goto nla_put_failure; + + rcu_read_lock(); + slave = hsr->slave[1]; + if (slave) + res = nla_put_u32(skb, IFLA_HSR_SLAVE2, slave->ifindex); + rcu_read_unlock(); + if (res) + goto nla_put_failure; if (nla_put(skb, IFLA_HSR_SUPERVISION_ADDR, ETH_ALEN, hsr->sup_multicast_addr) || @@ -132,6 +144,7 @@ void hsr_nl_ringerror(struct hsr_priv *hsr, unsigned char addr[ETH_ALEN], enum hsr_dev_idx dev_idx) { struct sk_buff *skb; + struct net_device *slave; void *msg_head; int res; int ifindex; @@ -148,10 +161,14 @@ void hsr_nl_ringerror(struct hsr_priv *hsr, unsigned char addr[ETH_ALEN], if (res < 0) goto nla_put_failure; - if (hsr->slave[dev_idx]) - ifindex = hsr->slave[dev_idx]->ifindex; + rcu_read_lock(); + slave = hsr->slave[dev_idx]; + if (slave) + ifindex = slave->ifindex; else ifindex = -1; + rcu_read_unlock(); + res = nla_put_u32(skb, HSR_A_IFINDEX, ifindex); if (res < 0) goto nla_put_failure; @@ -215,7 +232,7 @@ static int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info) { /* For receiving */ struct nlattr *na; - struct net_device *hsr_dev; + struct net_device *hsr_dev, *slave; /* For sending */ struct sk_buff *skb_out; @@ -301,9 +318,11 @@ static int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info) res = nla_put_u16(skb_out, HSR_A_IF1_SEQ, hsr_node_if1_seq); if (res < 0) goto nla_put_failure; - if (hsr->slave[0]) - res = nla_put_u32(skb_out, HSR_A_IF1_IFINDEX, - hsr->slave[0]->ifindex); + rcu_read_lock(); + slave = hsr->slave[0]; + if (slave) + res = nla_put_u32(skb_out, HSR_A_IF1_IFINDEX, slave->ifindex); + rcu_read_unlock(); if (res < 0) goto nla_put_failure; @@ -313,9 +332,13 @@ static int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info) res = nla_put_u16(skb_out, HSR_A_IF2_SEQ, hsr_node_if2_seq); if (res < 0) goto nla_put_failure; - if (hsr->slave[1]) - res = nla_put_u32(skb_out, HSR_A_IF2_IFINDEX, - hsr->slave[1]->ifindex); + rcu_read_lock(); + slave = hsr->slave[1]; + if (slave) + res = nla_put_u32(skb_out, HSR_A_IF2_IFINDEX, slave->ifindex); + rcu_read_unlock(); + if (res < 0) + goto nla_put_failure; genlmsg_end(skb_out, msg_head); genlmsg_unicast(genl_info_net(info), skb_out, info->snd_portid); diff --git a/net/hsr/hsr_slave.c b/net/hsr/hsr_slave.c index 702814631ee1..d676090f7900 100644 --- a/net/hsr/hsr_slave.c +++ b/net/hsr/hsr_slave.c @@ -11,10 +11,45 @@ #include "hsr_slave.h" #include +#include #include "hsr_main.h" +#include "hsr_device.h" #include "hsr_framereg.h" +static int check_slave_ok(struct net_device *dev) +{ + /* Don't allow HSR on non-ethernet like devices */ + if ((dev->flags & IFF_LOOPBACK) || (dev->type != ARPHRD_ETHER) || + (dev->addr_len != ETH_ALEN)) { + netdev_info(dev, "Cannot use loopback or non-ethernet device as HSR slave.\n"); + return -EINVAL; + } + + /* Don't allow enslaving hsr devices */ + if (is_hsr_master(dev)) { + netdev_info(dev, "Cannot create trees of HSR devices.\n"); + return -EINVAL; + } + + if (is_hsr_slave(dev)) { + netdev_info(dev, "This device is already a HSR slave.\n"); + return -EINVAL; + } + + if (dev->priv_flags & IFF_802_1Q_VLAN) { + netdev_info(dev, "HSR on top of VLAN is not yet supported in this driver.\n"); + return -EINVAL; + } + + /* HSR over bonded devices has not been tested, but I'm not sure it + * won't work... + */ + + return 0; +} + + static struct sk_buff *hsr_pull_tag(struct sk_buff *skb) { struct hsr_tag *hsr_tag; @@ -241,3 +276,59 @@ forward: return RX_HANDLER_CONSUMED; } + +int hsr_add_slave(struct hsr_priv *hsr, struct net_device *dev, int idx) +{ + int res; + + dev_hold(dev); + + res = check_slave_ok(dev); + if (res) + goto fail; + + res = dev_set_promiscuity(dev, 1); + if (res) + goto fail; + + res = netdev_rx_handler_register(dev, hsr_handle_frame, hsr); + if (res) + goto fail_rx_handler; + + + hsr->slave[idx] = dev; + + /* Set required header length */ + if (dev->hard_header_len + HSR_HLEN > hsr->dev->hard_header_len) + hsr->dev->hard_header_len = dev->hard_header_len + HSR_HLEN; + + dev_set_mtu(hsr->dev, hsr_get_max_mtu(hsr)); + + return 0; + +fail_rx_handler: + dev_set_promiscuity(dev, -1); + +fail: + dev_put(dev); + return res; +} + +void hsr_del_slave(struct hsr_priv *hsr, int idx) +{ + struct net_device *slave; + + slave = hsr->slave[idx]; + hsr->slave[idx] = NULL; + + netdev_update_features(hsr->dev); + dev_set_mtu(hsr->dev, hsr_get_max_mtu(hsr)); + + if (slave) { + netdev_rx_handler_unregister(slave); + dev_set_promiscuity(slave, -1); + } + + synchronize_rcu(); + dev_put(slave); +} diff --git a/net/hsr/hsr_slave.h b/net/hsr/hsr_slave.h index ae90c8d0fde4..03c15fda39a8 100644 --- a/net/hsr/hsr_slave.h +++ b/net/hsr/hsr_slave.h @@ -14,7 +14,10 @@ #include #include +#include "hsr_main.h" +int hsr_add_slave(struct hsr_priv *hsr, struct net_device *dev, int idx); +void hsr_del_slave(struct hsr_priv *hsr, int idx); rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb); #endif /* __HSR_SLAVE_H */ -- cgit v1.2.3 From c5a7591172100269e426cf630da0f2dc8138a206 Mon Sep 17 00:00:00 2001 From: Arvid Brodin Date: Fri, 4 Jul 2014 23:38:05 +0200 Subject: net/hsr: Use list_head (and rcu) instead of array for slave devices. Signed-off-by: Arvid Brodin Signed-off-by: David S. Miller --- net/hsr/hsr_device.c | 242 +++++++++++++++++++++++++++---------------------- net/hsr/hsr_framereg.c | 142 +++++++++++++---------------- net/hsr/hsr_framereg.h | 12 +-- net/hsr/hsr_main.c | 128 ++++++++------------------ net/hsr/hsr_main.h | 31 ++++--- net/hsr/hsr_netlink.c | 57 ++++++------ net/hsr/hsr_netlink.h | 3 +- net/hsr/hsr_slave.c | 199 ++++++++++++++++++++++++---------------- net/hsr/hsr_slave.h | 29 +++++- 9 files changed, 438 insertions(+), 405 deletions(-) (limited to 'net/hsr/hsr_main.c') diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c index 1f8869cb70ae..1fc4ea20752e 100644 --- a/net/hsr/hsr_device.c +++ b/net/hsr/hsr_device.c @@ -45,29 +45,38 @@ static void __hsr_set_operstate(struct net_device *dev, int transition) } } -static void hsr_set_operstate(struct net_device *hsr_dev, bool has_carrier) +static void hsr_set_operstate(struct hsr_port *master, bool has_carrier) { - if (!is_admin_up(hsr_dev)) { - __hsr_set_operstate(hsr_dev, IF_OPER_DOWN); + if (!is_admin_up(master->dev)) { + __hsr_set_operstate(master->dev, IF_OPER_DOWN); return; } if (has_carrier) - __hsr_set_operstate(hsr_dev, IF_OPER_UP); + __hsr_set_operstate(master->dev, IF_OPER_UP); else - __hsr_set_operstate(hsr_dev, IF_OPER_LOWERLAYERDOWN); + __hsr_set_operstate(master->dev, IF_OPER_LOWERLAYERDOWN); } -static bool hsr_check_carrier(struct hsr_priv *hsr) +static bool hsr_check_carrier(struct hsr_port *master) { + struct hsr_port *port; bool has_carrier; - has_carrier = (is_slave_up(hsr->slave[0]) || is_slave_up(hsr->slave[1])); + has_carrier = false; + + rcu_read_lock(); + hsr_for_each_port(master->hsr, port) + if ((port->type != HSR_PT_MASTER) && is_slave_up(port->dev)) { + has_carrier = true; + break; + } + rcu_read_unlock(); if (has_carrier) - netif_carrier_on(hsr->dev); + netif_carrier_on(master->dev); else - netif_carrier_off(hsr->dev); + netif_carrier_off(master->dev); return has_carrier; } @@ -95,31 +104,30 @@ static void hsr_check_announce(struct net_device *hsr_dev, void hsr_check_carrier_and_operstate(struct hsr_priv *hsr) { + struct hsr_port *master; unsigned char old_operstate; bool has_carrier; + master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); /* netif_stacked_transfer_operstate() cannot be used here since * it doesn't set IF_OPER_LOWERLAYERDOWN (?) */ - old_operstate = hsr->dev->operstate; - has_carrier = hsr_check_carrier(hsr); - hsr_set_operstate(hsr->dev, has_carrier); - hsr_check_announce(hsr->dev, old_operstate); + old_operstate = master->dev->operstate; + has_carrier = hsr_check_carrier(master); + hsr_set_operstate(master, has_carrier); + hsr_check_announce(master->dev, old_operstate); } int hsr_get_max_mtu(struct hsr_priv *hsr) { unsigned int mtu_max; - struct net_device *slave; + struct hsr_port *port; mtu_max = ETH_DATA_LEN; rcu_read_lock(); - slave = hsr->slave[0]; - if (slave) - mtu_max = min(slave->mtu, mtu_max); - slave = hsr->slave[1]; - if (slave) - mtu_max = min(slave->mtu, mtu_max); + hsr_for_each_port(hsr, port) + if (port->type != HSR_PT_MASTER) + mtu_max = min(port->dev->mtu, mtu_max); rcu_read_unlock(); if (mtu_max < HSR_HLEN) @@ -131,11 +139,13 @@ int hsr_get_max_mtu(struct hsr_priv *hsr) static int hsr_dev_change_mtu(struct net_device *dev, int new_mtu) { struct hsr_priv *hsr; + struct hsr_port *master; hsr = netdev_priv(dev); + master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); if (new_mtu > hsr_get_max_mtu(hsr)) { - netdev_info(hsr->dev, "A HSR master's MTU cannot be greater than the smallest MTU of its slaves minus the HSR Tag length (%d octets).\n", + netdev_info(master->dev, "A HSR master's MTU cannot be greater than the smallest MTU of its slaves minus the HSR Tag length (%d octets).\n", HSR_HLEN); return -EINVAL; } @@ -148,35 +158,42 @@ static int hsr_dev_change_mtu(struct net_device *dev, int new_mtu) static int hsr_dev_open(struct net_device *dev) { struct hsr_priv *hsr; - struct net_device *slave; - int i; - char *slave_name; + struct hsr_port *port; + char designation; hsr = netdev_priv(dev); + designation = '\0'; - for (i = 0; i < HSR_MAX_SLAVE; i++) { - slave = hsr->slave[i]; - if (slave) - slave_name = slave->name; - else - slave_name = "null"; - - if (!is_slave_up(slave)) - netdev_warn(dev, "Slave %c (%s) is not up; please bring it up to get a working HSR network\n", - 'A' + i, slave_name); + rcu_read_lock(); + hsr_for_each_port(hsr, port) { + if (port->type == HSR_PT_MASTER) + continue; + switch (port->type) { + case HSR_PT_SLAVE_A: + designation = 'A'; + break; + case HSR_PT_SLAVE_B: + designation = 'B'; + break; + default: + designation = '?'; + } + if (!is_slave_up(port->dev)) + netdev_warn(dev, "Slave %c (%s) is not up; please bring it up to get a fully working HSR network\n", + designation, port->dev->name); } + rcu_read_unlock(); + + if (designation == '\0') + netdev_warn(dev, "No slave devices configured\n"); return 0; } + static int hsr_dev_close(struct net_device *dev) { - /* Nothing to do here. We could try to restore the state of the slaves - * to what they were before being changed by the hsr master dev's state, - * but they might have been changed manually in the mean time too, so - * taking them up or down here might be confusing and is probably not a - * good idea. - */ + /* Nothing to do here. */ return 0; } @@ -220,18 +237,24 @@ static void hsr_fill_tag(struct hsr_ethhdr *hsr_ethhdr, struct hsr_priv *hsr) hsr_ethhdr->ethhdr.h_proto = htons(ETH_P_PRP); } -static int slave_xmit(struct sk_buff *skb, struct hsr_priv *hsr, - enum hsr_dev_idx dev_idx) +static int slave_xmit(struct hsr_priv *hsr, struct sk_buff *skb, + enum hsr_port_type type) { + struct hsr_port *port; struct hsr_ethhdr *hsr_ethhdr; hsr_ethhdr = (struct hsr_ethhdr *) skb->data; - skb->dev = hsr->slave[dev_idx]; - if (unlikely(!skb->dev)) + rcu_read_lock(); + port = hsr_port_get_hsr(hsr, type); + if (!port) { + rcu_read_unlock(); return NET_XMIT_DROP; + } + skb->dev = port->dev; - hsr_addr_subst_dest(hsr, &hsr_ethhdr->ethhdr, dev_idx); + hsr_addr_subst_dest(port->hsr, &hsr_ethhdr->ethhdr, port); + rcu_read_unlock(); /* Address substitution (IEC62439-3 pp 26, 50): replace mac * address of outgoing frame with that of the outgoing slave's. @@ -241,10 +264,10 @@ static int slave_xmit(struct sk_buff *skb, struct hsr_priv *hsr, return dev_queue_xmit(skb); } - static int hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev) { struct hsr_priv *hsr; + struct hsr_port *master; struct hsr_ethhdr *hsr_ethhdr; struct sk_buff *skb2; int res1, res2; @@ -259,16 +282,23 @@ static int hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev) } skb2 = pskb_copy(skb, GFP_ATOMIC); - res1 = slave_xmit(skb, hsr, HSR_DEV_SLAVE_A); - res2 = slave_xmit(skb2, hsr, HSR_DEV_SLAVE_B); + res1 = slave_xmit(hsr, skb, HSR_PT_SLAVE_A); + if (skb2) + res2 = slave_xmit(hsr, skb2, HSR_PT_SLAVE_B); + else + res2 = NET_XMIT_DROP; + + rcu_read_lock(); + master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); if (likely(res1 == NET_XMIT_SUCCESS || res1 == NET_XMIT_CN || res2 == NET_XMIT_SUCCESS || res2 == NET_XMIT_CN)) { - hsr->dev->stats.tx_packets++; - hsr->dev->stats.tx_bytes += skb->len; + master->dev->stats.tx_packets++; + master->dev->stats.tx_bytes += skb->len; } else { - hsr->dev->stats.tx_dropped++; + master->dev->stats.tx_dropped++; } + rcu_read_unlock(); return NETDEV_TX_OK; } @@ -322,31 +352,35 @@ static int hsr_pad(int size) static void send_hsr_supervision_frame(struct net_device *hsr_dev, u8 type) { struct hsr_priv *hsr; + struct hsr_port *master; struct sk_buff *skb; int hlen, tlen; struct hsr_sup_tag *hsr_stag; struct hsr_sup_payload *hsr_sp; unsigned long irqflags; + int res; + + hsr = netdev_priv(hsr_dev); + master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); - hlen = LL_RESERVED_SPACE(hsr_dev); - tlen = hsr_dev->needed_tailroom; + hlen = LL_RESERVED_SPACE(master->dev); + tlen = master->dev->needed_tailroom; skb = alloc_skb(hsr_pad(sizeof(struct hsr_sup_payload)) + hlen + tlen, GFP_ATOMIC); if (skb == NULL) return; - hsr = netdev_priv(hsr_dev); - skb_reserve(skb, hlen); - skb->dev = hsr_dev; + skb->dev = master->dev; skb->protocol = htons(ETH_P_PRP); skb->priority = TC_PRIO_CONTROL; - if (dev_hard_header(skb, skb->dev, ETH_P_PRP, - hsr->sup_multicast_addr, - skb->dev->dev_addr, skb->len) < 0) + res = dev_hard_header(skb, skb->dev, ETH_P_PRP, + hsr->sup_multicast_addr, + skb->dev->dev_addr, skb->len); + if (res <= 0) goto out; skb_pull(skb, sizeof(struct ethhdr)); @@ -367,12 +401,13 @@ static void send_hsr_supervision_frame(struct net_device *hsr_dev, u8 type) /* Payload: MacAddressA */ hsr_sp = (typeof(hsr_sp)) skb_put(skb, sizeof(*hsr_sp)); - ether_addr_copy(hsr_sp->MacAddressA, hsr_dev->dev_addr); + ether_addr_copy(hsr_sp->MacAddressA, master->dev->dev_addr); dev_queue_xmit(skb); return; out: + WARN_ON_ONCE("HSR: Could not send supervision frame\n"); kfree_skb(skb); } @@ -382,14 +417,16 @@ out: static void hsr_announce(unsigned long data) { struct hsr_priv *hsr; + struct hsr_port *master; hsr = (struct hsr_priv *) data; + master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); if (hsr->announce_count < 3) { - send_hsr_supervision_frame(hsr->dev, HSR_TLV_ANNOUNCE); + send_hsr_supervision_frame(master->dev, HSR_TLV_ANNOUNCE); hsr->announce_count++; } else { - send_hsr_supervision_frame(hsr->dev, HSR_TLV_LIFE_CHECK); + send_hsr_supervision_frame(master->dev, HSR_TLV_LIFE_CHECK); } if (hsr->announce_count < 3) @@ -399,43 +436,28 @@ static void hsr_announce(unsigned long data) hsr->announce_timer.expires = jiffies + msecs_to_jiffies(HSR_LIFE_CHECK_INTERVAL); - if (is_admin_up(hsr->dev)) + if (is_admin_up(master->dev)) add_timer(&hsr->announce_timer); } -static void restore_slaves(struct net_device *hsr_dev) -{ - struct hsr_priv *hsr; - - hsr = netdev_priv(hsr_dev); - hsr_del_slave(hsr, 1); - hsr_del_slave(hsr, 0); -} - -static void reclaim_hsr_dev(struct rcu_head *rh) -{ - struct hsr_priv *hsr; - - hsr = container_of(rh, struct hsr_priv, rcu_head); - free_netdev(hsr->dev); -} - - /* According to comments in the declaration of struct net_device, this function * is "Called from unregister, can be used to call free_netdev". Ok then... */ static void hsr_dev_destroy(struct net_device *hsr_dev) { struct hsr_priv *hsr; + struct hsr_port *port; hsr = netdev_priv(hsr_dev); + hsr_for_each_port(hsr, port) + hsr_del_port(port); del_timer_sync(&hsr->prune_timer); del_timer_sync(&hsr->announce_timer); - unregister_hsr_master(hsr); /* calls list_del_rcu on hsr */ - restore_slaves(hsr_dev); - call_rcu(&hsr->rcu_head, reclaim_hsr_dev); /* reclaim hsr */ + + synchronize_rcu(); + free_netdev(hsr_dev); } static const struct net_device_ops hsr_device_ops = { @@ -461,12 +483,11 @@ void hsr_dev_setup(struct net_device *dev) /* Return true if dev is a HSR master; return false otherwise. */ -bool is_hsr_master(struct net_device *dev) +inline bool is_hsr_master(struct net_device *dev) { return (dev->netdev_ops->ndo_start_xmit == hsr_dev_xmit); } - /* Default multicast address for HSR Supervision frames */ static const unsigned char def_multicast_addr[ETH_ALEN] __aligned(2) = { 0x01, 0x15, 0x4e, 0x00, 0x01, 0x00 @@ -476,12 +497,11 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2], unsigned char multicast_spec) { struct hsr_priv *hsr; + struct hsr_port *port; int res; hsr = netdev_priv(hsr_dev); - hsr->dev = hsr_dev; - hsr->slave[0] = NULL; - hsr->slave[1] = NULL; + INIT_LIST_HEAD(&hsr->ports); INIT_LIST_HEAD(&hsr->node_db); INIT_LIST_HEAD(&hsr->self_node_db); @@ -516,36 +536,42 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2], ether_addr_copy(hsr->sup_multicast_addr, def_multicast_addr); hsr->sup_multicast_addr[ETH_ALEN - 1] = multicast_spec; -/* FIXME: should I modify the value of these? - * - * - hsr_dev->flags - i.e. - * IFF_MASTER/SLAVE? - * - hsr_dev->priv_flags - i.e. - * IFF_EBRIDGE? - * IFF_TX_SKB_SHARING? - * IFF_HSR_MASTER/SLAVE? - */ + /* FIXME: should I modify the value of these? + * + * - hsr_dev->flags - i.e. + * IFF_MASTER/SLAVE? + * - hsr_dev->priv_flags - i.e. + * IFF_EBRIDGE? + * IFF_TX_SKB_SHARING? + * IFF_HSR_MASTER/SLAVE? + */ /* Make sure the 1st call to netif_carrier_on() gets through */ netif_carrier_off(hsr_dev); - res = register_netdevice(hsr_dev); + res = hsr_add_port(hsr, hsr_dev, HSR_PT_MASTER); if (res) return res; - res = hsr_add_slave(hsr, slave[0], 0); + res = register_netdevice(hsr_dev); if (res) - return res; - res = hsr_add_slave(hsr, slave[1], 1); - if (res) { - hsr_del_slave(hsr, 0); - return res; - } + goto fail; + + res = hsr_add_port(hsr, slave[0], HSR_PT_SLAVE_A); + if (res) + goto fail; + res = hsr_add_port(hsr, slave[1], HSR_PT_SLAVE_B); + if (res) + goto fail; hsr->prune_timer.expires = jiffies + msecs_to_jiffies(PRUNE_PERIOD); add_timer(&hsr->prune_timer); - register_hsr_master(hsr); - return 0; + +fail: + hsr_for_each_port(hsr, port) + hsr_del_port(port); + + return res; } diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c index 3666f94c526f..c9b78c52c75b 100644 --- a/net/hsr/hsr_framereg.c +++ b/net/hsr/hsr_framereg.c @@ -27,12 +27,11 @@ struct hsr_node { struct list_head mac_list; unsigned char MacAddressA[ETH_ALEN]; unsigned char MacAddressB[ETH_ALEN]; - enum hsr_dev_idx AddrB_if;/* The local slave through which AddrB - * frames are received from this node - */ - unsigned long time_in[HSR_MAX_SLAVE]; - bool time_in_stale[HSR_MAX_SLAVE]; - u16 seq_out[HSR_MAX_DEV]; + /* Local slave through which AddrB frames are received from this node */ + enum hsr_port_type AddrB_port; + unsigned long time_in[HSR_PT_PORTS]; + bool time_in_stale[HSR_PT_PORTS]; + u16 seq_out[HSR_PT_PORTS]; struct rcu_head rcu_head; }; @@ -154,11 +153,10 @@ int hsr_create_self_node(struct list_head *self_node_db, * We also need to detect if the sender's SlaveA and SlaveB cables have been * swapped. */ -struct hsr_node *hsr_merge_node(struct hsr_priv *hsr, - struct hsr_node *node, - struct sk_buff *skb, - enum hsr_dev_idx dev_idx) +struct hsr_node *hsr_merge_node(struct hsr_node *node, struct sk_buff *skb, + struct hsr_port *port) { + struct hsr_priv *hsr; struct hsr_sup_payload *hsr_sp; struct hsr_ethhdr_sp *hsr_ethsup; int i; @@ -166,6 +164,7 @@ struct hsr_node *hsr_merge_node(struct hsr_priv *hsr, hsr_ethsup = (struct hsr_ethhdr_sp *) skb_mac_header(skb); hsr_sp = (struct hsr_sup_payload *) skb->data; + hsr = port->hsr; if (node && !ether_addr_equal(node->MacAddressA, hsr_sp->MacAddressA)) { /* Node has changed its AddrA, frame was received from SlaveB */ @@ -174,7 +173,7 @@ struct hsr_node *hsr_merge_node(struct hsr_priv *hsr, node = NULL; } - if (node && (dev_idx == node->AddrB_if) && + if (node && (port->type == node->AddrB_port) && !ether_addr_equal(node->MacAddressB, hsr_ethsup->ethhdr.h_source)) { /* Cables have been swapped */ list_del_rcu(&node->mac_list); @@ -182,8 +181,8 @@ struct hsr_node *hsr_merge_node(struct hsr_priv *hsr, node = NULL; } - if (node && (dev_idx != node->AddrB_if) && - (node->AddrB_if != HSR_DEV_NONE) && + if (node && (port->type != node->AddrB_port) && + (node->AddrB_port != HSR_PT_NONE) && !ether_addr_equal(node->MacAddressA, hsr_ethsup->ethhdr.h_source)) { /* Cables have been swapped */ list_del_rcu(&node->mac_list); @@ -200,7 +199,7 @@ struct hsr_node *hsr_merge_node(struct hsr_priv *hsr, * address. Node is PICS_SUBS capable; merge its AddrB. */ ether_addr_copy(node->MacAddressB, hsr_ethsup->ethhdr.h_source); - node->AddrB_if = dev_idx; + node->AddrB_port = port->type; return node; } @@ -211,17 +210,15 @@ struct hsr_node *hsr_merge_node(struct hsr_priv *hsr, ether_addr_copy(node->MacAddressA, hsr_sp->MacAddressA); ether_addr_copy(node->MacAddressB, hsr_ethsup->ethhdr.h_source); if (!ether_addr_equal(hsr_sp->MacAddressA, hsr_ethsup->ethhdr.h_source)) - node->AddrB_if = dev_idx; - else - node->AddrB_if = HSR_DEV_NONE; + node->AddrB_port = port->type; /* We are only interested in time diffs here, so use current jiffies * as initialization. (0 could trigger an spurious ring error warning). */ now = jiffies; - for (i = 0; i < HSR_MAX_SLAVE; i++) + for (i = 0; i < HSR_PT_PORTS; i++) node->time_in[i] = now; - for (i = 0; i < HSR_MAX_DEV; i++) + for (i = 0; i < HSR_PT_PORTS; i++) node->seq_out[i] = ntohs(hsr_ethsup->hsr_sup.sequence_nr) - 1; list_add_tail_rcu(&node->mac_list, &hsr->node_db); @@ -265,13 +262,13 @@ void hsr_addr_subst_source(struct hsr_priv *hsr, struct sk_buff *skb) * which "side" the different interfaces are. */ void hsr_addr_subst_dest(struct hsr_priv *hsr, struct ethhdr *ethhdr, - enum hsr_dev_idx dev_idx) + struct hsr_port *port) { struct hsr_node *node; rcu_read_lock(); node = find_node_by_AddrA(&hsr->node_db, ethhdr->h_dest); - if (node && (node->AddrB_if == dev_idx)) + if (node && (node->AddrB_port == port->type)) ether_addr_copy(ethhdr->h_dest, node->MacAddressB); rcu_read_unlock(); } @@ -295,14 +292,10 @@ static bool seq_nr_after(u16 a, u16 b) #define seq_nr_before_or_eq(a, b) (!seq_nr_after((a), (b))) -void hsr_register_frame_in(struct hsr_node *node, enum hsr_dev_idx dev_idx) +void hsr_register_frame_in(struct hsr_node *node, struct hsr_port *port) { - if ((dev_idx < 0) || (dev_idx >= HSR_MAX_SLAVE)) { - WARN_ONCE(1, "%s: Invalid dev_idx (%d)\n", __func__, dev_idx); - return; - } - node->time_in[dev_idx] = jiffies; - node->time_in_stale[dev_idx] = false; + node->time_in[port->type] = jiffies; + node->time_in_stale[port->type] = false; } @@ -314,16 +307,12 @@ void hsr_register_frame_in(struct hsr_node *node, enum hsr_dev_idx dev_idx) * 0 otherwise, or * negative error code on error */ -int hsr_register_frame_out(struct hsr_node *node, enum hsr_dev_idx dev_idx, +int hsr_register_frame_out(struct hsr_node *node, struct hsr_port *port, struct sk_buff *skb) { struct hsr_ethhdr *hsr_ethhdr; u16 sequence_nr; - if ((dev_idx < 0) || (dev_idx >= HSR_MAX_DEV)) { - WARN_ONCE(1, "%s: Invalid dev_idx (%d)\n", __func__, dev_idx); - return -EINVAL; - } if (!skb_mac_header_was_set(skb)) { WARN_ONCE(1, "%s: Mac header not set\n", __func__); return -EINVAL; @@ -331,35 +320,32 @@ int hsr_register_frame_out(struct hsr_node *node, enum hsr_dev_idx dev_idx, hsr_ethhdr = (struct hsr_ethhdr *) skb_mac_header(skb); sequence_nr = ntohs(hsr_ethhdr->hsr_tag.sequence_nr); - if (seq_nr_before_or_eq(sequence_nr, node->seq_out[dev_idx])) + if (seq_nr_before_or_eq(sequence_nr, node->seq_out[port->type])) return 1; - node->seq_out[dev_idx] = sequence_nr; + node->seq_out[port->type] = sequence_nr; return 0; } - -static bool is_late(struct hsr_node *node, enum hsr_dev_idx dev_idx) +static struct hsr_port *get_late_port(struct hsr_priv *hsr, + struct hsr_node *node) { - enum hsr_dev_idx other; - - if (node->time_in_stale[dev_idx]) - return true; - - if (dev_idx == HSR_DEV_SLAVE_A) - other = HSR_DEV_SLAVE_B; - else - other = HSR_DEV_SLAVE_A; - - if (node->time_in_stale[other]) - return false; + if (node->time_in_stale[HSR_PT_SLAVE_A]) + return hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A); + if (node->time_in_stale[HSR_PT_SLAVE_B]) + return hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B); + + if (time_after(node->time_in[HSR_PT_SLAVE_B], + node->time_in[HSR_PT_SLAVE_A] + + msecs_to_jiffies(MAX_SLAVE_DIFF))) + return hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A); + if (time_after(node->time_in[HSR_PT_SLAVE_A], + node->time_in[HSR_PT_SLAVE_B] + + msecs_to_jiffies(MAX_SLAVE_DIFF))) + return hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B); - if (time_after(node->time_in[other], node->time_in[dev_idx] + - msecs_to_jiffies(MAX_SLAVE_DIFF))) - return true; - - return false; + return NULL; } @@ -370,6 +356,7 @@ void hsr_prune_nodes(unsigned long data) { struct hsr_priv *hsr; struct hsr_node *node; + struct hsr_port *port; unsigned long timestamp; unsigned long time_a, time_b; @@ -378,35 +365,33 @@ void hsr_prune_nodes(unsigned long data) rcu_read_lock(); list_for_each_entry_rcu(node, &hsr->node_db, mac_list) { /* Shorthand */ - time_a = node->time_in[HSR_DEV_SLAVE_A]; - time_b = node->time_in[HSR_DEV_SLAVE_B]; + time_a = node->time_in[HSR_PT_SLAVE_A]; + time_b = node->time_in[HSR_PT_SLAVE_B]; /* Check for timestamps old enough to risk wrap-around */ if (time_after(jiffies, time_a + MAX_JIFFY_OFFSET/2)) - node->time_in_stale[HSR_DEV_SLAVE_A] = true; + node->time_in_stale[HSR_PT_SLAVE_A] = true; if (time_after(jiffies, time_b + MAX_JIFFY_OFFSET/2)) - node->time_in_stale[HSR_DEV_SLAVE_B] = true; + node->time_in_stale[HSR_PT_SLAVE_B] = true; /* Get age of newest frame from node. * At least one time_in is OK here; nodes get pruned long * before both time_ins can get stale */ timestamp = time_a; - if (node->time_in_stale[HSR_DEV_SLAVE_A] || - (!node->time_in_stale[HSR_DEV_SLAVE_B] && + if (node->time_in_stale[HSR_PT_SLAVE_A] || + (!node->time_in_stale[HSR_PT_SLAVE_B] && time_after(time_b, time_a))) timestamp = time_b; /* Warn of ring error only as long as we get frames at all */ if (time_is_after_jiffies(timestamp + msecs_to_jiffies(1.5*MAX_SLAVE_DIFF))) { - - if (is_late(node, HSR_DEV_SLAVE_A)) - hsr_nl_ringerror(hsr, node->MacAddressA, - HSR_DEV_SLAVE_A); - else if (is_late(node, HSR_DEV_SLAVE_B)) - hsr_nl_ringerror(hsr, node->MacAddressA, - HSR_DEV_SLAVE_B); + rcu_read_lock(); + port = get_late_port(hsr, node); + if (port != NULL) + hsr_nl_ringerror(hsr, node->MacAddressA, port); + rcu_read_unlock(); } /* Prune old entries */ @@ -455,7 +440,7 @@ int hsr_get_node_data(struct hsr_priv *hsr, u16 *if2_seq) { struct hsr_node *node; - struct net_device *slave; + struct hsr_port *port; unsigned long tdiff; @@ -468,8 +453,8 @@ int hsr_get_node_data(struct hsr_priv *hsr, ether_addr_copy(addr_b, node->MacAddressB); - tdiff = jiffies - node->time_in[HSR_DEV_SLAVE_A]; - if (node->time_in_stale[HSR_DEV_SLAVE_A]) + tdiff = jiffies - node->time_in[HSR_PT_SLAVE_A]; + if (node->time_in_stale[HSR_PT_SLAVE_A]) *if1_age = INT_MAX; #if HZ <= MSEC_PER_SEC else if (tdiff > msecs_to_jiffies(INT_MAX)) @@ -478,8 +463,8 @@ int hsr_get_node_data(struct hsr_priv *hsr, else *if1_age = jiffies_to_msecs(tdiff); - tdiff = jiffies - node->time_in[HSR_DEV_SLAVE_B]; - if (node->time_in_stale[HSR_DEV_SLAVE_B]) + tdiff = jiffies - node->time_in[HSR_PT_SLAVE_B]; + if (node->time_in_stale[HSR_PT_SLAVE_B]) *if2_age = INT_MAX; #if HZ <= MSEC_PER_SEC else if (tdiff > msecs_to_jiffies(INT_MAX)) @@ -489,14 +474,15 @@ int hsr_get_node_data(struct hsr_priv *hsr, *if2_age = jiffies_to_msecs(tdiff); /* Present sequence numbers as if they were incoming on interface */ - *if1_seq = node->seq_out[HSR_DEV_SLAVE_B]; - *if2_seq = node->seq_out[HSR_DEV_SLAVE_A]; + *if1_seq = node->seq_out[HSR_PT_SLAVE_B]; + *if2_seq = node->seq_out[HSR_PT_SLAVE_A]; - slave = hsr->slave[node->AddrB_if]; - if ((node->AddrB_if != HSR_DEV_NONE) && slave) - *addr_b_ifindex = slave->ifindex; - else + if (node->AddrB_port != HSR_PT_NONE) { + port = hsr_port_get_hsr(hsr, node->AddrB_port); + *addr_b_ifindex = port->dev->ifindex; + } else { *addr_b_ifindex = -1; + } rcu_read_unlock(); diff --git a/net/hsr/hsr_framereg.h b/net/hsr/hsr_framereg.h index ccb09cf4ec5b..c87f36fc154c 100644 --- a/net/hsr/hsr_framereg.h +++ b/net/hsr/hsr_framereg.h @@ -18,18 +18,16 @@ struct hsr_node; struct hsr_node *hsr_find_node(struct list_head *node_db, struct sk_buff *skb); -struct hsr_node *hsr_merge_node(struct hsr_priv *hsr, - struct hsr_node *node, - struct sk_buff *skb, - enum hsr_dev_idx dev_idx); +struct hsr_node *hsr_merge_node(struct hsr_node *node, struct sk_buff *skb, + struct hsr_port *port); void hsr_addr_subst_source(struct hsr_priv *hsr, struct sk_buff *skb); void hsr_addr_subst_dest(struct hsr_priv *hsr, struct ethhdr *ethhdr, - enum hsr_dev_idx dev_idx); + struct hsr_port *port); -void hsr_register_frame_in(struct hsr_node *node, enum hsr_dev_idx dev_idx); +void hsr_register_frame_in(struct hsr_node *node, struct hsr_port *port); -int hsr_register_frame_out(struct hsr_node *node, enum hsr_dev_idx dev_idx, +int hsr_register_frame_out(struct hsr_node *node, struct hsr_port *port, struct sk_buff *skb); void hsr_prune_nodes(unsigned long data); diff --git a/net/hsr/hsr_main.c b/net/hsr/hsr_main.c index b5abe26f7b4c..a06cab57ab68 100644 --- a/net/hsr/hsr_main.c +++ b/net/hsr/hsr_main.c @@ -39,76 +39,25 @@ void unregister_hsr_master(struct hsr_priv *hsr) } } -bool is_hsr_slave(struct net_device *dev) -{ - struct hsr_priv *hsr_it; - - list_for_each_entry_rcu(hsr_it, &hsr_list, hsr_list) { - if (dev == hsr_it->slave[0]) - return true; - if (dev == hsr_it->slave[1]) - return true; - } - - return false; -} - -/* If dev is a HSR slave device, return the virtual master device. Return NULL - * otherwise. - */ -struct hsr_priv *get_hsr_master(struct net_device *dev) -{ - struct hsr_priv *hsr; - - rcu_read_lock(); - list_for_each_entry_rcu(hsr, &hsr_list, hsr_list) - if ((dev == hsr->slave[0]) || - (dev == hsr->slave[1])) { - rcu_read_unlock(); - return hsr; - } - - rcu_read_unlock(); - return NULL; -} - -/* If dev is a HSR slave device, return the other slave device. Return NULL - * otherwise. - */ -struct net_device *get_other_slave(struct hsr_priv *hsr, - struct net_device *dev) -{ - if (dev == hsr->slave[0]) - return hsr->slave[1]; - if (dev == hsr->slave[1]) - return hsr->slave[0]; - - return NULL; -} - static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event, void *ptr) { - struct net_device *slave, *other_slave; + struct net_device *dev; + struct hsr_port *port, *master; struct hsr_priv *hsr; int mtu_max; int res; - struct net_device *dev; dev = netdev_notifier_info_to_dev(ptr); - - hsr = get_hsr_master(dev); - if (hsr) { - /* dev is a slave device */ - slave = dev; - other_slave = get_other_slave(hsr, slave); - } else { + port = hsr_port_get_rtnl(dev); + if (port == NULL) { if (!is_hsr_master(dev)) - return NOTIFY_DONE; + return NOTIFY_DONE; /* Not an HSR device */ hsr = netdev_priv(dev); - slave = hsr->slave[0]; - other_slave = hsr->slave[1]; + port = hsr_port_get_hsr(hsr, HSR_PT_MASTER); + } else { + hsr = port->hsr; } switch (event) { @@ -118,48 +67,41 @@ static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event, hsr_check_carrier_and_operstate(hsr); break; case NETDEV_CHANGEADDR: - - /* This should not happen since there's no ndo_set_mac_address() - * for HSR devices - i.e. not supported. - */ - if (dev == hsr->dev) + if (port->type == HSR_PT_MASTER) { + /* This should not happen since there's no + * ndo_set_mac_address() for HSR devices - i.e. not + * supported. + */ break; + } - if (dev == hsr->slave[0]) { - ether_addr_copy(hsr->dev->dev_addr, dev->dev_addr); - call_netdevice_notifiers(NETDEV_CHANGEADDR, hsr->dev); + master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); + + if (port->type == HSR_PT_SLAVE_A) { + ether_addr_copy(master->dev->dev_addr, dev->dev_addr); + call_netdevice_notifiers(NETDEV_CHANGEADDR, master->dev); } /* Make sure we recognize frames from ourselves in hsr_rcv() */ - other_slave = hsr->slave[1]; + port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B); res = hsr_create_self_node(&hsr->self_node_db, - hsr->dev->dev_addr, - other_slave ? - other_slave->dev_addr : - hsr->dev->dev_addr); + master->dev->dev_addr, + port ? + port->dev->dev_addr : + master->dev->dev_addr); if (res) - netdev_warn(hsr->dev, + netdev_warn(master->dev, "Could not update HSR node address.\n"); break; case NETDEV_CHANGEMTU: - if (dev == hsr->dev) + if (port->type == HSR_PT_MASTER) break; /* Handled in ndo_change_mtu() */ - mtu_max = hsr_get_max_mtu(hsr); - if (hsr->dev->mtu > mtu_max) - dev_set_mtu(hsr->dev, mtu_max); + mtu_max = hsr_get_max_mtu(port->hsr); + master = hsr_port_get_hsr(port->hsr, HSR_PT_MASTER); + master->dev->mtu = mtu_max; break; case NETDEV_UNREGISTER: - if (dev == hsr->slave[0]) { - hsr->slave[0] = NULL; - hsr_del_slave(hsr, 0); - } - if (dev == hsr->slave[1]) { - hsr->slave[1] = NULL; - hsr_del_slave(hsr, 1); - } - - /* There should really be a way to set a new slave device... */ - + hsr_del_port(port); break; case NETDEV_PRE_TYPE_CHANGE: /* HSR works only on Ethernet devices. Refuse slave to change @@ -172,6 +114,16 @@ static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event, } +struct hsr_port *hsr_port_get_hsr(struct hsr_priv *hsr, enum hsr_port_type pt) +{ + struct hsr_port *port; + + hsr_for_each_port(hsr, port) + if (port->type == pt) + return port; + return NULL; +} + static struct notifier_block hsr_nb = { .notifier_call = hsr_netdev_notify, /* Slave event notifications */ }; diff --git a/net/hsr/hsr_main.h b/net/hsr/hsr_main.h index 43689a6d731f..e31c3069fdf8 100644 --- a/net/hsr/hsr_main.h +++ b/net/hsr/hsr_main.h @@ -136,20 +136,26 @@ struct hsr_ethhdr_sp { } __packed; -enum hsr_dev_idx { - HSR_DEV_NONE = -1, - HSR_DEV_SLAVE_A = 0, - HSR_DEV_SLAVE_B, - HSR_DEV_MASTER, +enum hsr_port_type { + HSR_PT_NONE = 0, /* Must be 0, used by framereg */ + HSR_PT_SLAVE_A, + HSR_PT_SLAVE_B, + HSR_PT_INTERLINK, + HSR_PT_MASTER, + HSR_PT_PORTS, /* This must be the last item in the enum */ +}; + +struct hsr_port { + struct list_head port_list; + struct net_device *dev; + struct hsr_priv *hsr; + enum hsr_port_type type; }; -#define HSR_MAX_SLAVE (HSR_DEV_SLAVE_B + 1) -#define HSR_MAX_DEV (HSR_DEV_MASTER + 1) struct hsr_priv { struct list_head hsr_list; /* List of hsr devices */ struct rcu_head rcu_head; - struct net_device *dev; - struct net_device *slave[HSR_MAX_SLAVE]; + struct list_head ports; struct list_head node_db; /* Other HSR nodes */ struct list_head self_node_db; /* MACs of slaves */ struct timer_list announce_timer; /* Supervision frame dispatch */ @@ -160,11 +166,6 @@ struct hsr_priv { unsigned char sup_multicast_addr[ETH_ALEN]; }; -void register_hsr_master(struct hsr_priv *hsr); -void unregister_hsr_master(struct hsr_priv *hsr); -bool is_hsr_slave(struct net_device *dev); -struct hsr_priv *get_hsr_master(struct net_device *dev); -struct net_device *get_other_slave(struct hsr_priv *hsr, - struct net_device *dev); +struct hsr_port *hsr_port_get_hsr(struct hsr_priv *hsr, enum hsr_port_type pt); #endif /* __HSR_PRIVATE_H */ diff --git a/net/hsr/hsr_netlink.c b/net/hsr/hsr_netlink.c index a2ce359774f3..67082453928c 100644 --- a/net/hsr/hsr_netlink.c +++ b/net/hsr/hsr_netlink.c @@ -64,7 +64,7 @@ static int hsr_newlink(struct net *src_net, struct net_device *dev, static int hsr_fill_info(struct sk_buff *skb, const struct net_device *dev) { struct hsr_priv *hsr; - struct net_device *slave; + struct hsr_port *port; int res; hsr = netdev_priv(dev); @@ -72,17 +72,17 @@ static int hsr_fill_info(struct sk_buff *skb, const struct net_device *dev) res = 0; rcu_read_lock(); - slave = hsr->slave[0]; - if (slave) - res = nla_put_u32(skb, IFLA_HSR_SLAVE1, slave->ifindex); + port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A); + if (port) + res = nla_put_u32(skb, IFLA_HSR_SLAVE1, port->dev->ifindex); rcu_read_unlock(); if (res) goto nla_put_failure; rcu_read_lock(); - slave = hsr->slave[1]; - if (slave) - res = nla_put_u32(skb, IFLA_HSR_SLAVE2, slave->ifindex); + port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B); + if (port) + res = nla_put_u32(skb, IFLA_HSR_SLAVE2, port->dev->ifindex); rcu_read_unlock(); if (res) goto nla_put_failure; @@ -141,13 +141,12 @@ static const struct genl_multicast_group hsr_mcgrps[] = { * (i.e. a link has failed somewhere). */ void hsr_nl_ringerror(struct hsr_priv *hsr, unsigned char addr[ETH_ALEN], - enum hsr_dev_idx dev_idx) + struct hsr_port *port) { struct sk_buff *skb; - struct net_device *slave; void *msg_head; + struct hsr_port *master; int res; - int ifindex; skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); if (!skb) @@ -161,15 +160,7 @@ void hsr_nl_ringerror(struct hsr_priv *hsr, unsigned char addr[ETH_ALEN], if (res < 0) goto nla_put_failure; - rcu_read_lock(); - slave = hsr->slave[dev_idx]; - if (slave) - ifindex = slave->ifindex; - else - ifindex = -1; - rcu_read_unlock(); - - res = nla_put_u32(skb, HSR_A_IFINDEX, ifindex); + res = nla_put_u32(skb, HSR_A_IFINDEX, port->dev->ifindex); if (res < 0) goto nla_put_failure; @@ -182,7 +173,10 @@ nla_put_failure: kfree_skb(skb); fail: - netdev_warn(hsr->dev, "Could not send HSR ring error message\n"); + rcu_read_lock(); + master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); + netdev_warn(master->dev, "Could not send HSR ring error message\n"); + rcu_read_unlock(); } /* This is called when we haven't heard from the node with MAC address addr for @@ -192,6 +186,7 @@ void hsr_nl_nodedown(struct hsr_priv *hsr, unsigned char addr[ETH_ALEN]) { struct sk_buff *skb; void *msg_head; + struct hsr_port *master; int res; skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); @@ -216,7 +211,10 @@ nla_put_failure: kfree_skb(skb); fail: - netdev_warn(hsr->dev, "Could not send HSR node down\n"); + rcu_read_lock(); + master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); + netdev_warn(master->dev, "Could not send HSR node down\n"); + rcu_read_unlock(); } @@ -232,12 +230,13 @@ static int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info) { /* For receiving */ struct nlattr *na; - struct net_device *hsr_dev, *slave; + struct net_device *hsr_dev; /* For sending */ struct sk_buff *skb_out; void *msg_head; struct hsr_priv *hsr; + struct hsr_port *port; unsigned char hsr_node_addr_b[ETH_ALEN]; int hsr_node_if1_age; u16 hsr_node_if1_seq; @@ -319,9 +318,10 @@ static int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info) if (res < 0) goto nla_put_failure; rcu_read_lock(); - slave = hsr->slave[0]; - if (slave) - res = nla_put_u32(skb_out, HSR_A_IF1_IFINDEX, slave->ifindex); + port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A); + if (port) + res = nla_put_u32(skb_out, HSR_A_IF1_IFINDEX, + port->dev->ifindex); rcu_read_unlock(); if (res < 0) goto nla_put_failure; @@ -333,9 +333,10 @@ static int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info) if (res < 0) goto nla_put_failure; rcu_read_lock(); - slave = hsr->slave[1]; - if (slave) - res = nla_put_u32(skb_out, HSR_A_IF2_IFINDEX, slave->ifindex); + port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B); + if (port) + res = nla_put_u32(skb_out, HSR_A_IF2_IFINDEX, + port->dev->ifindex); rcu_read_unlock(); if (res < 0) goto nla_put_failure; diff --git a/net/hsr/hsr_netlink.h b/net/hsr/hsr_netlink.h index 3047f9cea5f5..3f6b95b5b6b8 100644 --- a/net/hsr/hsr_netlink.h +++ b/net/hsr/hsr_netlink.h @@ -17,12 +17,13 @@ #include struct hsr_priv; +struct hsr_port; int __init hsr_netlink_init(void); void __exit hsr_netlink_exit(void); void hsr_nl_ringerror(struct hsr_priv *hsr, unsigned char addr[ETH_ALEN], - int dev_idx); + struct hsr_port *port); void hsr_nl_nodedown(struct hsr_priv *hsr, unsigned char addr[ETH_ALEN]); void hsr_nl_framedrop(int dropcount, int dev_idx); void hsr_nl_linkdown(int dev_idx); diff --git a/net/hsr/hsr_slave.c b/net/hsr/hsr_slave.c index d676090f7900..fffd69297c3e 100644 --- a/net/hsr/hsr_slave.c +++ b/net/hsr/hsr_slave.c @@ -17,7 +17,7 @@ #include "hsr_framereg.h" -static int check_slave_ok(struct net_device *dev) +static int hsr_check_dev_ok(struct net_device *dev) { /* Don't allow HSR on non-ethernet like devices */ if ((dev->flags & IFF_LOOPBACK) || (dev->type != ARPHRD_ETHER) || @@ -32,7 +32,7 @@ static int check_slave_ok(struct net_device *dev) return -EINVAL; } - if (is_hsr_slave(dev)) { + if (hsr_port_exists(dev)) { netdev_info(dev, "This device is already a HSR slave.\n"); return -EINVAL; } @@ -116,38 +116,29 @@ static bool is_supervision_frame(struct hsr_priv *hsr, struct sk_buff *skb) rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb) { struct sk_buff *skb = *pskb; - struct net_device *dev = skb->dev; + struct hsr_port *port, *other_port, *master; struct hsr_priv *hsr; - struct net_device *other_slave; struct hsr_node *node; bool deliver_to_self; struct sk_buff *skb_deliver; - enum hsr_dev_idx dev_in_idx, dev_other_idx; bool dup_out; int ret; if (eth_hdr(skb)->h_proto != htons(ETH_P_PRP)) return RX_HANDLER_PASS; - hsr = get_hsr_master(dev); - if (!hsr) { - WARN_ON_ONCE(1); - return RX_HANDLER_PASS; - } + rcu_read_lock(); /* ports & node */ - if (dev == hsr->slave[0]) { - dev_in_idx = HSR_DEV_SLAVE_A; - dev_other_idx = HSR_DEV_SLAVE_B; - } else { - dev_in_idx = HSR_DEV_SLAVE_B; - dev_other_idx = HSR_DEV_SLAVE_A; - } + port = hsr_port_get_rcu(skb->dev); + hsr = port->hsr; + master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); node = hsr_find_node(&hsr->self_node_db, skb); if (node) { /* Always kill frames sent by ourselves */ kfree_skb(skb); - return RX_HANDLER_CONSUMED; + ret = RX_HANDLER_CONSUMED; + goto finish; } /* Is this frame a candidate for local reception? */ @@ -156,23 +147,22 @@ rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb) (skb->pkt_type == PACKET_MULTICAST) || (skb->pkt_type == PACKET_BROADCAST)) deliver_to_self = true; - else if (ether_addr_equal(eth_hdr(skb)->h_dest, hsr->dev->dev_addr)) { + else if (ether_addr_equal(eth_hdr(skb)->h_dest, + master->dev->dev_addr)) { skb->pkt_type = PACKET_HOST; deliver_to_self = true; } - - rcu_read_lock(); /* node_db */ node = hsr_find_node(&hsr->node_db, skb); if (is_supervision_frame(hsr, skb)) { skb_pull(skb, sizeof(struct hsr_sup_tag)); - node = hsr_merge_node(hsr, node, skb, dev_in_idx); + node = hsr_merge_node(node, skb, port); if (!node) { - rcu_read_unlock(); /* node_db */ kfree_skb(skb); - hsr->dev->stats.rx_dropped++; - return RX_HANDLER_CONSUMED; + master->dev->stats.rx_dropped++; + ret = RX_HANDLER_CONSUMED; + goto finish; } skb_push(skb, sizeof(struct hsr_sup_tag)); deliver_to_self = false; @@ -182,46 +172,51 @@ rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb) /* Source node unknown; this might be a HSR frame from * another net (different multicast address). Ignore it. */ - rcu_read_unlock(); /* node_db */ kfree_skb(skb); - return RX_HANDLER_CONSUMED; + ret = RX_HANDLER_CONSUMED; + goto finish; } + if (port->type == HSR_PT_SLAVE_A) + other_port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B); + else + other_port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A); + /* Register ALL incoming frames as outgoing through the other interface. * This allows us to register frames as incoming only if they are valid * for the receiving interface, without using a specific counter for * incoming frames. */ - dup_out = hsr_register_frame_out(node, dev_other_idx, skb); + if (other_port) + dup_out = hsr_register_frame_out(node, other_port, skb); + else + dup_out = 0; if (!dup_out) - hsr_register_frame_in(node, dev_in_idx); + hsr_register_frame_in(node, port); /* Forward this frame? */ - if (!dup_out && (skb->pkt_type != PACKET_HOST)) - other_slave = get_other_slave(hsr, dev); - else - other_slave = NULL; + if (dup_out || (skb->pkt_type == PACKET_HOST)) + other_port = NULL; - if (hsr_register_frame_out(node, HSR_DEV_MASTER, skb)) + if (hsr_register_frame_out(node, master, skb)) deliver_to_self = false; - rcu_read_unlock(); /* node_db */ - - if (!deliver_to_self && !other_slave) { + if (!deliver_to_self && !other_port) { kfree_skb(skb); /* Circulated frame; silently remove it. */ - return RX_HANDLER_CONSUMED; + ret = RX_HANDLER_CONSUMED; + goto finish; } skb_deliver = skb; - if (deliver_to_self && other_slave) { + if (deliver_to_self && other_port) { /* skb_clone() is not enough since we will strip the hsr tag * and do address substitution below */ skb_deliver = pskb_copy(skb, GFP_ATOMIC); if (!skb_deliver) { deliver_to_self = false; - hsr->dev->stats.rx_dropped++; + master->dev->stats.rx_dropped++; } } @@ -230,7 +225,7 @@ rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb) skb_deliver = hsr_pull_tag(skb_deliver); if (!skb_deliver) { - hsr->dev->stats.rx_dropped++; + master->dev->stats.rx_dropped++; goto forward; } #if !defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) @@ -253,82 +248,130 @@ rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb) skb_deliver->data -= HSR_HLEN; skb_deliver->tail -= HSR_HLEN; #endif - skb_deliver->dev = hsr->dev; + skb_deliver->dev = master->dev; hsr_addr_subst_source(hsr, skb_deliver); multicast_frame = (skb_deliver->pkt_type == PACKET_MULTICAST); ret = netif_rx(skb_deliver); if (ret == NET_RX_DROP) { - hsr->dev->stats.rx_dropped++; + master->dev->stats.rx_dropped++; } else { - hsr->dev->stats.rx_packets++; - hsr->dev->stats.rx_bytes += skb->len; + master->dev->stats.rx_packets++; + master->dev->stats.rx_bytes += skb->len; if (multicast_frame) - hsr->dev->stats.multicast++; + master->dev->stats.multicast++; } } forward: - if (other_slave) { + if (other_port) { skb_push(skb, ETH_HLEN); - skb->dev = other_slave; + skb->dev = other_port->dev; dev_queue_xmit(skb); } - return RX_HANDLER_CONSUMED; + ret = RX_HANDLER_CONSUMED; + +finish: + rcu_read_unlock(); + return ret; } -int hsr_add_slave(struct hsr_priv *hsr, struct net_device *dev, int idx) +/* Setup device to be added to the HSR bridge. */ +static int hsr_portdev_setup(struct net_device *dev, struct hsr_port *port) { int res; dev_hold(dev); - - res = check_slave_ok(dev); - if (res) - goto fail; - res = dev_set_promiscuity(dev, 1); if (res) - goto fail; - - res = netdev_rx_handler_register(dev, hsr_handle_frame, hsr); + goto fail_promiscuity; + res = netdev_rx_handler_register(dev, hsr_handle_frame, port); if (res) goto fail_rx_handler; + dev_disable_lro(dev); - - hsr->slave[idx] = dev; - - /* Set required header length */ - if (dev->hard_header_len + HSR_HLEN > hsr->dev->hard_header_len) - hsr->dev->hard_header_len = dev->hard_header_len + HSR_HLEN; - - dev_set_mtu(hsr->dev, hsr_get_max_mtu(hsr)); + /* FIXME: + * What does net device "adjacency" mean? Should we do + * res = netdev_master_upper_dev_link(port->dev, port->hsr->dev); ? + */ return 0; fail_rx_handler: dev_set_promiscuity(dev, -1); - -fail: +fail_promiscuity: dev_put(dev); + return res; } -void hsr_del_slave(struct hsr_priv *hsr, int idx) +int hsr_add_port(struct hsr_priv *hsr, struct net_device *dev, + enum hsr_port_type type) { - struct net_device *slave; + struct hsr_port *port, *master; + int res; - slave = hsr->slave[idx]; - hsr->slave[idx] = NULL; + if (type != HSR_PT_MASTER) { + res = hsr_check_dev_ok(dev); + if (res) + return res; + } + + port = hsr_port_get_hsr(hsr, type); + if (port != NULL) + return -EBUSY; /* This port already exists */ + + port = kzalloc(sizeof(*port), GFP_KERNEL); + if (port == NULL) + return -ENOMEM; + + if (type != HSR_PT_MASTER) { + res = hsr_portdev_setup(dev, port); + if (res) + goto fail_dev_setup; + } + + port->hsr = hsr; + port->dev = dev; + port->type = type; + + list_add_tail_rcu(&port->port_list, &hsr->ports); + synchronize_rcu(); + + master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); + + /* Set required header length */ + if (dev->hard_header_len + HSR_HLEN > master->dev->hard_header_len) + master->dev->hard_header_len = dev->hard_header_len + HSR_HLEN; - netdev_update_features(hsr->dev); - dev_set_mtu(hsr->dev, hsr_get_max_mtu(hsr)); + dev_set_mtu(master->dev, hsr_get_max_mtu(hsr)); - if (slave) { - netdev_rx_handler_unregister(slave); - dev_set_promiscuity(slave, -1); + return 0; + +fail_dev_setup: + kfree(port); + return res; +} + +void hsr_del_port(struct hsr_port *port) +{ + struct hsr_priv *hsr; + struct hsr_port *master; + + hsr = port->hsr; + master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); + list_del_rcu(&port->port_list); + + if (port != master) { + dev_set_mtu(master->dev, hsr_get_max_mtu(hsr)); + netdev_rx_handler_unregister(port->dev); + dev_set_promiscuity(port->dev, -1); } + /* FIXME? + * netdev_upper_dev_unlink(port->dev, port->hsr->dev); + */ + synchronize_rcu(); - dev_put(slave); + dev_put(port->dev); } diff --git a/net/hsr/hsr_slave.h b/net/hsr/hsr_slave.h index 03c15fda39a8..3055022eddb3 100644 --- a/net/hsr/hsr_slave.h +++ b/net/hsr/hsr_slave.h @@ -14,10 +14,35 @@ #include #include +#include #include "hsr_main.h" -int hsr_add_slave(struct hsr_priv *hsr, struct net_device *dev, int idx); -void hsr_del_slave(struct hsr_priv *hsr, int idx); +int hsr_add_port(struct hsr_priv *hsr, struct net_device *dev, + enum hsr_port_type pt); +void hsr_del_port(struct hsr_port *port); rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb); + +#define hsr_for_each_port(hsr, port) \ + list_for_each_entry_rcu((port), &(hsr)->ports, port_list) + + +static inline bool hsr_port_exists(const struct net_device *dev) +{ + return dev->rx_handler == hsr_handle_frame; +} + +static inline struct hsr_port *hsr_port_get_rtnl(const struct net_device *dev) +{ + ASSERT_RTNL(); + return hsr_port_exists(dev) ? + rtnl_dereference(dev->rx_handler_data) : NULL; +} + +static inline struct hsr_port *hsr_port_get_rcu(const struct net_device *dev) +{ + return hsr_port_exists(dev) ? + rcu_dereference(dev->rx_handler_data) : NULL; +} + #endif /* __HSR_SLAVE_H */ -- cgit v1.2.3 From f266a683a4804dc499efc6c2206ef68efed029d0 Mon Sep 17 00:00:00 2001 From: Arvid Brodin Date: Fri, 4 Jul 2014 23:41:03 +0200 Subject: net/hsr: Better frame dispatch This patch removes the separate paths for frames coming from the outside, and frames sent from the HSR device, and instead makes all frames go through hsr_forward_skb() in hsr_forward.c. This greatly improves code readability and also opens up the possibility for future support of the HSR Interlink device that is the basis for HSR RedBoxes and HSR QuadBoxes, as well as VLAN compatibility. Other improvements: * A reduction in the number of times an skb is copied on machines without HAVE_EFFICIENT_UNALIGNED_ACCESS, which improves throughput somewhat. * Headers are now created using the standard eth_header(), and using the standard hard_header_len. * Each HSR slave now gets its own private skb, so slave-specific fields can be correctly set. Signed-off-by: Arvid Brodin Signed-off-by: David S. Miller --- net/hsr/Makefile | 4 +- net/hsr/hsr_device.c | 170 ++++------------------- net/hsr/hsr_forward.c | 368 +++++++++++++++++++++++++++++++++++++++++++++++++ net/hsr/hsr_forward.h | 20 +++ net/hsr/hsr_framereg.c | 318 +++++++++++++++++++++--------------------- net/hsr/hsr_framereg.h | 25 ++-- net/hsr/hsr_main.c | 20 --- net/hsr/hsr_main.h | 16 ++- net/hsr/hsr_netlink.c | 2 +- net/hsr/hsr_slave.c | 287 +++++++------------------------------- net/hsr/hsr_slave.h | 12 +- 11 files changed, 656 insertions(+), 586 deletions(-) create mode 100644 net/hsr/hsr_forward.c create mode 100644 net/hsr/hsr_forward.h (limited to 'net/hsr/hsr_main.c') diff --git a/net/hsr/Makefile b/net/hsr/Makefile index b533b243a48b..9ae972a820f4 100644 --- a/net/hsr/Makefile +++ b/net/hsr/Makefile @@ -4,5 +4,5 @@ obj-$(CONFIG_HSR) += hsr.o -hsr-y := hsr_main.o hsr_framereg.o hsr_device.o hsr_netlink.o \ - hsr_slave.o +hsr-y := hsr_main.o hsr_framereg.o hsr_device.o \ + hsr_netlink.o hsr_slave.o hsr_forward.o diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c index c2ae54924a0f..a138d75751df 100644 --- a/net/hsr/hsr_device.c +++ b/net/hsr/hsr_device.c @@ -21,6 +21,7 @@ #include "hsr_slave.h" #include "hsr_framereg.h" #include "hsr_main.h" +#include "hsr_forward.h" static bool is_admin_up(struct net_device *dev) @@ -231,141 +232,21 @@ static netdev_features_t hsr_fix_features(struct net_device *dev, } -static void hsr_fill_tag(struct hsr_ethhdr *hsr_ethhdr, struct hsr_priv *hsr) -{ - unsigned long irqflags; - - /* IEC 62439-1:2010, p 48, says the 4-bit "path" field can take values - * between 0001-1001 ("ring identifier", for regular HSR frames), - * or 1111 ("HSR management", supervision frames). Unfortunately, the - * spec writers forgot to explain what a "ring identifier" is, or - * how it is used. So we just set this to 0001 for regular frames, - * and 1111 for supervision frames. - */ - set_hsr_tag_path(&hsr_ethhdr->hsr_tag, 0x1); - - /* IEC 62439-1:2010, p 12: "The link service data unit in an Ethernet - * frame is the content of the frame located between the Length/Type - * field and the Frame Check Sequence." - * - * IEC 62439-3, p 48, specifies the "original LPDU" to include the - * original "LT" field (what "LT" means is not explained anywhere as - * far as I can see - perhaps "Length/Type"?). So LSDU_size might - * equal original length + 2. - * Also, the fact that this field is not used anywhere (might be used - * by a RedBox connecting HSR and PRP nets?) means I cannot test its - * correctness. Instead of guessing, I set this to 0 here, to make any - * problems immediately apparent. Anyone using this driver with PRP/HSR - * RedBoxes might need to fix this... - */ - set_hsr_tag_LSDU_size(&hsr_ethhdr->hsr_tag, 0); - - spin_lock_irqsave(&hsr->seqnr_lock, irqflags); - hsr_ethhdr->hsr_tag.sequence_nr = htons(hsr->sequence_nr); - hsr->sequence_nr++; - spin_unlock_irqrestore(&hsr->seqnr_lock, irqflags); - - hsr_ethhdr->hsr_tag.encap_proto = hsr_ethhdr->ethhdr.h_proto; - - hsr_ethhdr->ethhdr.h_proto = htons(ETH_P_PRP); -} - -static int slave_xmit(struct hsr_priv *hsr, struct sk_buff *skb, - enum hsr_port_type type) -{ - struct hsr_port *port; - struct hsr_ethhdr *hsr_ethhdr; - - hsr_ethhdr = (struct hsr_ethhdr *) skb->data; - - rcu_read_lock(); - port = hsr_port_get_hsr(hsr, type); - if (!port) { - rcu_read_unlock(); - return NET_XMIT_DROP; - } - skb->dev = port->dev; - - hsr_addr_subst_dest(port->hsr, &hsr_ethhdr->ethhdr, port); - rcu_read_unlock(); - - /* Address substitution (IEC62439-3 pp 26, 50): replace mac - * address of outgoing frame with that of the outgoing slave's. - */ - ether_addr_copy(hsr_ethhdr->ethhdr.h_source, skb->dev->dev_addr); - - return dev_queue_xmit(skb); -} - static int hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev) { - struct hsr_priv *hsr; + struct hsr_priv *hsr = netdev_priv(dev); struct hsr_port *master; - struct hsr_ethhdr *hsr_ethhdr; - struct sk_buff *skb2; - int res1, res2; - - hsr = netdev_priv(dev); - hsr_ethhdr = (struct hsr_ethhdr *) skb->data; - - if ((skb->protocol != htons(ETH_P_PRP)) || - (hsr_ethhdr->ethhdr.h_proto != htons(ETH_P_PRP))) { - hsr_fill_tag(hsr_ethhdr, hsr); - skb->protocol = htons(ETH_P_PRP); - } - skb2 = pskb_copy(skb, GFP_ATOMIC); - - res1 = slave_xmit(hsr, skb, HSR_PT_SLAVE_A); - if (skb2) - res2 = slave_xmit(hsr, skb2, HSR_PT_SLAVE_B); - else - res2 = NET_XMIT_DROP; - - rcu_read_lock(); master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); - if (likely(res1 == NET_XMIT_SUCCESS || res1 == NET_XMIT_CN || - res2 == NET_XMIT_SUCCESS || res2 == NET_XMIT_CN)) { - master->dev->stats.tx_packets++; - master->dev->stats.tx_bytes += skb->len; - } else { - master->dev->stats.tx_dropped++; - } - rcu_read_unlock(); + skb->dev = master->dev; + hsr_forward_skb(skb, master); return NETDEV_TX_OK; } -static int hsr_header_create(struct sk_buff *skb, struct net_device *dev, - unsigned short type, const void *daddr, - const void *saddr, unsigned int len) -{ - int res; - - /* Make room for the HSR tag now. We will fill it in later (in - * hsr_dev_xmit) - */ - if (skb_headroom(skb) < HSR_HLEN + ETH_HLEN) - return -ENOBUFS; - skb_push(skb, HSR_HLEN); - - /* To allow VLAN/HSR combos we should probably use - * res = dev_hard_header(skb, dev, type, daddr, saddr, len + HSR_HLEN); - * here instead. It would require other changes too, though - e.g. - * separate headers for each slave etc... - */ - res = eth_header(skb, dev, type, daddr, saddr, len + HSR_HLEN); - if (res <= 0) - return res; - skb_reset_mac_header(skb); - - return res + HSR_HLEN; -} - - static const struct header_ops hsr_header_ops = { - .create = hsr_header_create, + .create = eth_header, .parse = eth_header_parse, }; @@ -382,19 +263,13 @@ static int hsr_pad(int size) return min_size; } -static void send_hsr_supervision_frame(struct net_device *hsr_dev, u8 type) +static void send_hsr_supervision_frame(struct hsr_port *master, u8 type) { - struct hsr_priv *hsr; - struct hsr_port *master; struct sk_buff *skb; int hlen, tlen; struct hsr_sup_tag *hsr_stag; struct hsr_sup_payload *hsr_sp; unsigned long irqflags; - int res; - - hsr = netdev_priv(hsr_dev); - master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); hlen = LL_RESERVED_SPACE(master->dev); tlen = master->dev->needed_tailroom; @@ -410,33 +285,30 @@ static void send_hsr_supervision_frame(struct net_device *hsr_dev, u8 type) skb->protocol = htons(ETH_P_PRP); skb->priority = TC_PRIO_CONTROL; - res = dev_hard_header(skb, skb->dev, ETH_P_PRP, - hsr->sup_multicast_addr, - skb->dev->dev_addr, skb->len); - if (res <= 0) + if (dev_hard_header(skb, skb->dev, ETH_P_PRP, + master->hsr->sup_multicast_addr, + skb->dev->dev_addr, skb->len) <= 0) goto out; + skb_reset_mac_header(skb); - skb_pull(skb, sizeof(struct ethhdr)); - hsr_stag = (typeof(hsr_stag)) skb->data; + hsr_stag = (typeof(hsr_stag)) skb_put(skb, sizeof(*hsr_stag)); set_hsr_stag_path(hsr_stag, 0xf); set_hsr_stag_HSR_Ver(hsr_stag, 0); - spin_lock_irqsave(&hsr->seqnr_lock, irqflags); - hsr_stag->sequence_nr = htons(hsr->sequence_nr); - hsr->sequence_nr++; - spin_unlock_irqrestore(&hsr->seqnr_lock, irqflags); + spin_lock_irqsave(&master->hsr->seqnr_lock, irqflags); + hsr_stag->sequence_nr = htons(master->hsr->sequence_nr); + master->hsr->sequence_nr++; + spin_unlock_irqrestore(&master->hsr->seqnr_lock, irqflags); hsr_stag->HSR_TLV_Type = type; hsr_stag->HSR_TLV_Length = 12; - skb_push(skb, sizeof(struct ethhdr)); - /* Payload: MacAddressA */ hsr_sp = (typeof(hsr_sp)) skb_put(skb, sizeof(*hsr_sp)); ether_addr_copy(hsr_sp->MacAddressA, master->dev->dev_addr); - dev_queue_xmit(skb); + hsr_forward_skb(skb, master); return; out: @@ -453,13 +325,15 @@ static void hsr_announce(unsigned long data) struct hsr_port *master; hsr = (struct hsr_priv *) data; + + rcu_read_lock(); master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); if (hsr->announce_count < 3) { - send_hsr_supervision_frame(master->dev, HSR_TLV_ANNOUNCE); + send_hsr_supervision_frame(master, HSR_TLV_ANNOUNCE); hsr->announce_count++; } else { - send_hsr_supervision_frame(master->dev, HSR_TLV_LIFE_CHECK); + send_hsr_supervision_frame(master, HSR_TLV_LIFE_CHECK); } if (hsr->announce_count < 3) @@ -471,6 +345,8 @@ static void hsr_announce(unsigned long data) if (is_admin_up(master->dev)) add_timer(&hsr->announce_timer); + + rcu_read_unlock(); } @@ -570,7 +446,7 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2], spin_lock_init(&hsr->seqnr_lock); /* Overflow soon to find bugs easier: */ - hsr->sequence_nr = USHRT_MAX - 1024; + hsr->sequence_nr = HSR_SEQNR_START; init_timer(&hsr->announce_timer); hsr->announce_timer.function = hsr_announce; diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c new file mode 100644 index 000000000000..7871ed6d3825 --- /dev/null +++ b/net/hsr/hsr_forward.c @@ -0,0 +1,368 @@ +/* Copyright 2011-2014 Autronica Fire and Security AS + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Author(s): + * 2011-2014 Arvid Brodin, arvid.brodin@alten.se + */ + +#include "hsr_forward.h" +#include +#include +#include +#include +#include "hsr_main.h" +#include "hsr_framereg.h" + + +struct hsr_node; + +struct hsr_frame_info { + struct sk_buff *skb_std; + struct sk_buff *skb_hsr; + struct hsr_port *port_rcv; + struct hsr_node *node_src; + u16 sequence_nr; + bool is_supervision; + bool is_vlan; + bool is_local_dest; + bool is_local_exclusive; +}; + + +/* The uses I can see for these HSR supervision frames are: + * 1) Use the frames that are sent after node initialization ("HSR_TLV.Type = + * 22") to reset any sequence_nr counters belonging to that node. Useful if + * the other node's counter has been reset for some reason. + * -- + * Or not - resetting the counter and bridging the frame would create a + * loop, unfortunately. + * + * 2) Use the LifeCheck frames to detect ring breaks. I.e. if no LifeCheck + * frame is received from a particular node, we know something is wrong. + * We just register these (as with normal frames) and throw them away. + * + * 3) Allow different MAC addresses for the two slave interfaces, using the + * MacAddressA field. + */ +static bool is_supervision_frame(struct hsr_priv *hsr, struct sk_buff *skb) +{ + struct hsr_ethhdr_sp *hdr; + + WARN_ON_ONCE(!skb_mac_header_was_set(skb)); + hdr = (struct hsr_ethhdr_sp *) skb_mac_header(skb); + + if (!ether_addr_equal(hdr->ethhdr.h_dest, + hsr->sup_multicast_addr)) + return false; + + if (get_hsr_stag_path(&hdr->hsr_sup) != 0x0f) + return false; + if ((hdr->hsr_sup.HSR_TLV_Type != HSR_TLV_ANNOUNCE) && + (hdr->hsr_sup.HSR_TLV_Type != HSR_TLV_LIFE_CHECK)) + return false; + if (hdr->hsr_sup.HSR_TLV_Length != 12) + return false; + + return true; +} + + +static struct sk_buff *create_stripped_skb(struct sk_buff *skb_in, + struct hsr_frame_info *frame) +{ + struct sk_buff *skb; + int copylen; + unsigned char *dst, *src; + + skb_pull(skb_in, HSR_HLEN); + skb = __pskb_copy(skb_in, skb_headroom(skb_in) - HSR_HLEN, GFP_ATOMIC); + skb_push(skb_in, HSR_HLEN); + if (skb == NULL) + return NULL; + + skb_reset_mac_header(skb); + + if (skb->ip_summed == CHECKSUM_PARTIAL) + skb->csum_start -= HSR_HLEN; + + copylen = 2*ETH_ALEN; + if (frame->is_vlan) + copylen += VLAN_HLEN; + src = skb_mac_header(skb_in); + dst = skb_mac_header(skb); + memcpy(dst, src, copylen); + + skb->protocol = eth_hdr(skb)->h_proto; + return skb; +} + +static struct sk_buff *frame_get_stripped_skb(struct hsr_frame_info *frame, + struct hsr_port *port) +{ + if (!frame->skb_std) + frame->skb_std = create_stripped_skb(frame->skb_hsr, frame); + return skb_clone(frame->skb_std, GFP_ATOMIC); +} + + +static void hsr_fill_tag(struct sk_buff *skb, struct hsr_frame_info *frame, + struct hsr_port *port) +{ + struct hsr_ethhdr *hsr_ethhdr; + int lane_id; + int lsdu_size; + + if (port->type == HSR_PT_SLAVE_A) + lane_id = 0; + else + lane_id = 1; + + lsdu_size = skb->len - 14; + if (frame->is_vlan) + lsdu_size -= 4; + + hsr_ethhdr = (struct hsr_ethhdr *) skb_mac_header(skb); + + set_hsr_tag_path(&hsr_ethhdr->hsr_tag, lane_id); + set_hsr_tag_LSDU_size(&hsr_ethhdr->hsr_tag, lsdu_size); + hsr_ethhdr->hsr_tag.sequence_nr = htons(frame->sequence_nr); + hsr_ethhdr->hsr_tag.encap_proto = hsr_ethhdr->ethhdr.h_proto; + hsr_ethhdr->ethhdr.h_proto = htons(ETH_P_PRP); +} + +static struct sk_buff *create_tagged_skb(struct sk_buff *skb_o, + struct hsr_frame_info *frame, + struct hsr_port *port) +{ + int movelen; + unsigned char *dst, *src; + struct sk_buff *skb; + + /* Create the new skb with enough headroom to fit the HSR tag */ + skb = __pskb_copy(skb_o, skb_headroom(skb_o) + HSR_HLEN, GFP_ATOMIC); + if (skb == NULL) + return NULL; + skb_reset_mac_header(skb); + + if (skb->ip_summed == CHECKSUM_PARTIAL) + skb->csum_start += HSR_HLEN; + + movelen = ETH_HLEN; + if (frame->is_vlan) + movelen += VLAN_HLEN; + + src = skb_mac_header(skb); + dst = skb_push(skb, HSR_HLEN); + memmove(dst, src, movelen); + skb_reset_mac_header(skb); + + hsr_fill_tag(skb, frame, port); + + return skb; +} + +/* If the original frame was an HSR tagged frame, just clone it to be sent + * unchanged. Otherwise, create a private frame especially tagged for 'port'. + */ +static struct sk_buff *frame_get_tagged_skb(struct hsr_frame_info *frame, + struct hsr_port *port) +{ + if (frame->skb_hsr) + return skb_clone(frame->skb_hsr, GFP_ATOMIC); + + if ((port->type != HSR_PT_SLAVE_A) && (port->type != HSR_PT_SLAVE_B)) { + WARN_ONCE(1, "HSR: Bug: trying to create a tagged frame for a non-ring port"); + return NULL; + } + + return create_tagged_skb(frame->skb_std, frame, port); +} + + +static void hsr_deliver_master(struct sk_buff *skb, struct net_device *dev, + struct hsr_node *node_src) +{ + bool was_multicast_frame; + int res; + + was_multicast_frame = (skb->pkt_type == PACKET_MULTICAST); + hsr_addr_subst_source(node_src, skb); + skb_pull(skb, ETH_HLEN); + res = netif_rx(skb); + if (res == NET_RX_DROP) { + dev->stats.rx_dropped++; + } else { + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + if (was_multicast_frame) + dev->stats.multicast++; + } +} + +static int hsr_xmit(struct sk_buff *skb, struct hsr_port *port, + struct hsr_frame_info *frame) +{ + if (frame->port_rcv->type == HSR_PT_MASTER) { + hsr_addr_subst_dest(frame->node_src, skb, port); + + /* Address substitution (IEC62439-3 pp 26, 50): replace mac + * address of outgoing frame with that of the outgoing slave's. + */ + ether_addr_copy(eth_hdr(skb)->h_source, port->dev->dev_addr); + } + return dev_queue_xmit(skb); +} + + +/* Forward the frame through all devices except: + * - Back through the receiving device + * - If it's a HSR frame: through a device where it has passed before + * - To the local HSR master only if the frame is directly addressed to it, or + * a non-supervision multicast or broadcast frame. + * + * HSR slave devices should insert a HSR tag into the frame, or forward the + * frame unchanged if it's already tagged. Interlink devices should strip HSR + * tags if they're of the non-HSR type (but only after duplicate discard). The + * master device always strips HSR tags. + */ +static void hsr_forward_do(struct hsr_frame_info *frame) +{ + struct hsr_port *port; + struct sk_buff *skb; + + hsr_for_each_port(frame->port_rcv->hsr, port) { + /* Don't send frame back the way it came */ + if (port == frame->port_rcv) + continue; + + /* Don't deliver locally unless we should */ + if ((port->type == HSR_PT_MASTER) && !frame->is_local_dest) + continue; + + /* Deliver frames directly addressed to us to master only */ + if ((port->type != HSR_PT_MASTER) && frame->is_local_exclusive) + continue; + + /* Don't send frame over port where it has been sent before */ + if (hsr_register_frame_out(port, frame->node_src, + frame->sequence_nr)) + continue; + + if (frame->is_supervision && (port->type == HSR_PT_MASTER)) { + hsr_handle_sup_frame(frame->skb_hsr, + frame->node_src, + frame->port_rcv); + continue; + } + + if (port->type != HSR_PT_MASTER) + skb = frame_get_tagged_skb(frame, port); + else + skb = frame_get_stripped_skb(frame, port); + if (skb == NULL) { + /* FIXME: Record the dropped frame? */ + continue; + } + + skb->dev = port->dev; + if (port->type == HSR_PT_MASTER) + hsr_deliver_master(skb, port->dev, frame->node_src); + else + hsr_xmit(skb, port, frame); + } +} + + +static void check_local_dest(struct hsr_priv *hsr, struct sk_buff *skb, + struct hsr_frame_info *frame) +{ + struct net_device *master_dev; + + master_dev = hsr_port_get_hsr(hsr, HSR_PT_MASTER)->dev; + + if (hsr_addr_is_self(hsr, eth_hdr(skb)->h_dest)) { + frame->is_local_exclusive = true; + skb->pkt_type = PACKET_HOST; + } else { + frame->is_local_exclusive = false; + } + + if ((skb->pkt_type == PACKET_HOST) || + (skb->pkt_type == PACKET_MULTICAST) || + (skb->pkt_type == PACKET_BROADCAST)) { + frame->is_local_dest = true; + } else { + frame->is_local_dest = false; + } +} + + +static int hsr_fill_frame_info(struct hsr_frame_info *frame, + struct sk_buff *skb, struct hsr_port *port) +{ + struct ethhdr *ethhdr; + unsigned long irqflags; + + frame->is_supervision = is_supervision_frame(port->hsr, skb); + frame->node_src = hsr_get_node(&port->hsr->node_db, skb, + frame->is_supervision); + if (frame->node_src == NULL) + return -1; /* Unknown node and !is_supervision, or no mem */ + + ethhdr = (struct ethhdr *) skb_mac_header(skb); + frame->is_vlan = false; + if (ethhdr->h_proto == htons(ETH_P_8021Q)) { + frame->is_vlan = true; + /* FIXME: */ + WARN_ONCE(1, "HSR: VLAN not yet supported"); + } + if (ethhdr->h_proto == htons(ETH_P_PRP)) { + frame->skb_std = NULL; + frame->skb_hsr = skb; + frame->sequence_nr = hsr_get_skb_sequence_nr(skb); + } else { + frame->skb_std = skb; + frame->skb_hsr = NULL; + /* Sequence nr for the master node */ + spin_lock_irqsave(&port->hsr->seqnr_lock, irqflags); + frame->sequence_nr = port->hsr->sequence_nr; + port->hsr->sequence_nr++; + spin_unlock_irqrestore(&port->hsr->seqnr_lock, irqflags); + } + + frame->port_rcv = port; + check_local_dest(port->hsr, skb, frame); + + return 0; +} + +/* Must be called holding rcu read lock (because of the port parameter) */ +void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port) +{ + struct hsr_frame_info frame; + + if (skb_mac_header(skb) != skb->data) { + WARN_ONCE(1, "%s:%d: Malformed frame (port_src %s)\n", + __FILE__, __LINE__, port->dev->name); + goto out_drop; + } + + if (hsr_fill_frame_info(&frame, skb, port) < 0) + goto out_drop; + hsr_register_frame_in(frame.node_src, port, frame.sequence_nr); + hsr_forward_do(&frame); + + if (frame.skb_hsr != NULL) + kfree_skb(frame.skb_hsr); + if (frame.skb_std != NULL) + kfree_skb(frame.skb_std); + return; + +out_drop: + port->dev->stats.tx_dropped++; + kfree_skb(skb); +} diff --git a/net/hsr/hsr_forward.h b/net/hsr/hsr_forward.h new file mode 100644 index 000000000000..5c5bc4b6b75f --- /dev/null +++ b/net/hsr/hsr_forward.h @@ -0,0 +1,20 @@ +/* Copyright 2011-2014 Autronica Fire and Security AS + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Author(s): + * 2011-2014 Arvid Brodin, arvid.brodin@alten.se + */ + +#ifndef __HSR_FORWARD_H +#define __HSR_FORWARD_H + +#include +#include "hsr_main.h" + +void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port); + +#endif /* __HSR_FORWARD_H */ diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c index c9b78c52c75b..c709c13842cb 100644 --- a/net/hsr/hsr_framereg.c +++ b/net/hsr/hsr_framereg.c @@ -35,58 +35,56 @@ struct hsr_node { struct rcu_head rcu_head; }; -/* TODO: use hash lists for mac addresses (linux/jhash.h)? */ +/* TODO: use hash lists for mac addresses (linux/jhash.h)? */ -/* Search for mac entry. Caller must hold rcu read lock. +/* seq_nr_after(a, b) - return true if a is after (higher in sequence than) b, + * false otherwise. */ -static struct hsr_node *find_node_by_AddrA(struct list_head *node_db, - const unsigned char addr[ETH_ALEN]) +static bool seq_nr_after(u16 a, u16 b) { - struct hsr_node *node; - - list_for_each_entry_rcu(node, node_db, mac_list) { - if (ether_addr_equal(node->MacAddressA, addr)) - return node; - } + /* Remove inconsistency where + * seq_nr_after(a, b) == seq_nr_before(a, b) + */ + if ((int) b - a == 32768) + return false; - return NULL; + return (((s16) (b - a)) < 0); } +#define seq_nr_before(a, b) seq_nr_after((b), (a)) +#define seq_nr_after_or_eq(a, b) (!seq_nr_before((a), (b))) +#define seq_nr_before_or_eq(a, b) (!seq_nr_after((a), (b))) -/* Search for mac entry. Caller must hold rcu read lock. - */ -static struct hsr_node *find_node_by_AddrB(struct list_head *node_db, - const unsigned char addr[ETH_ALEN]) +bool hsr_addr_is_self(struct hsr_priv *hsr, unsigned char *addr) { struct hsr_node *node; - list_for_each_entry_rcu(node, node_db, mac_list) { - if (ether_addr_equal(node->MacAddressB, addr)) - return node; + node = list_first_or_null_rcu(&hsr->self_node_db, struct hsr_node, + mac_list); + if (!node) { + WARN_ONCE(1, "HSR: No self node\n"); + return false; } - return NULL; -} + if (ether_addr_equal(addr, node->MacAddressA)) + return true; + if (ether_addr_equal(addr, node->MacAddressB)) + return true; + return false; +} /* Search for mac entry. Caller must hold rcu read lock. */ -struct hsr_node *hsr_find_node(struct list_head *node_db, struct sk_buff *skb) +static struct hsr_node *find_node_by_AddrA(struct list_head *node_db, + const unsigned char addr[ETH_ALEN]) { struct hsr_node *node; - struct ethhdr *ethhdr; - - if (!skb_mac_header_was_set(skb)) - return NULL; - - ethhdr = (struct ethhdr *) skb_mac_header(skb); list_for_each_entry_rcu(node, node_db, mac_list) { - if (ether_addr_equal(node->MacAddressA, ethhdr->h_source)) - return node; - if (ether_addr_equal(node->MacAddressB, ethhdr->h_source)) + if (ether_addr_equal(node->MacAddressA, addr)) return node; } @@ -127,90 +125,22 @@ int hsr_create_self_node(struct list_head *self_node_db, } -/* Add/merge node to the database of nodes. 'skb' must contain an HSR - * supervision frame. - * - If the supervision header's MacAddressA field is not yet in the database, - * this frame is from an hitherto unknown node - add it to the database. - * - If the sender's MAC address is not the same as its MacAddressA address, - * the node is using PICS_SUBS (address substitution). Record the sender's - * address as the node's MacAddressB. - * - * This function needs to work even if the sender node has changed one of its - * slaves' MAC addresses. In this case, there are four different cases described - * by (Addr-changed, received-from) pairs as follows. Note that changing the - * SlaveA address is equal to changing the node's own address: - * - * - (AddrB, SlaveB): The new AddrB will be recorded by PICS_SUBS code since - * node == NULL. - * - (AddrB, SlaveA): Will work as usual (the AddrB change won't be detected - * from this frame). - * - * - (AddrA, SlaveB): The old node will be found. We need to detect this and - * remove the node. - * - (AddrA, SlaveA): A new node will be registered (non-PICS_SUBS at first). - * The old one will be pruned after HSR_NODE_FORGET_TIME. - * - * We also need to detect if the sender's SlaveA and SlaveB cables have been - * swapped. +/* Allocate an hsr_node and add it to node_db. 'addr' is the node's AddressA; + * seq_out is used to initialize filtering of outgoing duplicate frames + * originating from the newly added node. */ -struct hsr_node *hsr_merge_node(struct hsr_node *node, struct sk_buff *skb, - struct hsr_port *port) +struct hsr_node *hsr_add_node(struct list_head *node_db, unsigned char addr[], + u16 seq_out) { - struct hsr_priv *hsr; - struct hsr_sup_payload *hsr_sp; - struct hsr_ethhdr_sp *hsr_ethsup; - int i; + struct hsr_node *node; unsigned long now; - - hsr_ethsup = (struct hsr_ethhdr_sp *) skb_mac_header(skb); - hsr_sp = (struct hsr_sup_payload *) skb->data; - hsr = port->hsr; - - if (node && !ether_addr_equal(node->MacAddressA, hsr_sp->MacAddressA)) { - /* Node has changed its AddrA, frame was received from SlaveB */ - list_del_rcu(&node->mac_list); - kfree_rcu(node, rcu_head); - node = NULL; - } - - if (node && (port->type == node->AddrB_port) && - !ether_addr_equal(node->MacAddressB, hsr_ethsup->ethhdr.h_source)) { - /* Cables have been swapped */ - list_del_rcu(&node->mac_list); - kfree_rcu(node, rcu_head); - node = NULL; - } - - if (node && (port->type != node->AddrB_port) && - (node->AddrB_port != HSR_PT_NONE) && - !ether_addr_equal(node->MacAddressA, hsr_ethsup->ethhdr.h_source)) { - /* Cables have been swapped */ - list_del_rcu(&node->mac_list); - kfree_rcu(node, rcu_head); - node = NULL; - } - - if (node) - return node; - - node = find_node_by_AddrA(&hsr->node_db, hsr_sp->MacAddressA); - if (node) { - /* Node is known, but frame was received from an unknown - * address. Node is PICS_SUBS capable; merge its AddrB. - */ - ether_addr_copy(node->MacAddressB, hsr_ethsup->ethhdr.h_source); - node->AddrB_port = port->type; - return node; - } + int i; node = kzalloc(sizeof(*node), GFP_ATOMIC); if (!node) return NULL; - ether_addr_copy(node->MacAddressA, hsr_sp->MacAddressA); - ether_addr_copy(node->MacAddressB, hsr_ethsup->ethhdr.h_source); - if (!ether_addr_equal(hsr_sp->MacAddressA, hsr_ethsup->ethhdr.h_source)) - node->AddrB_port = port->type; + ether_addr_copy(node->MacAddressA, addr); /* We are only interested in time diffs here, so use current jiffies * as initialization. (0 could trigger an spurious ring error warning). @@ -219,41 +149,120 @@ struct hsr_node *hsr_merge_node(struct hsr_node *node, struct sk_buff *skb, for (i = 0; i < HSR_PT_PORTS; i++) node->time_in[i] = now; for (i = 0; i < HSR_PT_PORTS; i++) - node->seq_out[i] = ntohs(hsr_ethsup->hsr_sup.sequence_nr) - 1; + node->seq_out[i] = seq_out; - list_add_tail_rcu(&node->mac_list, &hsr->node_db); + list_add_tail_rcu(&node->mac_list, node_db); return node; } +/* Get the hsr_node from which 'skb' was sent. + */ +struct hsr_node *hsr_get_node(struct list_head *node_db, struct sk_buff *skb, + bool is_sup) +{ + struct hsr_node *node; + struct ethhdr *ethhdr; + u16 seq_out; + + if (!skb_mac_header_was_set(skb)) + return NULL; + + ethhdr = (struct ethhdr *) skb_mac_header(skb); + + list_for_each_entry_rcu(node, node_db, mac_list) { + if (ether_addr_equal(node->MacAddressA, ethhdr->h_source)) + return node; + if (ether_addr_equal(node->MacAddressB, ethhdr->h_source)) + return node; + } + + if (!is_sup) + return NULL; /* Only supervision frame may create node entry */ + + if (ethhdr->h_proto == htons(ETH_P_PRP)) { + /* Use the existing sequence_nr from the tag as starting point + * for filtering duplicate frames. + */ + seq_out = hsr_get_skb_sequence_nr(skb) - 1; + } else { + WARN_ONCE(1, "%s: Non-HSR frame\n", __func__); + seq_out = 0; + } + + return hsr_add_node(node_db, ethhdr->h_source, seq_out); +} + +/* Use the Supervision frame's info about an eventual MacAddressB for merging + * nodes that has previously had their MacAddressB registered as a separate + * node. + */ +void hsr_handle_sup_frame(struct sk_buff *skb, struct hsr_node *node_curr, + struct hsr_port *port_rcv) +{ + struct hsr_node *node_real; + struct hsr_sup_payload *hsr_sp; + struct list_head *node_db; + int i; + + skb_pull(skb, sizeof(struct hsr_ethhdr_sp)); + hsr_sp = (struct hsr_sup_payload *) skb->data; + + if (ether_addr_equal(eth_hdr(skb)->h_source, hsr_sp->MacAddressA)) + /* Not sent from MacAddressB of a PICS_SUBS capable node */ + goto done; + + /* Merge node_curr (registered on MacAddressB) into node_real */ + node_db = &port_rcv->hsr->node_db; + node_real = find_node_by_AddrA(node_db, hsr_sp->MacAddressA); + if (!node_real) + /* No frame received from AddrA of this node yet */ + node_real = hsr_add_node(node_db, hsr_sp->MacAddressA, + HSR_SEQNR_START - 1); + if (!node_real) + goto done; /* No mem */ + if (node_real == node_curr) + /* Node has already been merged */ + goto done; + + ether_addr_copy(node_real->MacAddressB, eth_hdr(skb)->h_source); + for (i = 0; i < HSR_PT_PORTS; i++) { + if (!node_curr->time_in_stale[i] && + time_after(node_curr->time_in[i], node_real->time_in[i])) { + node_real->time_in[i] = node_curr->time_in[i]; + node_real->time_in_stale[i] = node_curr->time_in_stale[i]; + } + if (seq_nr_after(node_curr->seq_out[i], node_real->seq_out[i])) + node_real->seq_out[i] = node_curr->seq_out[i]; + } + node_real->AddrB_port = port_rcv->type; + + list_del_rcu(&node_curr->mac_list); + kfree_rcu(node_curr, rcu_head); + +done: + skb_push(skb, sizeof(struct hsr_ethhdr_sp)); +} + /* 'skb' is a frame meant for this host, that is to be passed to upper layers. * - * If the frame was sent by a node's B interface, replace the sender + * If the frame was sent by a node's B interface, replace the source * address with that node's "official" address (MacAddressA) so that upper * layers recognize where it came from. */ -void hsr_addr_subst_source(struct hsr_priv *hsr, struct sk_buff *skb) +void hsr_addr_subst_source(struct hsr_node *node, struct sk_buff *skb) { - struct ethhdr *ethhdr; - struct hsr_node *node; - if (!skb_mac_header_was_set(skb)) { WARN_ONCE(1, "%s: Mac header not set\n", __func__); return; } - ethhdr = (struct ethhdr *) skb_mac_header(skb); - rcu_read_lock(); - node = find_node_by_AddrB(&hsr->node_db, ethhdr->h_source); - if (node) - ether_addr_copy(ethhdr->h_source, node->MacAddressA); - rcu_read_unlock(); + memcpy(ð_hdr(skb)->h_source, node->MacAddressA, ETH_ALEN); } - /* 'skb' is a frame meant for another host. - * 'hsr_dev_idx' is the HSR index of the outgoing device + * 'port' is the outgoing interface * * Substitute the target (dest) MAC address if necessary, so the it matches the * recipient interface MAC address, regardless of whether that is the @@ -261,44 +270,49 @@ void hsr_addr_subst_source(struct hsr_priv *hsr, struct sk_buff *skb) * This is needed to keep the packets flowing through switches that learn on * which "side" the different interfaces are. */ -void hsr_addr_subst_dest(struct hsr_priv *hsr, struct ethhdr *ethhdr, +void hsr_addr_subst_dest(struct hsr_node *node_src, struct sk_buff *skb, struct hsr_port *port) { - struct hsr_node *node; + struct hsr_node *node_dst; - rcu_read_lock(); - node = find_node_by_AddrA(&hsr->node_db, ethhdr->h_dest); - if (node && (node->AddrB_port == port->type)) - ether_addr_copy(ethhdr->h_dest, node->MacAddressB); - rcu_read_unlock(); -} + if (!skb_mac_header_was_set(skb)) { + WARN_ONCE(1, "%s: Mac header not set\n", __func__); + return; + } + if (!is_unicast_ether_addr(eth_hdr(skb)->h_dest)) + return; -/* seq_nr_after(a, b) - return true if a is after (higher in sequence than) b, - * false otherwise. - */ -static bool seq_nr_after(u16 a, u16 b) -{ - /* Remove inconsistency where - * seq_nr_after(a, b) == seq_nr_before(a, b) - */ - if ((int) b - a == 32768) - return false; + node_dst = find_node_by_AddrA(&port->hsr->node_db, eth_hdr(skb)->h_dest); + if (!node_dst) { + WARN_ONCE(1, "%s: Unknown node\n", __func__); + return; + } + if (port->type != node_dst->AddrB_port) + return; + if (!node_dst->MacAddressB) { + WARN_ONCE(1, "%s: No MacAddressB\n", __func__); + return; + } - return (((s16) (b - a)) < 0); + ether_addr_copy(eth_hdr(skb)->h_dest, node_dst->MacAddressB); } -#define seq_nr_before(a, b) seq_nr_after((b), (a)) -#define seq_nr_after_or_eq(a, b) (!seq_nr_before((a), (b))) -#define seq_nr_before_or_eq(a, b) (!seq_nr_after((a), (b))) -void hsr_register_frame_in(struct hsr_node *node, struct hsr_port *port) +void hsr_register_frame_in(struct hsr_node *node, struct hsr_port *port, + u16 sequence_nr) { + /* Don't register incoming frames without a valid sequence number. This + * ensures entries of restarted nodes gets pruned so that they can + * re-register and resume communications. + */ + if (seq_nr_before(sequence_nr, node->seq_out[port->type])) + return; + node->time_in[port->type] = jiffies; node->time_in_stale[port->type] = false; } - /* 'skb' is a HSR Ethernet frame (with a HSR tag inserted), with a valid * ethhdr->h_source address and skb->mac_header set. * @@ -307,19 +321,9 @@ 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_node *node, struct hsr_port *port, - struct sk_buff *skb) +int hsr_register_frame_out(struct hsr_port *port, struct hsr_node *node, + u16 sequence_nr) { - struct hsr_ethhdr *hsr_ethhdr; - u16 sequence_nr; - - if (!skb_mac_header_was_set(skb)) { - WARN_ONCE(1, "%s: Mac header not set\n", __func__); - return -EINVAL; - } - hsr_ethhdr = (struct hsr_ethhdr *) skb_mac_header(skb); - - sequence_nr = ntohs(hsr_ethhdr->hsr_tag.sequence_nr); if (seq_nr_before_or_eq(sequence_nr, node->seq_out[port->type])) return 1; diff --git a/net/hsr/hsr_framereg.h b/net/hsr/hsr_framereg.h index c87f36fc154c..438b40f98f5a 100644 --- a/net/hsr/hsr_framereg.h +++ b/net/hsr/hsr_framereg.h @@ -16,19 +16,22 @@ struct hsr_node; -struct hsr_node *hsr_find_node(struct list_head *node_db, struct sk_buff *skb); - -struct hsr_node *hsr_merge_node(struct hsr_node *node, struct sk_buff *skb, - struct hsr_port *port); - -void hsr_addr_subst_source(struct hsr_priv *hsr, struct sk_buff *skb); -void hsr_addr_subst_dest(struct hsr_priv *hsr, struct ethhdr *ethhdr, +struct hsr_node *hsr_add_node(struct list_head *node_db, unsigned char addr[], + u16 seq_out); +struct hsr_node *hsr_get_node(struct list_head *node_db, struct sk_buff *skb, + bool is_sup); +void hsr_handle_sup_frame(struct sk_buff *skb, struct hsr_node *node_curr, + struct hsr_port *port); +bool hsr_addr_is_self(struct hsr_priv *hsr, unsigned char *addr); + +void hsr_addr_subst_source(struct hsr_node *node, struct sk_buff *skb); +void hsr_addr_subst_dest(struct hsr_node *node_src, struct sk_buff *skb, struct hsr_port *port); -void hsr_register_frame_in(struct hsr_node *node, struct hsr_port *port); - -int hsr_register_frame_out(struct hsr_node *node, struct hsr_port *port, - 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); void hsr_prune_nodes(unsigned long data); diff --git a/net/hsr/hsr_main.c b/net/hsr/hsr_main.c index a06cab57ab68..779d28b65417 100644 --- a/net/hsr/hsr_main.c +++ b/net/hsr/hsr_main.c @@ -20,26 +20,6 @@ #include "hsr_slave.h" -/* List of all registered virtual HSR devices */ -static LIST_HEAD(hsr_list); - -void register_hsr_master(struct hsr_priv *hsr) -{ - list_add_tail_rcu(&hsr->hsr_list, &hsr_list); -} - -void unregister_hsr_master(struct hsr_priv *hsr) -{ - struct hsr_priv *hsr_it; - - list_for_each_entry(hsr_it, &hsr_list, hsr_list) - if (hsr_it == hsr) { - list_del_rcu(&hsr_it->hsr_list); - return; - } -} - - static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event, void *ptr) { diff --git a/net/hsr/hsr_main.h b/net/hsr/hsr_main.h index e31c3069fdf8..5a9c69962ded 100644 --- a/net/hsr/hsr_main.h +++ b/net/hsr/hsr_main.h @@ -29,6 +29,7 @@ * each node differ before we notify of communication problem? */ #define MAX_SLAVE_DIFF 3000 /* ms */ +#define HSR_SEQNR_START (USHRT_MAX - 1024) /* How often shall we check for broken ring and remove node entries older than @@ -153,10 +154,9 @@ struct hsr_port { }; struct hsr_priv { - struct list_head hsr_list; /* List of hsr devices */ struct rcu_head rcu_head; struct list_head ports; - struct list_head node_db; /* Other HSR nodes */ + struct list_head node_db; /* Known HSR nodes */ struct list_head self_node_db; /* MACs of slaves */ struct timer_list announce_timer; /* Supervision frame dispatch */ struct timer_list prune_timer; @@ -166,6 +166,18 @@ struct hsr_priv { unsigned char sup_multicast_addr[ETH_ALEN]; }; +#define hsr_for_each_port(hsr, port) \ + list_for_each_entry_rcu((port), &(hsr)->ports, port_list) + struct hsr_port *hsr_port_get_hsr(struct hsr_priv *hsr, enum hsr_port_type pt); +/* Caller must ensure skb is a valid HSR frame */ +static inline u16 hsr_get_skb_sequence_nr(struct sk_buff *skb) +{ + struct hsr_ethhdr *hsr_ethhdr; + + hsr_ethhdr = (struct hsr_ethhdr *) skb_mac_header(skb); + return ntohs(hsr_ethhdr->hsr_tag.sequence_nr); +} + #endif /* __HSR_PRIVATE_H */ diff --git a/net/hsr/hsr_netlink.c b/net/hsr/hsr_netlink.c index 67082453928c..fbdf53f1d874 100644 --- a/net/hsr/hsr_netlink.c +++ b/net/hsr/hsr_netlink.c @@ -358,7 +358,7 @@ fail: return res; } -/* Get a list of MacAddressA of all nodes known to this node (other than self). +/* Get a list of MacAddressA of all nodes known to this node (including self). */ static int hsr_get_node_list(struct sk_buff *skb_in, struct genl_info *info) { diff --git a/net/hsr/hsr_slave.c b/net/hsr/hsr_slave.c index 23817d0b765b..a348dcbcd683 100644 --- a/net/hsr/hsr_slave.c +++ b/net/hsr/hsr_slave.c @@ -14,9 +14,51 @@ #include #include "hsr_main.h" #include "hsr_device.h" +#include "hsr_forward.h" #include "hsr_framereg.h" +static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb) +{ + struct sk_buff *skb = *pskb; + struct hsr_port *port; + + if (!skb_mac_header_was_set(skb)) { + WARN_ONCE(1, "%s: skb invalid", __func__); + return RX_HANDLER_PASS; + } + + rcu_read_lock(); /* hsr->node_db, hsr->ports */ + port = hsr_port_get_rcu(skb->dev); + + if (hsr_addr_is_self(port->hsr, eth_hdr(skb)->h_source)) { + /* Directly kill frames sent by ourselves */ + kfree_skb(skb); + goto finish_consume; + } + + if (eth_hdr(skb)->h_proto != htons(ETH_P_PRP)) + goto finish_pass; + + skb_push(skb, ETH_HLEN); + + hsr_forward_skb(skb, port); + +finish_consume: + rcu_read_unlock(); /* hsr->node_db, hsr->ports */ + return RX_HANDLER_CONSUMED; + +finish_pass: + rcu_read_unlock(); /* hsr->node_db, hsr->ports */ + return RX_HANDLER_PASS; +} + +bool hsr_port_exists(const struct net_device *dev) +{ + return rcu_access_pointer(dev->rx_handler) == hsr_handle_frame; +} + + static int hsr_check_dev_ok(struct net_device *dev) { /* Don't allow HSR on non-ethernet like devices */ @@ -42,6 +84,11 @@ static int hsr_check_dev_ok(struct net_device *dev) return -EINVAL; } + if (dev->priv_flags & IFF_DONT_BRIDGE) { + netdev_info(dev, "This device does not support bridging.\n"); + return -EOPNOTSUPP; + } + /* HSR over bonded devices has not been tested, but I'm not sure it * won't work... */ @@ -50,232 +97,6 @@ static int hsr_check_dev_ok(struct net_device *dev) } -static struct sk_buff *hsr_pull_tag(struct sk_buff *skb) -{ - struct hsr_tag *hsr_tag; - struct sk_buff *skb2; - - skb2 = skb_share_check(skb, GFP_ATOMIC); - if (unlikely(!skb2)) - goto err_free; - skb = skb2; - - if (unlikely(!pskb_may_pull(skb, HSR_HLEN))) - goto err_free; - - hsr_tag = (struct hsr_tag *) skb->data; - skb->protocol = hsr_tag->encap_proto; - skb_pull(skb, HSR_HLEN); - - return skb; - -err_free: - kfree_skb(skb); - return NULL; -} - - -/* The uses I can see for these HSR supervision frames are: - * 1) Use the frames that are sent after node initialization ("HSR_TLV.Type = - * 22") to reset any sequence_nr counters belonging to that node. Useful if - * the other node's counter has been reset for some reason. - * -- - * Or not - resetting the counter and bridging the frame would create a - * loop, unfortunately. - * - * 2) Use the LifeCheck frames to detect ring breaks. I.e. if no LifeCheck - * frame is received from a particular node, we know something is wrong. - * We just register these (as with normal frames) and throw them away. - * - * 3) Allow different MAC addresses for the two slave interfaces, using the - * MacAddressA field. - */ -static bool is_supervision_frame(struct hsr_priv *hsr, struct sk_buff *skb) -{ - struct hsr_sup_tag *hsr_stag; - - if (!ether_addr_equal(eth_hdr(skb)->h_dest, - hsr->sup_multicast_addr)) - return false; - - hsr_stag = (struct hsr_sup_tag *) skb->data; - if (get_hsr_stag_path(hsr_stag) != 0x0f) - return false; - if ((hsr_stag->HSR_TLV_Type != HSR_TLV_ANNOUNCE) && - (hsr_stag->HSR_TLV_Type != HSR_TLV_LIFE_CHECK)) - return false; - if (hsr_stag->HSR_TLV_Length != 12) - return false; - - return true; -} - - -/* Implementation somewhat according to IEC-62439-3, p. 43 - */ -rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb) -{ - struct sk_buff *skb = *pskb; - struct hsr_port *port, *other_port, *master; - struct hsr_priv *hsr; - struct hsr_node *node; - bool deliver_to_self; - struct sk_buff *skb_deliver; - bool dup_out; - int ret; - - if (eth_hdr(skb)->h_proto != htons(ETH_P_PRP)) - return RX_HANDLER_PASS; - - rcu_read_lock(); /* ports & node */ - - port = hsr_port_get_rcu(skb->dev); - hsr = port->hsr; - master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); - - node = hsr_find_node(&hsr->self_node_db, skb); - if (node) { - /* Always kill frames sent by ourselves */ - kfree_skb(skb); - ret = RX_HANDLER_CONSUMED; - goto finish; - } - - /* Is this frame a candidate for local reception? */ - deliver_to_self = false; - if ((skb->pkt_type == PACKET_HOST) || - (skb->pkt_type == PACKET_MULTICAST) || - (skb->pkt_type == PACKET_BROADCAST)) - deliver_to_self = true; - else if (ether_addr_equal(eth_hdr(skb)->h_dest, - master->dev->dev_addr)) { - skb->pkt_type = PACKET_HOST; - deliver_to_self = true; - } - - node = hsr_find_node(&hsr->node_db, skb); - - if (is_supervision_frame(hsr, skb)) { - skb_pull(skb, sizeof(struct hsr_sup_tag)); - node = hsr_merge_node(node, skb, port); - if (!node) { - kfree_skb(skb); - master->dev->stats.rx_dropped++; - ret = RX_HANDLER_CONSUMED; - goto finish; - } - skb_push(skb, sizeof(struct hsr_sup_tag)); - deliver_to_self = false; - } - - if (!node) { - /* Source node unknown; this might be a HSR frame from - * another net (different multicast address). Ignore it. - */ - kfree_skb(skb); - ret = RX_HANDLER_CONSUMED; - goto finish; - } - - if (port->type == HSR_PT_SLAVE_A) - other_port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B); - else - other_port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A); - - /* Register ALL incoming frames as outgoing through the other interface. - * This allows us to register frames as incoming only if they are valid - * for the receiving interface, without using a specific counter for - * incoming frames. - */ - if (other_port) - dup_out = hsr_register_frame_out(node, other_port, skb); - else - dup_out = 0; - if (!dup_out) - hsr_register_frame_in(node, port); - - /* Forward this frame? */ - if (dup_out || (skb->pkt_type == PACKET_HOST)) - other_port = NULL; - - if (hsr_register_frame_out(node, master, skb)) - deliver_to_self = false; - - if (!deliver_to_self && !other_port) { - kfree_skb(skb); - /* Circulated frame; silently remove it. */ - ret = RX_HANDLER_CONSUMED; - goto finish; - } - - skb_deliver = skb; - if (deliver_to_self && other_port) { - /* skb_clone() is not enough since we will strip the hsr tag - * and do address substitution below - */ - skb_deliver = pskb_copy(skb, GFP_ATOMIC); - if (!skb_deliver) { - deliver_to_self = false; - master->dev->stats.rx_dropped++; - } - } - - if (deliver_to_self) { - bool multicast_frame; - - skb_deliver = hsr_pull_tag(skb_deliver); - if (!skb_deliver) { - master->dev->stats.rx_dropped++; - goto forward; - } -#if !defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) - /* Move everything in the header that is after the HSR tag, - * to work around alignment problems caused by the 6-byte HSR - * tag. In practice, this removes/overwrites the HSR tag in - * the header and restores a "standard" packet. - */ - memmove(skb_deliver->data - HSR_HLEN, skb_deliver->data, - skb_headlen(skb_deliver)); - - /* Adjust skb members so they correspond with the move above. - * This cannot possibly underflow skb->data since hsr_pull_tag() - * above succeeded. - * At this point in the protocol stack, the transport and - * network headers have not been set yet, and we haven't touched - * the mac header nor the head. So we only need to adjust data - * and tail: - */ - skb_deliver->data -= HSR_HLEN; - skb_deliver->tail -= HSR_HLEN; -#endif - skb_deliver->dev = master->dev; - hsr_addr_subst_source(hsr, skb_deliver); - multicast_frame = (skb_deliver->pkt_type == PACKET_MULTICAST); - ret = netif_rx(skb_deliver); - if (ret == NET_RX_DROP) { - master->dev->stats.rx_dropped++; - } else { - master->dev->stats.rx_packets++; - master->dev->stats.rx_bytes += skb->len; - if (multicast_frame) - master->dev->stats.multicast++; - } - } - -forward: - if (other_port) { - skb_push(skb, ETH_HLEN); - skb->dev = other_port->dev; - dev_queue_xmit(skb); - } - - ret = RX_HANDLER_CONSUMED; - -finish: - rcu_read_unlock(); - return ret; -} - /* Setup device to be added to the HSR bridge. */ static int hsr_portdev_setup(struct net_device *dev, struct hsr_port *port) { @@ -285,16 +106,17 @@ static int hsr_portdev_setup(struct net_device *dev, struct hsr_port *port) res = dev_set_promiscuity(dev, 1); if (res) goto fail_promiscuity; - res = netdev_rx_handler_register(dev, hsr_handle_frame, port); - if (res) - goto fail_rx_handler; - dev_disable_lro(dev); /* FIXME: * What does net device "adjacency" mean? Should we do * res = netdev_master_upper_dev_link(port->dev, port->hsr->dev); ? */ + res = netdev_rx_handler_register(dev, hsr_handle_frame, port); + if (res) + goto fail_rx_handler; + dev_disable_lro(dev); + return 0; fail_rx_handler: @@ -339,11 +161,6 @@ int hsr_add_port(struct hsr_priv *hsr, struct net_device *dev, synchronize_rcu(); master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); - - /* Set required header length */ - if (dev->hard_header_len + HSR_HLEN > master->dev->hard_header_len) - master->dev->hard_header_len = dev->hard_header_len + HSR_HLEN; - netdev_update_features(master->dev); dev_set_mtu(master->dev, hsr_get_max_mtu(hsr)); diff --git a/net/hsr/hsr_slave.h b/net/hsr/hsr_slave.h index 3055022eddb3..3ccfbf71c92e 100644 --- a/net/hsr/hsr_slave.h +++ b/net/hsr/hsr_slave.h @@ -20,17 +20,7 @@ int hsr_add_port(struct hsr_priv *hsr, struct net_device *dev, enum hsr_port_type pt); void hsr_del_port(struct hsr_port *port); -rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb); - - -#define hsr_for_each_port(hsr, port) \ - list_for_each_entry_rcu((port), &(hsr)->ports, port_list) - - -static inline bool hsr_port_exists(const struct net_device *dev) -{ - return dev->rx_handler == hsr_handle_frame; -} +bool hsr_port_exists(const struct net_device *dev); static inline struct hsr_port *hsr_port_get_rtnl(const struct net_device *dev) { -- cgit v1.2.3