Merge tag 'for_linux-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jwess...
[cascardo/linux.git] / net / switchdev / switchdev.c
1 /*
2  * net/switchdev/switchdev.c - Switch device API
3  * Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  */
10
11 #include <linux/kernel.h>
12 #include <linux/types.h>
13 #include <linux/init.h>
14 #include <linux/mutex.h>
15 #include <linux/notifier.h>
16 #include <linux/netdevice.h>
17 #include <net/switchdev.h>
18
19 /**
20  *      netdev_switch_parent_id_get - Get ID of a switch
21  *      @dev: port device
22  *      @psid: switch ID
23  *
24  *      Get ID of a switch this port is part of.
25  */
26 int netdev_switch_parent_id_get(struct net_device *dev,
27                                 struct netdev_phys_item_id *psid)
28 {
29         const struct net_device_ops *ops = dev->netdev_ops;
30
31         if (!ops->ndo_switch_parent_id_get)
32                 return -EOPNOTSUPP;
33         return ops->ndo_switch_parent_id_get(dev, psid);
34 }
35 EXPORT_SYMBOL(netdev_switch_parent_id_get);
36
37 /**
38  *      netdev_switch_port_stp_update - Notify switch device port of STP
39  *                                      state change
40  *      @dev: port device
41  *      @state: port STP state
42  *
43  *      Notify switch device port of bridge port STP state change.
44  */
45 int netdev_switch_port_stp_update(struct net_device *dev, u8 state)
46 {
47         const struct net_device_ops *ops = dev->netdev_ops;
48
49         if (!ops->ndo_switch_port_stp_update)
50                 return -EOPNOTSUPP;
51         WARN_ON(!ops->ndo_switch_parent_id_get);
52         return ops->ndo_switch_port_stp_update(dev, state);
53 }
54 EXPORT_SYMBOL(netdev_switch_port_stp_update);
55
56 static DEFINE_MUTEX(netdev_switch_mutex);
57 static RAW_NOTIFIER_HEAD(netdev_switch_notif_chain);
58
59 /**
60  *      register_netdev_switch_notifier - Register nofifier
61  *      @nb: notifier_block
62  *
63  *      Register switch device notifier. This should be used by code
64  *      which needs to monitor events happening in particular device.
65  *      Return values are same as for atomic_notifier_chain_register().
66  */
67 int register_netdev_switch_notifier(struct notifier_block *nb)
68 {
69         int err;
70
71         mutex_lock(&netdev_switch_mutex);
72         err = raw_notifier_chain_register(&netdev_switch_notif_chain, nb);
73         mutex_unlock(&netdev_switch_mutex);
74         return err;
75 }
76 EXPORT_SYMBOL(register_netdev_switch_notifier);
77
78 /**
79  *      unregister_netdev_switch_notifier - Unregister nofifier
80  *      @nb: notifier_block
81  *
82  *      Unregister switch device notifier.
83  *      Return values are same as for atomic_notifier_chain_unregister().
84  */
85 int unregister_netdev_switch_notifier(struct notifier_block *nb)
86 {
87         int err;
88
89         mutex_lock(&netdev_switch_mutex);
90         err = raw_notifier_chain_unregister(&netdev_switch_notif_chain, nb);
91         mutex_unlock(&netdev_switch_mutex);
92         return err;
93 }
94 EXPORT_SYMBOL(unregister_netdev_switch_notifier);
95
96 /**
97  *      call_netdev_switch_notifiers - Call nofifiers
98  *      @val: value passed unmodified to notifier function
99  *      @dev: port device
100  *      @info: notifier information data
101  *
102  *      Call all network notifier blocks. This should be called by driver
103  *      when it needs to propagate hardware event.
104  *      Return values are same as for atomic_notifier_call_chain().
105  */
106 int call_netdev_switch_notifiers(unsigned long val, struct net_device *dev,
107                                  struct netdev_switch_notifier_info *info)
108 {
109         int err;
110
111         info->dev = dev;
112         mutex_lock(&netdev_switch_mutex);
113         err = raw_notifier_call_chain(&netdev_switch_notif_chain, val, info);
114         mutex_unlock(&netdev_switch_mutex);
115         return err;
116 }
117 EXPORT_SYMBOL(call_netdev_switch_notifiers);
118
119 /**
120  *      netdev_switch_port_bridge_setlink - Notify switch device port of bridge
121  *      port attributes
122  *
123  *      @dev: port device
124  *      @nlh: netlink msg with bridge port attributes
125  *      @flags: bridge setlink flags
126  *
127  *      Notify switch device port of bridge port attributes
128  */
129 int netdev_switch_port_bridge_setlink(struct net_device *dev,
130                                       struct nlmsghdr *nlh, u16 flags)
131 {
132         const struct net_device_ops *ops = dev->netdev_ops;
133
134         if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
135                 return 0;
136
137         if (!ops->ndo_bridge_setlink)
138                 return -EOPNOTSUPP;
139
140         return ops->ndo_bridge_setlink(dev, nlh, flags);
141 }
142 EXPORT_SYMBOL(netdev_switch_port_bridge_setlink);
143
144 /**
145  *      netdev_switch_port_bridge_dellink - Notify switch device port of bridge
146  *      port attribute delete
147  *
148  *      @dev: port device
149  *      @nlh: netlink msg with bridge port attributes
150  *      @flags: bridge setlink flags
151  *
152  *      Notify switch device port of bridge port attribute delete
153  */
154 int netdev_switch_port_bridge_dellink(struct net_device *dev,
155                                       struct nlmsghdr *nlh, u16 flags)
156 {
157         const struct net_device_ops *ops = dev->netdev_ops;
158
159         if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
160                 return 0;
161
162         if (!ops->ndo_bridge_dellink)
163                 return -EOPNOTSUPP;
164
165         return ops->ndo_bridge_dellink(dev, nlh, flags);
166 }
167 EXPORT_SYMBOL(netdev_switch_port_bridge_dellink);
168
169 /**
170  *      ndo_dflt_netdev_switch_port_bridge_setlink - default ndo bridge setlink
171  *                                                   op for master devices
172  *
173  *      @dev: port device
174  *      @nlh: netlink msg with bridge port attributes
175  *      @flags: bridge setlink flags
176  *
177  *      Notify master device slaves of bridge port attributes
178  */
179 int ndo_dflt_netdev_switch_port_bridge_setlink(struct net_device *dev,
180                                                struct nlmsghdr *nlh, u16 flags)
181 {
182         struct net_device *lower_dev;
183         struct list_head *iter;
184         int ret = 0, err = 0;
185
186         if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
187                 return ret;
188
189         netdev_for_each_lower_dev(dev, lower_dev, iter) {
190                 err = netdev_switch_port_bridge_setlink(lower_dev, nlh, flags);
191                 if (err && err != -EOPNOTSUPP)
192                         ret = err;
193         }
194
195         return ret;
196 }
197 EXPORT_SYMBOL(ndo_dflt_netdev_switch_port_bridge_setlink);
198
199 /**
200  *      ndo_dflt_netdev_switch_port_bridge_dellink - default ndo bridge dellink
201  *                                                   op for master devices
202  *
203  *      @dev: port device
204  *      @nlh: netlink msg with bridge port attributes
205  *      @flags: bridge dellink flags
206  *
207  *      Notify master device slaves of bridge port attribute deletes
208  */
209 int ndo_dflt_netdev_switch_port_bridge_dellink(struct net_device *dev,
210                                                struct nlmsghdr *nlh, u16 flags)
211 {
212         struct net_device *lower_dev;
213         struct list_head *iter;
214         int ret = 0, err = 0;
215
216         if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
217                 return ret;
218
219         netdev_for_each_lower_dev(dev, lower_dev, iter) {
220                 err = netdev_switch_port_bridge_dellink(lower_dev, nlh, flags);
221                 if (err && err != -EOPNOTSUPP)
222                         ret = err;
223         }
224
225         return ret;
226 }
227 EXPORT_SYMBOL(ndo_dflt_netdev_switch_port_bridge_dellink);