summary refs log tree commit diff
path: root/net/hsr/hsr_device.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/hsr/hsr_device.c')
-rw-r--r--net/hsr/hsr_device.c170
1 files changed, 23 insertions, 147 deletions
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;