162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * CAIF Interface registration. 462306a36Sopenharmony_ci * Copyright (C) ST-Ericsson AB 2010 562306a36Sopenharmony_ci * Author: Sjur Brendeland 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Borrowed heavily from file: pn_dev.c. Thanks to Remi Denis-Courmont 862306a36Sopenharmony_ci * and Sakari Ailus <sakari.ailus@nokia.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/if_arp.h> 1562306a36Sopenharmony_ci#include <linux/net.h> 1662306a36Sopenharmony_ci#include <linux/netdevice.h> 1762306a36Sopenharmony_ci#include <linux/mutex.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/spinlock.h> 2062306a36Sopenharmony_ci#include <net/netns/generic.h> 2162306a36Sopenharmony_ci#include <net/net_namespace.h> 2262306a36Sopenharmony_ci#include <net/pkt_sched.h> 2362306a36Sopenharmony_ci#include <net/caif/caif_device.h> 2462306a36Sopenharmony_ci#include <net/caif/caif_layer.h> 2562306a36Sopenharmony_ci#include <net/caif/caif_dev.h> 2662306a36Sopenharmony_ci#include <net/caif/cfpkt.h> 2762306a36Sopenharmony_ci#include <net/caif/cfcnfg.h> 2862306a36Sopenharmony_ci#include <net/caif/cfserl.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* Used for local tracking of the CAIF net devices */ 3362306a36Sopenharmony_cistruct caif_device_entry { 3462306a36Sopenharmony_ci struct cflayer layer; 3562306a36Sopenharmony_ci struct list_head list; 3662306a36Sopenharmony_ci struct net_device *netdev; 3762306a36Sopenharmony_ci int __percpu *pcpu_refcnt; 3862306a36Sopenharmony_ci spinlock_t flow_lock; 3962306a36Sopenharmony_ci struct sk_buff *xoff_skb; 4062306a36Sopenharmony_ci void (*xoff_skb_dtor)(struct sk_buff *skb); 4162306a36Sopenharmony_ci bool xoff; 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistruct caif_device_entry_list { 4562306a36Sopenharmony_ci struct list_head list; 4662306a36Sopenharmony_ci /* Protects simulanous deletes in list */ 4762306a36Sopenharmony_ci struct mutex lock; 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistruct caif_net { 5162306a36Sopenharmony_ci struct cfcnfg *cfg; 5262306a36Sopenharmony_ci struct caif_device_entry_list caifdevs; 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic unsigned int caif_net_id; 5662306a36Sopenharmony_cistatic int q_high = 50; /* Percent */ 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistruct cfcnfg *get_cfcnfg(struct net *net) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct caif_net *caifn; 6162306a36Sopenharmony_ci caifn = net_generic(net, caif_net_id); 6262306a36Sopenharmony_ci return caifn->cfg; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ciEXPORT_SYMBOL(get_cfcnfg); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic struct caif_device_entry_list *caif_device_list(struct net *net) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct caif_net *caifn; 6962306a36Sopenharmony_ci caifn = net_generic(net, caif_net_id); 7062306a36Sopenharmony_ci return &caifn->caifdevs; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic void caifd_put(struct caif_device_entry *e) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci this_cpu_dec(*e->pcpu_refcnt); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic void caifd_hold(struct caif_device_entry *e) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci this_cpu_inc(*e->pcpu_refcnt); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic int caifd_refcnt_read(struct caif_device_entry *e) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci int i, refcnt = 0; 8662306a36Sopenharmony_ci for_each_possible_cpu(i) 8762306a36Sopenharmony_ci refcnt += *per_cpu_ptr(e->pcpu_refcnt, i); 8862306a36Sopenharmony_ci return refcnt; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* Allocate new CAIF device. */ 9262306a36Sopenharmony_cistatic struct caif_device_entry *caif_device_alloc(struct net_device *dev) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci struct caif_device_entry *caifd; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci caifd = kzalloc(sizeof(*caifd), GFP_KERNEL); 9762306a36Sopenharmony_ci if (!caifd) 9862306a36Sopenharmony_ci return NULL; 9962306a36Sopenharmony_ci caifd->pcpu_refcnt = alloc_percpu(int); 10062306a36Sopenharmony_ci if (!caifd->pcpu_refcnt) { 10162306a36Sopenharmony_ci kfree(caifd); 10262306a36Sopenharmony_ci return NULL; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci caifd->netdev = dev; 10562306a36Sopenharmony_ci dev_hold(dev); 10662306a36Sopenharmony_ci return caifd; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic struct caif_device_entry *caif_get(struct net_device *dev) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci struct caif_device_entry_list *caifdevs = 11262306a36Sopenharmony_ci caif_device_list(dev_net(dev)); 11362306a36Sopenharmony_ci struct caif_device_entry *caifd; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci list_for_each_entry_rcu(caifd, &caifdevs->list, list, 11662306a36Sopenharmony_ci lockdep_rtnl_is_held()) { 11762306a36Sopenharmony_ci if (caifd->netdev == dev) 11862306a36Sopenharmony_ci return caifd; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci return NULL; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic void caif_flow_cb(struct sk_buff *skb) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct caif_device_entry *caifd; 12662306a36Sopenharmony_ci void (*dtor)(struct sk_buff *skb) = NULL; 12762306a36Sopenharmony_ci bool send_xoff; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci WARN_ON(skb->dev == NULL); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci rcu_read_lock(); 13262306a36Sopenharmony_ci caifd = caif_get(skb->dev); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci WARN_ON(caifd == NULL); 13562306a36Sopenharmony_ci if (!caifd) { 13662306a36Sopenharmony_ci rcu_read_unlock(); 13762306a36Sopenharmony_ci return; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci caifd_hold(caifd); 14162306a36Sopenharmony_ci rcu_read_unlock(); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci spin_lock_bh(&caifd->flow_lock); 14462306a36Sopenharmony_ci send_xoff = caifd->xoff; 14562306a36Sopenharmony_ci caifd->xoff = false; 14662306a36Sopenharmony_ci dtor = caifd->xoff_skb_dtor; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (WARN_ON(caifd->xoff_skb != skb)) 14962306a36Sopenharmony_ci skb = NULL; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci caifd->xoff_skb = NULL; 15262306a36Sopenharmony_ci caifd->xoff_skb_dtor = NULL; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci spin_unlock_bh(&caifd->flow_lock); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (dtor && skb) 15762306a36Sopenharmony_ci dtor(skb); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (send_xoff) 16062306a36Sopenharmony_ci caifd->layer.up-> 16162306a36Sopenharmony_ci ctrlcmd(caifd->layer.up, 16262306a36Sopenharmony_ci _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND, 16362306a36Sopenharmony_ci caifd->layer.id); 16462306a36Sopenharmony_ci caifd_put(caifd); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic int transmit(struct cflayer *layer, struct cfpkt *pkt) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci int err, high = 0, qlen = 0; 17062306a36Sopenharmony_ci struct caif_device_entry *caifd = 17162306a36Sopenharmony_ci container_of(layer, struct caif_device_entry, layer); 17262306a36Sopenharmony_ci struct sk_buff *skb; 17362306a36Sopenharmony_ci struct netdev_queue *txq; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci rcu_read_lock_bh(); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci skb = cfpkt_tonative(pkt); 17862306a36Sopenharmony_ci skb->dev = caifd->netdev; 17962306a36Sopenharmony_ci skb_reset_network_header(skb); 18062306a36Sopenharmony_ci skb->protocol = htons(ETH_P_CAIF); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* Check if we need to handle xoff */ 18362306a36Sopenharmony_ci if (likely(caifd->netdev->priv_flags & IFF_NO_QUEUE)) 18462306a36Sopenharmony_ci goto noxoff; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (unlikely(caifd->xoff)) 18762306a36Sopenharmony_ci goto noxoff; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (likely(!netif_queue_stopped(caifd->netdev))) { 19062306a36Sopenharmony_ci struct Qdisc *sch; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* If we run with a TX queue, check if the queue is too long*/ 19362306a36Sopenharmony_ci txq = netdev_get_tx_queue(skb->dev, 0); 19462306a36Sopenharmony_ci sch = rcu_dereference_bh(txq->qdisc); 19562306a36Sopenharmony_ci if (likely(qdisc_is_empty(sch))) 19662306a36Sopenharmony_ci goto noxoff; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* can check for explicit qdisc len value only !NOLOCK, 19962306a36Sopenharmony_ci * always set flow off otherwise 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_ci high = (caifd->netdev->tx_queue_len * q_high) / 100; 20262306a36Sopenharmony_ci if (!(sch->flags & TCQ_F_NOLOCK) && likely(sch->q.qlen < high)) 20362306a36Sopenharmony_ci goto noxoff; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* Hold lock while accessing xoff */ 20762306a36Sopenharmony_ci spin_lock_bh(&caifd->flow_lock); 20862306a36Sopenharmony_ci if (caifd->xoff) { 20962306a36Sopenharmony_ci spin_unlock_bh(&caifd->flow_lock); 21062306a36Sopenharmony_ci goto noxoff; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* 21462306a36Sopenharmony_ci * Handle flow off, we do this by temporary hi-jacking this 21562306a36Sopenharmony_ci * skb's destructor function, and replace it with our own 21662306a36Sopenharmony_ci * flow-on callback. The callback will set flow-on and call 21762306a36Sopenharmony_ci * the original destructor. 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci pr_debug("queue has stopped(%d) or is full (%d > %d)\n", 22162306a36Sopenharmony_ci netif_queue_stopped(caifd->netdev), 22262306a36Sopenharmony_ci qlen, high); 22362306a36Sopenharmony_ci caifd->xoff = true; 22462306a36Sopenharmony_ci caifd->xoff_skb = skb; 22562306a36Sopenharmony_ci caifd->xoff_skb_dtor = skb->destructor; 22662306a36Sopenharmony_ci skb->destructor = caif_flow_cb; 22762306a36Sopenharmony_ci spin_unlock_bh(&caifd->flow_lock); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci caifd->layer.up->ctrlcmd(caifd->layer.up, 23062306a36Sopenharmony_ci _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND, 23162306a36Sopenharmony_ci caifd->layer.id); 23262306a36Sopenharmony_cinoxoff: 23362306a36Sopenharmony_ci rcu_read_unlock_bh(); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci err = dev_queue_xmit(skb); 23662306a36Sopenharmony_ci if (err > 0) 23762306a36Sopenharmony_ci err = -EIO; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return err; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci/* 24362306a36Sopenharmony_ci * Stuff received packets into the CAIF stack. 24462306a36Sopenharmony_ci * On error, returns non-zero and releases the skb. 24562306a36Sopenharmony_ci */ 24662306a36Sopenharmony_cistatic int receive(struct sk_buff *skb, struct net_device *dev, 24762306a36Sopenharmony_ci struct packet_type *pkttype, struct net_device *orig_dev) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci struct cfpkt *pkt; 25062306a36Sopenharmony_ci struct caif_device_entry *caifd; 25162306a36Sopenharmony_ci int err; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci pkt = cfpkt_fromnative(CAIF_DIR_IN, skb); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci rcu_read_lock(); 25662306a36Sopenharmony_ci caifd = caif_get(dev); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (!caifd || !caifd->layer.up || !caifd->layer.up->receive || 25962306a36Sopenharmony_ci !netif_oper_up(caifd->netdev)) { 26062306a36Sopenharmony_ci rcu_read_unlock(); 26162306a36Sopenharmony_ci kfree_skb(skb); 26262306a36Sopenharmony_ci return NET_RX_DROP; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* Hold reference to netdevice while using CAIF stack */ 26662306a36Sopenharmony_ci caifd_hold(caifd); 26762306a36Sopenharmony_ci rcu_read_unlock(); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci err = caifd->layer.up->receive(caifd->layer.up, pkt); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* For -EILSEQ the packet is not freed so free it now */ 27262306a36Sopenharmony_ci if (err == -EILSEQ) 27362306a36Sopenharmony_ci cfpkt_destroy(pkt); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* Release reference to stack upwards */ 27662306a36Sopenharmony_ci caifd_put(caifd); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (err != 0) 27962306a36Sopenharmony_ci err = NET_RX_DROP; 28062306a36Sopenharmony_ci return err; 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic struct packet_type caif_packet_type __read_mostly = { 28462306a36Sopenharmony_ci .type = cpu_to_be16(ETH_P_CAIF), 28562306a36Sopenharmony_ci .func = receive, 28662306a36Sopenharmony_ci}; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic void dev_flowctrl(struct net_device *dev, int on) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct caif_device_entry *caifd; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci rcu_read_lock(); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci caifd = caif_get(dev); 29562306a36Sopenharmony_ci if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) { 29662306a36Sopenharmony_ci rcu_read_unlock(); 29762306a36Sopenharmony_ci return; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci caifd_hold(caifd); 30162306a36Sopenharmony_ci rcu_read_unlock(); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci caifd->layer.up->ctrlcmd(caifd->layer.up, 30462306a36Sopenharmony_ci on ? 30562306a36Sopenharmony_ci _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND : 30662306a36Sopenharmony_ci _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND, 30762306a36Sopenharmony_ci caifd->layer.id); 30862306a36Sopenharmony_ci caifd_put(caifd); 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ciint caif_enroll_dev(struct net_device *dev, struct caif_dev_common *caifdev, 31262306a36Sopenharmony_ci struct cflayer *link_support, int head_room, 31362306a36Sopenharmony_ci struct cflayer **layer, 31462306a36Sopenharmony_ci int (**rcv_func)(struct sk_buff *, struct net_device *, 31562306a36Sopenharmony_ci struct packet_type *, 31662306a36Sopenharmony_ci struct net_device *)) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci struct caif_device_entry *caifd; 31962306a36Sopenharmony_ci enum cfcnfg_phy_preference pref; 32062306a36Sopenharmony_ci struct cfcnfg *cfg = get_cfcnfg(dev_net(dev)); 32162306a36Sopenharmony_ci struct caif_device_entry_list *caifdevs; 32262306a36Sopenharmony_ci int res; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci caifdevs = caif_device_list(dev_net(dev)); 32562306a36Sopenharmony_ci caifd = caif_device_alloc(dev); 32662306a36Sopenharmony_ci if (!caifd) 32762306a36Sopenharmony_ci return -ENOMEM; 32862306a36Sopenharmony_ci *layer = &caifd->layer; 32962306a36Sopenharmony_ci spin_lock_init(&caifd->flow_lock); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci switch (caifdev->link_select) { 33262306a36Sopenharmony_ci case CAIF_LINK_HIGH_BANDW: 33362306a36Sopenharmony_ci pref = CFPHYPREF_HIGH_BW; 33462306a36Sopenharmony_ci break; 33562306a36Sopenharmony_ci case CAIF_LINK_LOW_LATENCY: 33662306a36Sopenharmony_ci pref = CFPHYPREF_LOW_LAT; 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci default: 33962306a36Sopenharmony_ci pref = CFPHYPREF_HIGH_BW; 34062306a36Sopenharmony_ci break; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci mutex_lock(&caifdevs->lock); 34362306a36Sopenharmony_ci list_add_rcu(&caifd->list, &caifdevs->list); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci strscpy(caifd->layer.name, dev->name, 34662306a36Sopenharmony_ci sizeof(caifd->layer.name)); 34762306a36Sopenharmony_ci caifd->layer.transmit = transmit; 34862306a36Sopenharmony_ci res = cfcnfg_add_phy_layer(cfg, 34962306a36Sopenharmony_ci dev, 35062306a36Sopenharmony_ci &caifd->layer, 35162306a36Sopenharmony_ci pref, 35262306a36Sopenharmony_ci link_support, 35362306a36Sopenharmony_ci caifdev->use_fcs, 35462306a36Sopenharmony_ci head_room); 35562306a36Sopenharmony_ci mutex_unlock(&caifdevs->lock); 35662306a36Sopenharmony_ci if (rcv_func) 35762306a36Sopenharmony_ci *rcv_func = receive; 35862306a36Sopenharmony_ci return res; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ciEXPORT_SYMBOL(caif_enroll_dev); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci/* notify Caif of device events */ 36362306a36Sopenharmony_cistatic int caif_device_notify(struct notifier_block *me, unsigned long what, 36462306a36Sopenharmony_ci void *ptr) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 36762306a36Sopenharmony_ci struct caif_device_entry *caifd = NULL; 36862306a36Sopenharmony_ci struct caif_dev_common *caifdev; 36962306a36Sopenharmony_ci struct cfcnfg *cfg; 37062306a36Sopenharmony_ci struct cflayer *layer, *link_support; 37162306a36Sopenharmony_ci int head_room = 0; 37262306a36Sopenharmony_ci struct caif_device_entry_list *caifdevs; 37362306a36Sopenharmony_ci int res; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci cfg = get_cfcnfg(dev_net(dev)); 37662306a36Sopenharmony_ci caifdevs = caif_device_list(dev_net(dev)); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci caifd = caif_get(dev); 37962306a36Sopenharmony_ci if (caifd == NULL && dev->type != ARPHRD_CAIF) 38062306a36Sopenharmony_ci return 0; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci switch (what) { 38362306a36Sopenharmony_ci case NETDEV_REGISTER: 38462306a36Sopenharmony_ci if (caifd != NULL) 38562306a36Sopenharmony_ci break; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci caifdev = netdev_priv(dev); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci link_support = NULL; 39062306a36Sopenharmony_ci if (caifdev->use_frag) { 39162306a36Sopenharmony_ci head_room = 1; 39262306a36Sopenharmony_ci link_support = cfserl_create(dev->ifindex, 39362306a36Sopenharmony_ci caifdev->use_stx); 39462306a36Sopenharmony_ci if (!link_support) { 39562306a36Sopenharmony_ci pr_warn("Out of memory\n"); 39662306a36Sopenharmony_ci break; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci res = caif_enroll_dev(dev, caifdev, link_support, head_room, 40062306a36Sopenharmony_ci &layer, NULL); 40162306a36Sopenharmony_ci if (res) 40262306a36Sopenharmony_ci cfserl_release(link_support); 40362306a36Sopenharmony_ci caifdev->flowctrl = dev_flowctrl; 40462306a36Sopenharmony_ci break; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci case NETDEV_UP: 40762306a36Sopenharmony_ci rcu_read_lock(); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci caifd = caif_get(dev); 41062306a36Sopenharmony_ci if (caifd == NULL) { 41162306a36Sopenharmony_ci rcu_read_unlock(); 41262306a36Sopenharmony_ci break; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci caifd->xoff = false; 41662306a36Sopenharmony_ci cfcnfg_set_phy_state(cfg, &caifd->layer, true); 41762306a36Sopenharmony_ci rcu_read_unlock(); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci break; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci case NETDEV_DOWN: 42262306a36Sopenharmony_ci rcu_read_lock(); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci caifd = caif_get(dev); 42562306a36Sopenharmony_ci if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) { 42662306a36Sopenharmony_ci rcu_read_unlock(); 42762306a36Sopenharmony_ci return -EINVAL; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci cfcnfg_set_phy_state(cfg, &caifd->layer, false); 43162306a36Sopenharmony_ci caifd_hold(caifd); 43262306a36Sopenharmony_ci rcu_read_unlock(); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci caifd->layer.up->ctrlcmd(caifd->layer.up, 43562306a36Sopenharmony_ci _CAIF_CTRLCMD_PHYIF_DOWN_IND, 43662306a36Sopenharmony_ci caifd->layer.id); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci spin_lock_bh(&caifd->flow_lock); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* 44162306a36Sopenharmony_ci * Replace our xoff-destructor with original destructor. 44262306a36Sopenharmony_ci * We trust that skb->destructor *always* is called before 44362306a36Sopenharmony_ci * the skb reference is invalid. The hijacked SKB destructor 44462306a36Sopenharmony_ci * takes the flow_lock so manipulating the skb->destructor here 44562306a36Sopenharmony_ci * should be safe. 44662306a36Sopenharmony_ci */ 44762306a36Sopenharmony_ci if (caifd->xoff_skb_dtor != NULL && caifd->xoff_skb != NULL) 44862306a36Sopenharmony_ci caifd->xoff_skb->destructor = caifd->xoff_skb_dtor; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci caifd->xoff = false; 45162306a36Sopenharmony_ci caifd->xoff_skb_dtor = NULL; 45262306a36Sopenharmony_ci caifd->xoff_skb = NULL; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci spin_unlock_bh(&caifd->flow_lock); 45562306a36Sopenharmony_ci caifd_put(caifd); 45662306a36Sopenharmony_ci break; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci case NETDEV_UNREGISTER: 45962306a36Sopenharmony_ci mutex_lock(&caifdevs->lock); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci caifd = caif_get(dev); 46262306a36Sopenharmony_ci if (caifd == NULL) { 46362306a36Sopenharmony_ci mutex_unlock(&caifdevs->lock); 46462306a36Sopenharmony_ci break; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci list_del_rcu(&caifd->list); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* 46962306a36Sopenharmony_ci * NETDEV_UNREGISTER is called repeatedly until all reference 47062306a36Sopenharmony_ci * counts for the net-device are released. If references to 47162306a36Sopenharmony_ci * caifd is taken, simply ignore NETDEV_UNREGISTER and wait for 47262306a36Sopenharmony_ci * the next call to NETDEV_UNREGISTER. 47362306a36Sopenharmony_ci * 47462306a36Sopenharmony_ci * If any packets are in flight down the CAIF Stack, 47562306a36Sopenharmony_ci * cfcnfg_del_phy_layer will return nonzero. 47662306a36Sopenharmony_ci * If no packets are in flight, the CAIF Stack associated 47762306a36Sopenharmony_ci * with the net-device un-registering is freed. 47862306a36Sopenharmony_ci */ 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (caifd_refcnt_read(caifd) != 0 || 48162306a36Sopenharmony_ci cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0) { 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci pr_info("Wait for device inuse\n"); 48462306a36Sopenharmony_ci /* Enrole device if CAIF Stack is still in use */ 48562306a36Sopenharmony_ci list_add_rcu(&caifd->list, &caifdevs->list); 48662306a36Sopenharmony_ci mutex_unlock(&caifdevs->lock); 48762306a36Sopenharmony_ci break; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci synchronize_rcu(); 49162306a36Sopenharmony_ci dev_put(caifd->netdev); 49262306a36Sopenharmony_ci free_percpu(caifd->pcpu_refcnt); 49362306a36Sopenharmony_ci kfree(caifd); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci mutex_unlock(&caifdevs->lock); 49662306a36Sopenharmony_ci break; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci return 0; 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistatic struct notifier_block caif_device_notifier = { 50262306a36Sopenharmony_ci .notifier_call = caif_device_notify, 50362306a36Sopenharmony_ci .priority = 0, 50462306a36Sopenharmony_ci}; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci/* Per-namespace Caif devices handling */ 50762306a36Sopenharmony_cistatic int caif_init_net(struct net *net) 50862306a36Sopenharmony_ci{ 50962306a36Sopenharmony_ci struct caif_net *caifn = net_generic(net, caif_net_id); 51062306a36Sopenharmony_ci INIT_LIST_HEAD(&caifn->caifdevs.list); 51162306a36Sopenharmony_ci mutex_init(&caifn->caifdevs.lock); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci caifn->cfg = cfcnfg_create(); 51462306a36Sopenharmony_ci if (!caifn->cfg) 51562306a36Sopenharmony_ci return -ENOMEM; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci return 0; 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistatic void caif_exit_net(struct net *net) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci struct caif_device_entry *caifd, *tmp; 52362306a36Sopenharmony_ci struct caif_device_entry_list *caifdevs = 52462306a36Sopenharmony_ci caif_device_list(net); 52562306a36Sopenharmony_ci struct cfcnfg *cfg = get_cfcnfg(net); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci rtnl_lock(); 52862306a36Sopenharmony_ci mutex_lock(&caifdevs->lock); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci list_for_each_entry_safe(caifd, tmp, &caifdevs->list, list) { 53162306a36Sopenharmony_ci int i = 0; 53262306a36Sopenharmony_ci list_del_rcu(&caifd->list); 53362306a36Sopenharmony_ci cfcnfg_set_phy_state(cfg, &caifd->layer, false); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci while (i < 10 && 53662306a36Sopenharmony_ci (caifd_refcnt_read(caifd) != 0 || 53762306a36Sopenharmony_ci cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0)) { 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci pr_info("Wait for device inuse\n"); 54062306a36Sopenharmony_ci msleep(250); 54162306a36Sopenharmony_ci i++; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci synchronize_rcu(); 54462306a36Sopenharmony_ci dev_put(caifd->netdev); 54562306a36Sopenharmony_ci free_percpu(caifd->pcpu_refcnt); 54662306a36Sopenharmony_ci kfree(caifd); 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci cfcnfg_remove(cfg); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci mutex_unlock(&caifdevs->lock); 55162306a36Sopenharmony_ci rtnl_unlock(); 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic struct pernet_operations caif_net_ops = { 55562306a36Sopenharmony_ci .init = caif_init_net, 55662306a36Sopenharmony_ci .exit = caif_exit_net, 55762306a36Sopenharmony_ci .id = &caif_net_id, 55862306a36Sopenharmony_ci .size = sizeof(struct caif_net), 55962306a36Sopenharmony_ci}; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci/* Initialize Caif devices list */ 56262306a36Sopenharmony_cistatic int __init caif_device_init(void) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci int result; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci result = register_pernet_subsys(&caif_net_ops); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if (result) 56962306a36Sopenharmony_ci return result; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci register_netdevice_notifier(&caif_device_notifier); 57262306a36Sopenharmony_ci dev_add_pack(&caif_packet_type); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci return result; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic void __exit caif_device_exit(void) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci unregister_netdevice_notifier(&caif_device_notifier); 58062306a36Sopenharmony_ci dev_remove_pack(&caif_packet_type); 58162306a36Sopenharmony_ci unregister_pernet_subsys(&caif_net_ops); 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cimodule_init(caif_device_init); 58562306a36Sopenharmony_cimodule_exit(caif_device_exit); 586