xref: /openbmc/linux/net/hsr/hsr_main.c (revision 5ee9cd065836e5934710ca35653bce7905add20b)
10e7623bdSMurali Karicheri // SPDX-License-Identifier: GPL-2.0
270ebe4a4SArvid Brodin /* Copyright 2011-2014 Autronica Fire and Security AS
3f421436aSArvid Brodin  *
4f421436aSArvid Brodin  * Author(s):
570ebe4a4SArvid Brodin  *	2011-2014 Arvid Brodin, arvid.brodin@alten.se
68f4c0e01SMurali Karicheri  *
78f4c0e01SMurali Karicheri  * Event handling for HSR and PRP devices.
8f421436aSArvid Brodin  */
9f421436aSArvid Brodin 
10f421436aSArvid Brodin #include <linux/netdevice.h>
11de0083c7STaehee Yoo #include <net/rtnetlink.h>
12f421436aSArvid Brodin #include <linux/rculist.h>
13f421436aSArvid Brodin #include <linux/timer.h>
14f421436aSArvid Brodin #include <linux/etherdevice.h>
15f421436aSArvid Brodin #include "hsr_main.h"
16f421436aSArvid Brodin #include "hsr_device.h"
17f421436aSArvid Brodin #include "hsr_netlink.h"
18f421436aSArvid Brodin #include "hsr_framereg.h"
1951f3c605SArvid Brodin #include "hsr_slave.h"
20f421436aSArvid Brodin 
hsr_slave_empty(struct hsr_priv * hsr)2134a9c361STaehee Yoo static bool hsr_slave_empty(struct hsr_priv *hsr)
2234a9c361STaehee Yoo {
2334a9c361STaehee Yoo 	struct hsr_port *port;
2434a9c361STaehee Yoo 
2534a9c361STaehee Yoo 	hsr_for_each_port(hsr, port)
2634a9c361STaehee Yoo 		if (port->type != HSR_PT_MASTER)
2734a9c361STaehee Yoo 			return false;
2834a9c361STaehee Yoo 	return true;
2934a9c361STaehee Yoo }
3034a9c361STaehee Yoo 
hsr_netdev_notify(struct notifier_block * nb,unsigned long event,void * ptr)31f421436aSArvid Brodin static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event,
32f421436aSArvid Brodin 			     void *ptr)
33f421436aSArvid Brodin {
34c5a75911SArvid Brodin 	struct hsr_port *port, *master;
3534a9c361STaehee Yoo 	struct net_device *dev;
3670ebe4a4SArvid Brodin 	struct hsr_priv *hsr;
3734a9c361STaehee Yoo 	LIST_HEAD(list_kill);
38f421436aSArvid Brodin 	int mtu_max;
39f421436aSArvid Brodin 	int res;
40f421436aSArvid Brodin 
41f421436aSArvid Brodin 	dev = netdev_notifier_info_to_dev(ptr);
42c5a75911SArvid Brodin 	port = hsr_port_get_rtnl(dev);
4305ca6e64SMurali Karicheri 	if (!port) {
44f421436aSArvid Brodin 		if (!is_hsr_master(dev))
45c5a75911SArvid Brodin 			return NOTIFY_DONE;	/* Not an HSR device */
4670ebe4a4SArvid Brodin 		hsr = netdev_priv(dev);
47c5a75911SArvid Brodin 		port = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
4805ca6e64SMurali Karicheri 		if (!port) {
4956b08fdcSArvid Brodin 			/* Resend of notification concerning removed device? */
5056b08fdcSArvid Brodin 			return NOTIFY_DONE;
5156b08fdcSArvid Brodin 		}
52c5a75911SArvid Brodin 	} else {
53c5a75911SArvid Brodin 		hsr = port->hsr;
54f421436aSArvid Brodin 	}
55f421436aSArvid Brodin 
56f421436aSArvid Brodin 	switch (event) {
57f421436aSArvid Brodin 	case NETDEV_UP:		/* Administrative state DOWN */
58f421436aSArvid Brodin 	case NETDEV_DOWN:	/* Administrative state UP */
59f421436aSArvid Brodin 	case NETDEV_CHANGE:	/* Link (carrier) state changes */
60e9aae56eSArvid Brodin 		hsr_check_carrier_and_operstate(hsr);
61f421436aSArvid Brodin 		break;
624c2d5e33STaehee Yoo 	case NETDEV_CHANGENAME:
6304b69426STaehee Yoo 		if (is_hsr_master(dev))
644c2d5e33STaehee Yoo 			hsr_debugfs_rename(dev);
654c2d5e33STaehee Yoo 		break;
66f421436aSArvid Brodin 	case NETDEV_CHANGEADDR:
67c5a75911SArvid Brodin 		if (port->type == HSR_PT_MASTER) {
68c5a75911SArvid Brodin 			/* This should not happen since there's no
69c5a75911SArvid Brodin 			 * ndo_set_mac_address() for HSR devices - i.e. not
70c5a75911SArvid Brodin 			 * supported.
71f421436aSArvid Brodin 			 */
72f421436aSArvid Brodin 			break;
73c5a75911SArvid Brodin 		}
74f421436aSArvid Brodin 
75c5a75911SArvid Brodin 		master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
76c5a75911SArvid Brodin 
77c5a75911SArvid Brodin 		if (port->type == HSR_PT_SLAVE_A) {
78e35b8d7dSJakub Kicinski 			eth_hw_addr_set(master->dev, dev->dev_addr);
79d595b85aSMurali Karicheri 			call_netdevice_notifiers(NETDEV_CHANGEADDR,
80d595b85aSMurali Karicheri 						 master->dev);
8151f3c605SArvid Brodin 		}
82f421436aSArvid Brodin 
83f421436aSArvid Brodin 		/* Make sure we recognize frames from ourselves in hsr_rcv() */
84c5a75911SArvid Brodin 		port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B);
8592a35678STaehee Yoo 		res = hsr_create_self_node(hsr,
86c5a75911SArvid Brodin 					   master->dev->dev_addr,
87c5a75911SArvid Brodin 					   port ?
88c5a75911SArvid Brodin 						port->dev->dev_addr :
89c5a75911SArvid Brodin 						master->dev->dev_addr);
90f421436aSArvid Brodin 		if (res)
91c5a75911SArvid Brodin 			netdev_warn(master->dev,
92f421436aSArvid Brodin 				    "Could not update HSR node address.\n");
93f421436aSArvid Brodin 		break;
94f421436aSArvid Brodin 	case NETDEV_CHANGEMTU:
95c5a75911SArvid Brodin 		if (port->type == HSR_PT_MASTER)
96f421436aSArvid Brodin 			break; /* Handled in ndo_change_mtu() */
97c5a75911SArvid Brodin 		mtu_max = hsr_get_max_mtu(port->hsr);
98c5a75911SArvid Brodin 		master = hsr_port_get_hsr(port->hsr, HSR_PT_MASTER);
99c5a75911SArvid Brodin 		master->dev->mtu = mtu_max;
100f421436aSArvid Brodin 		break;
101f421436aSArvid Brodin 	case NETDEV_UNREGISTER:
10234a9c361STaehee Yoo 		if (!is_hsr_master(dev)) {
10334a9c361STaehee Yoo 			master = hsr_port_get_hsr(port->hsr, HSR_PT_MASTER);
104c5a75911SArvid Brodin 			hsr_del_port(port);
10534a9c361STaehee Yoo 			if (hsr_slave_empty(master->hsr)) {
106de0083c7STaehee Yoo 				const struct rtnl_link_ops *ops;
107de0083c7STaehee Yoo 
108de0083c7STaehee Yoo 				ops = master->dev->rtnl_link_ops;
109de0083c7STaehee Yoo 				ops->dellink(master->dev, &list_kill);
11034a9c361STaehee Yoo 				unregister_netdevice_many(&list_kill);
11134a9c361STaehee Yoo 			}
11234a9c361STaehee Yoo 		}
113f421436aSArvid Brodin 		break;
114f421436aSArvid Brodin 	case NETDEV_PRE_TYPE_CHANGE:
115f421436aSArvid Brodin 		/* HSR works only on Ethernet devices. Refuse slave to change
116f421436aSArvid Brodin 		 * its type.
117f421436aSArvid Brodin 		 */
118f421436aSArvid Brodin 		return NOTIFY_BAD;
119f421436aSArvid Brodin 	}
120f421436aSArvid Brodin 
121f421436aSArvid Brodin 	return NOTIFY_DONE;
122f421436aSArvid Brodin }
123f421436aSArvid Brodin 
hsr_port_get_hsr(struct hsr_priv * hsr,enum hsr_port_type pt)124c5a75911SArvid Brodin struct hsr_port *hsr_port_get_hsr(struct hsr_priv *hsr, enum hsr_port_type pt)
125c5a75911SArvid Brodin {
126c5a75911SArvid Brodin 	struct hsr_port *port;
127c5a75911SArvid Brodin 
128c5a75911SArvid Brodin 	hsr_for_each_port(hsr, port)
129c5a75911SArvid Brodin 		if (port->type == pt)
130c5a75911SArvid Brodin 			return port;
131c5a75911SArvid Brodin 	return NULL;
132c5a75911SArvid Brodin }
133c5a75911SArvid Brodin 
hsr_get_version(struct net_device * dev,enum hsr_version * ver)134dcf0cd1cSGeorge McCollister int hsr_get_version(struct net_device *dev, enum hsr_version *ver)
135dcf0cd1cSGeorge McCollister {
136dcf0cd1cSGeorge McCollister 	struct hsr_priv *hsr;
137dcf0cd1cSGeorge McCollister 
138dcf0cd1cSGeorge McCollister 	hsr = netdev_priv(dev);
139dcf0cd1cSGeorge McCollister 	*ver = hsr->prot_version;
140dcf0cd1cSGeorge McCollister 
141dcf0cd1cSGeorge McCollister 	return 0;
142dcf0cd1cSGeorge McCollister }
143dcf0cd1cSGeorge McCollister EXPORT_SYMBOL(hsr_get_version);
144dcf0cd1cSGeorge McCollister 
145f421436aSArvid Brodin static struct notifier_block hsr_nb = {
146f421436aSArvid Brodin 	.notifier_call = hsr_netdev_notify,	/* Slave event notifications */
147f421436aSArvid Brodin };
148f421436aSArvid Brodin 
hsr_init(void)149f421436aSArvid Brodin static int __init hsr_init(void)
150f421436aSArvid Brodin {
151*0f25725dSFelix Maurer 	int err;
152f421436aSArvid Brodin 
15370ebe4a4SArvid Brodin 	BUILD_BUG_ON(sizeof(struct hsr_tag) != HSR_HLEN);
154f421436aSArvid Brodin 
155*0f25725dSFelix Maurer 	err = register_netdevice_notifier(&hsr_nb);
156*0f25725dSFelix Maurer 	if (err)
157*0f25725dSFelix Maurer 		return err;
158f421436aSArvid Brodin 
159*0f25725dSFelix Maurer 	err = hsr_netlink_init();
160*0f25725dSFelix Maurer 	if (err) {
161*0f25725dSFelix Maurer 		unregister_netdevice_notifier(&hsr_nb);
162*0f25725dSFelix Maurer 		return err;
163*0f25725dSFelix Maurer 	}
164*0f25725dSFelix Maurer 
165*0f25725dSFelix Maurer 	return 0;
166f421436aSArvid Brodin }
167f421436aSArvid Brodin 
hsr_exit(void)168f421436aSArvid Brodin static void __exit hsr_exit(void)
169f421436aSArvid Brodin {
170f421436aSArvid Brodin 	hsr_netlink_exit();
171c6c4ccd7STaehee Yoo 	hsr_debugfs_remove_root();
172de0083c7STaehee Yoo 	unregister_netdevice_notifier(&hsr_nb);
173f421436aSArvid Brodin }
174f421436aSArvid Brodin 
175f421436aSArvid Brodin module_init(hsr_init);
176f421436aSArvid Brodin module_exit(hsr_exit);
177f421436aSArvid Brodin MODULE_LICENSE("GPL");
178