18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * CAIF Interface registration. 48c2ecf20Sopenharmony_ci * Copyright (C) ST-Ericsson AB 2010 58c2ecf20Sopenharmony_ci * Author: Sjur Brendeland 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Borrowed heavily from file: pn_dev.c. Thanks to Remi Denis-Courmont 88c2ecf20Sopenharmony_ci * and Sakari Ailus <sakari.ailus@nokia.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 158c2ecf20Sopenharmony_ci#include <linux/net.h> 168c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 178c2ecf20Sopenharmony_ci#include <linux/mutex.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 208c2ecf20Sopenharmony_ci#include <net/netns/generic.h> 218c2ecf20Sopenharmony_ci#include <net/net_namespace.h> 228c2ecf20Sopenharmony_ci#include <net/pkt_sched.h> 238c2ecf20Sopenharmony_ci#include <net/caif/caif_device.h> 248c2ecf20Sopenharmony_ci#include <net/caif/caif_layer.h> 258c2ecf20Sopenharmony_ci#include <net/caif/caif_dev.h> 268c2ecf20Sopenharmony_ci#include <net/caif/cfpkt.h> 278c2ecf20Sopenharmony_ci#include <net/caif/cfcnfg.h> 288c2ecf20Sopenharmony_ci#include <net/caif/cfserl.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* Used for local tracking of the CAIF net devices */ 338c2ecf20Sopenharmony_cistruct caif_device_entry { 348c2ecf20Sopenharmony_ci struct cflayer layer; 358c2ecf20Sopenharmony_ci struct list_head list; 368c2ecf20Sopenharmony_ci struct net_device *netdev; 378c2ecf20Sopenharmony_ci int __percpu *pcpu_refcnt; 388c2ecf20Sopenharmony_ci spinlock_t flow_lock; 398c2ecf20Sopenharmony_ci struct sk_buff *xoff_skb; 408c2ecf20Sopenharmony_ci void (*xoff_skb_dtor)(struct sk_buff *skb); 418c2ecf20Sopenharmony_ci bool xoff; 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistruct caif_device_entry_list { 458c2ecf20Sopenharmony_ci struct list_head list; 468c2ecf20Sopenharmony_ci /* Protects simulanous deletes in list */ 478c2ecf20Sopenharmony_ci struct mutex lock; 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistruct caif_net { 518c2ecf20Sopenharmony_ci struct cfcnfg *cfg; 528c2ecf20Sopenharmony_ci struct caif_device_entry_list caifdevs; 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic unsigned int caif_net_id; 568c2ecf20Sopenharmony_cistatic int q_high = 50; /* Percent */ 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistruct cfcnfg *get_cfcnfg(struct net *net) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct caif_net *caifn; 618c2ecf20Sopenharmony_ci caifn = net_generic(net, caif_net_id); 628c2ecf20Sopenharmony_ci return caifn->cfg; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(get_cfcnfg); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic struct caif_device_entry_list *caif_device_list(struct net *net) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct caif_net *caifn; 698c2ecf20Sopenharmony_ci caifn = net_generic(net, caif_net_id); 708c2ecf20Sopenharmony_ci return &caifn->caifdevs; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void caifd_put(struct caif_device_entry *e) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci this_cpu_dec(*e->pcpu_refcnt); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic void caifd_hold(struct caif_device_entry *e) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci this_cpu_inc(*e->pcpu_refcnt); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int caifd_refcnt_read(struct caif_device_entry *e) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci int i, refcnt = 0; 868c2ecf20Sopenharmony_ci for_each_possible_cpu(i) 878c2ecf20Sopenharmony_ci refcnt += *per_cpu_ptr(e->pcpu_refcnt, i); 888c2ecf20Sopenharmony_ci return refcnt; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci/* Allocate new CAIF device. */ 928c2ecf20Sopenharmony_cistatic struct caif_device_entry *caif_device_alloc(struct net_device *dev) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci struct caif_device_entry *caifd; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci caifd = kzalloc(sizeof(*caifd), GFP_KERNEL); 978c2ecf20Sopenharmony_ci if (!caifd) 988c2ecf20Sopenharmony_ci return NULL; 998c2ecf20Sopenharmony_ci caifd->pcpu_refcnt = alloc_percpu(int); 1008c2ecf20Sopenharmony_ci if (!caifd->pcpu_refcnt) { 1018c2ecf20Sopenharmony_ci kfree(caifd); 1028c2ecf20Sopenharmony_ci return NULL; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci caifd->netdev = dev; 1058c2ecf20Sopenharmony_ci dev_hold(dev); 1068c2ecf20Sopenharmony_ci return caifd; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic struct caif_device_entry *caif_get(struct net_device *dev) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct caif_device_entry_list *caifdevs = 1128c2ecf20Sopenharmony_ci caif_device_list(dev_net(dev)); 1138c2ecf20Sopenharmony_ci struct caif_device_entry *caifd; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci list_for_each_entry_rcu(caifd, &caifdevs->list, list, 1168c2ecf20Sopenharmony_ci lockdep_rtnl_is_held()) { 1178c2ecf20Sopenharmony_ci if (caifd->netdev == dev) 1188c2ecf20Sopenharmony_ci return caifd; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci return NULL; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic void caif_flow_cb(struct sk_buff *skb) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct caif_device_entry *caifd; 1268c2ecf20Sopenharmony_ci void (*dtor)(struct sk_buff *skb) = NULL; 1278c2ecf20Sopenharmony_ci bool send_xoff; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci WARN_ON(skb->dev == NULL); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci rcu_read_lock(); 1328c2ecf20Sopenharmony_ci caifd = caif_get(skb->dev); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci WARN_ON(caifd == NULL); 1358c2ecf20Sopenharmony_ci if (!caifd) { 1368c2ecf20Sopenharmony_ci rcu_read_unlock(); 1378c2ecf20Sopenharmony_ci return; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci caifd_hold(caifd); 1418c2ecf20Sopenharmony_ci rcu_read_unlock(); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci spin_lock_bh(&caifd->flow_lock); 1448c2ecf20Sopenharmony_ci send_xoff = caifd->xoff; 1458c2ecf20Sopenharmony_ci caifd->xoff = false; 1468c2ecf20Sopenharmony_ci dtor = caifd->xoff_skb_dtor; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (WARN_ON(caifd->xoff_skb != skb)) 1498c2ecf20Sopenharmony_ci skb = NULL; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci caifd->xoff_skb = NULL; 1528c2ecf20Sopenharmony_ci caifd->xoff_skb_dtor = NULL; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci spin_unlock_bh(&caifd->flow_lock); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (dtor && skb) 1578c2ecf20Sopenharmony_ci dtor(skb); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (send_xoff) 1608c2ecf20Sopenharmony_ci caifd->layer.up-> 1618c2ecf20Sopenharmony_ci ctrlcmd(caifd->layer.up, 1628c2ecf20Sopenharmony_ci _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND, 1638c2ecf20Sopenharmony_ci caifd->layer.id); 1648c2ecf20Sopenharmony_ci caifd_put(caifd); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic int transmit(struct cflayer *layer, struct cfpkt *pkt) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci int err, high = 0, qlen = 0; 1708c2ecf20Sopenharmony_ci struct caif_device_entry *caifd = 1718c2ecf20Sopenharmony_ci container_of(layer, struct caif_device_entry, layer); 1728c2ecf20Sopenharmony_ci struct sk_buff *skb; 1738c2ecf20Sopenharmony_ci struct netdev_queue *txq; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci rcu_read_lock_bh(); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci skb = cfpkt_tonative(pkt); 1788c2ecf20Sopenharmony_ci skb->dev = caifd->netdev; 1798c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 1808c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_CAIF); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* Check if we need to handle xoff */ 1838c2ecf20Sopenharmony_ci if (likely(caifd->netdev->priv_flags & IFF_NO_QUEUE)) 1848c2ecf20Sopenharmony_ci goto noxoff; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (unlikely(caifd->xoff)) 1878c2ecf20Sopenharmony_ci goto noxoff; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (likely(!netif_queue_stopped(caifd->netdev))) { 1908c2ecf20Sopenharmony_ci struct Qdisc *sch; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* If we run with a TX queue, check if the queue is too long*/ 1938c2ecf20Sopenharmony_ci txq = netdev_get_tx_queue(skb->dev, 0); 1948c2ecf20Sopenharmony_ci sch = rcu_dereference_bh(txq->qdisc); 1958c2ecf20Sopenharmony_ci if (likely(qdisc_is_empty(sch))) 1968c2ecf20Sopenharmony_ci goto noxoff; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* can check for explicit qdisc len value only !NOLOCK, 1998c2ecf20Sopenharmony_ci * always set flow off otherwise 2008c2ecf20Sopenharmony_ci */ 2018c2ecf20Sopenharmony_ci high = (caifd->netdev->tx_queue_len * q_high) / 100; 2028c2ecf20Sopenharmony_ci if (!(sch->flags & TCQ_F_NOLOCK) && likely(sch->q.qlen < high)) 2038c2ecf20Sopenharmony_ci goto noxoff; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* Hold lock while accessing xoff */ 2078c2ecf20Sopenharmony_ci spin_lock_bh(&caifd->flow_lock); 2088c2ecf20Sopenharmony_ci if (caifd->xoff) { 2098c2ecf20Sopenharmony_ci spin_unlock_bh(&caifd->flow_lock); 2108c2ecf20Sopenharmony_ci goto noxoff; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* 2148c2ecf20Sopenharmony_ci * Handle flow off, we do this by temporary hi-jacking this 2158c2ecf20Sopenharmony_ci * skb's destructor function, and replace it with our own 2168c2ecf20Sopenharmony_ci * flow-on callback. The callback will set flow-on and call 2178c2ecf20Sopenharmony_ci * the original destructor. 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci pr_debug("queue has stopped(%d) or is full (%d > %d)\n", 2218c2ecf20Sopenharmony_ci netif_queue_stopped(caifd->netdev), 2228c2ecf20Sopenharmony_ci qlen, high); 2238c2ecf20Sopenharmony_ci caifd->xoff = true; 2248c2ecf20Sopenharmony_ci caifd->xoff_skb = skb; 2258c2ecf20Sopenharmony_ci caifd->xoff_skb_dtor = skb->destructor; 2268c2ecf20Sopenharmony_ci skb->destructor = caif_flow_cb; 2278c2ecf20Sopenharmony_ci spin_unlock_bh(&caifd->flow_lock); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci caifd->layer.up->ctrlcmd(caifd->layer.up, 2308c2ecf20Sopenharmony_ci _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND, 2318c2ecf20Sopenharmony_ci caifd->layer.id); 2328c2ecf20Sopenharmony_cinoxoff: 2338c2ecf20Sopenharmony_ci rcu_read_unlock_bh(); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci err = dev_queue_xmit(skb); 2368c2ecf20Sopenharmony_ci if (err > 0) 2378c2ecf20Sopenharmony_ci err = -EIO; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return err; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/* 2438c2ecf20Sopenharmony_ci * Stuff received packets into the CAIF stack. 2448c2ecf20Sopenharmony_ci * On error, returns non-zero and releases the skb. 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_cistatic int receive(struct sk_buff *skb, struct net_device *dev, 2478c2ecf20Sopenharmony_ci struct packet_type *pkttype, struct net_device *orig_dev) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct cfpkt *pkt; 2508c2ecf20Sopenharmony_ci struct caif_device_entry *caifd; 2518c2ecf20Sopenharmony_ci int err; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci pkt = cfpkt_fromnative(CAIF_DIR_IN, skb); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci rcu_read_lock(); 2568c2ecf20Sopenharmony_ci caifd = caif_get(dev); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (!caifd || !caifd->layer.up || !caifd->layer.up->receive || 2598c2ecf20Sopenharmony_ci !netif_oper_up(caifd->netdev)) { 2608c2ecf20Sopenharmony_ci rcu_read_unlock(); 2618c2ecf20Sopenharmony_ci kfree_skb(skb); 2628c2ecf20Sopenharmony_ci return NET_RX_DROP; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci /* Hold reference to netdevice while using CAIF stack */ 2668c2ecf20Sopenharmony_ci caifd_hold(caifd); 2678c2ecf20Sopenharmony_ci rcu_read_unlock(); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci err = caifd->layer.up->receive(caifd->layer.up, pkt); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* For -EILSEQ the packet is not freed so so it now */ 2728c2ecf20Sopenharmony_ci if (err == -EILSEQ) 2738c2ecf20Sopenharmony_ci cfpkt_destroy(pkt); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* Release reference to stack upwards */ 2768c2ecf20Sopenharmony_ci caifd_put(caifd); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (err != 0) 2798c2ecf20Sopenharmony_ci err = NET_RX_DROP; 2808c2ecf20Sopenharmony_ci return err; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic struct packet_type caif_packet_type __read_mostly = { 2848c2ecf20Sopenharmony_ci .type = cpu_to_be16(ETH_P_CAIF), 2858c2ecf20Sopenharmony_ci .func = receive, 2868c2ecf20Sopenharmony_ci}; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic void dev_flowctrl(struct net_device *dev, int on) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci struct caif_device_entry *caifd; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci rcu_read_lock(); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci caifd = caif_get(dev); 2958c2ecf20Sopenharmony_ci if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) { 2968c2ecf20Sopenharmony_ci rcu_read_unlock(); 2978c2ecf20Sopenharmony_ci return; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci caifd_hold(caifd); 3018c2ecf20Sopenharmony_ci rcu_read_unlock(); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci caifd->layer.up->ctrlcmd(caifd->layer.up, 3048c2ecf20Sopenharmony_ci on ? 3058c2ecf20Sopenharmony_ci _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND : 3068c2ecf20Sopenharmony_ci _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND, 3078c2ecf20Sopenharmony_ci caifd->layer.id); 3088c2ecf20Sopenharmony_ci caifd_put(caifd); 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ciint caif_enroll_dev(struct net_device *dev, struct caif_dev_common *caifdev, 3128c2ecf20Sopenharmony_ci struct cflayer *link_support, int head_room, 3138c2ecf20Sopenharmony_ci struct cflayer **layer, 3148c2ecf20Sopenharmony_ci int (**rcv_func)(struct sk_buff *, struct net_device *, 3158c2ecf20Sopenharmony_ci struct packet_type *, 3168c2ecf20Sopenharmony_ci struct net_device *)) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct caif_device_entry *caifd; 3198c2ecf20Sopenharmony_ci enum cfcnfg_phy_preference pref; 3208c2ecf20Sopenharmony_ci struct cfcnfg *cfg = get_cfcnfg(dev_net(dev)); 3218c2ecf20Sopenharmony_ci struct caif_device_entry_list *caifdevs; 3228c2ecf20Sopenharmony_ci int res; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci caifdevs = caif_device_list(dev_net(dev)); 3258c2ecf20Sopenharmony_ci caifd = caif_device_alloc(dev); 3268c2ecf20Sopenharmony_ci if (!caifd) 3278c2ecf20Sopenharmony_ci return -ENOMEM; 3288c2ecf20Sopenharmony_ci *layer = &caifd->layer; 3298c2ecf20Sopenharmony_ci spin_lock_init(&caifd->flow_lock); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci switch (caifdev->link_select) { 3328c2ecf20Sopenharmony_ci case CAIF_LINK_HIGH_BANDW: 3338c2ecf20Sopenharmony_ci pref = CFPHYPREF_HIGH_BW; 3348c2ecf20Sopenharmony_ci break; 3358c2ecf20Sopenharmony_ci case CAIF_LINK_LOW_LATENCY: 3368c2ecf20Sopenharmony_ci pref = CFPHYPREF_LOW_LAT; 3378c2ecf20Sopenharmony_ci break; 3388c2ecf20Sopenharmony_ci default: 3398c2ecf20Sopenharmony_ci pref = CFPHYPREF_HIGH_BW; 3408c2ecf20Sopenharmony_ci break; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci mutex_lock(&caifdevs->lock); 3438c2ecf20Sopenharmony_ci list_add_rcu(&caifd->list, &caifdevs->list); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci strlcpy(caifd->layer.name, dev->name, 3468c2ecf20Sopenharmony_ci sizeof(caifd->layer.name)); 3478c2ecf20Sopenharmony_ci caifd->layer.transmit = transmit; 3488c2ecf20Sopenharmony_ci res = cfcnfg_add_phy_layer(cfg, 3498c2ecf20Sopenharmony_ci dev, 3508c2ecf20Sopenharmony_ci &caifd->layer, 3518c2ecf20Sopenharmony_ci pref, 3528c2ecf20Sopenharmony_ci link_support, 3538c2ecf20Sopenharmony_ci caifdev->use_fcs, 3548c2ecf20Sopenharmony_ci head_room); 3558c2ecf20Sopenharmony_ci mutex_unlock(&caifdevs->lock); 3568c2ecf20Sopenharmony_ci if (rcv_func) 3578c2ecf20Sopenharmony_ci *rcv_func = receive; 3588c2ecf20Sopenharmony_ci return res; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(caif_enroll_dev); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci/* notify Caif of device events */ 3638c2ecf20Sopenharmony_cistatic int caif_device_notify(struct notifier_block *me, unsigned long what, 3648c2ecf20Sopenharmony_ci void *ptr) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 3678c2ecf20Sopenharmony_ci struct caif_device_entry *caifd = NULL; 3688c2ecf20Sopenharmony_ci struct caif_dev_common *caifdev; 3698c2ecf20Sopenharmony_ci struct cfcnfg *cfg; 3708c2ecf20Sopenharmony_ci struct cflayer *layer, *link_support; 3718c2ecf20Sopenharmony_ci int head_room = 0; 3728c2ecf20Sopenharmony_ci struct caif_device_entry_list *caifdevs; 3738c2ecf20Sopenharmony_ci int res; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci cfg = get_cfcnfg(dev_net(dev)); 3768c2ecf20Sopenharmony_ci caifdevs = caif_device_list(dev_net(dev)); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci caifd = caif_get(dev); 3798c2ecf20Sopenharmony_ci if (caifd == NULL && dev->type != ARPHRD_CAIF) 3808c2ecf20Sopenharmony_ci return 0; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci switch (what) { 3838c2ecf20Sopenharmony_ci case NETDEV_REGISTER: 3848c2ecf20Sopenharmony_ci if (caifd != NULL) 3858c2ecf20Sopenharmony_ci break; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci caifdev = netdev_priv(dev); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci link_support = NULL; 3908c2ecf20Sopenharmony_ci if (caifdev->use_frag) { 3918c2ecf20Sopenharmony_ci head_room = 1; 3928c2ecf20Sopenharmony_ci link_support = cfserl_create(dev->ifindex, 3938c2ecf20Sopenharmony_ci caifdev->use_stx); 3948c2ecf20Sopenharmony_ci if (!link_support) { 3958c2ecf20Sopenharmony_ci pr_warn("Out of memory\n"); 3968c2ecf20Sopenharmony_ci break; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci res = caif_enroll_dev(dev, caifdev, link_support, head_room, 4008c2ecf20Sopenharmony_ci &layer, NULL); 4018c2ecf20Sopenharmony_ci if (res) 4028c2ecf20Sopenharmony_ci cfserl_release(link_support); 4038c2ecf20Sopenharmony_ci caifdev->flowctrl = dev_flowctrl; 4048c2ecf20Sopenharmony_ci break; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci case NETDEV_UP: 4078c2ecf20Sopenharmony_ci rcu_read_lock(); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci caifd = caif_get(dev); 4108c2ecf20Sopenharmony_ci if (caifd == NULL) { 4118c2ecf20Sopenharmony_ci rcu_read_unlock(); 4128c2ecf20Sopenharmony_ci break; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci caifd->xoff = false; 4168c2ecf20Sopenharmony_ci cfcnfg_set_phy_state(cfg, &caifd->layer, true); 4178c2ecf20Sopenharmony_ci rcu_read_unlock(); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci break; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci case NETDEV_DOWN: 4228c2ecf20Sopenharmony_ci rcu_read_lock(); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci caifd = caif_get(dev); 4258c2ecf20Sopenharmony_ci if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) { 4268c2ecf20Sopenharmony_ci rcu_read_unlock(); 4278c2ecf20Sopenharmony_ci return -EINVAL; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci cfcnfg_set_phy_state(cfg, &caifd->layer, false); 4318c2ecf20Sopenharmony_ci caifd_hold(caifd); 4328c2ecf20Sopenharmony_ci rcu_read_unlock(); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci caifd->layer.up->ctrlcmd(caifd->layer.up, 4358c2ecf20Sopenharmony_ci _CAIF_CTRLCMD_PHYIF_DOWN_IND, 4368c2ecf20Sopenharmony_ci caifd->layer.id); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci spin_lock_bh(&caifd->flow_lock); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* 4418c2ecf20Sopenharmony_ci * Replace our xoff-destructor with original destructor. 4428c2ecf20Sopenharmony_ci * We trust that skb->destructor *always* is called before 4438c2ecf20Sopenharmony_ci * the skb reference is invalid. The hijacked SKB destructor 4448c2ecf20Sopenharmony_ci * takes the flow_lock so manipulating the skb->destructor here 4458c2ecf20Sopenharmony_ci * should be safe. 4468c2ecf20Sopenharmony_ci */ 4478c2ecf20Sopenharmony_ci if (caifd->xoff_skb_dtor != NULL && caifd->xoff_skb != NULL) 4488c2ecf20Sopenharmony_ci caifd->xoff_skb->destructor = caifd->xoff_skb_dtor; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci caifd->xoff = false; 4518c2ecf20Sopenharmony_ci caifd->xoff_skb_dtor = NULL; 4528c2ecf20Sopenharmony_ci caifd->xoff_skb = NULL; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci spin_unlock_bh(&caifd->flow_lock); 4558c2ecf20Sopenharmony_ci caifd_put(caifd); 4568c2ecf20Sopenharmony_ci break; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci case NETDEV_UNREGISTER: 4598c2ecf20Sopenharmony_ci mutex_lock(&caifdevs->lock); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci caifd = caif_get(dev); 4628c2ecf20Sopenharmony_ci if (caifd == NULL) { 4638c2ecf20Sopenharmony_ci mutex_unlock(&caifdevs->lock); 4648c2ecf20Sopenharmony_ci break; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci list_del_rcu(&caifd->list); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* 4698c2ecf20Sopenharmony_ci * NETDEV_UNREGISTER is called repeatedly until all reference 4708c2ecf20Sopenharmony_ci * counts for the net-device are released. If references to 4718c2ecf20Sopenharmony_ci * caifd is taken, simply ignore NETDEV_UNREGISTER and wait for 4728c2ecf20Sopenharmony_ci * the next call to NETDEV_UNREGISTER. 4738c2ecf20Sopenharmony_ci * 4748c2ecf20Sopenharmony_ci * If any packets are in flight down the CAIF Stack, 4758c2ecf20Sopenharmony_ci * cfcnfg_del_phy_layer will return nonzero. 4768c2ecf20Sopenharmony_ci * If no packets are in flight, the CAIF Stack associated 4778c2ecf20Sopenharmony_ci * with the net-device un-registering is freed. 4788c2ecf20Sopenharmony_ci */ 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (caifd_refcnt_read(caifd) != 0 || 4818c2ecf20Sopenharmony_ci cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0) { 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci pr_info("Wait for device inuse\n"); 4848c2ecf20Sopenharmony_ci /* Enrole device if CAIF Stack is still in use */ 4858c2ecf20Sopenharmony_ci list_add_rcu(&caifd->list, &caifdevs->list); 4868c2ecf20Sopenharmony_ci mutex_unlock(&caifdevs->lock); 4878c2ecf20Sopenharmony_ci break; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci synchronize_rcu(); 4918c2ecf20Sopenharmony_ci dev_put(caifd->netdev); 4928c2ecf20Sopenharmony_ci free_percpu(caifd->pcpu_refcnt); 4938c2ecf20Sopenharmony_ci kfree(caifd); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci mutex_unlock(&caifdevs->lock); 4968c2ecf20Sopenharmony_ci break; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci return 0; 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cistatic struct notifier_block caif_device_notifier = { 5028c2ecf20Sopenharmony_ci .notifier_call = caif_device_notify, 5038c2ecf20Sopenharmony_ci .priority = 0, 5048c2ecf20Sopenharmony_ci}; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci/* Per-namespace Caif devices handling */ 5078c2ecf20Sopenharmony_cistatic int caif_init_net(struct net *net) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct caif_net *caifn = net_generic(net, caif_net_id); 5108c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&caifn->caifdevs.list); 5118c2ecf20Sopenharmony_ci mutex_init(&caifn->caifdevs.lock); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci caifn->cfg = cfcnfg_create(); 5148c2ecf20Sopenharmony_ci if (!caifn->cfg) 5158c2ecf20Sopenharmony_ci return -ENOMEM; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci return 0; 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic void caif_exit_net(struct net *net) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci struct caif_device_entry *caifd, *tmp; 5238c2ecf20Sopenharmony_ci struct caif_device_entry_list *caifdevs = 5248c2ecf20Sopenharmony_ci caif_device_list(net); 5258c2ecf20Sopenharmony_ci struct cfcnfg *cfg = get_cfcnfg(net); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci rtnl_lock(); 5288c2ecf20Sopenharmony_ci mutex_lock(&caifdevs->lock); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci list_for_each_entry_safe(caifd, tmp, &caifdevs->list, list) { 5318c2ecf20Sopenharmony_ci int i = 0; 5328c2ecf20Sopenharmony_ci list_del_rcu(&caifd->list); 5338c2ecf20Sopenharmony_ci cfcnfg_set_phy_state(cfg, &caifd->layer, false); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci while (i < 10 && 5368c2ecf20Sopenharmony_ci (caifd_refcnt_read(caifd) != 0 || 5378c2ecf20Sopenharmony_ci cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0)) { 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci pr_info("Wait for device inuse\n"); 5408c2ecf20Sopenharmony_ci msleep(250); 5418c2ecf20Sopenharmony_ci i++; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci synchronize_rcu(); 5448c2ecf20Sopenharmony_ci dev_put(caifd->netdev); 5458c2ecf20Sopenharmony_ci free_percpu(caifd->pcpu_refcnt); 5468c2ecf20Sopenharmony_ci kfree(caifd); 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci cfcnfg_remove(cfg); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci mutex_unlock(&caifdevs->lock); 5518c2ecf20Sopenharmony_ci rtnl_unlock(); 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistatic struct pernet_operations caif_net_ops = { 5558c2ecf20Sopenharmony_ci .init = caif_init_net, 5568c2ecf20Sopenharmony_ci .exit = caif_exit_net, 5578c2ecf20Sopenharmony_ci .id = &caif_net_id, 5588c2ecf20Sopenharmony_ci .size = sizeof(struct caif_net), 5598c2ecf20Sopenharmony_ci}; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci/* Initialize Caif devices list */ 5628c2ecf20Sopenharmony_cistatic int __init caif_device_init(void) 5638c2ecf20Sopenharmony_ci{ 5648c2ecf20Sopenharmony_ci int result; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci result = register_pernet_subsys(&caif_net_ops); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if (result) 5698c2ecf20Sopenharmony_ci return result; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci register_netdevice_notifier(&caif_device_notifier); 5728c2ecf20Sopenharmony_ci dev_add_pack(&caif_packet_type); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci return result; 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cistatic void __exit caif_device_exit(void) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&caif_device_notifier); 5808c2ecf20Sopenharmony_ci dev_remove_pack(&caif_packet_type); 5818c2ecf20Sopenharmony_ci unregister_pernet_subsys(&caif_net_ops); 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_cimodule_init(caif_device_init); 5858c2ecf20Sopenharmony_cimodule_exit(caif_device_exit); 586