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