xref: /openbmc/linux/net/caif/caif_dev.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1af873fceSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2c72dfae2SSjur Braendeland /*
3c72dfae2SSjur Braendeland  * CAIF Interface registration.
4c72dfae2SSjur Braendeland  * Copyright (C) ST-Ericsson AB 2010
526ee65e6Ssjur.brandeland@stericsson.com  * Author:	Sjur Brendeland
6c72dfae2SSjur Braendeland  *
731fdc555SRémi Denis-Courmont  * Borrowed heavily from file: pn_dev.c. Thanks to Remi Denis-Courmont
8c72dfae2SSjur Braendeland  *  and Sakari Ailus <sakari.ailus@nokia.com>
9c72dfae2SSjur Braendeland  */
10c72dfae2SSjur Braendeland 
11b31fa5baSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
12b31fa5baSJoe Perches 
13c72dfae2SSjur Braendeland #include <linux/kernel.h>
14c72dfae2SSjur Braendeland #include <linux/if_arp.h>
15c72dfae2SSjur Braendeland #include <linux/net.h>
16c72dfae2SSjur Braendeland #include <linux/netdevice.h>
17bd30ce4bSsjur.brandeland@stericsson.com #include <linux/mutex.h>
183a9a231dSPaul Gortmaker #include <linux/module.h>
190e4c7d85Ssjur.brandeland@stericsson.com #include <linux/spinlock.h>
20c72dfae2SSjur Braendeland #include <net/netns/generic.h>
21c72dfae2SSjur Braendeland #include <net/net_namespace.h>
22c72dfae2SSjur Braendeland #include <net/pkt_sched.h>
23c72dfae2SSjur Braendeland #include <net/caif/caif_device.h>
24c72dfae2SSjur Braendeland #include <net/caif/caif_layer.h>
258203274eSRashika Kheria #include <net/caif/caif_dev.h>
26c72dfae2SSjur Braendeland #include <net/caif/cfpkt.h>
27c72dfae2SSjur Braendeland #include <net/caif/cfcnfg.h>
287c18d220Ssjur.brandeland@stericsson.com #include <net/caif/cfserl.h>
29c72dfae2SSjur Braendeland 
30c72dfae2SSjur Braendeland MODULE_LICENSE("GPL");
31c72dfae2SSjur Braendeland 
32c72dfae2SSjur Braendeland /* Used for local tracking of the CAIF net devices */
33c72dfae2SSjur Braendeland struct caif_device_entry {
34c72dfae2SSjur Braendeland 	struct cflayer layer;
35c72dfae2SSjur Braendeland 	struct list_head list;
36c72dfae2SSjur Braendeland 	struct net_device *netdev;
37bd30ce4bSsjur.brandeland@stericsson.com 	int __percpu *pcpu_refcnt;
380e4c7d85Ssjur.brandeland@stericsson.com 	spinlock_t flow_lock;
397d311304Ssjur.brandeland@stericsson.com 	struct sk_buff *xoff_skb;
407d311304Ssjur.brandeland@stericsson.com 	void (*xoff_skb_dtor)(struct sk_buff *skb);
410e4c7d85Ssjur.brandeland@stericsson.com 	bool xoff;
42c72dfae2SSjur Braendeland };
43c72dfae2SSjur Braendeland 
44c72dfae2SSjur Braendeland struct caif_device_entry_list {
45c72dfae2SSjur Braendeland 	struct list_head list;
46c72dfae2SSjur Braendeland 	/* Protects simulanous deletes in list */
47bd30ce4bSsjur.brandeland@stericsson.com 	struct mutex lock;
48c72dfae2SSjur Braendeland };
49c72dfae2SSjur Braendeland 
50c72dfae2SSjur Braendeland struct caif_net {
51bee925dbSsjur.brandeland@stericsson.com 	struct cfcnfg *cfg;
52c72dfae2SSjur Braendeland 	struct caif_device_entry_list caifdevs;
53c72dfae2SSjur Braendeland };
54c72dfae2SSjur Braendeland 
55c7d03a00SAlexey Dobriyan static unsigned int caif_net_id;
560e4c7d85Ssjur.brandeland@stericsson.com static int q_high = 50; /* Percent */
57bee925dbSsjur.brandeland@stericsson.com 
get_cfcnfg(struct net * net)58bee925dbSsjur.brandeland@stericsson.com struct cfcnfg *get_cfcnfg(struct net *net)
59bee925dbSsjur.brandeland@stericsson.com {
60bee925dbSsjur.brandeland@stericsson.com 	struct caif_net *caifn;
61bee925dbSsjur.brandeland@stericsson.com 	caifn = net_generic(net, caif_net_id);
62bee925dbSsjur.brandeland@stericsson.com 	return caifn->cfg;
63bee925dbSsjur.brandeland@stericsson.com }
64bee925dbSsjur.brandeland@stericsson.com EXPORT_SYMBOL(get_cfcnfg);
65c72dfae2SSjur Braendeland 
caif_device_list(struct net * net)66c72dfae2SSjur Braendeland static struct caif_device_entry_list *caif_device_list(struct net *net)
67c72dfae2SSjur Braendeland {
68c72dfae2SSjur Braendeland 	struct caif_net *caifn;
69c72dfae2SSjur Braendeland 	caifn = net_generic(net, caif_net_id);
70c72dfae2SSjur Braendeland 	return &caifn->caifdevs;
71c72dfae2SSjur Braendeland }
72c72dfae2SSjur Braendeland 
caifd_put(struct caif_device_entry * e)73bd30ce4bSsjur.brandeland@stericsson.com static void caifd_put(struct caif_device_entry *e)
74bd30ce4bSsjur.brandeland@stericsson.com {
75933393f5SChristoph Lameter 	this_cpu_dec(*e->pcpu_refcnt);
76bd30ce4bSsjur.brandeland@stericsson.com }
77bd30ce4bSsjur.brandeland@stericsson.com 
caifd_hold(struct caif_device_entry * e)78bd30ce4bSsjur.brandeland@stericsson.com static void caifd_hold(struct caif_device_entry *e)
79bd30ce4bSsjur.brandeland@stericsson.com {
80933393f5SChristoph Lameter 	this_cpu_inc(*e->pcpu_refcnt);
81bd30ce4bSsjur.brandeland@stericsson.com }
82bd30ce4bSsjur.brandeland@stericsson.com 
caifd_refcnt_read(struct caif_device_entry * e)83bd30ce4bSsjur.brandeland@stericsson.com static int caifd_refcnt_read(struct caif_device_entry *e)
84bd30ce4bSsjur.brandeland@stericsson.com {
85bd30ce4bSsjur.brandeland@stericsson.com 	int i, refcnt = 0;
86bd30ce4bSsjur.brandeland@stericsson.com 	for_each_possible_cpu(i)
87bd30ce4bSsjur.brandeland@stericsson.com 		refcnt += *per_cpu_ptr(e->pcpu_refcnt, i);
88bd30ce4bSsjur.brandeland@stericsson.com 	return refcnt;
89bd30ce4bSsjur.brandeland@stericsson.com }
90bd30ce4bSsjur.brandeland@stericsson.com 
91c72dfae2SSjur Braendeland /* Allocate new CAIF device. */
caif_device_alloc(struct net_device * dev)92c72dfae2SSjur Braendeland static struct caif_device_entry *caif_device_alloc(struct net_device *dev)
93c72dfae2SSjur Braendeland {
94c72dfae2SSjur Braendeland 	struct caif_device_entry *caifd;
95bd30ce4bSsjur.brandeland@stericsson.com 
964fb66b82SEric Dumazet 	caifd = kzalloc(sizeof(*caifd), GFP_KERNEL);
97c72dfae2SSjur Braendeland 	if (!caifd)
98c72dfae2SSjur Braendeland 		return NULL;
99bd30ce4bSsjur.brandeland@stericsson.com 	caifd->pcpu_refcnt = alloc_percpu(int);
1004fb66b82SEric Dumazet 	if (!caifd->pcpu_refcnt) {
1014fb66b82SEric Dumazet 		kfree(caifd);
1024fb66b82SEric Dumazet 		return NULL;
1034fb66b82SEric Dumazet 	}
104c72dfae2SSjur Braendeland 	caifd->netdev = dev;
105bd30ce4bSsjur.brandeland@stericsson.com 	dev_hold(dev);
106c72dfae2SSjur Braendeland 	return caifd;
107c72dfae2SSjur Braendeland }
108c72dfae2SSjur Braendeland 
caif_get(struct net_device * dev)109c72dfae2SSjur Braendeland static struct caif_device_entry *caif_get(struct net_device *dev)
110c72dfae2SSjur Braendeland {
111c72dfae2SSjur Braendeland 	struct caif_device_entry_list *caifdevs =
112c72dfae2SSjur Braendeland 	    caif_device_list(dev_net(dev));
113c72dfae2SSjur Braendeland 	struct caif_device_entry *caifd;
1147c18d220Ssjur.brandeland@stericsson.com 
115f9fc28a8SAmol Grover 	list_for_each_entry_rcu(caifd, &caifdevs->list, list,
116f9fc28a8SAmol Grover 				lockdep_rtnl_is_held()) {
117c72dfae2SSjur Braendeland 		if (caifd->netdev == dev)
118c72dfae2SSjur Braendeland 			return caifd;
119c72dfae2SSjur Braendeland 	}
120c72dfae2SSjur Braendeland 	return NULL;
121c72dfae2SSjur Braendeland }
122c72dfae2SSjur Braendeland 
caif_flow_cb(struct sk_buff * skb)123d6e89c0bSSilviu-Mihai Popescu static void caif_flow_cb(struct sk_buff *skb)
1240e4c7d85Ssjur.brandeland@stericsson.com {
1250e4c7d85Ssjur.brandeland@stericsson.com 	struct caif_device_entry *caifd;
1267d311304Ssjur.brandeland@stericsson.com 	void (*dtor)(struct sk_buff *skb) = NULL;
1270e4c7d85Ssjur.brandeland@stericsson.com 	bool send_xoff;
1280e4c7d85Ssjur.brandeland@stericsson.com 
1290e4c7d85Ssjur.brandeland@stericsson.com 	WARN_ON(skb->dev == NULL);
1300e4c7d85Ssjur.brandeland@stericsson.com 
1310e4c7d85Ssjur.brandeland@stericsson.com 	rcu_read_lock();
1320e4c7d85Ssjur.brandeland@stericsson.com 	caifd = caif_get(skb->dev);
133c95567c8SKim Lilliestierna XX 
134c95567c8SKim Lilliestierna XX 	WARN_ON(caifd == NULL);
13564119e05SYueHaibing 	if (!caifd) {
13664119e05SYueHaibing 		rcu_read_unlock();
137c95567c8SKim Lilliestierna XX 		return;
13864119e05SYueHaibing 	}
139c95567c8SKim Lilliestierna XX 
1400e4c7d85Ssjur.brandeland@stericsson.com 	caifd_hold(caifd);
1410e4c7d85Ssjur.brandeland@stericsson.com 	rcu_read_unlock();
1420e4c7d85Ssjur.brandeland@stericsson.com 
1430e4c7d85Ssjur.brandeland@stericsson.com 	spin_lock_bh(&caifd->flow_lock);
1440e4c7d85Ssjur.brandeland@stericsson.com 	send_xoff = caifd->xoff;
1458518307dSJason Yan 	caifd->xoff = false;
1467d311304Ssjur.brandeland@stericsson.com 	dtor = caifd->xoff_skb_dtor;
14759f608d8Ssjur.brandeland@stericsson.com 
14859f608d8Ssjur.brandeland@stericsson.com 	if (WARN_ON(caifd->xoff_skb != skb))
14959f608d8Ssjur.brandeland@stericsson.com 		skb = NULL;
15059f608d8Ssjur.brandeland@stericsson.com 
1517d311304Ssjur.brandeland@stericsson.com 	caifd->xoff_skb = NULL;
1527d311304Ssjur.brandeland@stericsson.com 	caifd->xoff_skb_dtor = NULL;
15359f608d8Ssjur.brandeland@stericsson.com 
1540e4c7d85Ssjur.brandeland@stericsson.com 	spin_unlock_bh(&caifd->flow_lock);
1550e4c7d85Ssjur.brandeland@stericsson.com 
15659f608d8Ssjur.brandeland@stericsson.com 	if (dtor && skb)
1577d311304Ssjur.brandeland@stericsson.com 		dtor(skb);
1587d311304Ssjur.brandeland@stericsson.com 
1590e4c7d85Ssjur.brandeland@stericsson.com 	if (send_xoff)
1600e4c7d85Ssjur.brandeland@stericsson.com 		caifd->layer.up->
1610e4c7d85Ssjur.brandeland@stericsson.com 			ctrlcmd(caifd->layer.up,
1620e4c7d85Ssjur.brandeland@stericsson.com 				_CAIF_CTRLCMD_PHYIF_FLOW_ON_IND,
1630e4c7d85Ssjur.brandeland@stericsson.com 				caifd->layer.id);
1640e4c7d85Ssjur.brandeland@stericsson.com 	caifd_put(caifd);
1650e4c7d85Ssjur.brandeland@stericsson.com }
1660e4c7d85Ssjur.brandeland@stericsson.com 
transmit(struct cflayer * layer,struct cfpkt * pkt)167c72dfae2SSjur Braendeland static int transmit(struct cflayer *layer, struct cfpkt *pkt)
168c72dfae2SSjur Braendeland {
1690e4c7d85Ssjur.brandeland@stericsson.com 	int err, high = 0, qlen = 0;
170c72dfae2SSjur Braendeland 	struct caif_device_entry *caifd =
171c72dfae2SSjur Braendeland 	    container_of(layer, struct caif_device_entry, layer);
1724dd820c0SSjur Brændeland 	struct sk_buff *skb;
1730e4c7d85Ssjur.brandeland@stericsson.com 	struct netdev_queue *txq;
1740e4c7d85Ssjur.brandeland@stericsson.com 
1750e4c7d85Ssjur.brandeland@stericsson.com 	rcu_read_lock_bh();
1764dd820c0SSjur Brændeland 
177c72dfae2SSjur Braendeland 	skb = cfpkt_tonative(pkt);
178c72dfae2SSjur Braendeland 	skb->dev = caifd->netdev;
1797c18d220Ssjur.brandeland@stericsson.com 	skb_reset_network_header(skb);
1807c18d220Ssjur.brandeland@stericsson.com 	skb->protocol = htons(ETH_P_CAIF);
1810e4c7d85Ssjur.brandeland@stericsson.com 
1820e4c7d85Ssjur.brandeland@stericsson.com 	/* Check if we need to handle xoff */
1834676a152SPhil Sutter 	if (likely(caifd->netdev->priv_flags & IFF_NO_QUEUE))
1840e4c7d85Ssjur.brandeland@stericsson.com 		goto noxoff;
1850e4c7d85Ssjur.brandeland@stericsson.com 
1860e4c7d85Ssjur.brandeland@stericsson.com 	if (unlikely(caifd->xoff))
1870e4c7d85Ssjur.brandeland@stericsson.com 		goto noxoff;
1880e4c7d85Ssjur.brandeland@stericsson.com 
1890e4c7d85Ssjur.brandeland@stericsson.com 	if (likely(!netif_queue_stopped(caifd->netdev))) {
190b0a231a2SPaolo Abeni 		struct Qdisc *sch;
191b0a231a2SPaolo Abeni 
1920e4c7d85Ssjur.brandeland@stericsson.com 		/* If we run with a TX queue, check if the queue is too long*/
1930e4c7d85Ssjur.brandeland@stericsson.com 		txq = netdev_get_tx_queue(skb->dev, 0);
194b0a231a2SPaolo Abeni 		sch = rcu_dereference_bh(txq->qdisc);
195b0a231a2SPaolo Abeni 		if (likely(qdisc_is_empty(sch)))
1960e4c7d85Ssjur.brandeland@stericsson.com 			goto noxoff;
1970e4c7d85Ssjur.brandeland@stericsson.com 
198b0a231a2SPaolo Abeni 		/* can check for explicit qdisc len value only !NOLOCK,
199b0a231a2SPaolo Abeni 		 * always set flow off otherwise
200b0a231a2SPaolo Abeni 		 */
2010e4c7d85Ssjur.brandeland@stericsson.com 		high = (caifd->netdev->tx_queue_len * q_high) / 100;
202b0a231a2SPaolo Abeni 		if (!(sch->flags & TCQ_F_NOLOCK) && likely(sch->q.qlen < high))
2030e4c7d85Ssjur.brandeland@stericsson.com 			goto noxoff;
2040e4c7d85Ssjur.brandeland@stericsson.com 	}
2050e4c7d85Ssjur.brandeland@stericsson.com 
2060e4c7d85Ssjur.brandeland@stericsson.com 	/* Hold lock while accessing xoff */
2070e4c7d85Ssjur.brandeland@stericsson.com 	spin_lock_bh(&caifd->flow_lock);
2080e4c7d85Ssjur.brandeland@stericsson.com 	if (caifd->xoff) {
2090e4c7d85Ssjur.brandeland@stericsson.com 		spin_unlock_bh(&caifd->flow_lock);
2100e4c7d85Ssjur.brandeland@stericsson.com 		goto noxoff;
2110e4c7d85Ssjur.brandeland@stericsson.com 	}
2120e4c7d85Ssjur.brandeland@stericsson.com 
2130e4c7d85Ssjur.brandeland@stericsson.com 	/*
2140e4c7d85Ssjur.brandeland@stericsson.com 	 * Handle flow off, we do this by temporary hi-jacking this
2150e4c7d85Ssjur.brandeland@stericsson.com 	 * skb's destructor function, and replace it with our own
2160e4c7d85Ssjur.brandeland@stericsson.com 	 * flow-on callback. The callback will set flow-on and call
2170e4c7d85Ssjur.brandeland@stericsson.com 	 * the original destructor.
2180e4c7d85Ssjur.brandeland@stericsson.com 	 */
2190e4c7d85Ssjur.brandeland@stericsson.com 
2200e4c7d85Ssjur.brandeland@stericsson.com 	pr_debug("queue has stopped(%d) or is full (%d > %d)\n",
2210e4c7d85Ssjur.brandeland@stericsson.com 			netif_queue_stopped(caifd->netdev),
2220e4c7d85Ssjur.brandeland@stericsson.com 			qlen, high);
2238518307dSJason Yan 	caifd->xoff = true;
2247d311304Ssjur.brandeland@stericsson.com 	caifd->xoff_skb = skb;
2257d311304Ssjur.brandeland@stericsson.com 	caifd->xoff_skb_dtor = skb->destructor;
2267d311304Ssjur.brandeland@stericsson.com 	skb->destructor = caif_flow_cb;
2270e4c7d85Ssjur.brandeland@stericsson.com 	spin_unlock_bh(&caifd->flow_lock);
2280e4c7d85Ssjur.brandeland@stericsson.com 
2290e4c7d85Ssjur.brandeland@stericsson.com 	caifd->layer.up->ctrlcmd(caifd->layer.up,
2300e4c7d85Ssjur.brandeland@stericsson.com 					_CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND,
2310e4c7d85Ssjur.brandeland@stericsson.com 					caifd->layer.id);
2320e4c7d85Ssjur.brandeland@stericsson.com noxoff:
2330e4c7d85Ssjur.brandeland@stericsson.com 	rcu_read_unlock_bh();
234c72dfae2SSjur Braendeland 
235c85c2951Ssjur.brandeland@stericsson.com 	err = dev_queue_xmit(skb);
236c85c2951Ssjur.brandeland@stericsson.com 	if (err > 0)
237c85c2951Ssjur.brandeland@stericsson.com 		err = -EIO;
238c72dfae2SSjur Braendeland 
239c85c2951Ssjur.brandeland@stericsson.com 	return err;
240c72dfae2SSjur Braendeland }
241c72dfae2SSjur Braendeland 
242c72dfae2SSjur Braendeland /*
243bd30ce4bSsjur.brandeland@stericsson.com  * Stuff received packets into the CAIF stack.
244c72dfae2SSjur Braendeland  * On error, returns non-zero and releases the skb.
245c72dfae2SSjur Braendeland  */
receive(struct sk_buff * skb,struct net_device * dev,struct packet_type * pkttype,struct net_device * orig_dev)246c72dfae2SSjur Braendeland static int receive(struct sk_buff *skb, struct net_device *dev,
247c72dfae2SSjur Braendeland 		   struct packet_type *pkttype, struct net_device *orig_dev)
248c72dfae2SSjur Braendeland {
249c72dfae2SSjur Braendeland 	struct cfpkt *pkt;
250c72dfae2SSjur Braendeland 	struct caif_device_entry *caifd;
25169c867c9Ssjur.brandeland@stericsson.com 	int err;
252bd30ce4bSsjur.brandeland@stericsson.com 
253c72dfae2SSjur Braendeland 	pkt = cfpkt_fromnative(CAIF_DIR_IN, skb);
254bd30ce4bSsjur.brandeland@stericsson.com 
255bd30ce4bSsjur.brandeland@stericsson.com 	rcu_read_lock();
256c72dfae2SSjur Braendeland 	caifd = caif_get(dev);
257c72dfae2SSjur Braendeland 
258bd30ce4bSsjur.brandeland@stericsson.com 	if (!caifd || !caifd->layer.up || !caifd->layer.up->receive ||
259bd30ce4bSsjur.brandeland@stericsson.com 			!netif_oper_up(caifd->netdev)) {
260bd30ce4bSsjur.brandeland@stericsson.com 		rcu_read_unlock();
261bd30ce4bSsjur.brandeland@stericsson.com 		kfree_skb(skb);
262c72dfae2SSjur Braendeland 		return NET_RX_DROP;
263bd30ce4bSsjur.brandeland@stericsson.com 	}
264c72dfae2SSjur Braendeland 
265bd30ce4bSsjur.brandeland@stericsson.com 	/* Hold reference to netdevice while using CAIF stack */
266bd30ce4bSsjur.brandeland@stericsson.com 	caifd_hold(caifd);
267bd30ce4bSsjur.brandeland@stericsson.com 	rcu_read_unlock();
268bd30ce4bSsjur.brandeland@stericsson.com 
26969c867c9Ssjur.brandeland@stericsson.com 	err = caifd->layer.up->receive(caifd->layer.up, pkt);
27069c867c9Ssjur.brandeland@stericsson.com 
2710812beb7STom Rix 	/* For -EILSEQ the packet is not freed so free it now */
27269c867c9Ssjur.brandeland@stericsson.com 	if (err == -EILSEQ)
27369c867c9Ssjur.brandeland@stericsson.com 		cfpkt_destroy(pkt);
274bd30ce4bSsjur.brandeland@stericsson.com 
275bd30ce4bSsjur.brandeland@stericsson.com 	/* Release reference to stack upwards */
276bd30ce4bSsjur.brandeland@stericsson.com 	caifd_put(caifd);
2777c18d220Ssjur.brandeland@stericsson.com 
2787c18d220Ssjur.brandeland@stericsson.com 	if (err != 0)
2797c18d220Ssjur.brandeland@stericsson.com 		err = NET_RX_DROP;
2807c18d220Ssjur.brandeland@stericsson.com 	return err;
281c72dfae2SSjur Braendeland }
282c72dfae2SSjur Braendeland 
283c72dfae2SSjur Braendeland static struct packet_type caif_packet_type __read_mostly = {
284c72dfae2SSjur Braendeland 	.type = cpu_to_be16(ETH_P_CAIF),
285c72dfae2SSjur Braendeland 	.func = receive,
286c72dfae2SSjur Braendeland };
287c72dfae2SSjur Braendeland 
dev_flowctrl(struct net_device * dev,int on)288c72dfae2SSjur Braendeland static void dev_flowctrl(struct net_device *dev, int on)
289c72dfae2SSjur Braendeland {
290bd30ce4bSsjur.brandeland@stericsson.com 	struct caif_device_entry *caifd;
291bd30ce4bSsjur.brandeland@stericsson.com 
292bd30ce4bSsjur.brandeland@stericsson.com 	rcu_read_lock();
293bd30ce4bSsjur.brandeland@stericsson.com 
294bd30ce4bSsjur.brandeland@stericsson.com 	caifd = caif_get(dev);
295bd30ce4bSsjur.brandeland@stericsson.com 	if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) {
296bd30ce4bSsjur.brandeland@stericsson.com 		rcu_read_unlock();
297c72dfae2SSjur Braendeland 		return;
298bd30ce4bSsjur.brandeland@stericsson.com 	}
299bd30ce4bSsjur.brandeland@stericsson.com 
300bd30ce4bSsjur.brandeland@stericsson.com 	caifd_hold(caifd);
301bd30ce4bSsjur.brandeland@stericsson.com 	rcu_read_unlock();
302c72dfae2SSjur Braendeland 
303c72dfae2SSjur Braendeland 	caifd->layer.up->ctrlcmd(caifd->layer.up,
304c72dfae2SSjur Braendeland 				 on ?
305c72dfae2SSjur Braendeland 				 _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND :
306c72dfae2SSjur Braendeland 				 _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND,
307c72dfae2SSjur Braendeland 				 caifd->layer.id);
308bd30ce4bSsjur.brandeland@stericsson.com 	caifd_put(caifd);
309c72dfae2SSjur Braendeland }
310c72dfae2SSjur Braendeland 
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 *))311a2805dcaSPavel Skripkin int caif_enroll_dev(struct net_device *dev, struct caif_dev_common *caifdev,
3127c18d220Ssjur.brandeland@stericsson.com 		     struct cflayer *link_support, int head_room,
3133bffc475SSilviu-Mihai Popescu 		     struct cflayer **layer,
3143bffc475SSilviu-Mihai Popescu 		     int (**rcv_func)(struct sk_buff *, struct net_device *,
3153bffc475SSilviu-Mihai Popescu 				      struct packet_type *,
3163bffc475SSilviu-Mihai Popescu 				      struct net_device *))
317c72dfae2SSjur Braendeland {
3187c18d220Ssjur.brandeland@stericsson.com 	struct caif_device_entry *caifd;
319c72dfae2SSjur Braendeland 	enum cfcnfg_phy_preference pref;
3207c18d220Ssjur.brandeland@stericsson.com 	struct cfcnfg *cfg = get_cfcnfg(dev_net(dev));
32108613e46SDavid Woodhouse 	struct caif_device_entry_list *caifdevs;
322a2805dcaSPavel Skripkin 	int res;
323c72dfae2SSjur Braendeland 
32408613e46SDavid Woodhouse 	caifdevs = caif_device_list(dev_net(dev));
325c72dfae2SSjur Braendeland 	caifd = caif_device_alloc(dev);
326bd30ce4bSsjur.brandeland@stericsson.com 	if (!caifd)
327a2805dcaSPavel Skripkin 		return -ENOMEM;
3287c18d220Ssjur.brandeland@stericsson.com 	*layer = &caifd->layer;
3290e4c7d85Ssjur.brandeland@stericsson.com 	spin_lock_init(&caifd->flow_lock);
330c72dfae2SSjur Braendeland 
331c72dfae2SSjur Braendeland 	switch (caifdev->link_select) {
332c72dfae2SSjur Braendeland 	case CAIF_LINK_HIGH_BANDW:
3332c485209SSjur Braendeland 		pref = CFPHYPREF_HIGH_BW;
334c72dfae2SSjur Braendeland 		break;
335c72dfae2SSjur Braendeland 	case CAIF_LINK_LOW_LATENCY:
3362c485209SSjur Braendeland 		pref = CFPHYPREF_LOW_LAT;
337c72dfae2SSjur Braendeland 		break;
338c72dfae2SSjur Braendeland 	default:
339c72dfae2SSjur Braendeland 		pref = CFPHYPREF_HIGH_BW;
340c72dfae2SSjur Braendeland 		break;
341c72dfae2SSjur Braendeland 	}
342bd30ce4bSsjur.brandeland@stericsson.com 	mutex_lock(&caifdevs->lock);
343bd30ce4bSsjur.brandeland@stericsson.com 	list_add_rcu(&caifd->list, &caifdevs->list);
344bd30ce4bSsjur.brandeland@stericsson.com 
345*df207b00SWolfram Sang 	strscpy(caifd->layer.name, dev->name,
3463dc2fa47SXiongfeng Wang 		sizeof(caifd->layer.name));
3477c18d220Ssjur.brandeland@stericsson.com 	caifd->layer.transmit = transmit;
348a2805dcaSPavel Skripkin 	res = cfcnfg_add_phy_layer(cfg,
349c72dfae2SSjur Braendeland 				dev,
350c72dfae2SSjur Braendeland 				&caifd->layer,
351c72dfae2SSjur Braendeland 				pref,
3527c18d220Ssjur.brandeland@stericsson.com 				link_support,
353c72dfae2SSjur Braendeland 				caifdev->use_fcs,
3547c18d220Ssjur.brandeland@stericsson.com 				head_room);
355bd30ce4bSsjur.brandeland@stericsson.com 	mutex_unlock(&caifdevs->lock);
3567c18d220Ssjur.brandeland@stericsson.com 	if (rcv_func)
3577c18d220Ssjur.brandeland@stericsson.com 		*rcv_func = receive;
358a2805dcaSPavel Skripkin 	return res;
3597c18d220Ssjur.brandeland@stericsson.com }
3607ad65bf6Ssjur.brandeland@stericsson.com EXPORT_SYMBOL(caif_enroll_dev);
3617c18d220Ssjur.brandeland@stericsson.com 
3627c18d220Ssjur.brandeland@stericsson.com /* notify Caif of device events */
caif_device_notify(struct notifier_block * me,unsigned long what,void * ptr)3637c18d220Ssjur.brandeland@stericsson.com static int caif_device_notify(struct notifier_block *me, unsigned long what,
364351638e7SJiri Pirko 			      void *ptr)
3657c18d220Ssjur.brandeland@stericsson.com {
366351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
3677c18d220Ssjur.brandeland@stericsson.com 	struct caif_device_entry *caifd = NULL;
3687c18d220Ssjur.brandeland@stericsson.com 	struct caif_dev_common *caifdev;
3697c18d220Ssjur.brandeland@stericsson.com 	struct cfcnfg *cfg;
3707c18d220Ssjur.brandeland@stericsson.com 	struct cflayer *layer, *link_support;
3717c18d220Ssjur.brandeland@stericsson.com 	int head_room = 0;
3727c18d220Ssjur.brandeland@stericsson.com 	struct caif_device_entry_list *caifdevs;
373b53558a9SPavel Skripkin 	int res;
3747c18d220Ssjur.brandeland@stericsson.com 
3757c18d220Ssjur.brandeland@stericsson.com 	cfg = get_cfcnfg(dev_net(dev));
3767c18d220Ssjur.brandeland@stericsson.com 	caifdevs = caif_device_list(dev_net(dev));
3777c18d220Ssjur.brandeland@stericsson.com 
3787c18d220Ssjur.brandeland@stericsson.com 	caifd = caif_get(dev);
3797c18d220Ssjur.brandeland@stericsson.com 	if (caifd == NULL && dev->type != ARPHRD_CAIF)
3807c18d220Ssjur.brandeland@stericsson.com 		return 0;
3817c18d220Ssjur.brandeland@stericsson.com 
3827c18d220Ssjur.brandeland@stericsson.com 	switch (what) {
3837c18d220Ssjur.brandeland@stericsson.com 	case NETDEV_REGISTER:
3847c18d220Ssjur.brandeland@stericsson.com 		if (caifd != NULL)
3857c18d220Ssjur.brandeland@stericsson.com 			break;
3867c18d220Ssjur.brandeland@stericsson.com 
3877c18d220Ssjur.brandeland@stericsson.com 		caifdev = netdev_priv(dev);
3887c18d220Ssjur.brandeland@stericsson.com 
3897c18d220Ssjur.brandeland@stericsson.com 		link_support = NULL;
3907c18d220Ssjur.brandeland@stericsson.com 		if (caifdev->use_frag) {
3917c18d220Ssjur.brandeland@stericsson.com 			head_room = 1;
3927c18d220Ssjur.brandeland@stericsson.com 			link_support = cfserl_create(dev->ifindex,
393e977b4cfSsjur.brandeland@stericsson.com 							caifdev->use_stx);
3947c18d220Ssjur.brandeland@stericsson.com 			if (!link_support) {
3957c18d220Ssjur.brandeland@stericsson.com 				pr_warn("Out of memory\n");
3967c18d220Ssjur.brandeland@stericsson.com 				break;
3977c18d220Ssjur.brandeland@stericsson.com 			}
3987c18d220Ssjur.brandeland@stericsson.com 		}
399b53558a9SPavel Skripkin 		res = caif_enroll_dev(dev, caifdev, link_support, head_room,
4007c18d220Ssjur.brandeland@stericsson.com 				&layer, NULL);
401b53558a9SPavel Skripkin 		if (res)
402b53558a9SPavel Skripkin 			cfserl_release(link_support);
4037c18d220Ssjur.brandeland@stericsson.com 		caifdev->flowctrl = dev_flowctrl;
404c72dfae2SSjur Braendeland 		break;
405c72dfae2SSjur Braendeland 
406bd30ce4bSsjur.brandeland@stericsson.com 	case NETDEV_UP:
407bd30ce4bSsjur.brandeland@stericsson.com 		rcu_read_lock();
408bd30ce4bSsjur.brandeland@stericsson.com 
409c72dfae2SSjur Braendeland 		caifd = caif_get(dev);
410bd30ce4bSsjur.brandeland@stericsson.com 		if (caifd == NULL) {
411bd30ce4bSsjur.brandeland@stericsson.com 			rcu_read_unlock();
412c72dfae2SSjur Braendeland 			break;
413bd30ce4bSsjur.brandeland@stericsson.com 		}
414c72dfae2SSjur Braendeland 
4158518307dSJason Yan 		caifd->xoff = false;
416bd30ce4bSsjur.brandeland@stericsson.com 		cfcnfg_set_phy_state(cfg, &caifd->layer, true);
417bd30ce4bSsjur.brandeland@stericsson.com 		rcu_read_unlock();
418c72dfae2SSjur Braendeland 
419c72dfae2SSjur Braendeland 		break;
420c72dfae2SSjur Braendeland 
421c72dfae2SSjur Braendeland 	case NETDEV_DOWN:
422bd30ce4bSsjur.brandeland@stericsson.com 		rcu_read_lock();
423bd30ce4bSsjur.brandeland@stericsson.com 
424c72dfae2SSjur Braendeland 		caifd = caif_get(dev);
425bd30ce4bSsjur.brandeland@stericsson.com 		if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) {
426bd30ce4bSsjur.brandeland@stericsson.com 			rcu_read_unlock();
427bd30ce4bSsjur.brandeland@stericsson.com 			return -EINVAL;
428bd30ce4bSsjur.brandeland@stericsson.com 		}
429bd30ce4bSsjur.brandeland@stericsson.com 
430bd30ce4bSsjur.brandeland@stericsson.com 		cfcnfg_set_phy_state(cfg, &caifd->layer, false);
431bd30ce4bSsjur.brandeland@stericsson.com 		caifd_hold(caifd);
432bd30ce4bSsjur.brandeland@stericsson.com 		rcu_read_unlock();
433bd30ce4bSsjur.brandeland@stericsson.com 
434bd30ce4bSsjur.brandeland@stericsson.com 		caifd->layer.up->ctrlcmd(caifd->layer.up,
435bd30ce4bSsjur.brandeland@stericsson.com 					 _CAIF_CTRLCMD_PHYIF_DOWN_IND,
436bd30ce4bSsjur.brandeland@stericsson.com 					 caifd->layer.id);
4377d311304Ssjur.brandeland@stericsson.com 
4387d311304Ssjur.brandeland@stericsson.com 		spin_lock_bh(&caifd->flow_lock);
4397d311304Ssjur.brandeland@stericsson.com 
4407d311304Ssjur.brandeland@stericsson.com 		/*
4417d311304Ssjur.brandeland@stericsson.com 		 * Replace our xoff-destructor with original destructor.
4427d311304Ssjur.brandeland@stericsson.com 		 * We trust that skb->destructor *always* is called before
4437d311304Ssjur.brandeland@stericsson.com 		 * the skb reference is invalid. The hijacked SKB destructor
4447d311304Ssjur.brandeland@stericsson.com 		 * takes the flow_lock so manipulating the skb->destructor here
4457d311304Ssjur.brandeland@stericsson.com 		 * should be safe.
4467d311304Ssjur.brandeland@stericsson.com 		*/
4477d311304Ssjur.brandeland@stericsson.com 		if (caifd->xoff_skb_dtor != NULL && caifd->xoff_skb != NULL)
4487d311304Ssjur.brandeland@stericsson.com 			caifd->xoff_skb->destructor = caifd->xoff_skb_dtor;
4497d311304Ssjur.brandeland@stericsson.com 
4508518307dSJason Yan 		caifd->xoff = false;
4517d311304Ssjur.brandeland@stericsson.com 		caifd->xoff_skb_dtor = NULL;
4527d311304Ssjur.brandeland@stericsson.com 		caifd->xoff_skb = NULL;
4537d311304Ssjur.brandeland@stericsson.com 
4547d311304Ssjur.brandeland@stericsson.com 		spin_unlock_bh(&caifd->flow_lock);
455bd30ce4bSsjur.brandeland@stericsson.com 		caifd_put(caifd);
456c72dfae2SSjur Braendeland 		break;
457c72dfae2SSjur Braendeland 
458c72dfae2SSjur Braendeland 	case NETDEV_UNREGISTER:
459bd30ce4bSsjur.brandeland@stericsson.com 		mutex_lock(&caifdevs->lock);
460bd30ce4bSsjur.brandeland@stericsson.com 
461c72dfae2SSjur Braendeland 		caifd = caif_get(dev);
462bd30ce4bSsjur.brandeland@stericsson.com 		if (caifd == NULL) {
463bd30ce4bSsjur.brandeland@stericsson.com 			mutex_unlock(&caifdevs->lock);
464f2527ec4SAndré Carvalho de Matos 			break;
465bd30ce4bSsjur.brandeland@stericsson.com 		}
466bd30ce4bSsjur.brandeland@stericsson.com 		list_del_rcu(&caifd->list);
467bd30ce4bSsjur.brandeland@stericsson.com 
468bd30ce4bSsjur.brandeland@stericsson.com 		/*
469bd30ce4bSsjur.brandeland@stericsson.com 		 * NETDEV_UNREGISTER is called repeatedly until all reference
470bd30ce4bSsjur.brandeland@stericsson.com 		 * counts for the net-device are released. If references to
471bd30ce4bSsjur.brandeland@stericsson.com 		 * caifd is taken, simply ignore NETDEV_UNREGISTER and wait for
472bd30ce4bSsjur.brandeland@stericsson.com 		 * the next call to NETDEV_UNREGISTER.
473bd30ce4bSsjur.brandeland@stericsson.com 		 *
474bd30ce4bSsjur.brandeland@stericsson.com 		 * If any packets are in flight down the CAIF Stack,
475bd30ce4bSsjur.brandeland@stericsson.com 		 * cfcnfg_del_phy_layer will return nonzero.
476bd30ce4bSsjur.brandeland@stericsson.com 		 * If no packets are in flight, the CAIF Stack associated
477bd30ce4bSsjur.brandeland@stericsson.com 		 * with the net-device un-registering is freed.
478bd30ce4bSsjur.brandeland@stericsson.com 		 */
479bd30ce4bSsjur.brandeland@stericsson.com 
480bd30ce4bSsjur.brandeland@stericsson.com 		if (caifd_refcnt_read(caifd) != 0 ||
481bd30ce4bSsjur.brandeland@stericsson.com 			cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0) {
482bd30ce4bSsjur.brandeland@stericsson.com 
483bd30ce4bSsjur.brandeland@stericsson.com 			pr_info("Wait for device inuse\n");
484bd30ce4bSsjur.brandeland@stericsson.com 			/* Enrole device if CAIF Stack is still in use */
485bd30ce4bSsjur.brandeland@stericsson.com 			list_add_rcu(&caifd->list, &caifdevs->list);
486bd30ce4bSsjur.brandeland@stericsson.com 			mutex_unlock(&caifdevs->lock);
487bd30ce4bSsjur.brandeland@stericsson.com 			break;
488bd30ce4bSsjur.brandeland@stericsson.com 		}
489bd30ce4bSsjur.brandeland@stericsson.com 
490bd30ce4bSsjur.brandeland@stericsson.com 		synchronize_rcu();
491bd30ce4bSsjur.brandeland@stericsson.com 		dev_put(caifd->netdev);
492bd30ce4bSsjur.brandeland@stericsson.com 		free_percpu(caifd->pcpu_refcnt);
493bd30ce4bSsjur.brandeland@stericsson.com 		kfree(caifd);
494bd30ce4bSsjur.brandeland@stericsson.com 
495bd30ce4bSsjur.brandeland@stericsson.com 		mutex_unlock(&caifdevs->lock);
496c72dfae2SSjur Braendeland 		break;
497c72dfae2SSjur Braendeland 	}
498c72dfae2SSjur Braendeland 	return 0;
499c72dfae2SSjur Braendeland }
500c72dfae2SSjur Braendeland 
501c72dfae2SSjur Braendeland static struct notifier_block caif_device_notifier = {
502c72dfae2SSjur Braendeland 	.notifier_call = caif_device_notify,
503c72dfae2SSjur Braendeland 	.priority = 0,
504c72dfae2SSjur Braendeland };
505c72dfae2SSjur Braendeland 
506c72dfae2SSjur Braendeland /* Per-namespace Caif devices handling */
caif_init_net(struct net * net)507c72dfae2SSjur Braendeland static int caif_init_net(struct net *net)
508c72dfae2SSjur Braendeland {
509c72dfae2SSjur Braendeland 	struct caif_net *caifn = net_generic(net, caif_net_id);
510c72dfae2SSjur Braendeland 	INIT_LIST_HEAD(&caifn->caifdevs.list);
511bd30ce4bSsjur.brandeland@stericsson.com 	mutex_init(&caifn->caifdevs.lock);
512bee925dbSsjur.brandeland@stericsson.com 
513bee925dbSsjur.brandeland@stericsson.com 	caifn->cfg = cfcnfg_create();
514f84ea779SRoar Førde 	if (!caifn->cfg)
515bee925dbSsjur.brandeland@stericsson.com 		return -ENOMEM;
516bee925dbSsjur.brandeland@stericsson.com 
517c72dfae2SSjur Braendeland 	return 0;
518c72dfae2SSjur Braendeland }
519c72dfae2SSjur Braendeland 
caif_exit_net(struct net * net)520c72dfae2SSjur Braendeland static void caif_exit_net(struct net *net)
521c72dfae2SSjur Braendeland {
522bd30ce4bSsjur.brandeland@stericsson.com 	struct caif_device_entry *caifd, *tmp;
523bd30ce4bSsjur.brandeland@stericsson.com 	struct caif_device_entry_list *caifdevs =
524bd30ce4bSsjur.brandeland@stericsson.com 	    caif_device_list(net);
5257c18d220Ssjur.brandeland@stericsson.com 	struct cfcnfg *cfg =  get_cfcnfg(net);
5267c18d220Ssjur.brandeland@stericsson.com 
527c72dfae2SSjur Braendeland 	rtnl_lock();
528bd30ce4bSsjur.brandeland@stericsson.com 	mutex_lock(&caifdevs->lock);
529bd30ce4bSsjur.brandeland@stericsson.com 
530bd30ce4bSsjur.brandeland@stericsson.com 	list_for_each_entry_safe(caifd, tmp, &caifdevs->list, list) {
531bd30ce4bSsjur.brandeland@stericsson.com 		int i = 0;
532bd30ce4bSsjur.brandeland@stericsson.com 		list_del_rcu(&caifd->list);
533bd30ce4bSsjur.brandeland@stericsson.com 		cfcnfg_set_phy_state(cfg, &caifd->layer, false);
534bd30ce4bSsjur.brandeland@stericsson.com 
535bd30ce4bSsjur.brandeland@stericsson.com 		while (i < 10 &&
536bd30ce4bSsjur.brandeland@stericsson.com 			(caifd_refcnt_read(caifd) != 0 ||
537bd30ce4bSsjur.brandeland@stericsson.com 			cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0)) {
538bd30ce4bSsjur.brandeland@stericsson.com 
539bd30ce4bSsjur.brandeland@stericsson.com 			pr_info("Wait for device inuse\n");
540bd30ce4bSsjur.brandeland@stericsson.com 			msleep(250);
541bd30ce4bSsjur.brandeland@stericsson.com 			i++;
542c72dfae2SSjur Braendeland 		}
543bd30ce4bSsjur.brandeland@stericsson.com 		synchronize_rcu();
544bd30ce4bSsjur.brandeland@stericsson.com 		dev_put(caifd->netdev);
545bd30ce4bSsjur.brandeland@stericsson.com 		free_percpu(caifd->pcpu_refcnt);
546bd30ce4bSsjur.brandeland@stericsson.com 		kfree(caifd);
547bd30ce4bSsjur.brandeland@stericsson.com 	}
548bee925dbSsjur.brandeland@stericsson.com 	cfcnfg_remove(cfg);
549bd30ce4bSsjur.brandeland@stericsson.com 
550bd30ce4bSsjur.brandeland@stericsson.com 	mutex_unlock(&caifdevs->lock);
551c72dfae2SSjur Braendeland 	rtnl_unlock();
552c72dfae2SSjur Braendeland }
553c72dfae2SSjur Braendeland 
554c72dfae2SSjur Braendeland static struct pernet_operations caif_net_ops = {
555c72dfae2SSjur Braendeland 	.init = caif_init_net,
556c72dfae2SSjur Braendeland 	.exit = caif_exit_net,
557c72dfae2SSjur Braendeland 	.id   = &caif_net_id,
558c72dfae2SSjur Braendeland 	.size = sizeof(struct caif_net),
559c72dfae2SSjur Braendeland };
560c72dfae2SSjur Braendeland 
561c72dfae2SSjur Braendeland /* Initialize Caif devices list */
caif_device_init(void)562c72dfae2SSjur Braendeland static int __init caif_device_init(void)
563c72dfae2SSjur Braendeland {
564c72dfae2SSjur Braendeland 	int result;
565bd30ce4bSsjur.brandeland@stericsson.com 
5668a8ee9afSEric W. Biederman 	result = register_pernet_subsys(&caif_net_ops);
567c72dfae2SSjur Braendeland 
568bee925dbSsjur.brandeland@stericsson.com 	if (result)
569c72dfae2SSjur Braendeland 		return result;
570bee925dbSsjur.brandeland@stericsson.com 
571c72dfae2SSjur Braendeland 	register_netdevice_notifier(&caif_device_notifier);
572bee925dbSsjur.brandeland@stericsson.com 	dev_add_pack(&caif_packet_type);
573c72dfae2SSjur Braendeland 
574c72dfae2SSjur Braendeland 	return result;
575c72dfae2SSjur Braendeland }
576c72dfae2SSjur Braendeland 
caif_device_exit(void)577c72dfae2SSjur Braendeland static void __exit caif_device_exit(void)
578c72dfae2SSjur Braendeland {
579c72dfae2SSjur Braendeland 	unregister_netdevice_notifier(&caif_device_notifier);
580bee925dbSsjur.brandeland@stericsson.com 	dev_remove_pack(&caif_packet_type);
58196f80d12SSjur Brændeland 	unregister_pernet_subsys(&caif_net_ops);
582c72dfae2SSjur Braendeland }
583c72dfae2SSjur Braendeland 
584c72dfae2SSjur Braendeland module_init(caif_device_init);
585c72dfae2SSjur Braendeland module_exit(caif_device_exit);
586