xref: /linux/net/caif/caif_dev.c (revision 06d07429858317ded2db7986113a9e0129cd599b)
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 
30*cb420106SJakub Kicinski MODULE_DESCRIPTION("ST-Ericsson CAIF modem protocol support");
31c72dfae2SSjur Braendeland MODULE_LICENSE("GPL");
32c72dfae2SSjur Braendeland 
33c72dfae2SSjur Braendeland /* Used for local tracking of the CAIF net devices */
34c72dfae2SSjur Braendeland struct caif_device_entry {
35c72dfae2SSjur Braendeland 	struct cflayer layer;
36c72dfae2SSjur Braendeland 	struct list_head list;
37c72dfae2SSjur Braendeland 	struct net_device *netdev;
38bd30ce4bSsjur.brandeland@stericsson.com 	int __percpu *pcpu_refcnt;
390e4c7d85Ssjur.brandeland@stericsson.com 	spinlock_t flow_lock;
407d311304Ssjur.brandeland@stericsson.com 	struct sk_buff *xoff_skb;
417d311304Ssjur.brandeland@stericsson.com 	void (*xoff_skb_dtor)(struct sk_buff *skb);
420e4c7d85Ssjur.brandeland@stericsson.com 	bool xoff;
43c72dfae2SSjur Braendeland };
44c72dfae2SSjur Braendeland 
45c72dfae2SSjur Braendeland struct caif_device_entry_list {
46c72dfae2SSjur Braendeland 	struct list_head list;
47c72dfae2SSjur Braendeland 	/* Protects simulanous deletes in list */
48bd30ce4bSsjur.brandeland@stericsson.com 	struct mutex lock;
49c72dfae2SSjur Braendeland };
50c72dfae2SSjur Braendeland 
51c72dfae2SSjur Braendeland struct caif_net {
52bee925dbSsjur.brandeland@stericsson.com 	struct cfcnfg *cfg;
53c72dfae2SSjur Braendeland 	struct caif_device_entry_list caifdevs;
54c72dfae2SSjur Braendeland };
55c72dfae2SSjur Braendeland 
56c7d03a00SAlexey Dobriyan static unsigned int caif_net_id;
570e4c7d85Ssjur.brandeland@stericsson.com static int q_high = 50; /* Percent */
58bee925dbSsjur.brandeland@stericsson.com 
get_cfcnfg(struct net * net)59bee925dbSsjur.brandeland@stericsson.com struct cfcnfg *get_cfcnfg(struct net *net)
60bee925dbSsjur.brandeland@stericsson.com {
61bee925dbSsjur.brandeland@stericsson.com 	struct caif_net *caifn;
62bee925dbSsjur.brandeland@stericsson.com 	caifn = net_generic(net, caif_net_id);
63bee925dbSsjur.brandeland@stericsson.com 	return caifn->cfg;
64bee925dbSsjur.brandeland@stericsson.com }
65bee925dbSsjur.brandeland@stericsson.com EXPORT_SYMBOL(get_cfcnfg);
66c72dfae2SSjur Braendeland 
caif_device_list(struct net * net)67c72dfae2SSjur Braendeland static struct caif_device_entry_list *caif_device_list(struct net *net)
68c72dfae2SSjur Braendeland {
69c72dfae2SSjur Braendeland 	struct caif_net *caifn;
70c72dfae2SSjur Braendeland 	caifn = net_generic(net, caif_net_id);
71c72dfae2SSjur Braendeland 	return &caifn->caifdevs;
72c72dfae2SSjur Braendeland }
73c72dfae2SSjur Braendeland 
caifd_put(struct caif_device_entry * e)74bd30ce4bSsjur.brandeland@stericsson.com static void caifd_put(struct caif_device_entry *e)
75bd30ce4bSsjur.brandeland@stericsson.com {
76933393f5SChristoph Lameter 	this_cpu_dec(*e->pcpu_refcnt);
77bd30ce4bSsjur.brandeland@stericsson.com }
78bd30ce4bSsjur.brandeland@stericsson.com 
caifd_hold(struct caif_device_entry * e)79bd30ce4bSsjur.brandeland@stericsson.com static void caifd_hold(struct caif_device_entry *e)
80bd30ce4bSsjur.brandeland@stericsson.com {
81933393f5SChristoph Lameter 	this_cpu_inc(*e->pcpu_refcnt);
82bd30ce4bSsjur.brandeland@stericsson.com }
83bd30ce4bSsjur.brandeland@stericsson.com 
caifd_refcnt_read(struct caif_device_entry * e)84bd30ce4bSsjur.brandeland@stericsson.com static int caifd_refcnt_read(struct caif_device_entry *e)
85bd30ce4bSsjur.brandeland@stericsson.com {
86bd30ce4bSsjur.brandeland@stericsson.com 	int i, refcnt = 0;
87bd30ce4bSsjur.brandeland@stericsson.com 	for_each_possible_cpu(i)
88bd30ce4bSsjur.brandeland@stericsson.com 		refcnt += *per_cpu_ptr(e->pcpu_refcnt, i);
89bd30ce4bSsjur.brandeland@stericsson.com 	return refcnt;
90bd30ce4bSsjur.brandeland@stericsson.com }
91bd30ce4bSsjur.brandeland@stericsson.com 
92c72dfae2SSjur Braendeland /* Allocate new CAIF device. */
caif_device_alloc(struct net_device * dev)93c72dfae2SSjur Braendeland static struct caif_device_entry *caif_device_alloc(struct net_device *dev)
94c72dfae2SSjur Braendeland {
95c72dfae2SSjur Braendeland 	struct caif_device_entry *caifd;
96bd30ce4bSsjur.brandeland@stericsson.com 
974fb66b82SEric Dumazet 	caifd = kzalloc(sizeof(*caifd), GFP_KERNEL);
98c72dfae2SSjur Braendeland 	if (!caifd)
99c72dfae2SSjur Braendeland 		return NULL;
100bd30ce4bSsjur.brandeland@stericsson.com 	caifd->pcpu_refcnt = alloc_percpu(int);
1014fb66b82SEric Dumazet 	if (!caifd->pcpu_refcnt) {
1024fb66b82SEric Dumazet 		kfree(caifd);
1034fb66b82SEric Dumazet 		return NULL;
1044fb66b82SEric Dumazet 	}
105c72dfae2SSjur Braendeland 	caifd->netdev = dev;
106bd30ce4bSsjur.brandeland@stericsson.com 	dev_hold(dev);
107c72dfae2SSjur Braendeland 	return caifd;
108c72dfae2SSjur Braendeland }
109c72dfae2SSjur Braendeland 
caif_get(struct net_device * dev)110c72dfae2SSjur Braendeland static struct caif_device_entry *caif_get(struct net_device *dev)
111c72dfae2SSjur Braendeland {
112c72dfae2SSjur Braendeland 	struct caif_device_entry_list *caifdevs =
113c72dfae2SSjur Braendeland 	    caif_device_list(dev_net(dev));
114c72dfae2SSjur Braendeland 	struct caif_device_entry *caifd;
1157c18d220Ssjur.brandeland@stericsson.com 
116f9fc28a8SAmol Grover 	list_for_each_entry_rcu(caifd, &caifdevs->list, list,
117f9fc28a8SAmol Grover 				lockdep_rtnl_is_held()) {
118c72dfae2SSjur Braendeland 		if (caifd->netdev == dev)
119c72dfae2SSjur Braendeland 			return caifd;
120c72dfae2SSjur Braendeland 	}
121c72dfae2SSjur Braendeland 	return NULL;
122c72dfae2SSjur Braendeland }
123c72dfae2SSjur Braendeland 
caif_flow_cb(struct sk_buff * skb)124d6e89c0bSSilviu-Mihai Popescu static void caif_flow_cb(struct sk_buff *skb)
1250e4c7d85Ssjur.brandeland@stericsson.com {
1260e4c7d85Ssjur.brandeland@stericsson.com 	struct caif_device_entry *caifd;
1277d311304Ssjur.brandeland@stericsson.com 	void (*dtor)(struct sk_buff *skb) = NULL;
1280e4c7d85Ssjur.brandeland@stericsson.com 	bool send_xoff;
1290e4c7d85Ssjur.brandeland@stericsson.com 
1300e4c7d85Ssjur.brandeland@stericsson.com 	WARN_ON(skb->dev == NULL);
1310e4c7d85Ssjur.brandeland@stericsson.com 
1320e4c7d85Ssjur.brandeland@stericsson.com 	rcu_read_lock();
1330e4c7d85Ssjur.brandeland@stericsson.com 	caifd = caif_get(skb->dev);
134c95567c8SKim Lilliestierna XX 
135c95567c8SKim Lilliestierna XX 	WARN_ON(caifd == NULL);
13664119e05SYueHaibing 	if (!caifd) {
13764119e05SYueHaibing 		rcu_read_unlock();
138c95567c8SKim Lilliestierna XX 		return;
13964119e05SYueHaibing 	}
140c95567c8SKim Lilliestierna XX 
1410e4c7d85Ssjur.brandeland@stericsson.com 	caifd_hold(caifd);
1420e4c7d85Ssjur.brandeland@stericsson.com 	rcu_read_unlock();
1430e4c7d85Ssjur.brandeland@stericsson.com 
1440e4c7d85Ssjur.brandeland@stericsson.com 	spin_lock_bh(&caifd->flow_lock);
1450e4c7d85Ssjur.brandeland@stericsson.com 	send_xoff = caifd->xoff;
1468518307dSJason Yan 	caifd->xoff = false;
1477d311304Ssjur.brandeland@stericsson.com 	dtor = caifd->xoff_skb_dtor;
14859f608d8Ssjur.brandeland@stericsson.com 
14959f608d8Ssjur.brandeland@stericsson.com 	if (WARN_ON(caifd->xoff_skb != skb))
15059f608d8Ssjur.brandeland@stericsson.com 		skb = NULL;
15159f608d8Ssjur.brandeland@stericsson.com 
1527d311304Ssjur.brandeland@stericsson.com 	caifd->xoff_skb = NULL;
1537d311304Ssjur.brandeland@stericsson.com 	caifd->xoff_skb_dtor = NULL;
15459f608d8Ssjur.brandeland@stericsson.com 
1550e4c7d85Ssjur.brandeland@stericsson.com 	spin_unlock_bh(&caifd->flow_lock);
1560e4c7d85Ssjur.brandeland@stericsson.com 
15759f608d8Ssjur.brandeland@stericsson.com 	if (dtor && skb)
1587d311304Ssjur.brandeland@stericsson.com 		dtor(skb);
1597d311304Ssjur.brandeland@stericsson.com 
1600e4c7d85Ssjur.brandeland@stericsson.com 	if (send_xoff)
1610e4c7d85Ssjur.brandeland@stericsson.com 		caifd->layer.up->
1620e4c7d85Ssjur.brandeland@stericsson.com 			ctrlcmd(caifd->layer.up,
1630e4c7d85Ssjur.brandeland@stericsson.com 				_CAIF_CTRLCMD_PHYIF_FLOW_ON_IND,
1640e4c7d85Ssjur.brandeland@stericsson.com 				caifd->layer.id);
1650e4c7d85Ssjur.brandeland@stericsson.com 	caifd_put(caifd);
1660e4c7d85Ssjur.brandeland@stericsson.com }
1670e4c7d85Ssjur.brandeland@stericsson.com 
transmit(struct cflayer * layer,struct cfpkt * pkt)168c72dfae2SSjur Braendeland static int transmit(struct cflayer *layer, struct cfpkt *pkt)
169c72dfae2SSjur Braendeland {
1700e4c7d85Ssjur.brandeland@stericsson.com 	int err, high = 0, qlen = 0;
171c72dfae2SSjur Braendeland 	struct caif_device_entry *caifd =
172c72dfae2SSjur Braendeland 	    container_of(layer, struct caif_device_entry, layer);
1734dd820c0SSjur Brændeland 	struct sk_buff *skb;
1740e4c7d85Ssjur.brandeland@stericsson.com 	struct netdev_queue *txq;
1750e4c7d85Ssjur.brandeland@stericsson.com 
1760e4c7d85Ssjur.brandeland@stericsson.com 	rcu_read_lock_bh();
1774dd820c0SSjur Brændeland 
178c72dfae2SSjur Braendeland 	skb = cfpkt_tonative(pkt);
179c72dfae2SSjur Braendeland 	skb->dev = caifd->netdev;
1807c18d220Ssjur.brandeland@stericsson.com 	skb_reset_network_header(skb);
1817c18d220Ssjur.brandeland@stericsson.com 	skb->protocol = htons(ETH_P_CAIF);
1820e4c7d85Ssjur.brandeland@stericsson.com 
1830e4c7d85Ssjur.brandeland@stericsson.com 	/* Check if we need to handle xoff */
1844676a152SPhil Sutter 	if (likely(caifd->netdev->priv_flags & IFF_NO_QUEUE))
1850e4c7d85Ssjur.brandeland@stericsson.com 		goto noxoff;
1860e4c7d85Ssjur.brandeland@stericsson.com 
1870e4c7d85Ssjur.brandeland@stericsson.com 	if (unlikely(caifd->xoff))
1880e4c7d85Ssjur.brandeland@stericsson.com 		goto noxoff;
1890e4c7d85Ssjur.brandeland@stericsson.com 
1900e4c7d85Ssjur.brandeland@stericsson.com 	if (likely(!netif_queue_stopped(caifd->netdev))) {
191b0a231a2SPaolo Abeni 		struct Qdisc *sch;
192b0a231a2SPaolo Abeni 
1930e4c7d85Ssjur.brandeland@stericsson.com 		/* If we run with a TX queue, check if the queue is too long*/
1940e4c7d85Ssjur.brandeland@stericsson.com 		txq = netdev_get_tx_queue(skb->dev, 0);
195b0a231a2SPaolo Abeni 		sch = rcu_dereference_bh(txq->qdisc);
196b0a231a2SPaolo Abeni 		if (likely(qdisc_is_empty(sch)))
1970e4c7d85Ssjur.brandeland@stericsson.com 			goto noxoff;
1980e4c7d85Ssjur.brandeland@stericsson.com 
199b0a231a2SPaolo Abeni 		/* can check for explicit qdisc len value only !NOLOCK,
200b0a231a2SPaolo Abeni 		 * always set flow off otherwise
201b0a231a2SPaolo Abeni 		 */
2020e4c7d85Ssjur.brandeland@stericsson.com 		high = (caifd->netdev->tx_queue_len * q_high) / 100;
203b0a231a2SPaolo Abeni 		if (!(sch->flags & TCQ_F_NOLOCK) && likely(sch->q.qlen < high))
2040e4c7d85Ssjur.brandeland@stericsson.com 			goto noxoff;
2050e4c7d85Ssjur.brandeland@stericsson.com 	}
2060e4c7d85Ssjur.brandeland@stericsson.com 
2070e4c7d85Ssjur.brandeland@stericsson.com 	/* Hold lock while accessing xoff */
2080e4c7d85Ssjur.brandeland@stericsson.com 	spin_lock_bh(&caifd->flow_lock);
2090e4c7d85Ssjur.brandeland@stericsson.com 	if (caifd->xoff) {
2100e4c7d85Ssjur.brandeland@stericsson.com 		spin_unlock_bh(&caifd->flow_lock);
2110e4c7d85Ssjur.brandeland@stericsson.com 		goto noxoff;
2120e4c7d85Ssjur.brandeland@stericsson.com 	}
2130e4c7d85Ssjur.brandeland@stericsson.com 
2140e4c7d85Ssjur.brandeland@stericsson.com 	/*
2150e4c7d85Ssjur.brandeland@stericsson.com 	 * Handle flow off, we do this by temporary hi-jacking this
2160e4c7d85Ssjur.brandeland@stericsson.com 	 * skb's destructor function, and replace it with our own
2170e4c7d85Ssjur.brandeland@stericsson.com 	 * flow-on callback. The callback will set flow-on and call
2180e4c7d85Ssjur.brandeland@stericsson.com 	 * the original destructor.
2190e4c7d85Ssjur.brandeland@stericsson.com 	 */
2200e4c7d85Ssjur.brandeland@stericsson.com 
2210e4c7d85Ssjur.brandeland@stericsson.com 	pr_debug("queue has stopped(%d) or is full (%d > %d)\n",
2220e4c7d85Ssjur.brandeland@stericsson.com 			netif_queue_stopped(caifd->netdev),
2230e4c7d85Ssjur.brandeland@stericsson.com 			qlen, high);
2248518307dSJason Yan 	caifd->xoff = true;
2257d311304Ssjur.brandeland@stericsson.com 	caifd->xoff_skb = skb;
2267d311304Ssjur.brandeland@stericsson.com 	caifd->xoff_skb_dtor = skb->destructor;
2277d311304Ssjur.brandeland@stericsson.com 	skb->destructor = caif_flow_cb;
2280e4c7d85Ssjur.brandeland@stericsson.com 	spin_unlock_bh(&caifd->flow_lock);
2290e4c7d85Ssjur.brandeland@stericsson.com 
2300e4c7d85Ssjur.brandeland@stericsson.com 	caifd->layer.up->ctrlcmd(caifd->layer.up,
2310e4c7d85Ssjur.brandeland@stericsson.com 					_CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND,
2320e4c7d85Ssjur.brandeland@stericsson.com 					caifd->layer.id);
2330e4c7d85Ssjur.brandeland@stericsson.com noxoff:
2340e4c7d85Ssjur.brandeland@stericsson.com 	rcu_read_unlock_bh();
235c72dfae2SSjur Braendeland 
236c85c2951Ssjur.brandeland@stericsson.com 	err = dev_queue_xmit(skb);
237c85c2951Ssjur.brandeland@stericsson.com 	if (err > 0)
238c85c2951Ssjur.brandeland@stericsson.com 		err = -EIO;
239c72dfae2SSjur Braendeland 
240c85c2951Ssjur.brandeland@stericsson.com 	return err;
241c72dfae2SSjur Braendeland }
242c72dfae2SSjur Braendeland 
243c72dfae2SSjur Braendeland /*
244bd30ce4bSsjur.brandeland@stericsson.com  * Stuff received packets into the CAIF stack.
245c72dfae2SSjur Braendeland  * On error, returns non-zero and releases the skb.
246c72dfae2SSjur Braendeland  */
receive(struct sk_buff * skb,struct net_device * dev,struct packet_type * pkttype,struct net_device * orig_dev)247c72dfae2SSjur Braendeland static int receive(struct sk_buff *skb, struct net_device *dev,
248c72dfae2SSjur Braendeland 		   struct packet_type *pkttype, struct net_device *orig_dev)
249c72dfae2SSjur Braendeland {
250c72dfae2SSjur Braendeland 	struct cfpkt *pkt;
251c72dfae2SSjur Braendeland 	struct caif_device_entry *caifd;
25269c867c9Ssjur.brandeland@stericsson.com 	int err;
253bd30ce4bSsjur.brandeland@stericsson.com 
254c72dfae2SSjur Braendeland 	pkt = cfpkt_fromnative(CAIF_DIR_IN, skb);
255bd30ce4bSsjur.brandeland@stericsson.com 
256bd30ce4bSsjur.brandeland@stericsson.com 	rcu_read_lock();
257c72dfae2SSjur Braendeland 	caifd = caif_get(dev);
258c72dfae2SSjur Braendeland 
259bd30ce4bSsjur.brandeland@stericsson.com 	if (!caifd || !caifd->layer.up || !caifd->layer.up->receive ||
260bd30ce4bSsjur.brandeland@stericsson.com 			!netif_oper_up(caifd->netdev)) {
261bd30ce4bSsjur.brandeland@stericsson.com 		rcu_read_unlock();
262bd30ce4bSsjur.brandeland@stericsson.com 		kfree_skb(skb);
263c72dfae2SSjur Braendeland 		return NET_RX_DROP;
264bd30ce4bSsjur.brandeland@stericsson.com 	}
265c72dfae2SSjur Braendeland 
266bd30ce4bSsjur.brandeland@stericsson.com 	/* Hold reference to netdevice while using CAIF stack */
267bd30ce4bSsjur.brandeland@stericsson.com 	caifd_hold(caifd);
268bd30ce4bSsjur.brandeland@stericsson.com 	rcu_read_unlock();
269bd30ce4bSsjur.brandeland@stericsson.com 
27069c867c9Ssjur.brandeland@stericsson.com 	err = caifd->layer.up->receive(caifd->layer.up, pkt);
27169c867c9Ssjur.brandeland@stericsson.com 
2720812beb7STom Rix 	/* For -EILSEQ the packet is not freed so free it now */
27369c867c9Ssjur.brandeland@stericsson.com 	if (err == -EILSEQ)
27469c867c9Ssjur.brandeland@stericsson.com 		cfpkt_destroy(pkt);
275bd30ce4bSsjur.brandeland@stericsson.com 
276bd30ce4bSsjur.brandeland@stericsson.com 	/* Release reference to stack upwards */
277bd30ce4bSsjur.brandeland@stericsson.com 	caifd_put(caifd);
2787c18d220Ssjur.brandeland@stericsson.com 
2797c18d220Ssjur.brandeland@stericsson.com 	if (err != 0)
2807c18d220Ssjur.brandeland@stericsson.com 		err = NET_RX_DROP;
2817c18d220Ssjur.brandeland@stericsson.com 	return err;
282c72dfae2SSjur Braendeland }
283c72dfae2SSjur Braendeland 
284c72dfae2SSjur Braendeland static struct packet_type caif_packet_type __read_mostly = {
285c72dfae2SSjur Braendeland 	.type = cpu_to_be16(ETH_P_CAIF),
286c72dfae2SSjur Braendeland 	.func = receive,
287c72dfae2SSjur Braendeland };
288c72dfae2SSjur Braendeland 
dev_flowctrl(struct net_device * dev,int on)289c72dfae2SSjur Braendeland static void dev_flowctrl(struct net_device *dev, int on)
290c72dfae2SSjur Braendeland {
291bd30ce4bSsjur.brandeland@stericsson.com 	struct caif_device_entry *caifd;
292bd30ce4bSsjur.brandeland@stericsson.com 
293bd30ce4bSsjur.brandeland@stericsson.com 	rcu_read_lock();
294bd30ce4bSsjur.brandeland@stericsson.com 
295bd30ce4bSsjur.brandeland@stericsson.com 	caifd = caif_get(dev);
296bd30ce4bSsjur.brandeland@stericsson.com 	if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) {
297bd30ce4bSsjur.brandeland@stericsson.com 		rcu_read_unlock();
298c72dfae2SSjur Braendeland 		return;
299bd30ce4bSsjur.brandeland@stericsson.com 	}
300bd30ce4bSsjur.brandeland@stericsson.com 
301bd30ce4bSsjur.brandeland@stericsson.com 	caifd_hold(caifd);
302bd30ce4bSsjur.brandeland@stericsson.com 	rcu_read_unlock();
303c72dfae2SSjur Braendeland 
304c72dfae2SSjur Braendeland 	caifd->layer.up->ctrlcmd(caifd->layer.up,
305c72dfae2SSjur Braendeland 				 on ?
306c72dfae2SSjur Braendeland 				 _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND :
307c72dfae2SSjur Braendeland 				 _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND,
308c72dfae2SSjur Braendeland 				 caifd->layer.id);
309bd30ce4bSsjur.brandeland@stericsson.com 	caifd_put(caifd);
310c72dfae2SSjur Braendeland }
311c72dfae2SSjur 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 *))312a2805dcaSPavel Skripkin int caif_enroll_dev(struct net_device *dev, struct caif_dev_common *caifdev,
3137c18d220Ssjur.brandeland@stericsson.com 		     struct cflayer *link_support, int head_room,
3143bffc475SSilviu-Mihai Popescu 		     struct cflayer **layer,
3153bffc475SSilviu-Mihai Popescu 		     int (**rcv_func)(struct sk_buff *, struct net_device *,
3163bffc475SSilviu-Mihai Popescu 				      struct packet_type *,
3173bffc475SSilviu-Mihai Popescu 				      struct net_device *))
318c72dfae2SSjur Braendeland {
3197c18d220Ssjur.brandeland@stericsson.com 	struct caif_device_entry *caifd;
320c72dfae2SSjur Braendeland 	enum cfcnfg_phy_preference pref;
3217c18d220Ssjur.brandeland@stericsson.com 	struct cfcnfg *cfg = get_cfcnfg(dev_net(dev));
32208613e46SDavid Woodhouse 	struct caif_device_entry_list *caifdevs;
323a2805dcaSPavel Skripkin 	int res;
324c72dfae2SSjur Braendeland 
32508613e46SDavid Woodhouse 	caifdevs = caif_device_list(dev_net(dev));
326c72dfae2SSjur Braendeland 	caifd = caif_device_alloc(dev);
327bd30ce4bSsjur.brandeland@stericsson.com 	if (!caifd)
328a2805dcaSPavel Skripkin 		return -ENOMEM;
3297c18d220Ssjur.brandeland@stericsson.com 	*layer = &caifd->layer;
3300e4c7d85Ssjur.brandeland@stericsson.com 	spin_lock_init(&caifd->flow_lock);
331c72dfae2SSjur Braendeland 
332c72dfae2SSjur Braendeland 	switch (caifdev->link_select) {
333c72dfae2SSjur Braendeland 	case CAIF_LINK_HIGH_BANDW:
3342c485209SSjur Braendeland 		pref = CFPHYPREF_HIGH_BW;
335c72dfae2SSjur Braendeland 		break;
336c72dfae2SSjur Braendeland 	case CAIF_LINK_LOW_LATENCY:
3372c485209SSjur Braendeland 		pref = CFPHYPREF_LOW_LAT;
338c72dfae2SSjur Braendeland 		break;
339c72dfae2SSjur Braendeland 	default:
340c72dfae2SSjur Braendeland 		pref = CFPHYPREF_HIGH_BW;
341c72dfae2SSjur Braendeland 		break;
342c72dfae2SSjur Braendeland 	}
343bd30ce4bSsjur.brandeland@stericsson.com 	mutex_lock(&caifdevs->lock);
344bd30ce4bSsjur.brandeland@stericsson.com 	list_add_rcu(&caifd->list, &caifdevs->list);
345bd30ce4bSsjur.brandeland@stericsson.com 
346df207b00SWolfram Sang 	strscpy(caifd->layer.name, dev->name,
3473dc2fa47SXiongfeng Wang 		sizeof(caifd->layer.name));
3487c18d220Ssjur.brandeland@stericsson.com 	caifd->layer.transmit = transmit;
349a2805dcaSPavel Skripkin 	res = cfcnfg_add_phy_layer(cfg,
350c72dfae2SSjur Braendeland 				dev,
351c72dfae2SSjur Braendeland 				&caifd->layer,
352c72dfae2SSjur Braendeland 				pref,
3537c18d220Ssjur.brandeland@stericsson.com 				link_support,
354c72dfae2SSjur Braendeland 				caifdev->use_fcs,
3557c18d220Ssjur.brandeland@stericsson.com 				head_room);
356bd30ce4bSsjur.brandeland@stericsson.com 	mutex_unlock(&caifdevs->lock);
3577c18d220Ssjur.brandeland@stericsson.com 	if (rcv_func)
3587c18d220Ssjur.brandeland@stericsson.com 		*rcv_func = receive;
359a2805dcaSPavel Skripkin 	return res;
3607c18d220Ssjur.brandeland@stericsson.com }
3617ad65bf6Ssjur.brandeland@stericsson.com EXPORT_SYMBOL(caif_enroll_dev);
3627c18d220Ssjur.brandeland@stericsson.com 
3637c18d220Ssjur.brandeland@stericsson.com /* notify Caif of device events */
caif_device_notify(struct notifier_block * me,unsigned long what,void * ptr)3647c18d220Ssjur.brandeland@stericsson.com static int caif_device_notify(struct notifier_block *me, unsigned long what,
365351638e7SJiri Pirko 			      void *ptr)
3667c18d220Ssjur.brandeland@stericsson.com {
367351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
3687c18d220Ssjur.brandeland@stericsson.com 	struct caif_device_entry *caifd = NULL;
3697c18d220Ssjur.brandeland@stericsson.com 	struct caif_dev_common *caifdev;
3707c18d220Ssjur.brandeland@stericsson.com 	struct cfcnfg *cfg;
3717c18d220Ssjur.brandeland@stericsson.com 	struct cflayer *layer, *link_support;
3727c18d220Ssjur.brandeland@stericsson.com 	int head_room = 0;
3737c18d220Ssjur.brandeland@stericsson.com 	struct caif_device_entry_list *caifdevs;
374b53558a9SPavel Skripkin 	int res;
3757c18d220Ssjur.brandeland@stericsson.com 
3767c18d220Ssjur.brandeland@stericsson.com 	cfg = get_cfcnfg(dev_net(dev));
3777c18d220Ssjur.brandeland@stericsson.com 	caifdevs = caif_device_list(dev_net(dev));
3787c18d220Ssjur.brandeland@stericsson.com 
3797c18d220Ssjur.brandeland@stericsson.com 	caifd = caif_get(dev);
3807c18d220Ssjur.brandeland@stericsson.com 	if (caifd == NULL && dev->type != ARPHRD_CAIF)
3817c18d220Ssjur.brandeland@stericsson.com 		return 0;
3827c18d220Ssjur.brandeland@stericsson.com 
3837c18d220Ssjur.brandeland@stericsson.com 	switch (what) {
3847c18d220Ssjur.brandeland@stericsson.com 	case NETDEV_REGISTER:
3857c18d220Ssjur.brandeland@stericsson.com 		if (caifd != NULL)
3867c18d220Ssjur.brandeland@stericsson.com 			break;
3877c18d220Ssjur.brandeland@stericsson.com 
3887c18d220Ssjur.brandeland@stericsson.com 		caifdev = netdev_priv(dev);
3897c18d220Ssjur.brandeland@stericsson.com 
3907c18d220Ssjur.brandeland@stericsson.com 		link_support = NULL;
3917c18d220Ssjur.brandeland@stericsson.com 		if (caifdev->use_frag) {
3927c18d220Ssjur.brandeland@stericsson.com 			head_room = 1;
3937c18d220Ssjur.brandeland@stericsson.com 			link_support = cfserl_create(dev->ifindex,
394e977b4cfSsjur.brandeland@stericsson.com 							caifdev->use_stx);
3957c18d220Ssjur.brandeland@stericsson.com 			if (!link_support) {
3967c18d220Ssjur.brandeland@stericsson.com 				pr_warn("Out of memory\n");
3977c18d220Ssjur.brandeland@stericsson.com 				break;
3987c18d220Ssjur.brandeland@stericsson.com 			}
3997c18d220Ssjur.brandeland@stericsson.com 		}
400b53558a9SPavel Skripkin 		res = caif_enroll_dev(dev, caifdev, link_support, head_room,
4017c18d220Ssjur.brandeland@stericsson.com 				&layer, NULL);
402b53558a9SPavel Skripkin 		if (res)
403b53558a9SPavel Skripkin 			cfserl_release(link_support);
4047c18d220Ssjur.brandeland@stericsson.com 		caifdev->flowctrl = dev_flowctrl;
405c72dfae2SSjur Braendeland 		break;
406c72dfae2SSjur Braendeland 
407bd30ce4bSsjur.brandeland@stericsson.com 	case NETDEV_UP:
408bd30ce4bSsjur.brandeland@stericsson.com 		rcu_read_lock();
409bd30ce4bSsjur.brandeland@stericsson.com 
410c72dfae2SSjur Braendeland 		caifd = caif_get(dev);
411bd30ce4bSsjur.brandeland@stericsson.com 		if (caifd == NULL) {
412bd30ce4bSsjur.brandeland@stericsson.com 			rcu_read_unlock();
413c72dfae2SSjur Braendeland 			break;
414bd30ce4bSsjur.brandeland@stericsson.com 		}
415c72dfae2SSjur Braendeland 
4168518307dSJason Yan 		caifd->xoff = false;
417bd30ce4bSsjur.brandeland@stericsson.com 		cfcnfg_set_phy_state(cfg, &caifd->layer, true);
418bd30ce4bSsjur.brandeland@stericsson.com 		rcu_read_unlock();
419c72dfae2SSjur Braendeland 
420c72dfae2SSjur Braendeland 		break;
421c72dfae2SSjur Braendeland 
422c72dfae2SSjur Braendeland 	case NETDEV_DOWN:
423bd30ce4bSsjur.brandeland@stericsson.com 		rcu_read_lock();
424bd30ce4bSsjur.brandeland@stericsson.com 
425c72dfae2SSjur Braendeland 		caifd = caif_get(dev);
426bd30ce4bSsjur.brandeland@stericsson.com 		if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) {
427bd30ce4bSsjur.brandeland@stericsson.com 			rcu_read_unlock();
428bd30ce4bSsjur.brandeland@stericsson.com 			return -EINVAL;
429bd30ce4bSsjur.brandeland@stericsson.com 		}
430bd30ce4bSsjur.brandeland@stericsson.com 
431bd30ce4bSsjur.brandeland@stericsson.com 		cfcnfg_set_phy_state(cfg, &caifd->layer, false);
432bd30ce4bSsjur.brandeland@stericsson.com 		caifd_hold(caifd);
433bd30ce4bSsjur.brandeland@stericsson.com 		rcu_read_unlock();
434bd30ce4bSsjur.brandeland@stericsson.com 
435bd30ce4bSsjur.brandeland@stericsson.com 		caifd->layer.up->ctrlcmd(caifd->layer.up,
436bd30ce4bSsjur.brandeland@stericsson.com 					 _CAIF_CTRLCMD_PHYIF_DOWN_IND,
437bd30ce4bSsjur.brandeland@stericsson.com 					 caifd->layer.id);
4387d311304Ssjur.brandeland@stericsson.com 
4397d311304Ssjur.brandeland@stericsson.com 		spin_lock_bh(&caifd->flow_lock);
4407d311304Ssjur.brandeland@stericsson.com 
4417d311304Ssjur.brandeland@stericsson.com 		/*
4427d311304Ssjur.brandeland@stericsson.com 		 * Replace our xoff-destructor with original destructor.
4437d311304Ssjur.brandeland@stericsson.com 		 * We trust that skb->destructor *always* is called before
4447d311304Ssjur.brandeland@stericsson.com 		 * the skb reference is invalid. The hijacked SKB destructor
4457d311304Ssjur.brandeland@stericsson.com 		 * takes the flow_lock so manipulating the skb->destructor here
4467d311304Ssjur.brandeland@stericsson.com 		 * should be safe.
4477d311304Ssjur.brandeland@stericsson.com 		*/
4487d311304Ssjur.brandeland@stericsson.com 		if (caifd->xoff_skb_dtor != NULL && caifd->xoff_skb != NULL)
4497d311304Ssjur.brandeland@stericsson.com 			caifd->xoff_skb->destructor = caifd->xoff_skb_dtor;
4507d311304Ssjur.brandeland@stericsson.com 
4518518307dSJason Yan 		caifd->xoff = false;
4527d311304Ssjur.brandeland@stericsson.com 		caifd->xoff_skb_dtor = NULL;
4537d311304Ssjur.brandeland@stericsson.com 		caifd->xoff_skb = NULL;
4547d311304Ssjur.brandeland@stericsson.com 
4557d311304Ssjur.brandeland@stericsson.com 		spin_unlock_bh(&caifd->flow_lock);
456bd30ce4bSsjur.brandeland@stericsson.com 		caifd_put(caifd);
457c72dfae2SSjur Braendeland 		break;
458c72dfae2SSjur Braendeland 
459c72dfae2SSjur Braendeland 	case NETDEV_UNREGISTER:
460bd30ce4bSsjur.brandeland@stericsson.com 		mutex_lock(&caifdevs->lock);
461bd30ce4bSsjur.brandeland@stericsson.com 
462c72dfae2SSjur Braendeland 		caifd = caif_get(dev);
463bd30ce4bSsjur.brandeland@stericsson.com 		if (caifd == NULL) {
464bd30ce4bSsjur.brandeland@stericsson.com 			mutex_unlock(&caifdevs->lock);
465f2527ec4SAndré Carvalho de Matos 			break;
466bd30ce4bSsjur.brandeland@stericsson.com 		}
467bd30ce4bSsjur.brandeland@stericsson.com 		list_del_rcu(&caifd->list);
468bd30ce4bSsjur.brandeland@stericsson.com 
469bd30ce4bSsjur.brandeland@stericsson.com 		/*
470bd30ce4bSsjur.brandeland@stericsson.com 		 * NETDEV_UNREGISTER is called repeatedly until all reference
471bd30ce4bSsjur.brandeland@stericsson.com 		 * counts for the net-device are released. If references to
472bd30ce4bSsjur.brandeland@stericsson.com 		 * caifd is taken, simply ignore NETDEV_UNREGISTER and wait for
473bd30ce4bSsjur.brandeland@stericsson.com 		 * the next call to NETDEV_UNREGISTER.
474bd30ce4bSsjur.brandeland@stericsson.com 		 *
475bd30ce4bSsjur.brandeland@stericsson.com 		 * If any packets are in flight down the CAIF Stack,
476bd30ce4bSsjur.brandeland@stericsson.com 		 * cfcnfg_del_phy_layer will return nonzero.
477bd30ce4bSsjur.brandeland@stericsson.com 		 * If no packets are in flight, the CAIF Stack associated
478bd30ce4bSsjur.brandeland@stericsson.com 		 * with the net-device un-registering is freed.
479bd30ce4bSsjur.brandeland@stericsson.com 		 */
480bd30ce4bSsjur.brandeland@stericsson.com 
481bd30ce4bSsjur.brandeland@stericsson.com 		if (caifd_refcnt_read(caifd) != 0 ||
482bd30ce4bSsjur.brandeland@stericsson.com 			cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0) {
483bd30ce4bSsjur.brandeland@stericsson.com 
484bd30ce4bSsjur.brandeland@stericsson.com 			pr_info("Wait for device inuse\n");
485bd30ce4bSsjur.brandeland@stericsson.com 			/* Enrole device if CAIF Stack is still in use */
486bd30ce4bSsjur.brandeland@stericsson.com 			list_add_rcu(&caifd->list, &caifdevs->list);
487bd30ce4bSsjur.brandeland@stericsson.com 			mutex_unlock(&caifdevs->lock);
488bd30ce4bSsjur.brandeland@stericsson.com 			break;
489bd30ce4bSsjur.brandeland@stericsson.com 		}
490bd30ce4bSsjur.brandeland@stericsson.com 
491bd30ce4bSsjur.brandeland@stericsson.com 		synchronize_rcu();
492bd30ce4bSsjur.brandeland@stericsson.com 		dev_put(caifd->netdev);
493bd30ce4bSsjur.brandeland@stericsson.com 		free_percpu(caifd->pcpu_refcnt);
494bd30ce4bSsjur.brandeland@stericsson.com 		kfree(caifd);
495bd30ce4bSsjur.brandeland@stericsson.com 
496bd30ce4bSsjur.brandeland@stericsson.com 		mutex_unlock(&caifdevs->lock);
497c72dfae2SSjur Braendeland 		break;
498c72dfae2SSjur Braendeland 	}
499c72dfae2SSjur Braendeland 	return 0;
500c72dfae2SSjur Braendeland }
501c72dfae2SSjur Braendeland 
502c72dfae2SSjur Braendeland static struct notifier_block caif_device_notifier = {
503c72dfae2SSjur Braendeland 	.notifier_call = caif_device_notify,
504c72dfae2SSjur Braendeland 	.priority = 0,
505c72dfae2SSjur Braendeland };
506c72dfae2SSjur Braendeland 
507c72dfae2SSjur Braendeland /* Per-namespace Caif devices handling */
caif_init_net(struct net * net)508c72dfae2SSjur Braendeland static int caif_init_net(struct net *net)
509c72dfae2SSjur Braendeland {
510c72dfae2SSjur Braendeland 	struct caif_net *caifn = net_generic(net, caif_net_id);
511c72dfae2SSjur Braendeland 	INIT_LIST_HEAD(&caifn->caifdevs.list);
512bd30ce4bSsjur.brandeland@stericsson.com 	mutex_init(&caifn->caifdevs.lock);
513bee925dbSsjur.brandeland@stericsson.com 
514bee925dbSsjur.brandeland@stericsson.com 	caifn->cfg = cfcnfg_create();
515f84ea779SRoar Førde 	if (!caifn->cfg)
516bee925dbSsjur.brandeland@stericsson.com 		return -ENOMEM;
517bee925dbSsjur.brandeland@stericsson.com 
518c72dfae2SSjur Braendeland 	return 0;
519c72dfae2SSjur Braendeland }
520c72dfae2SSjur Braendeland 
caif_exit_net(struct net * net)521c72dfae2SSjur Braendeland static void caif_exit_net(struct net *net)
522c72dfae2SSjur Braendeland {
523bd30ce4bSsjur.brandeland@stericsson.com 	struct caif_device_entry *caifd, *tmp;
524bd30ce4bSsjur.brandeland@stericsson.com 	struct caif_device_entry_list *caifdevs =
525bd30ce4bSsjur.brandeland@stericsson.com 	    caif_device_list(net);
5267c18d220Ssjur.brandeland@stericsson.com 	struct cfcnfg *cfg =  get_cfcnfg(net);
5277c18d220Ssjur.brandeland@stericsson.com 
528c72dfae2SSjur Braendeland 	rtnl_lock();
529bd30ce4bSsjur.brandeland@stericsson.com 	mutex_lock(&caifdevs->lock);
530bd30ce4bSsjur.brandeland@stericsson.com 
531bd30ce4bSsjur.brandeland@stericsson.com 	list_for_each_entry_safe(caifd, tmp, &caifdevs->list, list) {
532bd30ce4bSsjur.brandeland@stericsson.com 		int i = 0;
533bd30ce4bSsjur.brandeland@stericsson.com 		list_del_rcu(&caifd->list);
534bd30ce4bSsjur.brandeland@stericsson.com 		cfcnfg_set_phy_state(cfg, &caifd->layer, false);
535bd30ce4bSsjur.brandeland@stericsson.com 
536bd30ce4bSsjur.brandeland@stericsson.com 		while (i < 10 &&
537bd30ce4bSsjur.brandeland@stericsson.com 			(caifd_refcnt_read(caifd) != 0 ||
538bd30ce4bSsjur.brandeland@stericsson.com 			cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0)) {
539bd30ce4bSsjur.brandeland@stericsson.com 
540bd30ce4bSsjur.brandeland@stericsson.com 			pr_info("Wait for device inuse\n");
541bd30ce4bSsjur.brandeland@stericsson.com 			msleep(250);
542bd30ce4bSsjur.brandeland@stericsson.com 			i++;
543c72dfae2SSjur Braendeland 		}
544bd30ce4bSsjur.brandeland@stericsson.com 		synchronize_rcu();
545bd30ce4bSsjur.brandeland@stericsson.com 		dev_put(caifd->netdev);
546bd30ce4bSsjur.brandeland@stericsson.com 		free_percpu(caifd->pcpu_refcnt);
547bd30ce4bSsjur.brandeland@stericsson.com 		kfree(caifd);
548bd30ce4bSsjur.brandeland@stericsson.com 	}
549bee925dbSsjur.brandeland@stericsson.com 	cfcnfg_remove(cfg);
550bd30ce4bSsjur.brandeland@stericsson.com 
551bd30ce4bSsjur.brandeland@stericsson.com 	mutex_unlock(&caifdevs->lock);
552c72dfae2SSjur Braendeland 	rtnl_unlock();
553c72dfae2SSjur Braendeland }
554c72dfae2SSjur Braendeland 
555c72dfae2SSjur Braendeland static struct pernet_operations caif_net_ops = {
556c72dfae2SSjur Braendeland 	.init = caif_init_net,
557c72dfae2SSjur Braendeland 	.exit = caif_exit_net,
558c72dfae2SSjur Braendeland 	.id   = &caif_net_id,
559c72dfae2SSjur Braendeland 	.size = sizeof(struct caif_net),
560c72dfae2SSjur Braendeland };
561c72dfae2SSjur Braendeland 
562c72dfae2SSjur Braendeland /* Initialize Caif devices list */
caif_device_init(void)563c72dfae2SSjur Braendeland static int __init caif_device_init(void)
564c72dfae2SSjur Braendeland {
565c72dfae2SSjur Braendeland 	int result;
566bd30ce4bSsjur.brandeland@stericsson.com 
5678a8ee9afSEric W. Biederman 	result = register_pernet_subsys(&caif_net_ops);
568c72dfae2SSjur Braendeland 
569bee925dbSsjur.brandeland@stericsson.com 	if (result)
570c72dfae2SSjur Braendeland 		return result;
571bee925dbSsjur.brandeland@stericsson.com 
572c72dfae2SSjur Braendeland 	register_netdevice_notifier(&caif_device_notifier);
573bee925dbSsjur.brandeland@stericsson.com 	dev_add_pack(&caif_packet_type);
574c72dfae2SSjur Braendeland 
575c72dfae2SSjur Braendeland 	return result;
576c72dfae2SSjur Braendeland }
577c72dfae2SSjur Braendeland 
caif_device_exit(void)578c72dfae2SSjur Braendeland static void __exit caif_device_exit(void)
579c72dfae2SSjur Braendeland {
580c72dfae2SSjur Braendeland 	unregister_netdevice_notifier(&caif_device_notifier);
581bee925dbSsjur.brandeland@stericsson.com 	dev_remove_pack(&caif_packet_type);
58296f80d12SSjur Brændeland 	unregister_pernet_subsys(&caif_net_ops);
583c72dfae2SSjur Braendeland }
584c72dfae2SSjur Braendeland 
585c72dfae2SSjur Braendeland module_init(caif_device_init);
586c72dfae2SSjur Braendeland module_exit(caif_device_exit);
587