diff options
author | Arvid Brodin <arvid.brodin@alten.se> | 2014-07-04 17:37:27 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-07-08 14:35:31 -0400 |
commit | 51f3c605318b056ac5deb9079bbef2a976558827 (patch) | |
tree | 892ae91481ed018ce84f70a9b1c49ee0972d9406 | |
parent | e9aae56ea43ef4a32527b9d86c1f6b5eebfbd223 (diff) |
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 <arvid.brodin@alten.se>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/hsr/hsr_device.c | 192 | ||||
-rw-r--r-- | net/hsr/hsr_framereg.c | 6 | ||||
-rw-r--r-- | net/hsr/hsr_main.c | 24 | ||||
-rw-r--r-- | net/hsr/hsr_netlink.c | 53 | ||||
-rw-r--r-- | net/hsr/hsr_slave.c | 91 | ||||
-rw-r--r-- | net/hsr/hsr_slave.h | 3 |
6 files changed, 205 insertions, 164 deletions
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 @@ | |||
15 | #include <linux/netdevice.h> | 15 | #include <linux/netdevice.h> |
16 | #include <linux/skbuff.h> | 16 | #include <linux/skbuff.h> |
17 | #include <linux/etherdevice.h> | 17 | #include <linux/etherdevice.h> |
18 | #include <linux/if_arp.h> | ||
19 | #include <linux/rtnetlink.h> | 18 | #include <linux/rtnetlink.h> |
20 | #include <linux/pkt_sched.h> | 19 | #include <linux/pkt_sched.h> |
21 | #include "hsr_device.h" | 20 | #include "hsr_device.h" |
@@ -108,23 +107,27 @@ void hsr_check_carrier_and_operstate(struct hsr_priv *hsr) | |||
108 | hsr_check_announce(hsr->dev, old_operstate); | 107 | hsr_check_announce(hsr->dev, old_operstate); |
109 | } | 108 | } |
110 | 109 | ||
111 | |||
112 | int hsr_get_max_mtu(struct hsr_priv *hsr) | 110 | int hsr_get_max_mtu(struct hsr_priv *hsr) |
113 | { | 111 | { |
114 | int mtu_max; | 112 | unsigned int mtu_max; |
115 | 113 | struct net_device *slave; | |
116 | if (hsr->slave[0] && hsr->slave[1]) | 114 | |
117 | mtu_max = min(hsr->slave[0]->mtu, hsr->slave[1]->mtu); | 115 | mtu_max = ETH_DATA_LEN; |
118 | else if (hsr->slave[0]) | 116 | rcu_read_lock(); |
119 | mtu_max = hsr->slave[0]->mtu; | 117 | slave = hsr->slave[0]; |
120 | else if (hsr->slave[1]) | 118 | if (slave) |
121 | mtu_max = hsr->slave[1]->mtu; | 119 | mtu_max = min(slave->mtu, mtu_max); |
122 | else | 120 | slave = hsr->slave[1]; |
123 | mtu_max = HSR_HLEN; | 121 | if (slave) |
124 | 122 | mtu_max = min(slave->mtu, mtu_max); | |
123 | rcu_read_unlock(); | ||
124 | |||
125 | if (mtu_max < HSR_HLEN) | ||
126 | return 0; | ||
125 | return mtu_max - HSR_HLEN; | 127 | return mtu_max - HSR_HLEN; |
126 | } | 128 | } |
127 | 129 | ||
130 | |||
128 | static int hsr_dev_change_mtu(struct net_device *dev, int new_mtu) | 131 | static int hsr_dev_change_mtu(struct net_device *dev, int new_mtu) |
129 | { | 132 | { |
130 | struct hsr_priv *hsr; | 133 | struct hsr_priv *hsr; |
@@ -145,18 +148,20 @@ static int hsr_dev_change_mtu(struct net_device *dev, int new_mtu) | |||
145 | static int hsr_dev_open(struct net_device *dev) | 148 | static int hsr_dev_open(struct net_device *dev) |
146 | { | 149 | { |
147 | struct hsr_priv *hsr; | 150 | struct hsr_priv *hsr; |
151 | struct net_device *slave; | ||
148 | int i; | 152 | int i; |
149 | char *slave_name; | 153 | char *slave_name; |
150 | 154 | ||
151 | hsr = netdev_priv(dev); | 155 | hsr = netdev_priv(dev); |
152 | 156 | ||
153 | for (i = 0; i < HSR_MAX_SLAVE; i++) { | 157 | for (i = 0; i < HSR_MAX_SLAVE; i++) { |
154 | if (hsr->slave[i]) | 158 | slave = hsr->slave[i]; |
155 | slave_name = hsr->slave[i]->name; | 159 | if (slave) |
160 | slave_name = slave->name; | ||
156 | else | 161 | else |
157 | slave_name = "null"; | 162 | slave_name = "null"; |
158 | 163 | ||
159 | if (!is_slave_up(hsr->slave[i])) | 164 | if (!is_slave_up(slave)) |
160 | netdev_warn(dev, "Slave %c (%s) is not up; please bring it up to get a working HSR network\n", | 165 | netdev_warn(dev, "Slave %c (%s) is not up; please bring it up to get a working HSR network\n", |
161 | 'A' + i, slave_name); | 166 | 'A' + i, slave_name); |
162 | } | 167 | } |
@@ -223,6 +228,8 @@ static int slave_xmit(struct sk_buff *skb, struct hsr_priv *hsr, | |||
223 | hsr_ethhdr = (struct hsr_ethhdr *) skb->data; | 228 | hsr_ethhdr = (struct hsr_ethhdr *) skb->data; |
224 | 229 | ||
225 | skb->dev = hsr->slave[dev_idx]; | 230 | skb->dev = hsr->slave[dev_idx]; |
231 | if (unlikely(!skb->dev)) | ||
232 | return NET_XMIT_DROP; | ||
226 | 233 | ||
227 | hsr_addr_subst_dest(hsr, &hsr_ethhdr->ethhdr, dev_idx); | 234 | hsr_addr_subst_dest(hsr, &hsr_ethhdr->ethhdr, dev_idx); |
228 | 235 | ||
@@ -252,14 +259,8 @@ static int hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev) | |||
252 | } | 259 | } |
253 | 260 | ||
254 | skb2 = pskb_copy(skb, GFP_ATOMIC); | 261 | skb2 = pskb_copy(skb, GFP_ATOMIC); |
255 | 262 | res1 = slave_xmit(skb, hsr, HSR_DEV_SLAVE_A); | |
256 | res1 = NET_XMIT_DROP; | 263 | res2 = slave_xmit(skb2, hsr, HSR_DEV_SLAVE_B); |
257 | if (likely(hsr->slave[HSR_DEV_SLAVE_A])) | ||
258 | res1 = slave_xmit(skb, hsr, HSR_DEV_SLAVE_A); | ||
259 | |||
260 | res2 = NET_XMIT_DROP; | ||
261 | if (likely(skb2 && hsr->slave[HSR_DEV_SLAVE_B])) | ||
262 | res2 = slave_xmit(skb2, hsr, HSR_DEV_SLAVE_B); | ||
263 | 264 | ||
264 | if (likely(res1 == NET_XMIT_SUCCESS || res1 == NET_XMIT_CN || | 265 | if (likely(res1 == NET_XMIT_SUCCESS || res1 == NET_XMIT_CN || |
265 | res2 == NET_XMIT_SUCCESS || res2 == NET_XMIT_CN)) { | 266 | res2 == NET_XMIT_SUCCESS || res2 == NET_XMIT_CN)) { |
@@ -406,28 +407,10 @@ static void hsr_announce(unsigned long data) | |||
406 | static void restore_slaves(struct net_device *hsr_dev) | 407 | static void restore_slaves(struct net_device *hsr_dev) |
407 | { | 408 | { |
408 | struct hsr_priv *hsr; | 409 | struct hsr_priv *hsr; |
409 | int i; | ||
410 | int res; | ||
411 | 410 | ||
412 | hsr = netdev_priv(hsr_dev); | 411 | hsr = netdev_priv(hsr_dev); |
413 | 412 | hsr_del_slave(hsr, 1); | |
414 | rtnl_lock(); | 413 | hsr_del_slave(hsr, 0); |
415 | |||
416 | for (i = 0; i < HSR_MAX_SLAVE; i++) { | ||
417 | if (!hsr->slave[i]) | ||
418 | continue; | ||
419 | res = dev_set_promiscuity(hsr->slave[i], -1); | ||
420 | if (res) | ||
421 | netdev_info(hsr_dev, | ||
422 | "Cannot restore slave promiscuity (%s, %d)\n", | ||
423 | hsr->slave[i]->name, res); | ||
424 | |||
425 | if (hsr->slave[i]->rx_handler == hsr_handle_frame) | ||
426 | netdev_rx_handler_unregister(hsr->slave[i]); | ||
427 | } | ||
428 | |||
429 | |||
430 | rtnl_unlock(); | ||
431 | } | 414 | } |
432 | 415 | ||
433 | static void reclaim_hsr_dev(struct rcu_head *rh) | 416 | static void reclaim_hsr_dev(struct rcu_head *rh) |
@@ -483,38 +466,6 @@ bool is_hsr_master(struct net_device *dev) | |||
483 | return (dev->netdev_ops->ndo_start_xmit == hsr_dev_xmit); | 466 | return (dev->netdev_ops->ndo_start_xmit == hsr_dev_xmit); |
484 | } | 467 | } |
485 | 468 | ||
486 | static int check_slave_ok(struct net_device *dev) | ||
487 | { | ||
488 | /* Don't allow HSR on non-ethernet like devices */ | ||
489 | if ((dev->flags & IFF_LOOPBACK) || (dev->type != ARPHRD_ETHER) || | ||
490 | (dev->addr_len != ETH_ALEN)) { | ||
491 | netdev_info(dev, "Cannot use loopback or non-ethernet device as HSR slave.\n"); | ||
492 | return -EINVAL; | ||
493 | } | ||
494 | |||
495 | /* Don't allow enslaving hsr devices */ | ||
496 | if (is_hsr_master(dev)) { | ||
497 | netdev_info(dev, "Cannot create trees of HSR devices.\n"); | ||
498 | return -EINVAL; | ||
499 | } | ||
500 | |||
501 | if (is_hsr_slave(dev)) { | ||
502 | netdev_info(dev, "This device is already a HSR slave.\n"); | ||
503 | return -EINVAL; | ||
504 | } | ||
505 | |||
506 | if (dev->priv_flags & IFF_802_1Q_VLAN) { | ||
507 | netdev_info(dev, "HSR on top of VLAN is not yet supported in this driver.\n"); | ||
508 | return -EINVAL; | ||
509 | } | ||
510 | |||
511 | /* HSR over bonded devices has not been tested, but I'm not sure it | ||
512 | * won't work... | ||
513 | */ | ||
514 | |||
515 | return 0; | ||
516 | } | ||
517 | |||
518 | 469 | ||
519 | /* Default multicast address for HSR Supervision frames */ | 470 | /* Default multicast address for HSR Supervision frames */ |
520 | static const unsigned char def_multicast_addr[ETH_ALEN] __aligned(2) = { | 471 | 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], | |||
525 | unsigned char multicast_spec) | 476 | unsigned char multicast_spec) |
526 | { | 477 | { |
527 | struct hsr_priv *hsr; | 478 | struct hsr_priv *hsr; |
528 | int i; | ||
529 | int res; | 479 | int res; |
530 | 480 | ||
531 | hsr = netdev_priv(hsr_dev); | 481 | hsr = netdev_priv(hsr_dev); |
532 | hsr->dev = hsr_dev; | 482 | hsr->dev = hsr_dev; |
483 | hsr->slave[0] = NULL; | ||
484 | hsr->slave[1] = NULL; | ||
533 | INIT_LIST_HEAD(&hsr->node_db); | 485 | INIT_LIST_HEAD(&hsr->node_db); |
534 | INIT_LIST_HEAD(&hsr->self_node_db); | 486 | INIT_LIST_HEAD(&hsr->self_node_db); |
535 | for (i = 0; i < HSR_MAX_SLAVE; i++) | 487 | |
536 | hsr->slave[i] = slave[i]; | 488 | ether_addr_copy(hsr_dev->dev_addr, slave[0]->dev_addr); |
489 | |||
490 | /* Make sure we recognize frames from ourselves in hsr_rcv() */ | ||
491 | res = hsr_create_self_node(&hsr->self_node_db, hsr_dev->dev_addr, | ||
492 | slave[1]->dev_addr); | ||
493 | if (res < 0) | ||
494 | return res; | ||
495 | |||
496 | hsr_dev->features = slave[0]->features & slave[1]->features; | ||
497 | /* Prevent recursive tx locking */ | ||
498 | hsr_dev->features |= NETIF_F_LLTX; | ||
499 | /* VLAN on top of HSR needs testing and probably some work on | ||
500 | * hsr_header_create() etc. | ||
501 | */ | ||
502 | hsr_dev->features |= NETIF_F_VLAN_CHALLENGED; | ||
537 | 503 | ||
538 | spin_lock_init(&hsr->seqnr_lock); | 504 | spin_lock_init(&hsr->seqnr_lock); |
539 | /* Overflow soon to find bugs easier: */ | 505 | /* Overflow soon to find bugs easier: */ |
@@ -560,65 +526,21 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2], | |||
560 | * IFF_HSR_MASTER/SLAVE? | 526 | * IFF_HSR_MASTER/SLAVE? |
561 | */ | 527 | */ |
562 | 528 | ||
563 | for (i = 0; i < HSR_MAX_SLAVE; i++) { | ||
564 | res = check_slave_ok(slave[i]); | ||
565 | if (res) | ||
566 | return res; | ||
567 | } | ||
568 | |||
569 | hsr_dev->features = slave[0]->features & slave[1]->features; | ||
570 | /* Prevent recursive tx locking */ | ||
571 | hsr_dev->features |= NETIF_F_LLTX; | ||
572 | /* VLAN on top of HSR needs testing and probably some work on | ||
573 | * hsr_header_create() etc. | ||
574 | */ | ||
575 | hsr_dev->features |= NETIF_F_VLAN_CHALLENGED; | ||
576 | |||
577 | /* Set hsr_dev's MAC address to that of mac_slave1 */ | ||
578 | ether_addr_copy(hsr_dev->dev_addr, hsr->slave[0]->dev_addr); | ||
579 | |||
580 | /* Set required header length */ | ||
581 | for (i = 0; i < HSR_MAX_SLAVE; i++) { | ||
582 | if (slave[i]->hard_header_len + HSR_HLEN > | ||
583 | hsr_dev->hard_header_len) | ||
584 | hsr_dev->hard_header_len = | ||
585 | slave[i]->hard_header_len + HSR_HLEN; | ||
586 | } | ||
587 | |||
588 | /* MTU */ | ||
589 | for (i = 0; i < HSR_MAX_SLAVE; i++) | ||
590 | if (slave[i]->mtu - HSR_HLEN < hsr_dev->mtu) | ||
591 | hsr_dev->mtu = slave[i]->mtu - HSR_HLEN; | ||
592 | |||
593 | /* Make sure the 1st call to netif_carrier_on() gets through */ | 529 | /* Make sure the 1st call to netif_carrier_on() gets through */ |
594 | netif_carrier_off(hsr_dev); | 530 | netif_carrier_off(hsr_dev); |
595 | 531 | ||
596 | /* Promiscuity */ | ||
597 | for (i = 0; i < HSR_MAX_SLAVE; i++) { | ||
598 | res = dev_set_promiscuity(slave[i], 1); | ||
599 | if (res) { | ||
600 | netdev_info(hsr_dev, "Cannot set slave promiscuity (%s, %d)\n", | ||
601 | slave[i]->name, res); | ||
602 | goto fail; | ||
603 | } | ||
604 | } | ||
605 | |||
606 | for (i = 0; i < HSR_MAX_SLAVE; i++) { | ||
607 | res = netdev_rx_handler_register(slave[i], hsr_handle_frame, | ||
608 | hsr); | ||
609 | if (res) | ||
610 | goto fail; | ||
611 | } | ||
612 | |||
613 | /* Make sure we recognize frames from ourselves in hsr_rcv() */ | ||
614 | res = hsr_create_self_node(&hsr->self_node_db, hsr_dev->dev_addr, | ||
615 | hsr->slave[1]->dev_addr); | ||
616 | if (res < 0) | ||
617 | goto fail; | ||
618 | |||
619 | res = register_netdevice(hsr_dev); | 532 | res = register_netdevice(hsr_dev); |
620 | if (res) | 533 | if (res) |
621 | goto fail; | 534 | return res; |
535 | |||
536 | res = hsr_add_slave(hsr, slave[0], 0); | ||
537 | if (res) | ||
538 | return res; | ||
539 | res = hsr_add_slave(hsr, slave[1], 1); | ||
540 | if (res) { | ||
541 | hsr_del_slave(hsr, 0); | ||
542 | return res; | ||
543 | } | ||
622 | 544 | ||
623 | hsr->prune_timer.expires = jiffies + msecs_to_jiffies(PRUNE_PERIOD); | 545 | hsr->prune_timer.expires = jiffies + msecs_to_jiffies(PRUNE_PERIOD); |
624 | add_timer(&hsr->prune_timer); | 546 | add_timer(&hsr->prune_timer); |
@@ -626,8 +548,4 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2], | |||
626 | register_hsr_master(hsr); | 548 | register_hsr_master(hsr); |
627 | 549 | ||
628 | return 0; | 550 | return 0; |
629 | |||
630 | fail: | ||
631 | restore_slaves(hsr_dev); | ||
632 | return res; | ||
633 | } | 551 | } |
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, | |||
455 | u16 *if2_seq) | 455 | u16 *if2_seq) |
456 | { | 456 | { |
457 | struct hsr_node *node; | 457 | struct hsr_node *node; |
458 | struct net_device *slave; | ||
458 | unsigned long tdiff; | 459 | unsigned long tdiff; |
459 | 460 | ||
460 | 461 | ||
@@ -491,8 +492,9 @@ int hsr_get_node_data(struct hsr_priv *hsr, | |||
491 | *if1_seq = node->seq_out[HSR_DEV_SLAVE_B]; | 492 | *if1_seq = node->seq_out[HSR_DEV_SLAVE_B]; |
492 | *if2_seq = node->seq_out[HSR_DEV_SLAVE_A]; | 493 | *if2_seq = node->seq_out[HSR_DEV_SLAVE_A]; |
493 | 494 | ||
494 | if ((node->AddrB_if != HSR_DEV_NONE) && hsr->slave[node->AddrB_if]) | 495 | slave = hsr->slave[node->AddrB_if]; |
495 | *addr_b_ifindex = hsr->slave[node->AddrB_if]->ifindex; | 496 | if ((node->AddrB_if != HSR_DEV_NONE) && slave) |
497 | *addr_b_ifindex = slave->ifindex; | ||
496 | else | 498 | else |
497 | *addr_b_ifindex = -1; | 499 | *addr_b_ifindex = -1; |
498 | 500 | ||
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 @@ | |||
17 | #include "hsr_device.h" | 17 | #include "hsr_device.h" |
18 | #include "hsr_netlink.h" | 18 | #include "hsr_netlink.h" |
19 | #include "hsr_framereg.h" | 19 | #include "hsr_framereg.h" |
20 | #include "hsr_slave.h" | ||
20 | 21 | ||
21 | 22 | ||
22 | /* List of all registered virtual HSR devices */ | 23 | /* List of all registered virtual HSR devices */ |
@@ -124,22 +125,21 @@ static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event, | |||
124 | if (dev == hsr->dev) | 125 | if (dev == hsr->dev) |
125 | break; | 126 | break; |
126 | 127 | ||
127 | if (dev == hsr->slave[0]) | 128 | if (dev == hsr->slave[0]) { |
128 | ether_addr_copy(hsr->dev->dev_addr, | 129 | ether_addr_copy(hsr->dev->dev_addr, dev->dev_addr); |
129 | hsr->slave[0]->dev_addr); | 130 | call_netdevice_notifiers(NETDEV_CHANGEADDR, hsr->dev); |
131 | } | ||
130 | 132 | ||
131 | /* Make sure we recognize frames from ourselves in hsr_rcv() */ | 133 | /* Make sure we recognize frames from ourselves in hsr_rcv() */ |
134 | other_slave = hsr->slave[1]; | ||
132 | res = hsr_create_self_node(&hsr->self_node_db, | 135 | res = hsr_create_self_node(&hsr->self_node_db, |
133 | hsr->dev->dev_addr, | 136 | hsr->dev->dev_addr, |
134 | hsr->slave[1] ? | 137 | other_slave ? |
135 | hsr->slave[1]->dev_addr : | 138 | other_slave->dev_addr : |
136 | hsr->dev->dev_addr); | 139 | hsr->dev->dev_addr); |
137 | if (res) | 140 | if (res) |
138 | netdev_warn(hsr->dev, | 141 | netdev_warn(hsr->dev, |
139 | "Could not update HSR node address.\n"); | 142 | "Could not update HSR node address.\n"); |
140 | |||
141 | if (dev == hsr->slave[0]) | ||
142 | call_netdevice_notifiers(NETDEV_CHANGEADDR, hsr->dev); | ||
143 | break; | 143 | break; |
144 | case NETDEV_CHANGEMTU: | 144 | case NETDEV_CHANGEMTU: |
145 | if (dev == hsr->dev) | 145 | if (dev == hsr->dev) |
@@ -149,10 +149,14 @@ static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event, | |||
149 | dev_set_mtu(hsr->dev, mtu_max); | 149 | dev_set_mtu(hsr->dev, mtu_max); |
150 | break; | 150 | break; |
151 | case NETDEV_UNREGISTER: | 151 | case NETDEV_UNREGISTER: |
152 | if (dev == hsr->slave[0]) | 152 | if (dev == hsr->slave[0]) { |
153 | hsr->slave[0] = NULL; | 153 | hsr->slave[0] = NULL; |
154 | if (dev == hsr->slave[1]) | 154 | hsr_del_slave(hsr, 0); |
155 | } | ||
156 | if (dev == hsr->slave[1]) { | ||
155 | hsr->slave[1] = NULL; | 157 | hsr->slave[1] = NULL; |
158 | hsr_del_slave(hsr, 1); | ||
159 | } | ||
156 | 160 | ||
157 | /* There should really be a way to set a new slave device... */ | 161 | /* There should really be a way to set a new slave device... */ |
158 | 162 | ||
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, | |||
64 | static int hsr_fill_info(struct sk_buff *skb, const struct net_device *dev) | 64 | static int hsr_fill_info(struct sk_buff *skb, const struct net_device *dev) |
65 | { | 65 | { |
66 | struct hsr_priv *hsr; | 66 | struct hsr_priv *hsr; |
67 | struct net_device *slave; | ||
68 | int res; | ||
67 | 69 | ||
68 | hsr = netdev_priv(dev); | 70 | hsr = netdev_priv(dev); |
69 | 71 | ||
70 | if (hsr->slave[0]) | 72 | res = 0; |
71 | if (nla_put_u32(skb, IFLA_HSR_SLAVE1, hsr->slave[0]->ifindex)) | ||
72 | goto nla_put_failure; | ||
73 | 73 | ||
74 | if (hsr->slave[1]) | 74 | rcu_read_lock(); |
75 | if (nla_put_u32(skb, IFLA_HSR_SLAVE2, hsr->slave[1]->ifindex)) | 75 | slave = hsr->slave[0]; |
76 | goto nla_put_failure; | 76 | if (slave) |
77 | res = nla_put_u32(skb, IFLA_HSR_SLAVE1, slave->ifindex); | ||
78 | rcu_read_unlock(); | ||
79 | if (res) | ||
80 | goto nla_put_failure; | ||
81 | |||
82 | rcu_read_lock(); | ||
83 | slave = hsr->slave[1]; | ||
84 | if (slave) | ||
85 | res = nla_put_u32(skb, IFLA_HSR_SLAVE2, slave->ifindex); | ||
86 | rcu_read_unlock(); | ||
87 | if (res) | ||
88 | goto nla_put_failure; | ||
77 | 89 | ||
78 | if (nla_put(skb, IFLA_HSR_SUPERVISION_ADDR, ETH_ALEN, | 90 | if (nla_put(skb, IFLA_HSR_SUPERVISION_ADDR, ETH_ALEN, |
79 | hsr->sup_multicast_addr) || | 91 | hsr->sup_multicast_addr) || |
@@ -132,6 +144,7 @@ void hsr_nl_ringerror(struct hsr_priv *hsr, unsigned char addr[ETH_ALEN], | |||
132 | enum hsr_dev_idx dev_idx) | 144 | enum hsr_dev_idx dev_idx) |
133 | { | 145 | { |
134 | struct sk_buff *skb; | 146 | struct sk_buff *skb; |
147 | struct net_device *slave; | ||
135 | void *msg_head; | 148 | void *msg_head; |
136 | int res; | 149 | int res; |
137 | int ifindex; | 150 | int ifindex; |
@@ -148,10 +161,14 @@ void hsr_nl_ringerror(struct hsr_priv *hsr, unsigned char addr[ETH_ALEN], | |||
148 | if (res < 0) | 161 | if (res < 0) |
149 | goto nla_put_failure; | 162 | goto nla_put_failure; |
150 | 163 | ||
151 | if (hsr->slave[dev_idx]) | 164 | rcu_read_lock(); |
152 | ifindex = hsr->slave[dev_idx]->ifindex; | 165 | slave = hsr->slave[dev_idx]; |
166 | if (slave) | ||
167 | ifindex = slave->ifindex; | ||
153 | else | 168 | else |
154 | ifindex = -1; | 169 | ifindex = -1; |
170 | rcu_read_unlock(); | ||
171 | |||
155 | res = nla_put_u32(skb, HSR_A_IFINDEX, ifindex); | 172 | res = nla_put_u32(skb, HSR_A_IFINDEX, ifindex); |
156 | if (res < 0) | 173 | if (res < 0) |
157 | goto nla_put_failure; | 174 | goto nla_put_failure; |
@@ -215,7 +232,7 @@ static int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info) | |||
215 | { | 232 | { |
216 | /* For receiving */ | 233 | /* For receiving */ |
217 | struct nlattr *na; | 234 | struct nlattr *na; |
218 | struct net_device *hsr_dev; | 235 | struct net_device *hsr_dev, *slave; |
219 | 236 | ||
220 | /* For sending */ | 237 | /* For sending */ |
221 | struct sk_buff *skb_out; | 238 | struct sk_buff *skb_out; |
@@ -301,9 +318,11 @@ static int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info) | |||
301 | res = nla_put_u16(skb_out, HSR_A_IF1_SEQ, hsr_node_if1_seq); | 318 | res = nla_put_u16(skb_out, HSR_A_IF1_SEQ, hsr_node_if1_seq); |
302 | if (res < 0) | 319 | if (res < 0) |
303 | goto nla_put_failure; | 320 | goto nla_put_failure; |
304 | if (hsr->slave[0]) | 321 | rcu_read_lock(); |
305 | res = nla_put_u32(skb_out, HSR_A_IF1_IFINDEX, | 322 | slave = hsr->slave[0]; |
306 | hsr->slave[0]->ifindex); | 323 | if (slave) |
324 | res = nla_put_u32(skb_out, HSR_A_IF1_IFINDEX, slave->ifindex); | ||
325 | rcu_read_unlock(); | ||
307 | if (res < 0) | 326 | if (res < 0) |
308 | goto nla_put_failure; | 327 | goto nla_put_failure; |
309 | 328 | ||
@@ -313,9 +332,13 @@ static int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info) | |||
313 | res = nla_put_u16(skb_out, HSR_A_IF2_SEQ, hsr_node_if2_seq); | 332 | res = nla_put_u16(skb_out, HSR_A_IF2_SEQ, hsr_node_if2_seq); |
314 | if (res < 0) | 333 | if (res < 0) |
315 | goto nla_put_failure; | 334 | goto nla_put_failure; |
316 | if (hsr->slave[1]) | 335 | rcu_read_lock(); |
317 | res = nla_put_u32(skb_out, HSR_A_IF2_IFINDEX, | 336 | slave = hsr->slave[1]; |
318 | hsr->slave[1]->ifindex); | 337 | if (slave) |
338 | res = nla_put_u32(skb_out, HSR_A_IF2_IFINDEX, slave->ifindex); | ||
339 | rcu_read_unlock(); | ||
340 | if (res < 0) | ||
341 | goto nla_put_failure; | ||
319 | 342 | ||
320 | genlmsg_end(skb_out, msg_head); | 343 | genlmsg_end(skb_out, msg_head); |
321 | genlmsg_unicast(genl_info_net(info), skb_out, info->snd_portid); | 344 | 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 @@ | |||
11 | 11 | ||
12 | #include "hsr_slave.h" | 12 | #include "hsr_slave.h" |
13 | #include <linux/etherdevice.h> | 13 | #include <linux/etherdevice.h> |
14 | #include <linux/if_arp.h> | ||
14 | #include "hsr_main.h" | 15 | #include "hsr_main.h" |
16 | #include "hsr_device.h" | ||
15 | #include "hsr_framereg.h" | 17 | #include "hsr_framereg.h" |
16 | 18 | ||
17 | 19 | ||
20 | static int check_slave_ok(struct net_device *dev) | ||
21 | { | ||
22 | /* Don't allow HSR on non-ethernet like devices */ | ||
23 | if ((dev->flags & IFF_LOOPBACK) || (dev->type != ARPHRD_ETHER) || | ||
24 | (dev->addr_len != ETH_ALEN)) { | ||
25 | netdev_info(dev, "Cannot use loopback or non-ethernet device as HSR slave.\n"); | ||
26 | return -EINVAL; | ||
27 | } | ||
28 | |||
29 | /* Don't allow enslaving hsr devices */ | ||
30 | if (is_hsr_master(dev)) { | ||
31 | netdev_info(dev, "Cannot create trees of HSR devices.\n"); | ||
32 | return -EINVAL; | ||
33 | } | ||
34 | |||
35 | if (is_hsr_slave(dev)) { | ||
36 | netdev_info(dev, "This device is already a HSR slave.\n"); | ||
37 | return -EINVAL; | ||
38 | } | ||
39 | |||
40 | if (dev->priv_flags & IFF_802_1Q_VLAN) { | ||
41 | netdev_info(dev, "HSR on top of VLAN is not yet supported in this driver.\n"); | ||
42 | return -EINVAL; | ||
43 | } | ||
44 | |||
45 | /* HSR over bonded devices has not been tested, but I'm not sure it | ||
46 | * won't work... | ||
47 | */ | ||
48 | |||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | |||
18 | static struct sk_buff *hsr_pull_tag(struct sk_buff *skb) | 53 | static struct sk_buff *hsr_pull_tag(struct sk_buff *skb) |
19 | { | 54 | { |
20 | struct hsr_tag *hsr_tag; | 55 | struct hsr_tag *hsr_tag; |
@@ -241,3 +276,59 @@ forward: | |||
241 | 276 | ||
242 | return RX_HANDLER_CONSUMED; | 277 | return RX_HANDLER_CONSUMED; |
243 | } | 278 | } |
279 | |||
280 | int hsr_add_slave(struct hsr_priv *hsr, struct net_device *dev, int idx) | ||
281 | { | ||
282 | int res; | ||
283 | |||
284 | dev_hold(dev); | ||
285 | |||
286 | res = check_slave_ok(dev); | ||
287 | if (res) | ||
288 | goto fail; | ||
289 | |||
290 | res = dev_set_promiscuity(dev, 1); | ||
291 | if (res) | ||
292 | goto fail; | ||
293 | |||
294 | res = netdev_rx_handler_register(dev, hsr_handle_frame, hsr); | ||
295 | if (res) | ||
296 | goto fail_rx_handler; | ||
297 | |||
298 | |||
299 | hsr->slave[idx] = dev; | ||
300 | |||
301 | /* Set required header length */ | ||
302 | if (dev->hard_header_len + HSR_HLEN > hsr->dev->hard_header_len) | ||
303 | hsr->dev->hard_header_len = dev->hard_header_len + HSR_HLEN; | ||
304 | |||
305 | dev_set_mtu(hsr->dev, hsr_get_max_mtu(hsr)); | ||
306 | |||
307 | return 0; | ||
308 | |||
309 | fail_rx_handler: | ||
310 | dev_set_promiscuity(dev, -1); | ||
311 | |||
312 | fail: | ||
313 | dev_put(dev); | ||
314 | return res; | ||
315 | } | ||
316 | |||
317 | void hsr_del_slave(struct hsr_priv *hsr, int idx) | ||
318 | { | ||
319 | struct net_device *slave; | ||
320 | |||
321 | slave = hsr->slave[idx]; | ||
322 | hsr->slave[idx] = NULL; | ||
323 | |||
324 | netdev_update_features(hsr->dev); | ||
325 | dev_set_mtu(hsr->dev, hsr_get_max_mtu(hsr)); | ||
326 | |||
327 | if (slave) { | ||
328 | netdev_rx_handler_unregister(slave); | ||
329 | dev_set_promiscuity(slave, -1); | ||
330 | } | ||
331 | |||
332 | synchronize_rcu(); | ||
333 | dev_put(slave); | ||
334 | } | ||
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 @@ | |||
14 | 14 | ||
15 | #include <linux/skbuff.h> | 15 | #include <linux/skbuff.h> |
16 | #include <linux/netdevice.h> | 16 | #include <linux/netdevice.h> |
17 | #include "hsr_main.h" | ||
17 | 18 | ||
19 | int hsr_add_slave(struct hsr_priv *hsr, struct net_device *dev, int idx); | ||
20 | void hsr_del_slave(struct hsr_priv *hsr, int idx); | ||
18 | rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb); | 21 | rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb); |
19 | 22 | ||
20 | #endif /* __HSR_SLAVE_H */ | 23 | #endif /* __HSR_SLAVE_H */ |