summaryrefslogtreecommitdiff
path: root/net/hsr/hsr_main.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-08-06 09:38:14 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2014-08-06 09:38:14 -0700
commitae045e2455429c418a418a3376301a9e5753a0a8 (patch)
treeb445bdeecd3f38aa0d0a29c9585cee49e4ccb0f1 /net/hsr/hsr_main.c
parentf4f142ed4ef835709c7e6d12eaca10d190bcebed (diff)
parentd247b6ab3ce6dd43665780865ec5fa145d9ab6bd (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller: "Highlights: 1) Steady transitioning of the BPF instructure to a generic spot so all kernel subsystems can make use of it, from Alexei Starovoitov. 2) SFC driver supports busy polling, from Alexandre Rames. 3) Take advantage of hash table in UDP multicast delivery, from David Held. 4) Lighten locking, in particular by getting rid of the LRU lists, in inet frag handling. From Florian Westphal. 5) Add support for various RFC6458 control messages in SCTP, from Geir Ola Vaagland. 6) Allow to filter bridge forwarding database dumps by device, from Jamal Hadi Salim. 7) virtio-net also now supports busy polling, from Jason Wang. 8) Some low level optimization tweaks in pktgen from Jesper Dangaard Brouer. 9) Add support for ipv6 address generation modes, so that userland can have some input into the process. From Jiri Pirko. 10) Consolidate common TCP connection request code in ipv4 and ipv6, from Octavian Purdila. 11) New ARP packet logger in netfilter, from Pablo Neira Ayuso. 12) Generic resizable RCU hash table, with intial users in netlink and nftables. From Thomas Graf. 13) Maintain a name assignment type so that userspace can see where a network device name came from (enumerated by kernel, assigned explicitly by userspace, etc.) From Tom Gundersen. 14) Automatic flow label generation on transmit in ipv6, from Tom Herbert. 15) New packet timestamping facilities from Willem de Bruijn, meant to assist in measuring latencies going into/out-of the packet scheduler, latency from TCP data transmission to ACK, etc" * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1536 commits) cxgb4 : Disable recursive mailbox commands when enabling vi net: reduce USB network driver config options. tg3: Modify tg3_tso_bug() to handle multiple TX rings amd-xgbe: Perform phy connect/disconnect at dev open/stop amd-xgbe: Use dma_set_mask_and_coherent to set DMA mask net: sun4i-emac: fix memory leak on bad packet sctp: fix possible seqlock seadlock in sctp_packet_transmit() Revert "net: phy: Set the driver when registering an MDIO bus device" cxgb4vf: Turn off SGE RX/TX Callback Timers and interrupts in PCI shutdown routine team: Simplify return path of team_newlink bridge: Update outdated comment on promiscuous mode net-timestamp: ACK timestamp for bytestreams net-timestamp: TCP timestamping net-timestamp: SCHED timestamp on entering packet scheduler net-timestamp: add key to disambiguate concurrent datagrams net-timestamp: move timestamp flags out of sk_flags net-timestamp: extend SCM_TIMESTAMPING ancillary data struct cxgb4i : Move stray CPL definitions to cxgb4 driver tcp: reduce spurious retransmits due to transient SACK reneging qlcnic: Initialize dcbnl_ops before register_netdev ...
Diffstat (limited to 'net/hsr/hsr_main.c')
-rw-r--r--net/hsr/hsr_main.c425
1 files changed, 44 insertions, 381 deletions
diff --git a/net/hsr/hsr_main.c b/net/hsr/hsr_main.c
index 3fee5218a691..779d28b65417 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,11 +6,7 @@
* any later version.
*
* Author(s):
- * 2011-2013 Arvid Brodin, arvid.brodin@xdin.com
- *
- * 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.
+ * 2011-2014 Arvid Brodin, arvid.brodin@alten.se
*/
#include <linux/netdevice.h>
@@ -21,154 +17,71 @@
#include "hsr_device.h"
#include "hsr_netlink.h"
#include "hsr_framereg.h"
-
-
-/* List of all registered virtual HSR devices */
-static LIST_HEAD(hsr_list);
-
-void register_hsr_master(struct hsr_priv *hsr_priv)
-{
- list_add_tail_rcu(&hsr_priv->hsr_list, &hsr_list);
-}
-
-void unregister_hsr_master(struct hsr_priv *hsr_priv)
-{
- struct hsr_priv *hsr_priv_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);
- return;
- }
-}
-
-bool is_hsr_slave(struct net_device *dev)
-{
- struct hsr_priv *hsr_priv_it;
-
- list_for_each_entry_rcu(hsr_priv_it, &hsr_list, hsr_list) {
- if (dev == hsr_priv_it->slave[0])
- return true;
- if (dev == hsr_priv_it->slave[1])
- return true;
- }
-
- 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 *hsr_priv;
-
- 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])) {
- rcu_read_unlock();
- return hsr_priv;
- }
-
- rcu_read_unlock();
- 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_priv,
- 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];
-
- return NULL;
-}
+#include "hsr_slave.h"
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;
- int old_operstate;
+ 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_priv = get_hsr_master(dev);
- if (hsr_priv) {
- /* dev is a slave device */
- slave = dev;
- other_slave = get_other_slave(hsr_priv, slave);
- } else {
+ port = hsr_port_get_rtnl(dev);
+ if (port == NULL) {
if (!is_hsr_master(dev))
- return NOTIFY_DONE;
- hsr_priv = netdev_priv(dev);
- slave = hsr_priv->slave[0];
- other_slave = hsr_priv->slave[1];
+ return NOTIFY_DONE; /* Not an HSR device */
+ hsr = netdev_priv(dev);
+ port = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
+ } else {
+ hsr = port->hsr;
}
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);
- /* 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_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_priv->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_priv->slave[0])
- ether_addr_copy(hsr_priv->dev->dev_addr,
- hsr_priv->slave[0]->dev_addr);
+ 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() */
- 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);
+ port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B);
+ res = hsr_create_self_node(&hsr->self_node_db,
+ master->dev->dev_addr,
+ port ?
+ port->dev->dev_addr :
+ master->dev->dev_addr);
if (res)
- netdev_warn(hsr_priv->dev,
+ netdev_warn(master->dev,
"Could not update HSR node address.\n");
-
- if (dev == hsr_priv->slave[0])
- call_netdevice_notifiers(NETDEV_CHANGEADDR, hsr_priv->dev);
break;
case NETDEV_CHANGEMTU:
- if (dev == hsr_priv->dev)
+ if (port->type == HSR_PT_MASTER)
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(port->hsr);
+ master = hsr_port_get_hsr(port->hsr, HSR_PT_MASTER);
+ master->dev->mtu = 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;
-
- /* 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
@@ -181,255 +94,16 @@ 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_priv;
-
- rcu_read_lock();
- list_for_each_entry_rcu(hsr_priv, &hsr_list, hsr_list)
- hsr_prune_nodes(hsr_priv);
- rcu_read_unlock();
-
- prune_timer.expires = jiffies + msecs_to_jiffies(PRUNE_PERIOD);
- add_timer(&prune_timer);
-}
-
-
-static struct sk_buff *hsr_pull_tag(struct sk_buff *skb)
+struct hsr_port *hsr_port_get_hsr(struct hsr_priv *hsr, enum hsr_port_type pt)
{
- 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_TAGLEN)))
- goto err_free;
+ struct hsr_port *port;
- hsr_tag = (struct hsr_tag *) skb->data;
- skb->protocol = hsr_tag->encap_proto;
- skb_pull(skb, HSR_TAGLEN);
-
- return skb;
-
-err_free:
- kfree_skb(skb);
+ hsr_for_each_port(hsr, port)
+ if (port->type == pt)
+ return port;
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_priv, struct sk_buff *skb)
-{
- struct hsr_sup_tag *hsr_stag;
-
- if (!ether_addr_equal(eth_hdr(skb)->h_dest,
- hsr_priv->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_priv;
- struct net_device *other_slave;
- struct node_entry *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);
-
- if (!hsr_priv) {
- /* 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]) {
- 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_priv->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_priv->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);
-
- if (is_supervision_frame(hsr_priv, skb)) {
- skb_pull(skb, sizeof(struct hsr_sup_tag));
- node = hsr_merge_node(hsr_priv, node, skb, dev_in_idx);
- if (!node) {
- rcu_read_unlock(); /* node_db */
- kfree_skb(skb);
- hsr_priv->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_priv, 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_priv->dev->stats.rx_dropped++;
- }
- }
-
- if (deliver_to_self) {
- bool multicast_frame;
-
- skb_deliver = hsr_pull_tag(skb_deliver);
- if (!skb_deliver) {
- hsr_priv->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_TAGLEN, 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_TAGLEN;
- skb_deliver->tail -= HSR_TAGLEN;
-#endif
- skb_deliver->dev = hsr_priv->dev;
- hsr_addr_subst_source(hsr_priv, 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++;
- } else {
- hsr_priv->dev->stats.rx_packets++;
- hsr_priv->dev->stats.rx_bytes += skb->len;
- if (multicast_frame)
- hsr_priv->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 */
};
@@ -439,18 +113,9 @@ static int __init hsr_init(void)
{
int res;
- BUILD_BUG_ON(sizeof(struct hsr_tag) != HSR_TAGLEN);
-
- dev_add_pack(&hsr_pt);
-
- 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);
+ BUILD_BUG_ON(sizeof(struct hsr_tag) != HSR_HLEN);
register_netdevice_notifier(&hsr_nb);
-
res = hsr_netlink_init();
return res;
@@ -459,9 +124,7 @@ 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();
- dev_remove_pack(&hsr_pt);
}
module_init(hsr_init);