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