Blame view

net/caif/caif_dev.c 13.2 KB
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
1
2
3
4
5
6
7
8
9
10
  /*
   * CAIF Interface registration.
   * Copyright (C) ST-Ericsson AB 2010
   * Author:	Sjur Brendeland/sjur.brandeland@stericsson.com
   * License terms: GNU General Public License (GPL) version 2
   *
   * Borrowed heavily from file: pn_dev.c. Thanks to
   *  Remi Denis-Courmont <remi.denis-courmont@nokia.com>
   *  and Sakari Ailus <sakari.ailus@nokia.com>
   */
b31fa5bad   Joe Perches   net/caif: Use pr_fmt
11
  #define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
12
13
14
15
  #include <linux/kernel.h>
  #include <linux/if_arp.h>
  #include <linux/net.h>
  #include <linux/netdevice.h>
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
16
  #include <linux/mutex.h>
3a9a231d9   Paul Gortmaker   net: Fix files ex...
17
  #include <linux/module.h>
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
18
  #include <linux/spinlock.h>
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
19
20
21
22
  #include <net/netns/generic.h>
  #include <net/net_namespace.h>
  #include <net/pkt_sched.h>
  #include <net/caif/caif_device.h>
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
23
24
25
  #include <net/caif/caif_layer.h>
  #include <net/caif/cfpkt.h>
  #include <net/caif/cfcnfg.h>
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
26
  #include <net/caif/cfserl.h>
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
27
28
  
  MODULE_LICENSE("GPL");
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
29
30
31
32
33
  
  /* Used for local tracking of the CAIF net devices */
  struct caif_device_entry {
  	struct cflayer layer;
  	struct list_head list;
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
34
  	struct net_device *netdev;
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
35
  	int __percpu *pcpu_refcnt;
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
36
  	spinlock_t flow_lock;
7d3113042   sjur.brandeland@stericsson.com   caif: Stash away ...
37
38
  	struct sk_buff *xoff_skb;
  	void (*xoff_skb_dtor)(struct sk_buff *skb);
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
39
  	bool xoff;
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
40
41
42
43
44
  };
  
  struct caif_device_entry_list {
  	struct list_head list;
  	/* Protects simulanous deletes in list */
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
45
  	struct mutex lock;
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
46
47
48
  };
  
  struct caif_net {
bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
49
  	struct cfcnfg *cfg;
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
50
51
52
53
  	struct caif_device_entry_list caifdevs;
  };
  
  static int caif_net_id;
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
54
  static int q_high = 50; /* Percent */
bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
55
56
57
58
  
  struct cfcnfg *get_cfcnfg(struct net *net)
  {
  	struct caif_net *caifn;
bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
59
  	caifn = net_generic(net, caif_net_id);
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
60
61
  	if (!caifn)
  		return NULL;
bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
62
63
64
  	return caifn->cfg;
  }
  EXPORT_SYMBOL(get_cfcnfg);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
65
66
67
68
  
  static struct caif_device_entry_list *caif_device_list(struct net *net)
  {
  	struct caif_net *caifn;
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
69
  	caifn = net_generic(net, caif_net_id);
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
70
71
  	if (!caifn)
  		return NULL;
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
72
73
  	return &caifn->caifdevs;
  }
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
74
75
  static void caifd_put(struct caif_device_entry *e)
  {
933393f58   Christoph Lameter   percpu: Remove ir...
76
  	this_cpu_dec(*e->pcpu_refcnt);
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
77
78
79
80
  }
  
  static void caifd_hold(struct caif_device_entry *e)
  {
933393f58   Christoph Lameter   percpu: Remove ir...
81
  	this_cpu_inc(*e->pcpu_refcnt);
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
82
83
84
85
86
87
88
89
90
  }
  
  static int caifd_refcnt_read(struct caif_device_entry *e)
  {
  	int i, refcnt = 0;
  	for_each_possible_cpu(i)
  		refcnt += *per_cpu_ptr(e->pcpu_refcnt, i);
  	return refcnt;
  }
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
91
92
93
94
95
  /* Allocate new CAIF device. */
  static struct caif_device_entry *caif_device_alloc(struct net_device *dev)
  {
  	struct caif_device_entry_list *caifdevs;
  	struct caif_device_entry *caifd;
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
96

c72dfae2f   Sjur Braendeland   net-caif: add CAI...
97
  	caifdevs = caif_device_list(dev_net(dev));
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
98
99
  	if (!caifdevs)
  		return NULL;
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
100

4fb66b821   Eric Dumazet   caif: fix a poten...
101
  	caifd = kzalloc(sizeof(*caifd), GFP_KERNEL);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
102
103
  	if (!caifd)
  		return NULL;
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
104
  	caifd->pcpu_refcnt = alloc_percpu(int);
4fb66b821   Eric Dumazet   caif: fix a poten...
105
106
107
108
  	if (!caifd->pcpu_refcnt) {
  		kfree(caifd);
  		return NULL;
  	}
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
109
  	caifd->netdev = dev;
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
110
  	dev_hold(dev);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
111
112
113
114
115
116
117
118
  	return caifd;
  }
  
  static struct caif_device_entry *caif_get(struct net_device *dev)
  {
  	struct caif_device_entry_list *caifdevs =
  	    caif_device_list(dev_net(dev));
  	struct caif_device_entry *caifd;
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
119
120
  	if (!caifdevs)
  		return NULL;
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
121
  	list_for_each_entry_rcu(caifd, &caifdevs->list, list) {
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
122
123
124
125
126
  		if (caifd->netdev == dev)
  			return caifd;
  	}
  	return NULL;
  }
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
127
128
129
  void caif_flow_cb(struct sk_buff *skb)
  {
  	struct caif_device_entry *caifd;
7d3113042   sjur.brandeland@stericsson.com   caif: Stash away ...
130
  	void (*dtor)(struct sk_buff *skb) = NULL;
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
131
132
133
134
135
136
137
138
139
140
141
142
  	bool send_xoff;
  
  	WARN_ON(skb->dev == NULL);
  
  	rcu_read_lock();
  	caifd = caif_get(skb->dev);
  	caifd_hold(caifd);
  	rcu_read_unlock();
  
  	spin_lock_bh(&caifd->flow_lock);
  	send_xoff = caifd->xoff;
  	caifd->xoff = 0;
7d3113042   sjur.brandeland@stericsson.com   caif: Stash away ...
143
144
145
146
147
148
  	if (!WARN_ON(caifd->xoff_skb_dtor == NULL)) {
  		WARN_ON(caifd->xoff_skb != skb);
  		dtor = caifd->xoff_skb_dtor;
  		caifd->xoff_skb = NULL;
  		caifd->xoff_skb_dtor = NULL;
  	}
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
149
  	spin_unlock_bh(&caifd->flow_lock);
7d3113042   sjur.brandeland@stericsson.com   caif: Stash away ...
150
151
  	if (dtor)
  		dtor(skb);
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
152
153
154
155
156
157
158
  	if (send_xoff)
  		caifd->layer.up->
  			ctrlcmd(caifd->layer.up,
  				_CAIF_CTRLCMD_PHYIF_FLOW_ON_IND,
  				caifd->layer.id);
  	caifd_put(caifd);
  }
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
159
160
  static int transmit(struct cflayer *layer, struct cfpkt *pkt)
  {
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
161
162
  	int err, high = 0, qlen = 0;
  	struct caif_dev_common *caifdev;
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
163
164
  	struct caif_device_entry *caifd =
  	    container_of(layer, struct caif_device_entry, layer);
4dd820c08   Sjur Brændeland   caif: Don't resen...
165
  	struct sk_buff *skb;
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
166
167
168
  	struct netdev_queue *txq;
  
  	rcu_read_lock_bh();
4dd820c08   Sjur Brændeland   caif: Don't resen...
169

c72dfae2f   Sjur Braendeland   net-caif: add CAI...
170
171
  	skb = cfpkt_tonative(pkt);
  	skb->dev = caifd->netdev;
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
172
173
  	skb_reset_network_header(skb);
  	skb->protocol = htons(ETH_P_CAIF);
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
  	caifdev = netdev_priv(caifd->netdev);
  
  	/* Check if we need to handle xoff */
  	if (likely(caifd->netdev->tx_queue_len == 0))
  		goto noxoff;
  
  	if (unlikely(caifd->xoff))
  		goto noxoff;
  
  	if (likely(!netif_queue_stopped(caifd->netdev))) {
  		/* If we run with a TX queue, check if the queue is too long*/
  		txq = netdev_get_tx_queue(skb->dev, 0);
  		qlen = qdisc_qlen(rcu_dereference_bh(txq->qdisc));
  
  		if (likely(qlen == 0))
  			goto noxoff;
  
  		high = (caifd->netdev->tx_queue_len * q_high) / 100;
  		if (likely(qlen < high))
  			goto noxoff;
  	}
  
  	/* Hold lock while accessing xoff */
  	spin_lock_bh(&caifd->flow_lock);
  	if (caifd->xoff) {
  		spin_unlock_bh(&caifd->flow_lock);
  		goto noxoff;
  	}
  
  	/*
  	 * Handle flow off, we do this by temporary hi-jacking this
  	 * skb's destructor function, and replace it with our own
  	 * flow-on callback. The callback will set flow-on and call
  	 * the original destructor.
  	 */
  
  	pr_debug("queue has stopped(%d) or is full (%d > %d)
  ",
  			netif_queue_stopped(caifd->netdev),
  			qlen, high);
  	caifd->xoff = 1;
7d3113042   sjur.brandeland@stericsson.com   caif: Stash away ...
215
216
217
  	caifd->xoff_skb = skb;
  	caifd->xoff_skb_dtor = skb->destructor;
  	skb->destructor = caif_flow_cb;
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
218
  	spin_unlock_bh(&caifd->flow_lock);
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
219
220
221
222
223
224
  
  	caifd->layer.up->ctrlcmd(caifd->layer.up,
  					_CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND,
  					caifd->layer.id);
  noxoff:
  	rcu_read_unlock_bh();
4dd820c08   Sjur Brændeland   caif: Don't resen...
225

c85c2951d   sjur.brandeland@stericsson.com   caif: Handle dev_...
226
227
228
  	err = dev_queue_xmit(skb);
  	if (err > 0)
  		err = -EIO;
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
229

c85c2951d   sjur.brandeland@stericsson.com   caif: Handle dev_...
230
  	return err;
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
231
  }
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
232
  /*
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
233
   * Stuff received packets into the CAIF stack.
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
234
235
236
237
238
   * On error, returns non-zero and releases the skb.
   */
  static int receive(struct sk_buff *skb, struct net_device *dev,
  		   struct packet_type *pkttype, struct net_device *orig_dev)
  {
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
239
240
  	struct cfpkt *pkt;
  	struct caif_device_entry *caifd;
69c867c90   sjur.brandeland@stericsson.com   caif: Plug memory...
241
  	int err;
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
242

c72dfae2f   Sjur Braendeland   net-caif: add CAI...
243
  	pkt = cfpkt_fromnative(CAIF_DIR_IN, skb);
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
244
245
  
  	rcu_read_lock();
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
246
  	caifd = caif_get(dev);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
247

bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
248
249
250
251
  	if (!caifd || !caifd->layer.up || !caifd->layer.up->receive ||
  			!netif_oper_up(caifd->netdev)) {
  		rcu_read_unlock();
  		kfree_skb(skb);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
252
  		return NET_RX_DROP;
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
253
254
255
256
257
  	}
  
  	/* Hold reference to netdevice while using CAIF stack */
  	caifd_hold(caifd);
  	rcu_read_unlock();
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
258

69c867c90   sjur.brandeland@stericsson.com   caif: Plug memory...
259
260
261
262
263
  	err = caifd->layer.up->receive(caifd->layer.up, pkt);
  
  	/* For -EILSEQ the packet is not freed so so it now */
  	if (err == -EILSEQ)
  		cfpkt_destroy(pkt);
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
264
265
266
  
  	/* Release reference to stack upwards */
  	caifd_put(caifd);
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
267
268
269
270
  
  	if (err != 0)
  		err = NET_RX_DROP;
  	return err;
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
271
272
273
274
275
276
277
278
279
  }
  
  static struct packet_type caif_packet_type __read_mostly = {
  	.type = cpu_to_be16(ETH_P_CAIF),
  	.func = receive,
  };
  
  static void dev_flowctrl(struct net_device *dev, int on)
  {
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
280
281
282
283
284
285
286
  	struct caif_device_entry *caifd;
  
  	rcu_read_lock();
  
  	caifd = caif_get(dev);
  	if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) {
  		rcu_read_unlock();
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
287
  		return;
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
288
289
290
291
  	}
  
  	caifd_hold(caifd);
  	rcu_read_unlock();
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
292
293
294
295
296
297
  
  	caifd->layer.up->ctrlcmd(caifd->layer.up,
  				 on ?
  				 _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND :
  				 _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND,
  				 caifd->layer.id);
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
298
  	caifd_put(caifd);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
299
  }
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
  void caif_enroll_dev(struct net_device *dev, struct caif_dev_common *caifdev,
  			struct cflayer *link_support, int head_room,
  			struct cflayer **layer, int (**rcv_func)(
  				struct sk_buff *, struct net_device *,
  				struct packet_type *, struct net_device *))
  {
  	struct caif_device_entry *caifd;
  	enum cfcnfg_phy_preference pref;
  	struct cfcnfg *cfg = get_cfcnfg(dev_net(dev));
  	struct caif_device_entry_list *caifdevs;
  
  	caifdevs = caif_device_list(dev_net(dev));
  	if (!cfg || !caifdevs)
  		return;
  	caifd = caif_device_alloc(dev);
  	if (!caifd)
  		return;
  	*layer = &caifd->layer;
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
318
  	spin_lock_init(&caifd->flow_lock);
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
  
  	switch (caifdev->link_select) {
  	case CAIF_LINK_HIGH_BANDW:
  		pref = CFPHYPREF_HIGH_BW;
  		break;
  	case CAIF_LINK_LOW_LATENCY:
  		pref = CFPHYPREF_LOW_LAT;
  		break;
  	default:
  		pref = CFPHYPREF_HIGH_BW;
  		break;
  	}
  	mutex_lock(&caifdevs->lock);
  	list_add_rcu(&caifd->list, &caifdevs->list);
  
  	strncpy(caifd->layer.name, dev->name,
  		sizeof(caifd->layer.name) - 1);
  	caifd->layer.name[sizeof(caifd->layer.name) - 1] = 0;
  	caifd->layer.transmit = transmit;
  	cfcnfg_add_phy_layer(cfg,
  				dev,
  				&caifd->layer,
  				pref,
  				link_support,
  				caifdev->use_fcs,
  				head_room);
  	mutex_unlock(&caifdevs->lock);
  	if (rcv_func)
  		*rcv_func = receive;
  }
7ad65bf68   sjur.brandeland@stericsson.com   caif: Add support...
349
  EXPORT_SYMBOL(caif_enroll_dev);
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
350

c72dfae2f   Sjur Braendeland   net-caif: add CAI...
351
352
353
354
355
356
357
  /* notify Caif of device events */
  static int caif_device_notify(struct notifier_block *me, unsigned long what,
  			      void *arg)
  {
  	struct net_device *dev = arg;
  	struct caif_device_entry *caifd = NULL;
  	struct caif_dev_common *caifdev;
bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
358
  	struct cfcnfg *cfg;
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
359
360
  	struct cflayer *layer, *link_support;
  	int head_room = 0;
08613e462   David Woodhouse   caif: Fix BUG() w...
361
  	struct caif_device_entry_list *caifdevs;
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
362

bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
363
  	cfg = get_cfcnfg(dev_net(dev));
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
364
365
  	caifdevs = caif_device_list(dev_net(dev));
  	if (!cfg || !caifdevs)
bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
366
  		return 0;
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
367
368
369
  	caifd = caif_get(dev);
  	if (caifd == NULL && dev->type != ARPHRD_CAIF)
  		return 0;
08613e462   David Woodhouse   caif: Fix BUG() w...
370

c72dfae2f   Sjur Braendeland   net-caif: add CAI...
371
372
  	switch (what) {
  	case NETDEV_REGISTER:
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
373
374
  		if (caifd != NULL)
  			break;
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
375

c72dfae2f   Sjur Braendeland   net-caif: add CAI...
376
  		caifdev = netdev_priv(dev);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
377

7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
378
379
380
381
  		link_support = NULL;
  		if (caifdev->use_frag) {
  			head_room = 1;
  			link_support = cfserl_create(dev->ifindex,
e977b4cf6   sjur.brandeland@stericsson.com   caif: Remove unus...
382
  							caifdev->use_stx);
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
383
384
385
386
387
  			if (!link_support) {
  				pr_warn("Out of memory
  ");
  				break;
  			}
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
388
  		}
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
389
390
391
  		caif_enroll_dev(dev, caifdev, link_support, head_room,
  				&layer, NULL);
  		caifdev->flowctrl = dev_flowctrl;
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
392
  		break;
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
393
394
  	case NETDEV_UP:
  		rcu_read_lock();
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
395
  		caifd = caif_get(dev);
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
396
397
  		if (caifd == NULL) {
  			rcu_read_unlock();
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
398
  			break;
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
399
  		}
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
400

0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
401
  		caifd->xoff = 0;
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
402
403
  		cfcnfg_set_phy_state(cfg, &caifd->layer, true);
  		rcu_read_unlock();
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
404

c72dfae2f   Sjur Braendeland   net-caif: add CAI...
405
406
407
  		break;
  
  	case NETDEV_DOWN:
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
408
  		rcu_read_lock();
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
409
  		caifd = caif_get(dev);
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
410
411
412
413
414
415
416
417
418
419
420
421
  		if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) {
  			rcu_read_unlock();
  			return -EINVAL;
  		}
  
  		cfcnfg_set_phy_state(cfg, &caifd->layer, false);
  		caifd_hold(caifd);
  		rcu_read_unlock();
  
  		caifd->layer.up->ctrlcmd(caifd->layer.up,
  					 _CAIF_CTRLCMD_PHYIF_DOWN_IND,
  					 caifd->layer.id);
7d3113042   sjur.brandeland@stericsson.com   caif: Stash away ...
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
  
  		spin_lock_bh(&caifd->flow_lock);
  
  		/*
  		 * Replace our xoff-destructor with original destructor.
  		 * We trust that skb->destructor *always* is called before
  		 * the skb reference is invalid. The hijacked SKB destructor
  		 * takes the flow_lock so manipulating the skb->destructor here
  		 * should be safe.
  		*/
  		if (caifd->xoff_skb_dtor != NULL && caifd->xoff_skb != NULL)
  			caifd->xoff_skb->destructor = caifd->xoff_skb_dtor;
  
  		caifd->xoff = 0;
  		caifd->xoff_skb_dtor = NULL;
  		caifd->xoff_skb = NULL;
  
  		spin_unlock_bh(&caifd->flow_lock);
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
440
  		caifd_put(caifd);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
441
442
443
  		break;
  
  	case NETDEV_UNREGISTER:
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
444
  		mutex_lock(&caifdevs->lock);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
445
  		caifd = caif_get(dev);
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
446
447
  		if (caifd == NULL) {
  			mutex_unlock(&caifdevs->lock);
f2527ec43   André Carvalho de Matos   caif: Bugfix for ...
448
  			break;
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
  		}
  		list_del_rcu(&caifd->list);
  
  		/*
  		 * NETDEV_UNREGISTER is called repeatedly until all reference
  		 * counts for the net-device are released. If references to
  		 * caifd is taken, simply ignore NETDEV_UNREGISTER and wait for
  		 * the next call to NETDEV_UNREGISTER.
  		 *
  		 * If any packets are in flight down the CAIF Stack,
  		 * cfcnfg_del_phy_layer will return nonzero.
  		 * If no packets are in flight, the CAIF Stack associated
  		 * with the net-device un-registering is freed.
  		 */
  
  		if (caifd_refcnt_read(caifd) != 0 ||
  			cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0) {
  
  			pr_info("Wait for device inuse
  ");
  			/* Enrole device if CAIF Stack is still in use */
  			list_add_rcu(&caifd->list, &caifdevs->list);
  			mutex_unlock(&caifdevs->lock);
  			break;
  		}
  
  		synchronize_rcu();
  		dev_put(caifd->netdev);
  		free_percpu(caifd->pcpu_refcnt);
  		kfree(caifd);
  
  		mutex_unlock(&caifdevs->lock);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
481
482
483
484
485
486
487
488
489
  		break;
  	}
  	return 0;
  }
  
  static struct notifier_block caif_device_notifier = {
  	.notifier_call = caif_device_notify,
  	.priority = 0,
  };
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
490
491
492
493
  /* Per-namespace Caif devices handling */
  static int caif_init_net(struct net *net)
  {
  	struct caif_net *caifn = net_generic(net, caif_net_id);
f84ea779c   Roar Førde   caif: Replace BUG...
494
495
  	if (WARN_ON(!caifn))
  		return -EINVAL;
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
496
  	INIT_LIST_HEAD(&caifn->caifdevs.list);
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
497
  	mutex_init(&caifn->caifdevs.lock);
bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
498
499
  
  	caifn->cfg = cfcnfg_create();
f84ea779c   Roar Førde   caif: Replace BUG...
500
  	if (!caifn->cfg)
bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
501
  		return -ENOMEM;
bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
502

c72dfae2f   Sjur Braendeland   net-caif: add CAI...
503
504
505
506
507
  	return 0;
  }
  
  static void caif_exit_net(struct net *net)
  {
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
508
509
510
  	struct caif_device_entry *caifd, *tmp;
  	struct caif_device_entry_list *caifdevs =
  	    caif_device_list(net);
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
511
512
513
514
  	struct cfcnfg *cfg =  get_cfcnfg(net);
  
  	if (!cfg || !caifdevs)
  		return;
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
515

c72dfae2f   Sjur Braendeland   net-caif: add CAI...
516
  	rtnl_lock();
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
  	mutex_lock(&caifdevs->lock);
  
  	list_for_each_entry_safe(caifd, tmp, &caifdevs->list, list) {
  		int i = 0;
  		list_del_rcu(&caifd->list);
  		cfcnfg_set_phy_state(cfg, &caifd->layer, false);
  
  		while (i < 10 &&
  			(caifd_refcnt_read(caifd) != 0 ||
  			cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0)) {
  
  			pr_info("Wait for device inuse
  ");
  			msleep(250);
  			i++;
  		}
  		synchronize_rcu();
  		dev_put(caifd->netdev);
  		free_percpu(caifd->pcpu_refcnt);
  		kfree(caifd);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
537
  	}
bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
538
  	cfcnfg_remove(cfg);
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
539
540
  
  	mutex_unlock(&caifdevs->lock);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
541
542
543
544
545
546
547
548
549
550
551
552
553
554
  	rtnl_unlock();
  }
  
  static struct pernet_operations caif_net_ops = {
  	.init = caif_init_net,
  	.exit = caif_exit_net,
  	.id   = &caif_net_id,
  	.size = sizeof(struct caif_net),
  };
  
  /* Initialize Caif devices list */
  static int __init caif_device_init(void)
  {
  	int result;
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
555

c72dfae2f   Sjur Braendeland   net-caif: add CAI...
556
  	result = register_pernet_device(&caif_net_ops);
bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
557
  	if (result)
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
558
  		return result;
bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
559

c72dfae2f   Sjur Braendeland   net-caif: add CAI...
560
  	register_netdevice_notifier(&caif_device_notifier);
bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
561
  	dev_add_pack(&caif_packet_type);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
562
563
  
  	return result;
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
564
565
566
567
  }
  
  static void __exit caif_device_exit(void)
  {
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
568
569
  	unregister_pernet_device(&caif_net_ops);
  	unregister_netdevice_notifier(&caif_device_notifier);
bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
570
  	dev_remove_pack(&caif_packet_type);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
571
572
573
574
  }
  
  module_init(caif_device_init);
  module_exit(caif_device_exit);