blob: 74c12734db741f4c151adf408ba08d3db17f3931 [file] [log] [blame]
Sjur Braendelandc72dfae2010-03-30 13:56:25 +00001/*
2 * CAIF Interface registration.
3 * Copyright (C) ST-Ericsson AB 2010
4 * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
5 * License terms: GNU General Public License (GPL) version 2
6 *
7 * Borrowed heavily from file: pn_dev.c. Thanks to
8 * Remi Denis-Courmont <remi.denis-courmont@nokia.com>
9 * and Sakari Ailus <sakari.ailus@nokia.com>
10 */
11
Joe Perchesb31fa5b2010-09-05 21:31:11 +000012#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
13
Sjur Braendelandc72dfae2010-03-30 13:56:25 +000014#include <linux/kernel.h>
15#include <linux/if_arp.h>
16#include <linux/net.h>
17#include <linux/netdevice.h>
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +000018#include <linux/mutex.h>
Paul Gortmaker3a9a2312011-05-27 09:12:25 -040019#include <linux/module.h>
sjur.brandeland@stericsson.com0e4c7d82011-12-04 11:22:54 +000020#include <linux/spinlock.h>
Sjur Braendelandc72dfae2010-03-30 13:56:25 +000021#include <net/netns/generic.h>
22#include <net/net_namespace.h>
23#include <net/pkt_sched.h>
24#include <net/caif/caif_device.h>
Sjur Braendelandc72dfae2010-03-30 13:56:25 +000025#include <net/caif/caif_layer.h>
26#include <net/caif/cfpkt.h>
27#include <net/caif/cfcnfg.h>
sjur.brandeland@stericsson.com7c18d222011-11-30 09:22:47 +000028#include <net/caif/cfserl.h>
Sjur Braendelandc72dfae2010-03-30 13:56:25 +000029
30MODULE_LICENSE("GPL");
Sjur Braendelandc72dfae2010-03-30 13:56:25 +000031
32/* Used for local tracking of the CAIF net devices */
33struct caif_device_entry {
34 struct cflayer layer;
35 struct list_head list;
Sjur Braendelandc72dfae2010-03-30 13:56:25 +000036 struct net_device *netdev;
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +000037 int __percpu *pcpu_refcnt;
sjur.brandeland@stericsson.com0e4c7d82011-12-04 11:22:54 +000038 spinlock_t flow_lock;
39 bool xoff;
Sjur Braendelandc72dfae2010-03-30 13:56:25 +000040};
41
42struct caif_device_entry_list {
43 struct list_head list;
44 /* Protects simulanous deletes in list */
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +000045 struct mutex lock;
Sjur Braendelandc72dfae2010-03-30 13:56:25 +000046};
47
48struct caif_net {
sjur.brandeland@stericsson.combee925d2011-05-13 02:44:05 +000049 struct cfcnfg *cfg;
Sjur Braendelandc72dfae2010-03-30 13:56:25 +000050 struct caif_device_entry_list caifdevs;
51};
52
53static int caif_net_id;
sjur.brandeland@stericsson.com0e4c7d82011-12-04 11:22:54 +000054static int q_high = 50; /* Percent */
sjur.brandeland@stericsson.combee925d2011-05-13 02:44:05 +000055
56struct cfcnfg *get_cfcnfg(struct net *net)
57{
58 struct caif_net *caifn;
59 BUG_ON(!net);
60 caifn = net_generic(net, caif_net_id);
sjur.brandeland@stericsson.com7c18d222011-11-30 09:22:47 +000061 if (!caifn)
62 return NULL;
sjur.brandeland@stericsson.combee925d2011-05-13 02:44:05 +000063 return caifn->cfg;
64}
65EXPORT_SYMBOL(get_cfcnfg);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +000066
67static struct caif_device_entry_list *caif_device_list(struct net *net)
68{
69 struct caif_net *caifn;
70 BUG_ON(!net);
71 caifn = net_generic(net, caif_net_id);
sjur.brandeland@stericsson.com7c18d222011-11-30 09:22:47 +000072 if (!caifn)
73 return NULL;
Sjur Braendelandc72dfae2010-03-30 13:56:25 +000074 return &caifn->caifdevs;
75}
76
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +000077static void caifd_put(struct caif_device_entry *e)
78{
79 irqsafe_cpu_dec(*e->pcpu_refcnt);
80}
81
82static void caifd_hold(struct caif_device_entry *e)
83{
84 irqsafe_cpu_inc(*e->pcpu_refcnt);
85}
86
87static int caifd_refcnt_read(struct caif_device_entry *e)
88{
89 int i, refcnt = 0;
90 for_each_possible_cpu(i)
91 refcnt += *per_cpu_ptr(e->pcpu_refcnt, i);
92 return refcnt;
93}
94
Sjur Braendelandc72dfae2010-03-30 13:56:25 +000095/* Allocate new CAIF device. */
96static struct caif_device_entry *caif_device_alloc(struct net_device *dev)
97{
98 struct caif_device_entry_list *caifdevs;
99 struct caif_device_entry *caifd;
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000100
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000101 caifdevs = caif_device_list(dev_net(dev));
sjur.brandeland@stericsson.com7c18d222011-11-30 09:22:47 +0000102 if (!caifdevs)
103 return NULL;
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000104
Eric Dumazet4fb66b82011-09-02 02:19:23 +0000105 caifd = kzalloc(sizeof(*caifd), GFP_KERNEL);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000106 if (!caifd)
107 return NULL;
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000108 caifd->pcpu_refcnt = alloc_percpu(int);
Eric Dumazet4fb66b82011-09-02 02:19:23 +0000109 if (!caifd->pcpu_refcnt) {
110 kfree(caifd);
111 return NULL;
112 }
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000113 caifd->netdev = dev;
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000114 dev_hold(dev);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000115 return caifd;
116}
117
118static struct caif_device_entry *caif_get(struct net_device *dev)
119{
120 struct caif_device_entry_list *caifdevs =
121 caif_device_list(dev_net(dev));
122 struct caif_device_entry *caifd;
sjur.brandeland@stericsson.com7c18d222011-11-30 09:22:47 +0000123 if (!caifdevs)
124 return NULL;
125
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000126 list_for_each_entry_rcu(caifd, &caifdevs->list, list) {
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000127 if (caifd->netdev == dev)
128 return caifd;
129 }
130 return NULL;
131}
132
sjur.brandeland@stericsson.com0e4c7d82011-12-04 11:22:54 +0000133void caif_flow_cb(struct sk_buff *skb)
134{
135 struct caif_device_entry *caifd;
136 bool send_xoff;
137
138 WARN_ON(skb->dev == NULL);
139
140 rcu_read_lock();
141 caifd = caif_get(skb->dev);
142 caifd_hold(caifd);
143 rcu_read_unlock();
144
145 spin_lock_bh(&caifd->flow_lock);
146 send_xoff = caifd->xoff;
147 caifd->xoff = 0;
148 spin_unlock_bh(&caifd->flow_lock);
149
150 if (send_xoff)
151 caifd->layer.up->
152 ctrlcmd(caifd->layer.up,
153 _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND,
154 caifd->layer.id);
155 caifd_put(caifd);
156}
157
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000158static int transmit(struct cflayer *layer, struct cfpkt *pkt)
159{
sjur.brandeland@stericsson.com0e4c7d82011-12-04 11:22:54 +0000160 int err, high = 0, qlen = 0;
161 struct caif_dev_common *caifdev;
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000162 struct caif_device_entry *caifd =
163 container_of(layer, struct caif_device_entry, layer);
Sjur Brændeland4dd820c2011-04-11 10:43:51 +0000164 struct sk_buff *skb;
sjur.brandeland@stericsson.com0e4c7d82011-12-04 11:22:54 +0000165 struct netdev_queue *txq;
166
167 rcu_read_lock_bh();
Sjur Brændeland4dd820c2011-04-11 10:43:51 +0000168
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000169 skb = cfpkt_tonative(pkt);
170 skb->dev = caifd->netdev;
sjur.brandeland@stericsson.com7c18d222011-11-30 09:22:47 +0000171 skb_reset_network_header(skb);
172 skb->protocol = htons(ETH_P_CAIF);
sjur.brandeland@stericsson.com0e4c7d82011-12-04 11:22:54 +0000173 caifdev = netdev_priv(caifd->netdev);
174
175 /* Check if we need to handle xoff */
176 if (likely(caifd->netdev->tx_queue_len == 0))
177 goto noxoff;
178
179 if (unlikely(caifd->xoff))
180 goto noxoff;
181
182 if (likely(!netif_queue_stopped(caifd->netdev))) {
183 /* If we run with a TX queue, check if the queue is too long*/
184 txq = netdev_get_tx_queue(skb->dev, 0);
185 qlen = qdisc_qlen(rcu_dereference_bh(txq->qdisc));
186
187 if (likely(qlen == 0))
188 goto noxoff;
189
190 high = (caifd->netdev->tx_queue_len * q_high) / 100;
191 if (likely(qlen < high))
192 goto noxoff;
193 }
194
195 /* Hold lock while accessing xoff */
196 spin_lock_bh(&caifd->flow_lock);
197 if (caifd->xoff) {
198 spin_unlock_bh(&caifd->flow_lock);
199 goto noxoff;
200 }
201
202 /*
203 * Handle flow off, we do this by temporary hi-jacking this
204 * skb's destructor function, and replace it with our own
205 * flow-on callback. The callback will set flow-on and call
206 * the original destructor.
207 */
208
209 pr_debug("queue has stopped(%d) or is full (%d > %d)\n",
210 netif_queue_stopped(caifd->netdev),
211 qlen, high);
212 caifd->xoff = 1;
213 spin_unlock_bh(&caifd->flow_lock);
214 skb_orphan(skb);
215
216 caifd->layer.up->ctrlcmd(caifd->layer.up,
217 _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND,
218 caifd->layer.id);
219noxoff:
220 rcu_read_unlock_bh();
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000221
sjur.brandeland@stericsson.comc85c2952011-05-13 02:44:06 +0000222 err = dev_queue_xmit(skb);
223 if (err > 0)
224 err = -EIO;
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000225
sjur.brandeland@stericsson.comc85c2952011-05-13 02:44:06 +0000226 return err;
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000227}
228
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000229/*
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000230 * Stuff received packets into the CAIF stack.
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000231 * On error, returns non-zero and releases the skb.
232 */
233static int receive(struct sk_buff *skb, struct net_device *dev,
234 struct packet_type *pkttype, struct net_device *orig_dev)
235{
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000236 struct cfpkt *pkt;
237 struct caif_device_entry *caifd;
sjur.brandeland@stericsson.com69c867c92011-05-22 11:18:54 +0000238 int err;
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000239
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000240 pkt = cfpkt_fromnative(CAIF_DIR_IN, skb);
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000241
242 rcu_read_lock();
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000243 caifd = caif_get(dev);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000244
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000245 if (!caifd || !caifd->layer.up || !caifd->layer.up->receive ||
246 !netif_oper_up(caifd->netdev)) {
247 rcu_read_unlock();
248 kfree_skb(skb);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000249 return NET_RX_DROP;
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000250 }
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000251
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000252 /* Hold reference to netdevice while using CAIF stack */
253 caifd_hold(caifd);
254 rcu_read_unlock();
255
sjur.brandeland@stericsson.com69c867c92011-05-22 11:18:54 +0000256 err = caifd->layer.up->receive(caifd->layer.up, pkt);
257
258 /* For -EILSEQ the packet is not freed so so it now */
259 if (err == -EILSEQ)
260 cfpkt_destroy(pkt);
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000261
262 /* Release reference to stack upwards */
263 caifd_put(caifd);
sjur.brandeland@stericsson.com7c18d222011-11-30 09:22:47 +0000264
265 if (err != 0)
266 err = NET_RX_DROP;
267 return err;
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000268}
269
270static struct packet_type caif_packet_type __read_mostly = {
271 .type = cpu_to_be16(ETH_P_CAIF),
272 .func = receive,
273};
274
275static void dev_flowctrl(struct net_device *dev, int on)
276{
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000277 struct caif_device_entry *caifd;
278
279 rcu_read_lock();
280
281 caifd = caif_get(dev);
282 if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) {
283 rcu_read_unlock();
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000284 return;
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000285 }
286
287 caifd_hold(caifd);
288 rcu_read_unlock();
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000289
290 caifd->layer.up->ctrlcmd(caifd->layer.up,
291 on ?
292 _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND :
293 _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND,
294 caifd->layer.id);
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000295 caifd_put(caifd);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000296}
297
sjur.brandeland@stericsson.com7c18d222011-11-30 09:22:47 +0000298void caif_enroll_dev(struct net_device *dev, struct caif_dev_common *caifdev,
299 struct cflayer *link_support, int head_room,
300 struct cflayer **layer, int (**rcv_func)(
301 struct sk_buff *, struct net_device *,
302 struct packet_type *, struct net_device *))
303{
304 struct caif_device_entry *caifd;
305 enum cfcnfg_phy_preference pref;
306 struct cfcnfg *cfg = get_cfcnfg(dev_net(dev));
307 struct caif_device_entry_list *caifdevs;
308
309 caifdevs = caif_device_list(dev_net(dev));
310 if (!cfg || !caifdevs)
311 return;
312 caifd = caif_device_alloc(dev);
313 if (!caifd)
314 return;
315 *layer = &caifd->layer;
sjur.brandeland@stericsson.com0e4c7d82011-12-04 11:22:54 +0000316 spin_lock_init(&caifd->flow_lock);
sjur.brandeland@stericsson.com7c18d222011-11-30 09:22:47 +0000317
318 switch (caifdev->link_select) {
319 case CAIF_LINK_HIGH_BANDW:
320 pref = CFPHYPREF_HIGH_BW;
321 break;
322 case CAIF_LINK_LOW_LATENCY:
323 pref = CFPHYPREF_LOW_LAT;
324 break;
325 default:
326 pref = CFPHYPREF_HIGH_BW;
327 break;
328 }
329 mutex_lock(&caifdevs->lock);
330 list_add_rcu(&caifd->list, &caifdevs->list);
331
332 strncpy(caifd->layer.name, dev->name,
333 sizeof(caifd->layer.name) - 1);
334 caifd->layer.name[sizeof(caifd->layer.name) - 1] = 0;
335 caifd->layer.transmit = transmit;
336 cfcnfg_add_phy_layer(cfg,
337 dev,
338 &caifd->layer,
339 pref,
340 link_support,
341 caifdev->use_fcs,
342 head_room);
343 mutex_unlock(&caifdevs->lock);
344 if (rcv_func)
345 *rcv_func = receive;
346}
sjur.brandeland@stericsson.com7ad65bf2011-12-04 11:22:53 +0000347EXPORT_SYMBOL(caif_enroll_dev);
sjur.brandeland@stericsson.com7c18d222011-11-30 09:22:47 +0000348
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000349/* notify Caif of device events */
350static int caif_device_notify(struct notifier_block *me, unsigned long what,
351 void *arg)
352{
353 struct net_device *dev = arg;
354 struct caif_device_entry *caifd = NULL;
355 struct caif_dev_common *caifdev;
sjur.brandeland@stericsson.combee925d2011-05-13 02:44:05 +0000356 struct cfcnfg *cfg;
sjur.brandeland@stericsson.com7c18d222011-11-30 09:22:47 +0000357 struct cflayer *layer, *link_support;
358 int head_room = 0;
David Woodhouse08613e42011-10-24 21:25:21 +0000359 struct caif_device_entry_list *caifdevs;
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000360
sjur.brandeland@stericsson.combee925d2011-05-13 02:44:05 +0000361 cfg = get_cfcnfg(dev_net(dev));
sjur.brandeland@stericsson.com7c18d222011-11-30 09:22:47 +0000362 caifdevs = caif_device_list(dev_net(dev));
363 if (!cfg || !caifdevs)
sjur.brandeland@stericsson.combee925d2011-05-13 02:44:05 +0000364 return 0;
365
sjur.brandeland@stericsson.com7c18d222011-11-30 09:22:47 +0000366 caifd = caif_get(dev);
367 if (caifd == NULL && dev->type != ARPHRD_CAIF)
368 return 0;
David Woodhouse08613e42011-10-24 21:25:21 +0000369
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000370 switch (what) {
371 case NETDEV_REGISTER:
sjur.brandeland@stericsson.com7c18d222011-11-30 09:22:47 +0000372 if (caifd != NULL)
373 break;
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000374
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000375 caifdev = netdev_priv(dev);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000376
sjur.brandeland@stericsson.com7c18d222011-11-30 09:22:47 +0000377 link_support = NULL;
378 if (caifdev->use_frag) {
379 head_room = 1;
380 link_support = cfserl_create(dev->ifindex,
sjur.brandeland@stericsson.come977b4c2011-11-30 09:22:48 +0000381 caifdev->use_stx);
sjur.brandeland@stericsson.com7c18d222011-11-30 09:22:47 +0000382 if (!link_support) {
383 pr_warn("Out of memory\n");
384 break;
385 }
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000386 }
sjur.brandeland@stericsson.com7c18d222011-11-30 09:22:47 +0000387 caif_enroll_dev(dev, caifdev, link_support, head_room,
388 &layer, NULL);
389 caifdev->flowctrl = dev_flowctrl;
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000390 break;
391
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000392 case NETDEV_UP:
393 rcu_read_lock();
394
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000395 caifd = caif_get(dev);
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000396 if (caifd == NULL) {
397 rcu_read_unlock();
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000398 break;
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000399 }
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000400
sjur.brandeland@stericsson.com0e4c7d82011-12-04 11:22:54 +0000401 caifd->xoff = 0;
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000402 cfcnfg_set_phy_state(cfg, &caifd->layer, true);
403 rcu_read_unlock();
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000404
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000405 break;
406
407 case NETDEV_DOWN:
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000408 rcu_read_lock();
409
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000410 caifd = caif_get(dev);
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000411 if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) {
412 rcu_read_unlock();
413 return -EINVAL;
414 }
415
416 cfcnfg_set_phy_state(cfg, &caifd->layer, false);
417 caifd_hold(caifd);
418 rcu_read_unlock();
419
420 caifd->layer.up->ctrlcmd(caifd->layer.up,
421 _CAIF_CTRLCMD_PHYIF_DOWN_IND,
422 caifd->layer.id);
423 caifd_put(caifd);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000424 break;
425
426 case NETDEV_UNREGISTER:
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000427 mutex_lock(&caifdevs->lock);
428
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000429 caifd = caif_get(dev);
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000430 if (caifd == NULL) {
431 mutex_unlock(&caifdevs->lock);
André Carvalho de Matosf2527ec2010-11-01 11:52:47 +0000432 break;
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000433 }
434 list_del_rcu(&caifd->list);
435
436 /*
437 * NETDEV_UNREGISTER is called repeatedly until all reference
438 * counts for the net-device are released. If references to
439 * caifd is taken, simply ignore NETDEV_UNREGISTER and wait for
440 * the next call to NETDEV_UNREGISTER.
441 *
442 * If any packets are in flight down the CAIF Stack,
443 * cfcnfg_del_phy_layer will return nonzero.
444 * If no packets are in flight, the CAIF Stack associated
445 * with the net-device un-registering is freed.
446 */
447
448 if (caifd_refcnt_read(caifd) != 0 ||
449 cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0) {
450
451 pr_info("Wait for device inuse\n");
452 /* Enrole device if CAIF Stack is still in use */
453 list_add_rcu(&caifd->list, &caifdevs->list);
454 mutex_unlock(&caifdevs->lock);
455 break;
456 }
457
458 synchronize_rcu();
459 dev_put(caifd->netdev);
460 free_percpu(caifd->pcpu_refcnt);
461 kfree(caifd);
462
463 mutex_unlock(&caifdevs->lock);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000464 break;
465 }
466 return 0;
467}
468
469static struct notifier_block caif_device_notifier = {
470 .notifier_call = caif_device_notify,
471 .priority = 0,
472};
473
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000474/* Per-namespace Caif devices handling */
475static int caif_init_net(struct net *net)
476{
477 struct caif_net *caifn = net_generic(net, caif_net_id);
sjur.brandeland@stericsson.combee925d2011-05-13 02:44:05 +0000478 BUG_ON(!caifn);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000479 INIT_LIST_HEAD(&caifn->caifdevs.list);
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000480 mutex_init(&caifn->caifdevs.lock);
sjur.brandeland@stericsson.combee925d2011-05-13 02:44:05 +0000481
482 caifn->cfg = cfcnfg_create();
483 if (!caifn->cfg) {
484 pr_warn("can't create cfcnfg\n");
485 return -ENOMEM;
486 }
487
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000488 return 0;
489}
490
491static void caif_exit_net(struct net *net)
492{
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000493 struct caif_device_entry *caifd, *tmp;
494 struct caif_device_entry_list *caifdevs =
495 caif_device_list(net);
sjur.brandeland@stericsson.com7c18d222011-11-30 09:22:47 +0000496 struct cfcnfg *cfg = get_cfcnfg(net);
497
498 if (!cfg || !caifdevs)
499 return;
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000500
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000501 rtnl_lock();
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000502 mutex_lock(&caifdevs->lock);
503
504 list_for_each_entry_safe(caifd, tmp, &caifdevs->list, list) {
505 int i = 0;
506 list_del_rcu(&caifd->list);
507 cfcnfg_set_phy_state(cfg, &caifd->layer, false);
508
509 while (i < 10 &&
510 (caifd_refcnt_read(caifd) != 0 ||
511 cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0)) {
512
513 pr_info("Wait for device inuse\n");
514 msleep(250);
515 i++;
516 }
517 synchronize_rcu();
518 dev_put(caifd->netdev);
519 free_percpu(caifd->pcpu_refcnt);
520 kfree(caifd);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000521 }
sjur.brandeland@stericsson.combee925d2011-05-13 02:44:05 +0000522 cfcnfg_remove(cfg);
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000523
524 mutex_unlock(&caifdevs->lock);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000525 rtnl_unlock();
526}
527
528static struct pernet_operations caif_net_ops = {
529 .init = caif_init_net,
530 .exit = caif_exit_net,
531 .id = &caif_net_id,
532 .size = sizeof(struct caif_net),
533};
534
535/* Initialize Caif devices list */
536static int __init caif_device_init(void)
537{
538 int result;
sjur.brandeland@stericsson.combd30ce42011-05-13 02:44:00 +0000539
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000540 result = register_pernet_device(&caif_net_ops);
541
sjur.brandeland@stericsson.combee925d2011-05-13 02:44:05 +0000542 if (result)
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000543 return result;
sjur.brandeland@stericsson.combee925d2011-05-13 02:44:05 +0000544
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000545 register_netdevice_notifier(&caif_device_notifier);
sjur.brandeland@stericsson.combee925d2011-05-13 02:44:05 +0000546 dev_add_pack(&caif_packet_type);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000547
548 return result;
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000549}
550
551static void __exit caif_device_exit(void)
552{
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000553 unregister_pernet_device(&caif_net_ops);
554 unregister_netdevice_notifier(&caif_device_notifier);
sjur.brandeland@stericsson.combee925d2011-05-13 02:44:05 +0000555 dev_remove_pack(&caif_packet_type);
Sjur Braendelandc72dfae2010-03-30 13:56:25 +0000556}
557
558module_init(caif_device_init);
559module_exit(caif_device_exit);