blob: 98f22551eb455a686fcdb8baada068fcfb6e89ae [file] [log] [blame]
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -06001/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * RMNET configuration engine
13 *
14 */
15
16#include <net/sock.h>
17#include <linux/module.h>
18#include <linux/netlink.h>
19#include <linux/netdevice.h>
20#include "rmnet_config.h"
21#include "rmnet_handlers.h"
22#include "rmnet_vnd.h"
23#include "rmnet_private.h"
24
25/* Locking scheme -
26 * The shared resource which needs to be protected is realdev->rx_handler_data.
27 * For the writer path, this is using rtnl_lock(). The writer paths are
28 * rmnet_newlink(), rmnet_dellink() and rmnet_force_unassociate_device(). These
29 * paths are already called with rtnl_lock() acquired in. There is also an
30 * ASSERT_RTNL() to ensure that we are calling with rtnl acquired. For
31 * dereference here, we will need to use rtnl_dereference(). Dev list writing
32 * needs to happen with rtnl_lock() acquired for netdev_master_upper_dev_link().
33 * For the reader path, the real_dev->rx_handler_data is called in the TX / RX
34 * path. We only need rcu_read_lock() for these scenarios. In these cases,
35 * the rcu_read_lock() is held in __dev_queue_xmit() and
36 * netif_receive_skb_internal(), so readers need to use rcu_dereference_rtnl()
37 * to get the relevant information. For dev list reading, we again acquire
38 * rcu_read_lock() in rmnet_dellink() for netdev_master_upper_dev_get_rcu().
39 * We also use unregister_netdevice_many() to free all rmnet devices in
40 * rmnet_force_unassociate_device() so we dont lose the rtnl_lock() and free in
41 * same context.
42 */
43
44/* Local Definitions and Declarations */
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -060045
46struct rmnet_walk_data {
47 struct net_device *real_dev;
48 struct list_head *head;
Subash Abhinov Kasiviswanathanb665f4f2017-09-02 23:30:46 -060049 struct rmnet_port *port;
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -060050};
51
52static int rmnet_is_real_dev_registered(const struct net_device *real_dev)
53{
54 rx_handler_func_t *rx_handler;
55
56 rx_handler = rcu_dereference(real_dev->rx_handler);
57 return (rx_handler == rmnet_rx_handler);
58}
59
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -060060/* Needs rtnl lock */
Subash Abhinov Kasiviswanathanb665f4f2017-09-02 23:30:46 -060061static struct rmnet_port*
62rmnet_get_port_rtnl(const struct net_device *real_dev)
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -060063{
64 return rtnl_dereference(real_dev->rx_handler_data);
65}
66
67static struct rmnet_endpoint*
68rmnet_get_endpoint(struct net_device *dev, int config_id)
69{
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -060070 struct rmnet_endpoint *ep;
Subash Abhinov Kasiviswanathanb665f4f2017-09-02 23:30:46 -060071 struct rmnet_port *port;
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -060072
73 if (!rmnet_is_real_dev_registered(dev)) {
74 ep = rmnet_vnd_get_endpoint(dev);
75 } else {
Subash Abhinov Kasiviswanathanb665f4f2017-09-02 23:30:46 -060076 port = rmnet_get_port_rtnl(dev);
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -060077
Subash Abhinov Kasiviswanathanb665f4f2017-09-02 23:30:46 -060078 ep = &port->muxed_ep[config_id];
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -060079 }
80
81 return ep;
82}
83
84static int rmnet_unregister_real_device(struct net_device *real_dev,
Subash Abhinov Kasiviswanathanb665f4f2017-09-02 23:30:46 -060085 struct rmnet_port *port)
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -060086{
Subash Abhinov Kasiviswanathanb665f4f2017-09-02 23:30:46 -060087 if (port->nr_rmnet_devs)
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -060088 return -EINVAL;
89
Subash Abhinov Kasiviswanathanb665f4f2017-09-02 23:30:46 -060090 kfree(port);
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -060091
92 netdev_rx_handler_unregister(real_dev);
93
94 /* release reference on real_dev */
95 dev_put(real_dev);
96
97 netdev_dbg(real_dev, "Removed from rmnet\n");
98 return 0;
99}
100
101static int rmnet_register_real_device(struct net_device *real_dev)
102{
Subash Abhinov Kasiviswanathanb665f4f2017-09-02 23:30:46 -0600103 struct rmnet_port *port;
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600104 int rc;
105
106 ASSERT_RTNL();
107
108 if (rmnet_is_real_dev_registered(real_dev))
109 return 0;
110
Subash Abhinov Kasiviswanathanb665f4f2017-09-02 23:30:46 -0600111 port = kzalloc(sizeof(*port), GFP_ATOMIC);
112 if (!port)
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600113 return -ENOMEM;
114
Subash Abhinov Kasiviswanathanb665f4f2017-09-02 23:30:46 -0600115 port->dev = real_dev;
116 rc = netdev_rx_handler_register(real_dev, rmnet_rx_handler, port);
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600117 if (rc) {
Subash Abhinov Kasiviswanathanb665f4f2017-09-02 23:30:46 -0600118 kfree(port);
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600119 return -EBUSY;
120 }
121
122 /* hold on to real dev for MAP data */
123 dev_hold(real_dev);
124
125 netdev_dbg(real_dev, "registered with rmnet\n");
126 return 0;
127}
128
Subash Abhinov Kasiviswanathan032ee462017-09-02 23:30:44 -0600129static void rmnet_set_endpoint_config(struct net_device *dev,
130 u8 mux_id, u8 rmnet_mode,
131 struct net_device *egress_dev)
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600132{
Subash Abhinov Kasiviswanathan032ee462017-09-02 23:30:44 -0600133 struct rmnet_endpoint *ep;
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600134
135 netdev_dbg(dev, "id %d mode %d dev %s\n",
Subash Abhinov Kasiviswanathan032ee462017-09-02 23:30:44 -0600136 mux_id, rmnet_mode, egress_dev->name);
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600137
Subash Abhinov Kasiviswanathan032ee462017-09-02 23:30:44 -0600138 ep = rmnet_get_endpoint(dev, mux_id);
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600139 /* This config is cleared on every set, so its ok to not
140 * clear it on a device delete.
141 */
Subash Abhinov Kasiviswanathan032ee462017-09-02 23:30:44 -0600142 memset(ep, 0, sizeof(struct rmnet_endpoint));
143 ep->rmnet_mode = rmnet_mode;
144 ep->egress_dev = egress_dev;
145 ep->mux_id = mux_id;
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600146}
147
148static int rmnet_newlink(struct net *src_net, struct net_device *dev,
149 struct nlattr *tb[], struct nlattr *data[],
150 struct netlink_ext_ack *extack)
151{
152 int ingress_format = RMNET_INGRESS_FORMAT_DEMUXING |
153 RMNET_INGRESS_FORMAT_DEAGGREGATION |
154 RMNET_INGRESS_FORMAT_MAP;
155 int egress_format = RMNET_EGRESS_FORMAT_MUXING |
156 RMNET_EGRESS_FORMAT_MAP;
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600157 struct net_device *real_dev;
158 int mode = RMNET_EPMODE_VND;
Subash Abhinov Kasiviswanathanb665f4f2017-09-02 23:30:46 -0600159 struct rmnet_port *port;
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600160 int err = 0;
161 u16 mux_id;
162
163 real_dev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK]));
164 if (!real_dev || !dev)
165 return -ENODEV;
166
167 if (!data[IFLA_VLAN_ID])
168 return -EINVAL;
169
170 mux_id = nla_get_u16(data[IFLA_VLAN_ID]);
171
172 err = rmnet_register_real_device(real_dev);
173 if (err)
174 goto err0;
175
Subash Abhinov Kasiviswanathanb665f4f2017-09-02 23:30:46 -0600176 port = rmnet_get_port_rtnl(real_dev);
177 err = rmnet_vnd_newlink(mux_id, dev, port, real_dev);
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600178 if (err)
179 goto err1;
180
181 err = netdev_master_upper_dev_link(dev, real_dev, NULL, NULL);
182 if (err)
183 goto err2;
184
Subash Abhinov Kasiviswanathan032ee462017-09-02 23:30:44 -0600185 netdev_dbg(dev, "data format [ingress 0x%08X] [egress 0x%08X]\n",
186 ingress_format, egress_format);
Subash Abhinov Kasiviswanathanb665f4f2017-09-02 23:30:46 -0600187 port->egress_data_format = egress_format;
188 port->ingress_data_format = ingress_format;
Subash Abhinov Kasiviswanathan032ee462017-09-02 23:30:44 -0600189
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600190 rmnet_set_endpoint_config(real_dev, mux_id, mode, dev);
191 rmnet_set_endpoint_config(dev, mux_id, mode, real_dev);
192 return 0;
193
194err2:
Subash Abhinov Kasiviswanathanb665f4f2017-09-02 23:30:46 -0600195 rmnet_vnd_dellink(mux_id, port);
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600196err1:
Subash Abhinov Kasiviswanathanb665f4f2017-09-02 23:30:46 -0600197 rmnet_unregister_real_device(real_dev, port);
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600198err0:
199 return err;
200}
201
202static void rmnet_dellink(struct net_device *dev, struct list_head *head)
203{
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600204 struct net_device *real_dev;
Subash Abhinov Kasiviswanathanb665f4f2017-09-02 23:30:46 -0600205 struct rmnet_port *port;
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600206 u8 mux_id;
207
208 rcu_read_lock();
209 real_dev = netdev_master_upper_dev_get_rcu(dev);
210 rcu_read_unlock();
211
212 if (!real_dev || !rmnet_is_real_dev_registered(real_dev))
213 return;
214
Subash Abhinov Kasiviswanathanb665f4f2017-09-02 23:30:46 -0600215 port = rmnet_get_port_rtnl(real_dev);
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600216
217 mux_id = rmnet_vnd_get_mux(dev);
Subash Abhinov Kasiviswanathanb665f4f2017-09-02 23:30:46 -0600218 rmnet_vnd_dellink(mux_id, port);
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600219 netdev_upper_dev_unlink(dev, real_dev);
Subash Abhinov Kasiviswanathanb665f4f2017-09-02 23:30:46 -0600220 rmnet_unregister_real_device(real_dev, port);
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600221
222 unregister_netdevice_queue(dev, head);
223}
224
225static int rmnet_dev_walk_unreg(struct net_device *rmnet_dev, void *data)
226{
227 struct rmnet_walk_data *d = data;
228 u8 mux_id;
229
230 mux_id = rmnet_vnd_get_mux(rmnet_dev);
231
Subash Abhinov Kasiviswanathanb665f4f2017-09-02 23:30:46 -0600232 rmnet_vnd_dellink(mux_id, d->port);
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600233 netdev_upper_dev_unlink(rmnet_dev, d->real_dev);
234 unregister_netdevice_queue(rmnet_dev, d->head);
235
236 return 0;
237}
238
239static void rmnet_force_unassociate_device(struct net_device *dev)
240{
241 struct net_device *real_dev = dev;
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600242 struct rmnet_walk_data d;
Subash Abhinov Kasiviswanathanb665f4f2017-09-02 23:30:46 -0600243 struct rmnet_port *port;
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600244 LIST_HEAD(list);
245
246 if (!rmnet_is_real_dev_registered(real_dev))
247 return;
248
249 ASSERT_RTNL();
250
251 d.real_dev = real_dev;
252 d.head = &list;
253
Subash Abhinov Kasiviswanathanb665f4f2017-09-02 23:30:46 -0600254 port = rmnet_get_port_rtnl(dev);
255 d.port = port;
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600256
257 rcu_read_lock();
258 netdev_walk_all_lower_dev_rcu(real_dev, rmnet_dev_walk_unreg, &d);
259 rcu_read_unlock();
260 unregister_netdevice_many(&list);
261
Subash Abhinov Kasiviswanathanb665f4f2017-09-02 23:30:46 -0600262 rmnet_unregister_real_device(real_dev, port);
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600263}
264
265static int rmnet_config_notify_cb(struct notifier_block *nb,
266 unsigned long event, void *data)
267{
268 struct net_device *dev = netdev_notifier_info_to_dev(data);
269
270 if (!dev)
271 return NOTIFY_DONE;
272
273 switch (event) {
274 case NETDEV_UNREGISTER:
275 netdev_dbg(dev, "Kernel unregister\n");
276 rmnet_force_unassociate_device(dev);
277 break;
278
279 default:
280 break;
281 }
282
283 return NOTIFY_DONE;
284}
285
286static struct notifier_block rmnet_dev_notifier __read_mostly = {
287 .notifier_call = rmnet_config_notify_cb,
288};
289
290static int rmnet_rtnl_validate(struct nlattr *tb[], struct nlattr *data[],
291 struct netlink_ext_ack *extack)
292{
293 u16 mux_id;
294
295 if (!data || !data[IFLA_VLAN_ID])
296 return -EINVAL;
297
298 mux_id = nla_get_u16(data[IFLA_VLAN_ID]);
299 if (mux_id > (RMNET_MAX_LOGICAL_EP - 1))
300 return -ERANGE;
301
302 return 0;
303}
304
305static size_t rmnet_get_size(const struct net_device *dev)
306{
307 return nla_total_size(2); /* IFLA_VLAN_ID */
308}
309
310struct rtnl_link_ops rmnet_link_ops __read_mostly = {
311 .kind = "rmnet",
312 .maxtype = __IFLA_VLAN_MAX,
313 .priv_size = sizeof(struct rmnet_priv),
314 .setup = rmnet_vnd_setup,
315 .validate = rmnet_rtnl_validate,
316 .newlink = rmnet_newlink,
317 .dellink = rmnet_dellink,
318 .get_size = rmnet_get_size,
319};
320
Subash Abhinov Kasiviswanathan032ee462017-09-02 23:30:44 -0600321/* Needs either rcu_read_lock() or rtnl lock */
Subash Abhinov Kasiviswanathanb665f4f2017-09-02 23:30:46 -0600322struct rmnet_port *rmnet_get_port(struct net_device *real_dev)
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600323{
Subash Abhinov Kasiviswanathan032ee462017-09-02 23:30:44 -0600324 if (rmnet_is_real_dev_registered(real_dev))
325 return rcu_dereference_rtnl(real_dev->rx_handler_data);
326 else
327 return NULL;
Subash Abhinov Kasiviswanathanceed73a2017-08-29 22:44:18 -0600328}
329
330/* Startup/Shutdown */
331
332static int __init rmnet_init(void)
333{
334 int rc;
335
336 rc = register_netdevice_notifier(&rmnet_dev_notifier);
337 if (rc != 0)
338 return rc;
339
340 rc = rtnl_link_register(&rmnet_link_ops);
341 if (rc != 0) {
342 unregister_netdevice_notifier(&rmnet_dev_notifier);
343 return rc;
344 }
345 return rc;
346}
347
348static void __exit rmnet_exit(void)
349{
350 unregister_netdevice_notifier(&rmnet_dev_notifier);
351 rtnl_link_unregister(&rmnet_link_ops);
352}
353
354module_init(rmnet_init)
355module_exit(rmnet_exit)
356MODULE_LICENSE("GPL v2");