Blame view

net/caif/caif_dev.c 12.9 KB
af873fcec   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
2
3
4
  /*
   * CAIF Interface registration.
   * Copyright (C) ST-Ericsson AB 2010
26ee65e68   sjur.brandeland@stericsson.com   caif: Remove my b...
5
   * Author:	Sjur Brendeland
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
6
   *
31fdc5553   Rémi Denis-Courmont   net: remove my fu...
7
   * Borrowed heavily from file: pn_dev.c. Thanks to Remi Denis-Courmont
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
8
9
   *  and Sakari Ailus <sakari.ailus@nokia.com>
   */
b31fa5bad   Joe Perches   net/caif: Use pr_fmt
10
  #define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
11
12
13
14
  #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...
15
  #include <linux/mutex.h>
3a9a231d9   Paul Gortmaker   net: Fix files ex...
16
  #include <linux/module.h>
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
17
  #include <linux/spinlock.h>
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
18
19
20
21
  #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...
22
  #include <net/caif/caif_layer.h>
8203274e1   Rashika Kheria   net: Include appr...
23
  #include <net/caif/caif_dev.h>
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
24
25
  #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
  	struct caif_device_entry_list caifdevs;
  };
c7d03a00b   Alexey Dobriyan   netns: make struc...
52
  static unsigned int caif_net_id;
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
53
  static int q_high = 50; /* Percent */
bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
54
55
56
57
  
  struct cfcnfg *get_cfcnfg(struct net *net)
  {
  	struct caif_net *caifn;
bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
58
  	caifn = net_generic(net, caif_net_id);
bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
59
60
61
  	return caifn->cfg;
  }
  EXPORT_SYMBOL(get_cfcnfg);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
62
63
64
65
  
  static struct caif_device_entry_list *caif_device_list(struct net *net)
  {
  	struct caif_net *caifn;
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
66
  	caifn = net_generic(net, caif_net_id);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
67
68
  	return &caifn->caifdevs;
  }
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
69
70
  static void caifd_put(struct caif_device_entry *e)
  {
933393f58   Christoph Lameter   percpu: Remove ir...
71
  	this_cpu_dec(*e->pcpu_refcnt);
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
72
73
74
75
  }
  
  static void caifd_hold(struct caif_device_entry *e)
  {
933393f58   Christoph Lameter   percpu: Remove ir...
76
  	this_cpu_inc(*e->pcpu_refcnt);
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
77
78
79
80
81
82
83
84
85
  }
  
  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...
86
87
88
  /* Allocate new CAIF device. */
  static struct caif_device_entry *caif_device_alloc(struct net_device *dev)
  {
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
89
  	struct caif_device_entry *caifd;
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
90

4fb66b821   Eric Dumazet   caif: fix a poten...
91
  	caifd = kzalloc(sizeof(*caifd), GFP_KERNEL);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
92
93
  	if (!caifd)
  		return NULL;
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
94
  	caifd->pcpu_refcnt = alloc_percpu(int);
4fb66b821   Eric Dumazet   caif: fix a poten...
95
96
97
98
  	if (!caifd->pcpu_refcnt) {
  		kfree(caifd);
  		return NULL;
  	}
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
99
  	caifd->netdev = dev;
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
100
  	dev_hold(dev);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
101
102
103
104
105
106
107
108
  	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...
109

bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
110
  	list_for_each_entry_rcu(caifd, &caifdevs->list, list) {
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
111
112
113
114
115
  		if (caifd->netdev == dev)
  			return caifd;
  	}
  	return NULL;
  }
d6e89c0b7   Silviu-Mihai Popescu   caif_dev: fix spa...
116
  static void caif_flow_cb(struct sk_buff *skb)
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
117
118
  {
  	struct caif_device_entry *caifd;
7d3113042   sjur.brandeland@stericsson.com   caif: Stash away ...
119
  	void (*dtor)(struct sk_buff *skb) = NULL;
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
120
121
122
123
124
125
  	bool send_xoff;
  
  	WARN_ON(skb->dev == NULL);
  
  	rcu_read_lock();
  	caifd = caif_get(skb->dev);
c95567c80   Kim Lilliestierna XX   caif: added check...
126
127
  
  	WARN_ON(caifd == NULL);
64119e05f   YueHaibing   net: caif: Add a ...
128
129
  	if (!caifd) {
  		rcu_read_unlock();
c95567c80   Kim Lilliestierna XX   caif: added check...
130
  		return;
64119e05f   YueHaibing   net: caif: Add a ...
131
  	}
c95567c80   Kim Lilliestierna XX   caif: added check...
132

0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
133
134
135
136
137
138
  	caifd_hold(caifd);
  	rcu_read_unlock();
  
  	spin_lock_bh(&caifd->flow_lock);
  	send_xoff = caifd->xoff;
  	caifd->xoff = 0;
59f608d84   sjur.brandeland@stericsson.com   caif: Remove bad ...
139
140
141
142
143
144
145
  	dtor = caifd->xoff_skb_dtor;
  
  	if (WARN_ON(caifd->xoff_skb != skb))
  		skb = NULL;
  
  	caifd->xoff_skb = NULL;
  	caifd->xoff_skb_dtor = NULL;
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
146
  	spin_unlock_bh(&caifd->flow_lock);
59f608d84   sjur.brandeland@stericsson.com   caif: Remove bad ...
147
  	if (dtor && skb)
7d3113042   sjur.brandeland@stericsson.com   caif: Stash away ...
148
  		dtor(skb);
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
149
150
151
152
153
154
155
  	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...
156
157
  static int transmit(struct cflayer *layer, struct cfpkt *pkt)
  {
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
158
  	int err, high = 0, qlen = 0;
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
159
160
  	struct caif_device_entry *caifd =
  	    container_of(layer, struct caif_device_entry, layer);
4dd820c08   Sjur Brændeland   caif: Don't resen...
161
  	struct sk_buff *skb;
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
162
163
164
  	struct netdev_queue *txq;
  
  	rcu_read_lock_bh();
4dd820c08   Sjur Brændeland   caif: Don't resen...
165

c72dfae2f   Sjur Braendeland   net-caif: add CAI...
166
167
  	skb = cfpkt_tonative(pkt);
  	skb->dev = caifd->netdev;
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
168
169
  	skb_reset_network_header(skb);
  	skb->protocol = htons(ETH_P_CAIF);
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
170
171
  
  	/* Check if we need to handle xoff */
4676a1520   Phil Sutter   net: caif: conver...
172
  	if (likely(caifd->netdev->priv_flags & IFF_NO_QUEUE))
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
173
174
175
176
177
178
  		goto noxoff;
  
  	if (unlikely(caifd->xoff))
  		goto noxoff;
  
  	if (likely(!netif_queue_stopped(caifd->netdev))) {
b0a231a26   Paolo Abeni   net: caif: avoid ...
179
  		struct Qdisc *sch;
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
180
181
  		/* If we run with a TX queue, check if the queue is too long*/
  		txq = netdev_get_tx_queue(skb->dev, 0);
b0a231a26   Paolo Abeni   net: caif: avoid ...
182
183
  		sch = rcu_dereference_bh(txq->qdisc);
  		if (likely(qdisc_is_empty(sch)))
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
184
  			goto noxoff;
b0a231a26   Paolo Abeni   net: caif: avoid ...
185
186
187
  		/* can check for explicit qdisc len value only !NOLOCK,
  		 * always set flow off otherwise
  		 */
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
188
  		high = (caifd->netdev->tx_queue_len * q_high) / 100;
b0a231a26   Paolo Abeni   net: caif: avoid ...
189
  		if (!(sch->flags & TCQ_F_NOLOCK) && likely(sch->q.qlen < high))
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
  			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 ...
212
213
214
  	caifd->xoff_skb = skb;
  	caifd->xoff_skb_dtor = skb->destructor;
  	skb->destructor = caif_flow_cb;
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
215
  	spin_unlock_bh(&caifd->flow_lock);
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
216
217
218
219
220
221
  
  	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...
222

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

c85c2951d   sjur.brandeland@stericsson.com   caif: Handle dev_...
227
  	return err;
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
228
  }
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
229
  /*
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
230
   * Stuff received packets into the CAIF stack.
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
231
232
233
234
235
   * 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...
236
237
  	struct cfpkt *pkt;
  	struct caif_device_entry *caifd;
69c867c90   sjur.brandeland@stericsson.com   caif: Plug memory...
238
  	int err;
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
239

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

bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
245
246
247
248
  	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...
249
  		return NET_RX_DROP;
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
250
251
252
253
254
  	}
  
  	/* Hold reference to netdevice while using CAIF stack */
  	caifd_hold(caifd);
  	rcu_read_unlock();
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
255

69c867c90   sjur.brandeland@stericsson.com   caif: Plug memory...
256
257
258
259
260
  	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...
261
262
263
  
  	/* Release reference to stack upwards */
  	caifd_put(caifd);
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
264
265
266
267
  
  	if (err != 0)
  		err = NET_RX_DROP;
  	return err;
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
268
269
270
271
272
273
274
275
276
  }
  
  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...
277
278
279
280
281
282
283
  	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...
284
  		return;
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
285
286
287
288
  	}
  
  	caifd_hold(caifd);
  	rcu_read_unlock();
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
289
290
291
292
293
294
  
  	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...
295
  	caifd_put(caifd);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
296
  }
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
297
  void caif_enroll_dev(struct net_device *dev, struct caif_dev_common *caifdev,
3bffc475f   Silviu-Mihai Popescu   CAIF: fix indenta...
298
299
300
301
302
  		     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 *))
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
303
304
305
306
307
308
309
  {
  	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));
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
310
311
312
313
  	caifd = caif_device_alloc(dev);
  	if (!caifd)
  		return;
  	*layer = &caifd->layer;
0e4c7d85d   sjur.brandeland@stericsson.com   caif: Add support...
314
  	spin_lock_init(&caifd->flow_lock);
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
315
316
317
318
319
320
321
322
323
324
325
326
327
328
  
  	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);
3dc2fa475   Xiongfeng Wang   net: caif: use st...
329
330
  	strlcpy(caifd->layer.name, dev->name,
  		sizeof(caifd->layer.name));
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
331
332
333
334
335
336
337
338
339
340
341
342
  	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...
343
  EXPORT_SYMBOL(caif_enroll_dev);
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
344

c72dfae2f   Sjur Braendeland   net-caif: add CAI...
345
346
  /* notify Caif of device events */
  static int caif_device_notify(struct notifier_block *me, unsigned long what,
351638e7d   Jiri Pirko   net: pass info st...
347
  			      void *ptr)
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
348
  {
351638e7d   Jiri Pirko   net: pass info st...
349
  	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
350
351
  	struct caif_device_entry *caifd = NULL;
  	struct caif_dev_common *caifdev;
bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
352
  	struct cfcnfg *cfg;
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
353
354
  	struct cflayer *layer, *link_support;
  	int head_room = 0;
08613e462   David Woodhouse   caif: Fix BUG() w...
355
  	struct caif_device_entry_list *caifdevs;
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
356

bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
357
  	cfg = get_cfcnfg(dev_net(dev));
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
358
  	caifdevs = caif_device_list(dev_net(dev));
bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
359

7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
360
361
362
  	caifd = caif_get(dev);
  	if (caifd == NULL && dev->type != ARPHRD_CAIF)
  		return 0;
08613e462   David Woodhouse   caif: Fix BUG() w...
363

c72dfae2f   Sjur Braendeland   net-caif: add CAI...
364
365
  	switch (what) {
  	case NETDEV_REGISTER:
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
366
367
  		if (caifd != NULL)
  			break;
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
368

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

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

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

c72dfae2f   Sjur Braendeland   net-caif: add CAI...
398
399
400
  		break;
  
  	case NETDEV_DOWN:
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
401
  		rcu_read_lock();
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
402
  		caifd = caif_get(dev);
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
403
404
405
406
407
408
409
410
411
412
413
414
  		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 ...
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
  
  		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...
433
  		caifd_put(caifd);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
434
435
436
  		break;
  
  	case NETDEV_UNREGISTER:
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
437
  		mutex_lock(&caifdevs->lock);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
438
  		caifd = caif_get(dev);
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
439
440
  		if (caifd == NULL) {
  			mutex_unlock(&caifdevs->lock);
f2527ec43   André Carvalho de Matos   caif: Bugfix for ...
441
  			break;
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
442
443
444
445
446
447
448
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
  		}
  		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...
474
475
476
477
478
479
480
481
482
  		break;
  	}
  	return 0;
  }
  
  static struct notifier_block caif_device_notifier = {
  	.notifier_call = caif_device_notify,
  	.priority = 0,
  };
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
483
484
485
486
487
  /* Per-namespace Caif devices handling */
  static int caif_init_net(struct net *net)
  {
  	struct caif_net *caifn = net_generic(net, caif_net_id);
  	INIT_LIST_HEAD(&caifn->caifdevs.list);
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
488
  	mutex_init(&caifn->caifdevs.lock);
bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
489
490
  
  	caifn->cfg = cfcnfg_create();
f84ea779c   Roar Førde   caif: Replace BUG...
491
  	if (!caifn->cfg)
bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
492
  		return -ENOMEM;
bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
493

c72dfae2f   Sjur Braendeland   net-caif: add CAI...
494
495
496
497
498
  	return 0;
  }
  
  static void caif_exit_net(struct net *net)
  {
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
499
500
501
  	struct caif_device_entry *caifd, *tmp;
  	struct caif_device_entry_list *caifdevs =
  	    caif_device_list(net);
7c18d2205   sjur.brandeland@stericsson.com   caif: Restructure...
502
  	struct cfcnfg *cfg =  get_cfcnfg(net);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
503
  	rtnl_lock();
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
  	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...
524
  	}
bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
525
  	cfcnfg_remove(cfg);
bd30ce4bc   sjur.brandeland@stericsson.com   caif: Use RCU ins...
526
527
  
  	mutex_unlock(&caifdevs->lock);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
528
529
530
531
532
533
534
535
536
537
538
539
540
541
  	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...
542

8a8ee9aff   Eric W. Biederman   net caif: Registe...
543
  	result = register_pernet_subsys(&caif_net_ops);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
544

bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
545
  	if (result)
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
546
  		return result;
bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
547

c72dfae2f   Sjur Braendeland   net-caif: add CAI...
548
  	register_netdevice_notifier(&caif_device_notifier);
bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
549
  	dev_add_pack(&caif_packet_type);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
550
551
  
  	return result;
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
552
553
554
555
  }
  
  static void __exit caif_device_exit(void)
  {
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
556
  	unregister_netdevice_notifier(&caif_device_notifier);
bee925db9   sjur.brandeland@stericsson.com   caif: prepare sup...
557
  	dev_remove_pack(&caif_packet_type);
96f80d123   Sjur Brændeland   caif: Fix access ...
558
  	unregister_pernet_subsys(&caif_net_ops);
c72dfae2f   Sjur Braendeland   net-caif: add CAI...
559
560
561
562
  }
  
  module_init(caif_device_init);
  module_exit(caif_device_exit);