diff options
Diffstat (limited to 'net/caif')
| -rw-r--r-- | net/caif/Kconfig | 13 | ||||
| -rw-r--r-- | net/caif/Makefile | 3 | ||||
| -rw-r--r-- | net/caif/caif_config_util.c | 99 | ||||
| -rw-r--r-- | net/caif/caif_dev.c | 585 | ||||
| -rw-r--r-- | net/caif/caif_socket.c | 257 | ||||
| -rw-r--r-- | net/caif/caif_usb.c | 204 | ||||
| -rw-r--r-- | net/caif/cfcnfg.c | 591 | ||||
| -rw-r--r-- | net/caif/cfctrl.c | 282 | ||||
| -rw-r--r-- | net/caif/cfdbgl.c | 13 | ||||
| -rw-r--r-- | net/caif/cfdgml.c | 32 | ||||
| -rw-r--r-- | net/caif/cffrml.c | 82 | ||||
| -rw-r--r-- | net/caif/cfmuxl.c | 197 | ||||
| -rw-r--r-- | net/caif/cfpkt_skbuff.c | 228 | ||||
| -rw-r--r-- | net/caif/cfrfml.c | 44 | ||||
| -rw-r--r-- | net/caif/cfserl.c | 24 | ||||
| -rw-r--r-- | net/caif/cfsrvl.c | 70 | ||||
| -rw-r--r-- | net/caif/cfutill.c | 23 | ||||
| -rw-r--r-- | net/caif/cfveil.c | 22 | ||||
| -rw-r--r-- | net/caif/cfvidl.c | 20 | ||||
| -rw-r--r-- | net/caif/chnl_net.c | 132 | 
20 files changed, 1552 insertions, 1369 deletions
diff --git a/net/caif/Kconfig b/net/caif/Kconfig index 529750da962..d3694953b1d 100644 --- a/net/caif/Kconfig +++ b/net/caif/Kconfig @@ -25,7 +25,7 @@ config  CAIF_DEBUG  	bool "Enable Debug"  	depends on CAIF  	default n -	--- help --- +	---help---  	Enable the inclusion of debug code in the CAIF stack.  	Be aware that doing this will impact performance.  	If unsure say N. @@ -40,3 +40,14 @@ config CAIF_NETDEV  	If you select to build it as a built-in then the main CAIF device must  	also be a built-in.  	If unsure say Y. + +config CAIF_USB +	tristate "CAIF USB support" +	depends on CAIF +	default n +	---help--- +	Say Y if you are using CAIF over USB CDC NCM. +	This can be either built-in or a loadable module, +	If you select to build it as a built-in then the main CAIF device must +	also be a built-in. +	If unsure say N. diff --git a/net/caif/Makefile b/net/caif/Makefile index 9d38e406e4a..cc2b51154d0 100644 --- a/net/caif/Makefile +++ b/net/caif/Makefile @@ -5,10 +5,11 @@ caif-y := caif_dev.o \  	cffrml.o cfveil.o cfdbgl.o\  	cfserl.o cfdgml.o  \  	cfrfml.o cfvidl.o cfutill.o \ -	cfsrvl.o cfpkt_skbuff.o caif_config_util.o +	cfsrvl.o cfpkt_skbuff.o  obj-$(CONFIG_CAIF) += caif.o  obj-$(CONFIG_CAIF_NETDEV) += chnl_net.o  obj-$(CONFIG_CAIF) += caif_socket.o +obj-$(CONFIG_CAIF_USB) += caif_usb.o  export-y := caif.o diff --git a/net/caif/caif_config_util.c b/net/caif/caif_config_util.c deleted file mode 100644 index d522d8c1703..00000000000 --- a/net/caif/caif_config_util.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author:	Sjur Brendeland sjur.brandeland@stericsson.com - * License terms: GNU General Public License (GPL) version 2 - */ - -#include <linux/module.h> -#include <linux/spinlock.h> -#include <net/caif/cfctrl.h> -#include <net/caif/cfcnfg.h> -#include <net/caif/caif_dev.h> - -int connect_req_to_link_param(struct cfcnfg *cnfg, -				struct caif_connect_request *s, -				struct cfctrl_link_param *l) -{ -	struct dev_info *dev_info; -	enum cfcnfg_phy_preference pref; -	int res; - -	memset(l, 0, sizeof(*l)); -	/* In caif protocol low value is high priority */ -	l->priority = CAIF_PRIO_MAX - s->priority + 1; - -	if (s->ifindex != 0){ -		res = cfcnfg_get_id_from_ifi(cnfg, s->ifindex); -		if (res < 0) -			return res; -		l->phyid = res; -	} -	else { -		switch (s->link_selector) { -		case CAIF_LINK_HIGH_BANDW: -			pref = CFPHYPREF_HIGH_BW; -			break; -		case CAIF_LINK_LOW_LATENCY: -			pref = CFPHYPREF_LOW_LAT; -			break; -		default: -			return -EINVAL; -		} -		dev_info = cfcnfg_get_phyid(cnfg, pref); -		if (dev_info == NULL) -			return -ENODEV; -		l->phyid = dev_info->id; -	} -	switch (s->protocol) { -	case CAIFPROTO_AT: -		l->linktype = CFCTRL_SRV_VEI; -		if (s->sockaddr.u.at.type == CAIF_ATTYPE_PLAIN) -			l->chtype = 0x02; -		else -			l->chtype = s->sockaddr.u.at.type; -		l->endpoint = 0x00; -		break; -	case CAIFPROTO_DATAGRAM: -		l->linktype = CFCTRL_SRV_DATAGRAM; -		l->chtype = 0x00; -		l->u.datagram.connid = s->sockaddr.u.dgm.connection_id; -		break; -	case CAIFPROTO_DATAGRAM_LOOP: -		l->linktype = CFCTRL_SRV_DATAGRAM; -		l->chtype = 0x03; -		l->endpoint = 0x00; -		l->u.datagram.connid = s->sockaddr.u.dgm.connection_id; -		break; -	case CAIFPROTO_RFM: -		l->linktype = CFCTRL_SRV_RFM; -		l->u.datagram.connid = s->sockaddr.u.rfm.connection_id; -		strncpy(l->u.rfm.volume, s->sockaddr.u.rfm.volume, -			sizeof(l->u.rfm.volume)-1); -		l->u.rfm.volume[sizeof(l->u.rfm.volume)-1] = 0; -		break; -	case CAIFPROTO_UTIL: -		l->linktype = CFCTRL_SRV_UTIL; -		l->endpoint = 0x00; -		l->chtype = 0x00; -		strncpy(l->u.utility.name, s->sockaddr.u.util.service, -			sizeof(l->u.utility.name)-1); -		l->u.utility.name[sizeof(l->u.utility.name)-1] = 0; -		caif_assert(sizeof(l->u.utility.name) > 10); -		l->u.utility.paramlen = s->param.size; -		if (l->u.utility.paramlen > sizeof(l->u.utility.params)) -			l->u.utility.paramlen = sizeof(l->u.utility.params); - -		memcpy(l->u.utility.params, s->param.data, -		       l->u.utility.paramlen); - -		break; -	case CAIFPROTO_DEBUG: -		l->linktype = CFCTRL_SRV_DBG; -		l->endpoint = s->sockaddr.u.dbg.service; -		l->chtype = s->sockaddr.u.dbg.type; -		break; -	default: -		return -EINVAL; -	} -	return 0; -} diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c index a42a408306e..edbca468fa7 100644 --- a/net/caif/caif_dev.c +++ b/net/caif/caif_dev.c @@ -1,83 +1,108 @@  /*   * CAIF Interface registration.   * Copyright (C) ST-Ericsson AB 2010 - * Author:	Sjur Brendeland/sjur.brandeland@stericsson.com + * Author:	Sjur Brendeland   * License terms: GNU General Public License (GPL) version 2   * - * Borrowed heavily from file: pn_dev.c. Thanks to - *  Remi Denis-Courmont <remi.denis-courmont@nokia.com> + * Borrowed heavily from file: pn_dev.c. Thanks to Remi Denis-Courmont   *  and Sakari Ailus <sakari.ailus@nokia.com>   */  #define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ -#include <linux/version.h> -#include <linux/module.h>  #include <linux/kernel.h>  #include <linux/if_arp.h>  #include <linux/net.h>  #include <linux/netdevice.h> -#include <linux/skbuff.h> -#include <linux/sched.h> -#include <linux/wait.h> +#include <linux/mutex.h> +#include <linux/module.h> +#include <linux/spinlock.h>  #include <net/netns/generic.h>  #include <net/net_namespace.h>  #include <net/pkt_sched.h>  #include <net/caif/caif_device.h> -#include <net/caif/caif_dev.h>  #include <net/caif/caif_layer.h> +#include <net/caif/caif_dev.h>  #include <net/caif/cfpkt.h>  #include <net/caif/cfcnfg.h> +#include <net/caif/cfserl.h>  MODULE_LICENSE("GPL"); -#define TIMEOUT (HZ*5)  /* Used for local tracking of the CAIF net devices */  struct caif_device_entry {  	struct cflayer layer;  	struct list_head list; -	atomic_t in_use; -	atomic_t state; -	u16 phyid;  	struct net_device *netdev; -	wait_queue_head_t event; +	int __percpu *pcpu_refcnt; +	spinlock_t flow_lock; +	struct sk_buff *xoff_skb; +	void (*xoff_skb_dtor)(struct sk_buff *skb); +	bool xoff;  };  struct caif_device_entry_list {  	struct list_head list;  	/* Protects simulanous deletes in list */ -	spinlock_t lock; +	struct mutex lock;  };  struct caif_net { +	struct cfcnfg *cfg;  	struct caif_device_entry_list caifdevs;  };  static int caif_net_id; -static struct cfcnfg *cfg; +static int q_high = 50; /* Percent */ + +struct cfcnfg *get_cfcnfg(struct net *net) +{ +	struct caif_net *caifn; +	caifn = net_generic(net, caif_net_id); +	return caifn->cfg; +} +EXPORT_SYMBOL(get_cfcnfg);  static struct caif_device_entry_list *caif_device_list(struct net *net)  {  	struct caif_net *caifn; -	BUG_ON(!net);  	caifn = net_generic(net, caif_net_id); -	BUG_ON(!caifn);  	return &caifn->caifdevs;  } +static void caifd_put(struct caif_device_entry *e) +{ +	this_cpu_dec(*e->pcpu_refcnt); +} + +static void caifd_hold(struct caif_device_entry *e) +{ +	this_cpu_inc(*e->pcpu_refcnt); +} + +static int caifd_refcnt_read(struct caif_device_entry *e) +{ +	int i, refcnt = 0; +	for_each_possible_cpu(i) +		refcnt += *per_cpu_ptr(e->pcpu_refcnt, i); +	return refcnt; +} +  /* Allocate new CAIF device. */  static struct caif_device_entry *caif_device_alloc(struct net_device *dev)  { -	struct caif_device_entry_list *caifdevs;  	struct caif_device_entry *caifd; -	caifdevs = caif_device_list(dev_net(dev)); -	BUG_ON(!caifdevs); -	caifd = kzalloc(sizeof(*caifd), GFP_ATOMIC); + +	caifd = kzalloc(sizeof(*caifd), GFP_KERNEL);  	if (!caifd)  		return NULL; +	caifd->pcpu_refcnt = alloc_percpu(int); +	if (!caifd->pcpu_refcnt) { +		kfree(caifd); +		return NULL; +	}  	caifd->netdev = dev; -	list_add(&caifd->list, &caifdevs->list); -	init_waitqueue_head(&caifd->event); +	dev_hold(dev);  	return caifd;  } @@ -86,100 +111,166 @@ static struct caif_device_entry *caif_get(struct net_device *dev)  	struct caif_device_entry_list *caifdevs =  	    caif_device_list(dev_net(dev));  	struct caif_device_entry *caifd; -	BUG_ON(!caifdevs); -	list_for_each_entry(caifd, &caifdevs->list, list) { + +	list_for_each_entry_rcu(caifd, &caifdevs->list, list) {  		if (caifd->netdev == dev)  			return caifd;  	}  	return NULL;  } -static void caif_device_destroy(struct net_device *dev) +static void caif_flow_cb(struct sk_buff *skb)  { -	struct caif_device_entry_list *caifdevs = -	    caif_device_list(dev_net(dev));  	struct caif_device_entry *caifd; -	ASSERT_RTNL(); -	if (dev->type != ARPHRD_CAIF) -		return; +	void (*dtor)(struct sk_buff *skb) = NULL; +	bool send_xoff; -	spin_lock_bh(&caifdevs->lock); -	caifd = caif_get(dev); -	if (caifd == NULL) { -		spin_unlock_bh(&caifdevs->lock); +	WARN_ON(skb->dev == NULL); + +	rcu_read_lock(); +	caifd = caif_get(skb->dev); + +	WARN_ON(caifd == NULL); +	if (caifd == NULL)  		return; -	} -	list_del(&caifd->list); -	spin_unlock_bh(&caifdevs->lock); +	caifd_hold(caifd); +	rcu_read_unlock(); + +	spin_lock_bh(&caifd->flow_lock); +	send_xoff = caifd->xoff; +	caifd->xoff = 0; +	dtor = caifd->xoff_skb_dtor; + +	if (WARN_ON(caifd->xoff_skb != skb)) +		skb = NULL; + +	caifd->xoff_skb = NULL; +	caifd->xoff_skb_dtor = NULL; + +	spin_unlock_bh(&caifd->flow_lock); -	kfree(caifd); +	if (dtor && skb) +		dtor(skb); + +	if (send_xoff) +		caifd->layer.up-> +			ctrlcmd(caifd->layer.up, +				_CAIF_CTRLCMD_PHYIF_FLOW_ON_IND, +				caifd->layer.id); +	caifd_put(caifd);  }  static int transmit(struct cflayer *layer, struct cfpkt *pkt)  { +	int err, high = 0, qlen = 0;  	struct caif_device_entry *caifd =  	    container_of(layer, struct caif_device_entry, layer); -	struct sk_buff *skb, *skb2; -	int ret = -EINVAL; +	struct sk_buff *skb; +	struct netdev_queue *txq; + +	rcu_read_lock_bh(); +  	skb = cfpkt_tonative(pkt);  	skb->dev = caifd->netdev; +	skb_reset_network_header(skb); +	skb->protocol = htons(ETH_P_CAIF); + +	/* Check if we need to handle xoff */ +	if (likely(caifd->netdev->tx_queue_len == 0)) +		goto noxoff; + +	if (unlikely(caifd->xoff)) +		goto noxoff; + +	if (likely(!netif_queue_stopped(caifd->netdev))) { +		/* If we run with a TX queue, check if the queue is too long*/ +		txq = netdev_get_tx_queue(skb->dev, 0); +		qlen = qdisc_qlen(rcu_dereference_bh(txq->qdisc)); + +		if (likely(qlen == 0)) +			goto noxoff; + +		high = (caifd->netdev->tx_queue_len * q_high) / 100; +		if (likely(qlen < high)) +			goto noxoff; +	} + +	/* Hold lock while accessing xoff */ +	spin_lock_bh(&caifd->flow_lock); +	if (caifd->xoff) { +		spin_unlock_bh(&caifd->flow_lock); +		goto noxoff; +	} +  	/* -	 * Don't allow SKB to be destroyed upon error, but signal resend -	 * notification to clients. We can't rely on the return value as -	 * congestion (NET_XMIT_CN) sometimes drops the packet, sometimes don't. +	 * Handle flow off, we do this by temporary hi-jacking this +	 * skb's destructor function, and replace it with our own +	 * flow-on callback. The callback will set flow-on and call +	 * the original destructor.  	 */ -	if (netif_queue_stopped(caifd->netdev)) -		return -EAGAIN; -	skb2 = skb_get(skb); -	ret = dev_queue_xmit(skb2); +	pr_debug("queue has stopped(%d) or is full (%d > %d)\n", +			netif_queue_stopped(caifd->netdev), +			qlen, high); +	caifd->xoff = 1; +	caifd->xoff_skb = skb; +	caifd->xoff_skb_dtor = skb->destructor; +	skb->destructor = caif_flow_cb; +	spin_unlock_bh(&caifd->flow_lock); -	if (!ret) -		kfree_skb(skb); -	else -		return -EAGAIN; +	caifd->layer.up->ctrlcmd(caifd->layer.up, +					_CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND, +					caifd->layer.id); +noxoff: +	rcu_read_unlock_bh(); -	return 0; -} +	err = dev_queue_xmit(skb); +	if (err > 0) +		err = -EIO; -static int modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl) -{ -	struct caif_device_entry *caifd; -	struct caif_dev_common *caifdev; -	caifd = container_of(layr, struct caif_device_entry, layer); -	caifdev = netdev_priv(caifd->netdev); -	if (ctrl == _CAIF_MODEMCMD_PHYIF_USEFULL) { -		atomic_set(&caifd->in_use, 1); -		wake_up_interruptible(&caifd->event); - -	} else if (ctrl == _CAIF_MODEMCMD_PHYIF_USELESS) { -		atomic_set(&caifd->in_use, 0); -		wake_up_interruptible(&caifd->event); -	} -	return 0; +	return err;  }  /* - * Stuff received packets to associated sockets. + * Stuff received packets into the CAIF stack.   * On error, returns non-zero and releases the skb.   */  static int receive(struct sk_buff *skb, struct net_device *dev,  		   struct packet_type *pkttype, struct net_device *orig_dev)  { -	struct net *net;  	struct cfpkt *pkt;  	struct caif_device_entry *caifd; -	net = dev_net(dev); +	int err; +  	pkt = cfpkt_fromnative(CAIF_DIR_IN, skb); + +	rcu_read_lock();  	caifd = caif_get(dev); -	if (!caifd || !caifd->layer.up || !caifd->layer.up->receive) -		return NET_RX_DROP; -	if (caifd->layer.up->receive(caifd->layer.up, pkt)) +	if (!caifd || !caifd->layer.up || !caifd->layer.up->receive || +			!netif_oper_up(caifd->netdev)) { +		rcu_read_unlock(); +		kfree_skb(skb);  		return NET_RX_DROP; +	} -	return 0; +	/* Hold reference to netdevice while using CAIF stack */ +	caifd_hold(caifd); +	rcu_read_unlock(); + +	err = caifd->layer.up->receive(caifd->layer.up, pkt); + +	/* For -EILSEQ the packet is not freed so so it now */ +	if (err == -EILSEQ) +		cfpkt_destroy(pkt); + +	/* Release reference to stack upwards */ +	caifd_put(caifd); + +	if (err != 0) +		err = NET_RX_DROP; +	return err;  }  static struct packet_type caif_packet_type __read_mostly = { @@ -189,129 +280,208 @@ static struct packet_type caif_packet_type __read_mostly = {  static void dev_flowctrl(struct net_device *dev, int on)  { -	struct caif_device_entry *caifd = caif_get(dev); -	if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) +	struct caif_device_entry *caifd; + +	rcu_read_lock(); + +	caifd = caif_get(dev); +	if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) { +		rcu_read_unlock();  		return; +	} + +	caifd_hold(caifd); +	rcu_read_unlock();  	caifd->layer.up->ctrlcmd(caifd->layer.up,  				 on ?  				 _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND :  				 _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND,  				 caifd->layer.id); +	caifd_put(caifd); +} + +void 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 *)) +{ +	struct caif_device_entry *caifd; +	enum cfcnfg_phy_preference pref; +	struct cfcnfg *cfg = get_cfcnfg(dev_net(dev)); +	struct caif_device_entry_list *caifdevs; + +	caifdevs = caif_device_list(dev_net(dev)); +	caifd = caif_device_alloc(dev); +	if (!caifd) +		return; +	*layer = &caifd->layer; +	spin_lock_init(&caifd->flow_lock); + +	switch (caifdev->link_select) { +	case CAIF_LINK_HIGH_BANDW: +		pref = CFPHYPREF_HIGH_BW; +		break; +	case CAIF_LINK_LOW_LATENCY: +		pref = CFPHYPREF_LOW_LAT; +		break; +	default: +		pref = CFPHYPREF_HIGH_BW; +		break; +	} +	mutex_lock(&caifdevs->lock); +	list_add_rcu(&caifd->list, &caifdevs->list); + +	strncpy(caifd->layer.name, dev->name, +		sizeof(caifd->layer.name) - 1); +	caifd->layer.name[sizeof(caifd->layer.name) - 1] = 0; +	caifd->layer.transmit = transmit; +	cfcnfg_add_phy_layer(cfg, +				dev, +				&caifd->layer, +				pref, +				link_support, +				caifdev->use_fcs, +				head_room); +	mutex_unlock(&caifdevs->lock); +	if (rcv_func) +		*rcv_func = receive;  } +EXPORT_SYMBOL(caif_enroll_dev);  /* notify Caif of device events */  static int caif_device_notify(struct notifier_block *me, unsigned long what, -			      void *arg) +			      void *ptr)  { -	struct net_device *dev = arg; +	struct net_device *dev = netdev_notifier_info_to_dev(ptr);  	struct caif_device_entry *caifd = NULL;  	struct caif_dev_common *caifdev; -	enum cfcnfg_phy_preference pref; -	int res = -EINVAL; -	enum cfcnfg_phy_type phy_type; +	struct cfcnfg *cfg; +	struct cflayer *layer, *link_support; +	int head_room = 0; +	struct caif_device_entry_list *caifdevs; + +	cfg = get_cfcnfg(dev_net(dev)); +	caifdevs = caif_device_list(dev_net(dev)); -	if (dev->type != ARPHRD_CAIF) +	caifd = caif_get(dev); +	if (caifd == NULL && dev->type != ARPHRD_CAIF)  		return 0;  	switch (what) {  	case NETDEV_REGISTER: -		netdev_info(dev, "register\n"); -		caifd = caif_device_alloc(dev); -		if (caifd == NULL) +		if (caifd != NULL)  			break; +  		caifdev = netdev_priv(dev); + +		link_support = NULL; +		if (caifdev->use_frag) { +			head_room = 1; +			link_support = cfserl_create(dev->ifindex, +							caifdev->use_stx); +			if (!link_support) { +				pr_warn("Out of memory\n"); +				break; +			} +		} +		caif_enroll_dev(dev, caifdev, link_support, head_room, +				&layer, NULL);  		caifdev->flowctrl = dev_flowctrl; -		atomic_set(&caifd->state, what); -		res = 0;  		break;  	case NETDEV_UP: -		netdev_info(dev, "up\n"); +		rcu_read_lock(); +  		caifd = caif_get(dev); -		if (caifd == NULL) -			break; -		caifdev = netdev_priv(dev); -		if (atomic_read(&caifd->state) == NETDEV_UP) { -			netdev_info(dev, "already up\n"); +		if (caifd == NULL) { +			rcu_read_unlock();  			break;  		} -		atomic_set(&caifd->state, what); -		caifd->layer.transmit = transmit; -		caifd->layer.modemcmd = modemcmd; - -		if (caifdev->use_frag) -			phy_type = CFPHYTYPE_FRAG; -		else -			phy_type = CFPHYTYPE_CAIF; - -		switch (caifdev->link_select) { -		case CAIF_LINK_HIGH_BANDW: -			pref = CFPHYPREF_HIGH_BW; -			break; -		case CAIF_LINK_LOW_LATENCY: -			pref = CFPHYPREF_LOW_LAT; -			break; -		default: -			pref = CFPHYPREF_HIGH_BW; -			break; -		} -		dev_hold(dev); -		cfcnfg_add_phy_layer(get_caif_conf(), -				     phy_type, -				     dev, -				     &caifd->layer, -				     &caifd->phyid, -				     pref, -				     caifdev->use_fcs, -				     caifdev->use_stx); -		strncpy(caifd->layer.name, dev->name, -			sizeof(caifd->layer.name) - 1); -		caifd->layer.name[sizeof(caifd->layer.name) - 1] = 0; + +		caifd->xoff = 0; +		cfcnfg_set_phy_state(cfg, &caifd->layer, true); +		rcu_read_unlock(); +  		break; -	case NETDEV_GOING_DOWN: +	case NETDEV_DOWN: +		rcu_read_lock(); +  		caifd = caif_get(dev); -		if (caifd == NULL) -			break; -		netdev_info(dev, "going down\n"); +		if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) { +			rcu_read_unlock(); +			return -EINVAL; +		} -		if (atomic_read(&caifd->state) == NETDEV_GOING_DOWN || -			atomic_read(&caifd->state) == NETDEV_DOWN) -			break; +		cfcnfg_set_phy_state(cfg, &caifd->layer, false); +		caifd_hold(caifd); +		rcu_read_unlock(); -		atomic_set(&caifd->state, what); -		if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) -			return -EINVAL;  		caifd->layer.up->ctrlcmd(caifd->layer.up,  					 _CAIF_CTRLCMD_PHYIF_DOWN_IND,  					 caifd->layer.id); -		might_sleep(); -		res = wait_event_interruptible_timeout(caifd->event, -					atomic_read(&caifd->in_use) == 0, -					TIMEOUT); -		break; -	case NETDEV_DOWN: -		caifd = caif_get(dev); -		if (caifd == NULL) -			break; -		netdev_info(dev, "down\n"); -		if (atomic_read(&caifd->in_use)) -			netdev_warn(dev, -				    "Unregistering an active CAIF device\n"); -		cfcnfg_del_phy_layer(get_caif_conf(), &caifd->layer); -		dev_put(dev); -		atomic_set(&caifd->state, what); +		spin_lock_bh(&caifd->flow_lock); + +		/* +		 * Replace our xoff-destructor with original destructor. +		 * We trust that skb->destructor *always* is called before +		 * the skb reference is invalid. The hijacked SKB destructor +		 * takes the flow_lock so manipulating the skb->destructor here +		 * should be safe. +		*/ +		if (caifd->xoff_skb_dtor != NULL && caifd->xoff_skb != NULL) +			caifd->xoff_skb->destructor = caifd->xoff_skb_dtor; + +		caifd->xoff = 0; +		caifd->xoff_skb_dtor = NULL; +		caifd->xoff_skb = NULL; + +		spin_unlock_bh(&caifd->flow_lock); +		caifd_put(caifd);  		break;  	case NETDEV_UNREGISTER: +		mutex_lock(&caifdevs->lock); +  		caifd = caif_get(dev); -		if (caifd == NULL) +		if (caifd == NULL) { +			mutex_unlock(&caifdevs->lock);  			break; -		netdev_info(dev, "unregister\n"); -		atomic_set(&caifd->state, what); -		caif_device_destroy(dev); +		} +		list_del_rcu(&caifd->list); + +		/* +		 * NETDEV_UNREGISTER is called repeatedly until all reference +		 * counts for the net-device are released. If references to +		 * caifd is taken, simply ignore NETDEV_UNREGISTER and wait for +		 * the next call to NETDEV_UNREGISTER. +		 * +		 * If any packets are in flight down the CAIF Stack, +		 * cfcnfg_del_phy_layer will return nonzero. +		 * If no packets are in flight, the CAIF Stack associated +		 * with the net-device un-registering is freed. +		 */ + +		if (caifd_refcnt_read(caifd) != 0 || +			cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0) { + +			pr_info("Wait for device inuse\n"); +			/* Enrole device if CAIF Stack is still in use */ +			list_add_rcu(&caifd->list, &caifdevs->list); +			mutex_unlock(&caifdevs->lock); +			break; +		} + +		synchronize_rcu(); +		dev_put(caifd->netdev); +		free_percpu(caifd->pcpu_refcnt); +		kfree(caifd); + +		mutex_unlock(&caifdevs->lock);  		break;  	}  	return 0; @@ -322,61 +492,51 @@ static struct notifier_block caif_device_notifier = {  	.priority = 0,  }; - -struct cfcnfg *get_caif_conf(void) -{ -	return cfg; -} -EXPORT_SYMBOL(get_caif_conf); - -int caif_connect_client(struct caif_connect_request *conn_req, -			struct cflayer *client_layer, int *ifindex, -			int *headroom, int *tailroom) -{ -	struct cfctrl_link_param param; -	int ret; -	ret = connect_req_to_link_param(get_caif_conf(), conn_req, ¶m); -	if (ret) -		return ret; -	/* Hook up the adaptation layer. */ -	return cfcnfg_add_adaptation_layer(get_caif_conf(), ¶m, -					client_layer, ifindex, -					headroom, tailroom); -} -EXPORT_SYMBOL(caif_connect_client); - -int caif_disconnect_client(struct cflayer *adap_layer) -{ -       return cfcnfg_disconn_adapt_layer(get_caif_conf(), adap_layer); -} -EXPORT_SYMBOL(caif_disconnect_client); - -void caif_release_client(struct cflayer *adap_layer) -{ -       cfcnfg_release_adap_layer(adap_layer); -} -EXPORT_SYMBOL(caif_release_client); -  /* Per-namespace Caif devices handling */  static int caif_init_net(struct net *net)  {  	struct caif_net *caifn = net_generic(net, caif_net_id);  	INIT_LIST_HEAD(&caifn->caifdevs.list); -	spin_lock_init(&caifn->caifdevs.lock); +	mutex_init(&caifn->caifdevs.lock); + +	caifn->cfg = cfcnfg_create(); +	if (!caifn->cfg) +		return -ENOMEM; +  	return 0;  }  static void caif_exit_net(struct net *net)  { -	struct net_device *dev; -	int res; +	struct caif_device_entry *caifd, *tmp; +	struct caif_device_entry_list *caifdevs = +	    caif_device_list(net); +	struct cfcnfg *cfg =  get_cfcnfg(net); +  	rtnl_lock(); -	for_each_netdev(net, dev) { -		if (dev->type != ARPHRD_CAIF) -			continue; -		res = dev_close(dev); -		caif_device_destroy(dev); +	mutex_lock(&caifdevs->lock); + +	list_for_each_entry_safe(caifd, tmp, &caifdevs->list, list) { +		int i = 0; +		list_del_rcu(&caifd->list); +		cfcnfg_set_phy_state(cfg, &caifd->layer, false); + +		while (i < 10 && +			(caifd_refcnt_read(caifd) != 0 || +			cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0)) { + +			pr_info("Wait for device inuse\n"); +			msleep(250); +			i++; +		} +		synchronize_rcu(); +		dev_put(caifd->netdev); +		free_percpu(caifd->pcpu_refcnt); +		kfree(caifd);  	} +	cfcnfg_remove(cfg); + +	mutex_unlock(&caifdevs->lock);  	rtnl_unlock();  } @@ -391,32 +551,23 @@ static struct pernet_operations caif_net_ops = {  static int __init caif_device_init(void)  {  	int result; -	cfg = cfcnfg_create(); -	if (!cfg) { -		pr_warn("can't create cfcnfg\n"); -		goto err_cfcnfg_create_failed; -	} -	result = register_pernet_device(&caif_net_ops); -	if (result) { -		kfree(cfg); -		cfg = NULL; +	result = register_pernet_subsys(&caif_net_ops); + +	if (result)  		return result; -	} -	dev_add_pack(&caif_packet_type); +  	register_netdevice_notifier(&caif_device_notifier); +	dev_add_pack(&caif_packet_type);  	return result; -err_cfcnfg_create_failed: -	return -ENODEV;  }  static void __exit caif_device_exit(void)  { -	dev_remove_pack(&caif_packet_type); -	unregister_pernet_device(&caif_net_ops);  	unregister_netdevice_notifier(&caif_device_notifier); -	cfcnfg_remove(cfg); +	dev_remove_pack(&caif_packet_type); +	unregister_pernet_subsys(&caif_net_ops);  }  module_init(caif_device_init); diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c index 1bf0cf50379..e8437094d15 100644 --- a/net/caif/caif_socket.c +++ b/net/caif/caif_socket.c @@ -1,6 +1,6 @@  /*   * Copyright (C) ST-Ericsson AB 2010 - * Author:	Sjur Brendeland sjur.brandeland@stericsson.com + * Author:	Sjur Brendeland   * License terms: GNU General Public License (GPL) version 2   */ @@ -19,7 +19,7 @@  #include <linux/uaccess.h>  #include <linux/debugfs.h>  #include <linux/caif/caif_socket.h> -#include <asm/atomic.h> +#include <linux/pkt_sched.h>  #include <net/sock.h>  #include <net/tcp_states.h>  #include <net/caif/caif_layer.h> @@ -43,33 +43,9 @@ enum caif_states {  #define TX_FLOW_ON_BIT	1  #define RX_FLOW_ON_BIT	2 -static struct dentry *debugfsdir; - -#ifdef CONFIG_DEBUG_FS -struct debug_fs_counter { -	atomic_t caif_nr_socks; -	atomic_t num_connect_req; -	atomic_t num_connect_resp; -	atomic_t num_connect_fail_resp; -	atomic_t num_disconnect; -	atomic_t num_remote_shutdown_ind; -	atomic_t num_tx_flow_off_ind; -	atomic_t num_tx_flow_on_ind; -	atomic_t num_rx_flow_off; -	atomic_t num_rx_flow_on; -}; -static struct debug_fs_counter cnt; -#define	dbfs_atomic_inc(v) atomic_inc(v) -#define	dbfs_atomic_dec(v) atomic_dec(v) -#else -#define	dbfs_atomic_inc(v) -#define	dbfs_atomic_dec(v) -#endif -  struct caifsock {  	struct sock sk; /* must be first member */  	struct cflayer layer; -	char name[CAIF_LAYER_NAME_SZ]; /* Used for debugging */  	u32 flow_state;  	struct caif_connect_request conn_req;  	struct mutex readlock; @@ -148,28 +124,25 @@ static void caif_flow_ctrl(struct sock *sk, int mode)  static int caif_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)  {  	int err; -	int skb_len;  	unsigned long flags;  	struct sk_buff_head *list = &sk->sk_receive_queue;  	struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);  	if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >= -		(unsigned)sk->sk_rcvbuf && rx_flow_is_on(cf_sk)) { -		pr_debug("sending flow OFF (queue len = %d %d)\n", -			atomic_read(&cf_sk->sk.sk_rmem_alloc), -			sk_rcvbuf_lowwater(cf_sk)); +		(unsigned int)sk->sk_rcvbuf && rx_flow_is_on(cf_sk)) { +		net_dbg_ratelimited("sending flow OFF (queue len = %d %d)\n", +				    atomic_read(&cf_sk->sk.sk_rmem_alloc), +				    sk_rcvbuf_lowwater(cf_sk));  		set_rx_flow_off(cf_sk); -		dbfs_atomic_inc(&cnt.num_rx_flow_off);  		caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_OFF_REQ);  	}  	err = sk_filter(sk, skb);  	if (err)  		return err; -	if (!sk_rmem_schedule(sk, skb->truesize) && rx_flow_is_on(cf_sk)) { +	if (!sk_rmem_schedule(sk, skb, skb->truesize) && rx_flow_is_on(cf_sk)) {  		set_rx_flow_off(cf_sk); -		pr_debug("sending flow OFF due to rmem_schedule\n"); -		dbfs_atomic_inc(&cnt.num_rx_flow_off); +		net_dbg_ratelimited("sending flow OFF due to rmem_schedule\n");  		caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_OFF_REQ);  	}  	skb->dev = NULL; @@ -179,14 +152,13 @@ static int caif_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)  	 * may be freed by other threads of control pulling packets  	 * from the queue.  	 */ -	skb_len = skb->len;  	spin_lock_irqsave(&list->lock, flags);  	if (!sock_flag(sk, SOCK_DEAD))  		__skb_queue_tail(list, skb);  	spin_unlock_irqrestore(&list->lock, flags);  	if (!sock_flag(sk, SOCK_DEAD)) -		sk->sk_data_ready(sk, skb_len); +		sk->sk_data_ready(sk);  	else  		kfree_skb(skb);  	return 0; @@ -202,39 +174,51 @@ static int caif_sktrecv_cb(struct cflayer *layr, struct cfpkt *pkt)  	skb = cfpkt_tonative(pkt);  	if (unlikely(cf_sk->sk.sk_state != CAIF_CONNECTED)) { -		cfpkt_destroy(pkt); +		kfree_skb(skb);  		return 0;  	}  	caif_queue_rcv_skb(&cf_sk->sk, skb);  	return 0;  } +static void cfsk_hold(struct cflayer *layr) +{ +	struct caifsock *cf_sk = container_of(layr, struct caifsock, layer); +	sock_hold(&cf_sk->sk); +} + +static void cfsk_put(struct cflayer *layr) +{ +	struct caifsock *cf_sk = container_of(layr, struct caifsock, layer); +	sock_put(&cf_sk->sk); +} +  /* Packet Control Callback function called from CAIF */  static void caif_ctrl_cb(struct cflayer *layr, -				enum caif_ctrlcmd flow, -				int phyid) +			 enum caif_ctrlcmd flow, +			 int phyid)  {  	struct caifsock *cf_sk = container_of(layr, struct caifsock, layer);  	switch (flow) {  	case CAIF_CTRLCMD_FLOW_ON_IND:  		/* OK from modem to start sending again */ -		dbfs_atomic_inc(&cnt.num_tx_flow_on_ind);  		set_tx_flow_on(cf_sk);  		cf_sk->sk.sk_state_change(&cf_sk->sk);  		break;  	case CAIF_CTRLCMD_FLOW_OFF_IND:  		/* Modem asks us to shut up */ -		dbfs_atomic_inc(&cnt.num_tx_flow_off_ind);  		set_tx_flow_off(cf_sk);  		cf_sk->sk.sk_state_change(&cf_sk->sk);  		break;  	case CAIF_CTRLCMD_INIT_RSP:  		/* We're now connected */ -		dbfs_atomic_inc(&cnt.num_connect_resp); +		caif_client_register_refcnt(&cf_sk->layer, +						cfsk_hold, cfsk_put);  		cf_sk->sk.sk_state = CAIF_CONNECTED;  		set_tx_flow_on(cf_sk); +		cf_sk->sk.sk_shutdown = 0;  		cf_sk->sk.sk_state_change(&cf_sk->sk);  		break; @@ -242,12 +226,10 @@ static void caif_ctrl_cb(struct cflayer *layr,  		/* We're now disconnected */  		cf_sk->sk.sk_state = CAIF_DISCONNECTED;  		cf_sk->sk.sk_state_change(&cf_sk->sk); -		cfcnfg_release_adap_layer(&cf_sk->layer);  		break;  	case CAIF_CTRLCMD_INIT_FAIL_RSP:  		/* Connect request failed */ -		dbfs_atomic_inc(&cnt.num_connect_fail_resp);  		cf_sk->sk.sk_err = ECONNREFUSED;  		cf_sk->sk.sk_state = CAIF_DISCONNECTED;  		cf_sk->sk.sk_shutdown = SHUTDOWN_MASK; @@ -261,7 +243,6 @@ static void caif_ctrl_cb(struct cflayer *layr,  	case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND:  		/* Modem has closed this connection, or device is down. */ -		dbfs_atomic_inc(&cnt.num_remote_shutdown_ind);  		cf_sk->sk.sk_shutdown = SHUTDOWN_MASK;  		cf_sk->sk.sk_err = ECONNRESET;  		set_rx_flow_on(cf_sk); @@ -281,7 +262,6 @@ static void caif_check_flow_release(struct sock *sk)  		return;  	if (atomic_read(&sk->sk_rmem_alloc) <= sk_rcvbuf_lowwater(cf_sk)) { -			dbfs_atomic_inc(&cnt.num_rx_flow_on);  			set_rx_flow_on(cf_sk);  			caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_ON_REQ);  	} @@ -292,7 +272,7 @@ static void caif_check_flow_release(struct sock *sk)   * changed locking, address handling and added MSG_TRUNC.   */  static int caif_seqpkt_recvmsg(struct kiocb *iocb, struct socket *sock, -				struct msghdr *m, size_t len, int flags) +			       struct msghdr *m, size_t len, int flags)  {  	struct sock *sk = sock->sk; @@ -364,8 +344,8 @@ static long caif_stream_data_wait(struct sock *sk, long timeo)   * changed locking calls, changed address handling.   */  static int caif_stream_recvmsg(struct kiocb *iocb, struct socket *sock, -				struct msghdr *msg, size_t size, -				int flags) +			       struct msghdr *msg, size_t size, +			       int flags)  {  	struct sock *sk = sock->sk;  	int copied = 0; @@ -377,8 +357,6 @@ static int caif_stream_recvmsg(struct kiocb *iocb, struct socket *sock,  	if (flags&MSG_OOB)  		goto out; -	msg->msg_namelen = 0; -  	/*  	 * Lock the socket to prevent queue disordering  	 * while sleeps in memcpy_tomsg @@ -480,7 +458,7 @@ out:   * CAIF flow-on and sock_writable.   */  static long caif_wait_for_flow_on(struct caifsock *cf_sk, -				int wait_writeable, long timeo, int *err) +				  int wait_writeable, long timeo, int *err)  {  	struct sock *sk = &cf_sk->sk;  	DEFINE_WAIT(wait); @@ -519,48 +497,22 @@ static int transmit_skb(struct sk_buff *skb, struct caifsock *cf_sk,  			int noblock, long timeo)  {  	struct cfpkt *pkt; -	int ret, loopcnt = 0;  	pkt = cfpkt_fromnative(CAIF_DIR_OUT, skb); -	memset(cfpkt_info(pkt), 0, sizeof(struct caif_payload_info)); -	do { +	memset(skb->cb, 0, sizeof(struct caif_payload_info)); +	cfpkt_set_prio(pkt, cf_sk->sk.sk_priority); -		ret = -ETIMEDOUT; - -		/* Slight paranoia, probably not needed. */ -		if (unlikely(loopcnt++ > 1000)) { -			pr_warn("transmit retries failed, error = %d\n", ret); -			break; -		} +	if (cf_sk->layer.dn == NULL) { +		kfree_skb(skb); +		return -EINVAL; +	} -		if (cf_sk->layer.dn != NULL) -			ret = cf_sk->layer.dn->transmit(cf_sk->layer.dn, pkt); -		if (likely(ret >= 0)) -			break; -		/* if transmit return -EAGAIN, then retry */ -		if (noblock && ret == -EAGAIN) -			break; -		timeo = caif_wait_for_flow_on(cf_sk, 0, timeo, &ret); -		if (signal_pending(current)) { -			ret = sock_intr_errno(timeo); -			break; -		} -		if (ret) -			break; -		if (cf_sk->sk.sk_state != CAIF_CONNECTED || -			sock_flag(&cf_sk->sk, SOCK_DEAD) || -			(cf_sk->sk.sk_shutdown & RCV_SHUTDOWN)) { -			ret = -EPIPE; -			cf_sk->sk.sk_err = EPIPE; -			break; -		} -	} while (ret == -EAGAIN); -	return ret; +	return cf_sk->layer.dn->transmit(cf_sk->layer.dn, pkt);  }  /* Copied from af_unix:unix_dgram_sendmsg, and adapted to CAIF */  static int caif_seqpkt_sendmsg(struct kiocb *kiocb, struct socket *sock, -			struct msghdr *msg, size_t len) +			       struct msghdr *msg, size_t len)  {  	struct sock *sk = sock->sk;  	struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); @@ -620,7 +572,9 @@ static int caif_seqpkt_sendmsg(struct kiocb *kiocb, struct socket *sock,  		goto err;  	ret = transmit_skb(skb, cf_sk, noblock, timeo);  	if (ret < 0) -		goto err; +		/* skb is already freed */ +		return ret; +  	return len;  err:  	kfree_skb(skb); @@ -633,7 +587,7 @@ err:   * and other minor adaptations.   */  static int caif_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, -				struct msghdr *msg, size_t len) +			       struct msghdr *msg, size_t len)  {  	struct sock *sk = sock->sk;  	struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); @@ -694,10 +648,10 @@ static int caif_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,  		}  		err = transmit_skb(skb, cf_sk,  				msg->msg_flags&MSG_DONTWAIT, timeo); -		if (err < 0) { -			kfree_skb(skb); +		if (err < 0) +			/* skb is already freed */  			goto pipe_err; -		} +  		sent += size;  	} @@ -712,7 +666,7 @@ out_err:  }  static int setsockopt(struct socket *sock, -			int lvl, int opt, char __user *ov, unsigned int ol) +		      int lvl, int opt, char __user *ov, unsigned int ol)  {  	struct sock *sk = sock->sk;  	struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); @@ -740,12 +694,12 @@ static int setsockopt(struct socket *sock,  		if (cf_sk->sk.sk_protocol != CAIFPROTO_UTIL)  			return -ENOPROTOOPT;  		lock_sock(&(cf_sk->sk)); -		cf_sk->conn_req.param.size = ol;  		if (ol > sizeof(cf_sk->conn_req.param.data) ||  			copy_from_user(&cf_sk->conn_req.param.data, ov, ol)) {  			release_sock(&cf_sk->sk);  			return -EINVAL;  		} +		cf_sk->conn_req.param.size = ol;  		release_sock(&cf_sk->sk);  		return 0; @@ -826,7 +780,8 @@ static int caif_connect(struct socket *sock, struct sockaddr *uaddr,  				sk->sk_state == CAIF_DISCONNECTED);  		if (sk->sk_shutdown & SHUTDOWN_MASK) {  			/* Allow re-connect after SHUTDOWN_IND */ -			caif_disconnect_client(&cf_sk->layer); +			caif_disconnect_client(sock_net(sk), &cf_sk->layer); +			caif_free_client(&cf_sk->layer);  			break;  		}  		/* No reconnect on a seqpacket socket */ @@ -864,10 +819,11 @@ static int caif_connect(struct socket *sock, struct sockaddr *uaddr,  	/*ifindex = id of the interface.*/  	cf_sk->conn_req.ifindex = cf_sk->sk.sk_bound_dev_if; -	dbfs_atomic_inc(&cnt.num_connect_req);  	cf_sk->layer.receive = caif_sktrecv_cb; -	err = caif_connect_client(&cf_sk->conn_req, + +	err = caif_connect_client(sock_net(sk), &cf_sk->conn_req,  				&cf_sk->layer, &ifindex, &headroom, &tailroom); +  	if (err < 0) {  		cf_sk->sk.sk_socket->state = SS_UNCONNECTED;  		cf_sk->sk.sk_state = CAIF_DISCONNECTED; @@ -935,7 +891,6 @@ static int caif_release(struct socket *sock)  {  	struct sock *sk = sock->sk;  	struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); -	int res = 0;  	if (!sk)  		return 0; @@ -947,13 +902,12 @@ static int caif_release(struct socket *sock)  	 * caif_queue_rcv_skb checks SOCK_DEAD holding the queue lock,  	 * this ensures no packets when sock is dead.  	 */ -	spin_lock(&sk->sk_receive_queue.lock); +	spin_lock_bh(&sk->sk_receive_queue.lock);  	sock_set_flag(sk, SOCK_DEAD); -	spin_unlock(&sk->sk_receive_queue.lock); +	spin_unlock_bh(&sk->sk_receive_queue.lock);  	sock->sk = NULL; -	dbfs_atomic_inc(&cnt.num_disconnect); - +	WARN_ON(IS_ERR(cf_sk->debugfs_socket_dir));  	if (cf_sk->debugfs_socket_dir != NULL)  		debugfs_remove_recursive(cf_sk->debugfs_socket_dir); @@ -961,24 +915,20 @@ static int caif_release(struct socket *sock)  	sk->sk_state = CAIF_DISCONNECTED;  	sk->sk_shutdown = SHUTDOWN_MASK; -	if (cf_sk->sk.sk_socket->state == SS_CONNECTED || -		cf_sk->sk.sk_socket->state == SS_CONNECTING) -		res = caif_disconnect_client(&cf_sk->layer); - +	caif_disconnect_client(sock_net(sk), &cf_sk->layer);  	cf_sk->sk.sk_socket->state = SS_DISCONNECTING;  	wake_up_interruptible_poll(sk_sleep(sk), POLLERR|POLLHUP);  	sock_orphan(sk); -	cf_sk->layer.dn = NULL;  	sk_stream_kill_queues(&cf_sk->sk);  	release_sock(sk);  	sock_put(sk); -	return res; +	return 0;  }  /* Copied from af_unix.c:unix_poll(), added CAIF tx_flow handling */  static unsigned int caif_poll(struct file *file, -				struct socket *sock, poll_table *wait) +			      struct socket *sock, poll_table *wait)  {  	struct sock *sk = sock->sk;  	unsigned int mask; @@ -1060,15 +1010,15 @@ static void caif_sock_destructor(struct sock *sk)  	caif_assert(sk_unhashed(sk));  	caif_assert(!sk->sk_socket);  	if (!sock_flag(sk, SOCK_DEAD)) { -		pr_info("Attempt to release alive CAIF socket: %p\n", sk); +		pr_debug("Attempt to release alive CAIF socket: %p\n", sk);  		return;  	}  	sk_stream_kill_queues(&cf_sk->sk); -	dbfs_atomic_dec(&cnt.caif_nr_socks); +	caif_free_client(&cf_sk->layer);  }  static int caif_create(struct net *net, struct socket *sock, int protocol, -			int kern) +		       int kern)  {  	struct sock *sk = NULL;  	struct caifsock *cf_sk = NULL; @@ -1108,6 +1058,18 @@ static int caif_create(struct net *net, struct socket *sock, int protocol,  	/* Store the protocol */  	sk->sk_protocol = (unsigned char) protocol; +	/* Initialize default priority for well-known cases */ +	switch (protocol) { +	case CAIFPROTO_AT: +		sk->sk_priority = TC_PRIO_CONTROL; +		break; +	case CAIFPROTO_RFM: +		sk->sk_priority = TC_PRIO_INTERACTIVE_BULK; +		break; +	default: +		sk->sk_priority = TC_PRIO_BESTEFFORT; +	} +  	/*  	 * Lock in order to try to stop someone from opening the socket  	 * too early. @@ -1127,35 +1089,8 @@ static int caif_create(struct net *net, struct socket *sock, int protocol,  	set_rx_flow_on(cf_sk);  	/* Set default options on configuration */ -	cf_sk->sk.sk_priority= CAIF_PRIO_NORMAL;  	cf_sk->conn_req.link_selector = CAIF_LINK_LOW_LATENCY;  	cf_sk->conn_req.protocol = protocol; -	/* Increase the number of sockets created. */ -	dbfs_atomic_inc(&cnt.caif_nr_socks); -#ifdef CONFIG_DEBUG_FS -	if (!IS_ERR(debugfsdir)) { -		/* Fill in some information concerning the misc socket. */ -		snprintf(cf_sk->name, sizeof(cf_sk->name), "cfsk%d", -				atomic_read(&cnt.caif_nr_socks)); - -		cf_sk->debugfs_socket_dir = -			debugfs_create_dir(cf_sk->name, debugfsdir); -		debugfs_create_u32("sk_state", S_IRUSR | S_IWUSR, -				cf_sk->debugfs_socket_dir, -				(u32 *) &cf_sk->sk.sk_state); -		debugfs_create_u32("flow_state", S_IRUSR | S_IWUSR, -				cf_sk->debugfs_socket_dir, &cf_sk->flow_state); -		debugfs_create_u32("sk_rmem_alloc", S_IRUSR | S_IWUSR, -				cf_sk->debugfs_socket_dir, -				(u32 *) &cf_sk->sk.sk_rmem_alloc); -		debugfs_create_u32("sk_wmem_alloc", S_IRUSR | S_IWUSR, -				cf_sk->debugfs_socket_dir, -				(u32 *) &cf_sk->sk.sk_wmem_alloc); -		debugfs_create_u32("identity", S_IRUSR | S_IWUSR, -				cf_sk->debugfs_socket_dir, -				(u32 *) &cf_sk->layer.id); -	} -#endif  	release_sock(&cf_sk->sk);  	return 0;  } @@ -1167,7 +1102,7 @@ static struct net_proto_family caif_family_ops = {  	.owner = THIS_MODULE,  }; -static int af_caif_init(void) +static int __init caif_sktinit_module(void)  {  	int err = sock_register(&caif_family_ops);  	if (!err) @@ -1175,51 +1110,9 @@ static int af_caif_init(void)  	return 0;  } -static int __init caif_sktinit_module(void) -{ -#ifdef CONFIG_DEBUG_FS -	debugfsdir = debugfs_create_dir("caif_sk", NULL); -	if (!IS_ERR(debugfsdir)) { -		debugfs_create_u32("num_sockets", S_IRUSR | S_IWUSR, -				debugfsdir, -				(u32 *) &cnt.caif_nr_socks); -		debugfs_create_u32("num_connect_req", S_IRUSR | S_IWUSR, -				debugfsdir, -				(u32 *) &cnt.num_connect_req); -		debugfs_create_u32("num_connect_resp", S_IRUSR | S_IWUSR, -				debugfsdir, -				(u32 *) &cnt.num_connect_resp); -		debugfs_create_u32("num_connect_fail_resp", S_IRUSR | S_IWUSR, -				debugfsdir, -				(u32 *) &cnt.num_connect_fail_resp); -		debugfs_create_u32("num_disconnect", S_IRUSR | S_IWUSR, -				debugfsdir, -				(u32 *) &cnt.num_disconnect); -		debugfs_create_u32("num_remote_shutdown_ind", -				S_IRUSR | S_IWUSR, debugfsdir, -				(u32 *) &cnt.num_remote_shutdown_ind); -		debugfs_create_u32("num_tx_flow_off_ind", S_IRUSR | S_IWUSR, -				debugfsdir, -				(u32 *) &cnt.num_tx_flow_off_ind); -		debugfs_create_u32("num_tx_flow_on_ind", S_IRUSR | S_IWUSR, -				debugfsdir, -				(u32 *) &cnt.num_tx_flow_on_ind); -		debugfs_create_u32("num_rx_flow_off", S_IRUSR | S_IWUSR, -				debugfsdir, -				(u32 *) &cnt.num_rx_flow_off); -		debugfs_create_u32("num_rx_flow_on", S_IRUSR | S_IWUSR, -				debugfsdir, -				(u32 *) &cnt.num_rx_flow_on); -	} -#endif -	return af_caif_init(); -} -  static void __exit caif_sktexit_module(void)  {  	sock_unregister(PF_CAIF); -	if (debugfsdir != NULL) -		debugfs_remove_recursive(debugfsdir);  }  module_init(caif_sktinit_module);  module_exit(caif_sktexit_module); diff --git a/net/caif/caif_usb.c b/net/caif/caif_usb.c new file mode 100644 index 00000000000..ba02db02290 --- /dev/null +++ b/net/caif/caif_usb.c @@ -0,0 +1,204 @@ +/* + * CAIF USB handler + * Copyright (C) ST-Ericsson AB 2011 + * Author:	Sjur Brendeland + * License terms: GNU General Public License (GPL) version 2 + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ + +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/slab.h> +#include <linux/mii.h> +#include <linux/usb.h> +#include <linux/usb/usbnet.h> +#include <linux/etherdevice.h> +#include <net/netns/generic.h> +#include <net/caif/caif_dev.h> +#include <net/caif/caif_layer.h> +#include <net/caif/cfpkt.h> +#include <net/caif/cfcnfg.h> + +MODULE_LICENSE("GPL"); + +#define CFUSB_PAD_DESCR_SZ 1	/* Alignment descriptor length */ +#define CFUSB_ALIGNMENT 4	/* Number of bytes to align. */ +#define CFUSB_MAX_HEADLEN (CFUSB_PAD_DESCR_SZ + CFUSB_ALIGNMENT-1) +#define STE_USB_VID 0x04cc	/* USB Product ID for ST-Ericsson */ +#define STE_USB_PID_CAIF 0x230f	/* Product id for CAIF Modems */ + +struct cfusbl { +	struct cflayer layer; +	u8 tx_eth_hdr[ETH_HLEN]; +}; + +static bool pack_added; + +static int cfusbl_receive(struct cflayer *layr, struct cfpkt *pkt) +{ +	u8 hpad; + +	/* Remove padding. */ +	cfpkt_extr_head(pkt, &hpad, 1); +	cfpkt_extr_head(pkt, NULL, hpad); +	return layr->up->receive(layr->up, pkt); +} + +static int cfusbl_transmit(struct cflayer *layr, struct cfpkt *pkt) +{ +	struct caif_payload_info *info; +	u8 hpad; +	u8 zeros[CFUSB_ALIGNMENT]; +	struct sk_buff *skb; +	struct cfusbl *usbl = container_of(layr, struct cfusbl, layer); + +	skb = cfpkt_tonative(pkt); + +	skb_reset_network_header(skb); +	skb->protocol = htons(ETH_P_IP); + +	info = cfpkt_info(pkt); +	hpad = (info->hdr_len + CFUSB_PAD_DESCR_SZ) & (CFUSB_ALIGNMENT - 1); + +	if (skb_headroom(skb) < ETH_HLEN + CFUSB_PAD_DESCR_SZ + hpad) { +		pr_warn("Headroom to small\n"); +		kfree_skb(skb); +		return -EIO; +	} +	memset(zeros, 0, hpad); + +	cfpkt_add_head(pkt, zeros, hpad); +	cfpkt_add_head(pkt, &hpad, 1); +	cfpkt_add_head(pkt, usbl->tx_eth_hdr, sizeof(usbl->tx_eth_hdr)); +	return layr->dn->transmit(layr->dn, pkt); +} + +static void cfusbl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, +			   int phyid) +{ +	if (layr->up && layr->up->ctrlcmd) +		layr->up->ctrlcmd(layr->up, ctrl, layr->id); +} + +static struct cflayer *cfusbl_create(int phyid, u8 ethaddr[ETH_ALEN], +				      u8 braddr[ETH_ALEN]) +{ +	struct cfusbl *this = kmalloc(sizeof(struct cfusbl), GFP_ATOMIC); + +	if (!this) { +		pr_warn("Out of memory\n"); +		return NULL; +	} +	caif_assert(offsetof(struct cfusbl, layer) == 0); + +	memset(this, 0, sizeof(struct cflayer)); +	this->layer.receive = cfusbl_receive; +	this->layer.transmit = cfusbl_transmit; +	this->layer.ctrlcmd = cfusbl_ctrlcmd; +	snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "usb%d", phyid); +	this->layer.id = phyid; + +	/* +	 * Construct TX ethernet header: +	 *	0-5	destination address +	 *	5-11	source address +	 *	12-13	protocol type +	 */ +	ether_addr_copy(&this->tx_eth_hdr[ETH_ALEN], braddr); +	ether_addr_copy(&this->tx_eth_hdr[ETH_ALEN], ethaddr); +	this->tx_eth_hdr[12] = cpu_to_be16(ETH_P_802_EX1) & 0xff; +	this->tx_eth_hdr[13] = (cpu_to_be16(ETH_P_802_EX1) >> 8) & 0xff; +	pr_debug("caif ethernet TX-header dst:%pM src:%pM type:%02x%02x\n", +			this->tx_eth_hdr, this->tx_eth_hdr + ETH_ALEN, +			this->tx_eth_hdr[12], this->tx_eth_hdr[13]); + +	return (struct cflayer *) this; +} + +static struct packet_type caif_usb_type __read_mostly = { +	.type = cpu_to_be16(ETH_P_802_EX1), +}; + +static int cfusbl_device_notify(struct notifier_block *me, unsigned long what, +				void *ptr) +{ +	struct net_device *dev = netdev_notifier_info_to_dev(ptr); +	struct caif_dev_common common; +	struct cflayer *layer, *link_support; +	struct usbnet *usbnet; +	struct usb_device *usbdev; + +	/* Check whether we have a NCM device, and find its VID/PID. */ +	if (!(dev->dev.parent && dev->dev.parent->driver && +	      strcmp(dev->dev.parent->driver->name, "cdc_ncm") == 0)) +		return 0; + +	usbnet = netdev_priv(dev); +	usbdev = usbnet->udev; + +	pr_debug("USB CDC NCM device VID:0x%4x PID:0x%4x\n", +		le16_to_cpu(usbdev->descriptor.idVendor), +		le16_to_cpu(usbdev->descriptor.idProduct)); + +	/* Check for VID/PID that supports CAIF */ +	if (!(le16_to_cpu(usbdev->descriptor.idVendor) == STE_USB_VID && +		le16_to_cpu(usbdev->descriptor.idProduct) == STE_USB_PID_CAIF)) +		return 0; + +	if (what == NETDEV_UNREGISTER) +		module_put(THIS_MODULE); + +	if (what != NETDEV_REGISTER) +		return 0; + +	__module_get(THIS_MODULE); + +	memset(&common, 0, sizeof(common)); +	common.use_frag = false; +	common.use_fcs = false; +	common.use_stx = false; +	common.link_select = CAIF_LINK_HIGH_BANDW; +	common.flowctrl = NULL; + +	link_support = cfusbl_create(dev->ifindex, dev->dev_addr, +					dev->broadcast); + +	if (!link_support) +		return -ENOMEM; + +	if (dev->num_tx_queues > 1) +		pr_warn("USB device uses more than one tx queue\n"); + +	caif_enroll_dev(dev, &common, link_support, CFUSB_MAX_HEADLEN, +			&layer, &caif_usb_type.func); +	if (!pack_added) +		dev_add_pack(&caif_usb_type); +	pack_added = true; + +	strncpy(layer->name, dev->name, +			sizeof(layer->name) - 1); +	layer->name[sizeof(layer->name) - 1] = 0; + +	return 0; +} + +static struct notifier_block caif_device_notifier = { +	.notifier_call = cfusbl_device_notify, +	.priority = 0, +}; + +static int __init cfusbl_init(void) +{ +	return register_netdevice_notifier(&caif_device_notifier); +} + +static void __exit cfusbl_exit(void) +{ +	unregister_netdevice_notifier(&caif_device_notifier); +	dev_remove_pack(&caif_usb_type); +} + +module_init(cfusbl_init); +module_exit(cfusbl_exit); diff --git a/net/caif/cfcnfg.c b/net/caif/cfcnfg.c index 21ede141018..fa39fc29870 100644 --- a/net/caif/cfcnfg.c +++ b/net/caif/cfcnfg.c @@ -1,6 +1,6 @@  /*   * Copyright (C) ST-Ericsson AB 2010 - * Author:	Sjur Brendeland/sjur.brandeland@stericsson.com + * Author:	Sjur Brendeland   * License terms: GNU General Public License (GPL) version 2   */ @@ -10,6 +10,7 @@  #include <linux/stddef.h>  #include <linux/slab.h>  #include <linux/netdevice.h> +#include <linux/module.h>  #include <net/caif/caif_layer.h>  #include <net/caif/cfpkt.h>  #include <net/caif/cfcnfg.h> @@ -18,20 +19,17 @@  #include <net/caif/cffrml.h>  #include <net/caif/cfserl.h>  #include <net/caif/cfsrvl.h> - -#include <linux/module.h> -#include <asm/atomic.h> - -#define MAX_PHY_LAYERS 7 -#define PHY_NAME_LEN 20 +#include <net/caif/caif_dev.h>  #define container_obj(layr) container_of(layr, struct cfcnfg, layer) -#define RFM_FRAGMENT_SIZE 4030  /* Information about CAIF physical interfaces held by Config Module in order   * to manage physical interfaces   */  struct cfcnfg_phyinfo { +	struct list_head node; +	bool up; +  	/* Pointer to the layer below the MUX (framing layer) */  	struct cflayer *frm_layer;  	/* Pointer to the lowest actual physical layer */ @@ -41,17 +39,14 @@ struct cfcnfg_phyinfo {  	/* Preference of the physical in interface */  	enum cfcnfg_phy_preference pref; -	/* Reference count, number of channels using the device */ -	int phy_ref_count; -  	/* Information about the physical device */  	struct dev_info dev_info;  	/* Interface index */  	int ifindex; -	/* Use Start of frame extension */ -	bool use_stx; +	/* Protocol head room added for CAIF link layer */ +	int head_room;  	/* Use Start of frame checksum */  	bool use_fcs; @@ -61,16 +56,16 @@ struct cfcnfg {  	struct cflayer layer;  	struct cflayer *ctrl;  	struct cflayer *mux; -	u8 last_phyid; -	struct cfcnfg_phyinfo phy_layers[MAX_PHY_LAYERS]; +	struct list_head phys; +	struct mutex lock;  };  static void cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, -			     enum cfctrl_srv serv, u8 phyid, -			     struct cflayer *adapt_layer); +			      enum cfctrl_srv serv, u8 phyid, +			      struct cflayer *adapt_layer);  static void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id);  static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id, -			     struct cflayer *adapt_layer); +			      struct cflayer *adapt_layer);  static void cfctrl_resp_func(void);  static void cfctrl_enum_resp(void); @@ -78,12 +73,13 @@ struct cfcnfg *cfcnfg_create(void)  {  	struct cfcnfg *this;  	struct cfctrl_rsp *resp; + +	might_sleep(); +  	/* Initiate this layer */  	this = kzalloc(sizeof(struct cfcnfg), GFP_ATOMIC); -	if (!this) { -		pr_warn("Out of memory\n"); +	if (!this)  		return NULL; -	}  	this->mux = cfmuxl_create();  	if (!this->mux)  		goto out_of_mem; @@ -101,27 +97,31 @@ struct cfcnfg *cfcnfg_create(void)  	resp->radioset_rsp = cfctrl_resp_func;  	resp->linksetup_rsp = cfcnfg_linkup_rsp;  	resp->reject_rsp = cfcnfg_reject_rsp; - -	this->last_phyid = 1; +	INIT_LIST_HEAD(&this->phys);  	cfmuxl_set_uplayer(this->mux, this->ctrl, 0);  	layer_set_dn(this->ctrl, this->mux);  	layer_set_up(this->ctrl, this); +	mutex_init(&this->lock); +  	return this;  out_of_mem: -	pr_warn("Out of memory\n"); +	synchronize_rcu(); +  	kfree(this->mux);  	kfree(this->ctrl);  	kfree(this);  	return NULL;  } -EXPORT_SYMBOL(cfcnfg_create);  void cfcnfg_remove(struct cfcnfg *cfg)  { +	might_sleep();  	if (cfg) { +		synchronize_rcu(); +  		kfree(cfg->mux); -		kfree(cfg->ctrl); +		cfctrl_remove(cfg->ctrl);  		kfree(cfg);  	}  } @@ -130,131 +130,83 @@ static void cfctrl_resp_func(void)  {  } +static struct cfcnfg_phyinfo *cfcnfg_get_phyinfo_rcu(struct cfcnfg *cnfg, +						     u8 phyid) +{ +	struct cfcnfg_phyinfo *phy; + +	list_for_each_entry_rcu(phy, &cnfg->phys, node) +		if (phy->id == phyid) +			return phy; +	return NULL; +} +  static void cfctrl_enum_resp(void)  {  } -struct dev_info *cfcnfg_get_phyid(struct cfcnfg *cnfg, +static struct dev_info *cfcnfg_get_phyid(struct cfcnfg *cnfg,  				  enum cfcnfg_phy_preference phy_pref)  { -	u16 i; -  	/* Try to match with specified preference */ -	for (i = 1; i < MAX_PHY_LAYERS; i++) { -		if (cnfg->phy_layers[i].id == i && -		     cnfg->phy_layers[i].pref == phy_pref && -		     cnfg->phy_layers[i].frm_layer != NULL) { -			caif_assert(cnfg->phy_layers != NULL); -			caif_assert(cnfg->phy_layers[i].id == i); -			return &cnfg->phy_layers[i].dev_info; -		} +	struct cfcnfg_phyinfo *phy; + +	list_for_each_entry_rcu(phy, &cnfg->phys, node) { +		if (phy->up && phy->pref == phy_pref && +				phy->frm_layer != NULL) + +			return &phy->dev_info;  	} +  	/* Otherwise just return something */ -	for (i = 1; i < MAX_PHY_LAYERS; i++) { -		if (cnfg->phy_layers[i].id == i) { -			caif_assert(cnfg->phy_layers != NULL); -			caif_assert(cnfg->phy_layers[i].id == i); -			return &cnfg->phy_layers[i].dev_info; -		} -	} +	list_for_each_entry_rcu(phy, &cnfg->phys, node) +		if (phy->up) +			return &phy->dev_info;  	return NULL;  } -static struct cfcnfg_phyinfo *cfcnfg_get_phyinfo(struct cfcnfg *cnfg, -							u8 phyid) +static int cfcnfg_get_id_from_ifi(struct cfcnfg *cnfg, int ifi)  { -	int i; -	/* Try to match with specified preference */ -	for (i = 0; i < MAX_PHY_LAYERS; i++) -		if (cnfg->phy_layers[i].frm_layer != NULL && -		    cnfg->phy_layers[i].id == phyid) -			return &cnfg->phy_layers[i]; -	return NULL; -} +	struct cfcnfg_phyinfo *phy; - -int cfcnfg_get_id_from_ifi(struct cfcnfg *cnfg, int ifi) -{ -	int i; -	for (i = 0; i < MAX_PHY_LAYERS; i++) -		if (cnfg->phy_layers[i].frm_layer != NULL && -				cnfg->phy_layers[i].ifindex == ifi) -			return i; +	list_for_each_entry_rcu(phy, &cnfg->phys, node) +		if (phy->ifindex == ifi && phy->up) +			return phy->id;  	return -ENODEV;  } -int cfcnfg_disconn_adapt_layer(struct cfcnfg *cnfg, struct cflayer *adap_layer) +int caif_disconnect_client(struct net *net, struct cflayer *adap_layer)  { -	u8 channel_id = 0; -	int ret = 0; -	struct cflayer *servl = NULL; -	struct cfcnfg_phyinfo *phyinfo = NULL; -	u8 phyid = 0; +	u8 channel_id; +	struct cfcnfg *cfg = get_cfcnfg(net); +  	caif_assert(adap_layer != NULL); +	cfctrl_cancel_req(cfg->ctrl, adap_layer);  	channel_id = adap_layer->id; -	if (adap_layer->dn == NULL || channel_id == 0) { -		pr_err("adap_layer->dn == NULL or adap_layer->id is 0\n"); -		ret = -ENOTCONN; -		goto end; -	} -	servl = cfmuxl_remove_uplayer(cnfg->mux, channel_id); -	if (servl == NULL) -		goto end; -	layer_set_up(servl, NULL); -	ret = cfctrl_linkdown_req(cnfg->ctrl, channel_id, adap_layer); -	if (servl == NULL) { -		pr_err("PROTOCOL ERROR - Error removing service_layer Channel_Id(%d)", -		       channel_id); -		ret = -EINVAL; -		goto end; -	} -	caif_assert(channel_id == servl->id); -	if (adap_layer->dn != NULL) { -		phyid = cfsrvl_getphyid(adap_layer->dn); - -		phyinfo = cfcnfg_get_phyinfo(cnfg, phyid); -		if (phyinfo == NULL) { -			pr_warn("No interface to send disconnect to\n"); -			ret = -ENODEV; -			goto end; -		} -		if (phyinfo->id != phyid || -			phyinfo->phy_layer->id != phyid || -			phyinfo->frm_layer->id != phyid) { -			pr_err("Inconsistency in phy registration\n"); -			ret = -EINVAL; -			goto end; -		} -	} -	if (phyinfo != NULL && --phyinfo->phy_ref_count == 0 && -		phyinfo->phy_layer != NULL && -		phyinfo->phy_layer->modemcmd != NULL) { -		phyinfo->phy_layer->modemcmd(phyinfo->phy_layer, -					     _CAIF_MODEMCMD_PHYIF_USELESS); -	} -end: -	cfsrvl_put(servl); -	cfctrl_cancel_req(cnfg->ctrl, adap_layer); +	if (channel_id != 0) { +		struct cflayer *servl; +		servl = cfmuxl_remove_uplayer(cfg->mux, channel_id); +		cfctrl_linkdown_req(cfg->ctrl, channel_id, adap_layer); +		if (servl != NULL) +			layer_set_up(servl, NULL); +	} else +		pr_debug("nothing to disconnect\n"); + +	/* Do RCU sync before initiating cleanup */ +	synchronize_rcu();  	if (adap_layer->ctrlcmd != NULL)  		adap_layer->ctrlcmd(adap_layer, CAIF_CTRLCMD_DEINIT_RSP, 0); -	return ret; - -} -EXPORT_SYMBOL(cfcnfg_disconn_adapt_layer); +	return 0; -void cfcnfg_release_adap_layer(struct cflayer *adap_layer) -{ -	if (adap_layer->dn) -		cfsrvl_put(adap_layer->dn);  } -EXPORT_SYMBOL(cfcnfg_release_adap_layer); +EXPORT_SYMBOL(caif_disconnect_client);  static void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id)  {  } -int protohead[CFCTRL_SRV_MASK] = { +static const int protohead[CFCTRL_SRV_MASK] = {  	[CFCTRL_SRV_VEI] = 4,  	[CFCTRL_SRV_DATAGRAM] = 7,  	[CFCTRL_SRV_UTIL] = 4, @@ -262,52 +214,156 @@ int protohead[CFCTRL_SRV_MASK] = {  	[CFCTRL_SRV_DBG] = 3,  }; -int cfcnfg_add_adaptation_layer(struct cfcnfg *cnfg, -				struct cfctrl_link_param *param, -				struct cflayer *adap_layer, -				int *ifindex, -				int *proto_head, -				int *proto_tail) + +static int caif_connect_req_to_link_param(struct cfcnfg *cnfg, +					  struct caif_connect_request *s, +					  struct cfctrl_link_param *l) +{ +	struct dev_info *dev_info; +	enum cfcnfg_phy_preference pref; +	int res; + +	memset(l, 0, sizeof(*l)); +	/* In caif protocol low value is high priority */ +	l->priority = CAIF_PRIO_MAX - s->priority + 1; + +	if (s->ifindex != 0) { +		res = cfcnfg_get_id_from_ifi(cnfg, s->ifindex); +		if (res < 0) +			return res; +		l->phyid = res; +	} else { +		switch (s->link_selector) { +		case CAIF_LINK_HIGH_BANDW: +			pref = CFPHYPREF_HIGH_BW; +			break; +		case CAIF_LINK_LOW_LATENCY: +			pref = CFPHYPREF_LOW_LAT; +			break; +		default: +			return -EINVAL; +		} +		dev_info = cfcnfg_get_phyid(cnfg, pref); +		if (dev_info == NULL) +			return -ENODEV; +		l->phyid = dev_info->id; +	} +	switch (s->protocol) { +	case CAIFPROTO_AT: +		l->linktype = CFCTRL_SRV_VEI; +		l->endpoint = (s->sockaddr.u.at.type >> 2) & 0x3; +		l->chtype = s->sockaddr.u.at.type & 0x3; +		break; +	case CAIFPROTO_DATAGRAM: +		l->linktype = CFCTRL_SRV_DATAGRAM; +		l->chtype = 0x00; +		l->u.datagram.connid = s->sockaddr.u.dgm.connection_id; +		break; +	case CAIFPROTO_DATAGRAM_LOOP: +		l->linktype = CFCTRL_SRV_DATAGRAM; +		l->chtype = 0x03; +		l->endpoint = 0x00; +		l->u.datagram.connid = s->sockaddr.u.dgm.connection_id; +		break; +	case CAIFPROTO_RFM: +		l->linktype = CFCTRL_SRV_RFM; +		l->u.datagram.connid = s->sockaddr.u.rfm.connection_id; +		strncpy(l->u.rfm.volume, s->sockaddr.u.rfm.volume, +			sizeof(l->u.rfm.volume)-1); +		l->u.rfm.volume[sizeof(l->u.rfm.volume)-1] = 0; +		break; +	case CAIFPROTO_UTIL: +		l->linktype = CFCTRL_SRV_UTIL; +		l->endpoint = 0x00; +		l->chtype = 0x00; +		strncpy(l->u.utility.name, s->sockaddr.u.util.service, +			sizeof(l->u.utility.name)-1); +		l->u.utility.name[sizeof(l->u.utility.name)-1] = 0; +		caif_assert(sizeof(l->u.utility.name) > 10); +		l->u.utility.paramlen = s->param.size; +		if (l->u.utility.paramlen > sizeof(l->u.utility.params)) +			l->u.utility.paramlen = sizeof(l->u.utility.params); + +		memcpy(l->u.utility.params, s->param.data, +		       l->u.utility.paramlen); + +		break; +	case CAIFPROTO_DEBUG: +		l->linktype = CFCTRL_SRV_DBG; +		l->endpoint = s->sockaddr.u.dbg.service; +		l->chtype = s->sockaddr.u.dbg.type; +		break; +	default: +		return -EINVAL; +	} +	return 0; +} + +int caif_connect_client(struct net *net, struct caif_connect_request *conn_req, +			struct cflayer *adap_layer, int *ifindex, +			int *proto_head, int *proto_tail)  {  	struct cflayer *frml; +	struct cfcnfg_phyinfo *phy; +	int err; +	struct cfctrl_link_param param; +	struct cfcnfg *cfg = get_cfcnfg(net); + +	rcu_read_lock(); +	err = caif_connect_req_to_link_param(cfg, conn_req, ¶m); +	if (err) +		goto unlock; + +	phy = cfcnfg_get_phyinfo_rcu(cfg, param.phyid); +	if (!phy) { +		err = -ENODEV; +		goto unlock; +	} +	err = -EINVAL; +  	if (adap_layer == NULL) {  		pr_err("adap_layer is zero\n"); -		return -EINVAL; +		goto unlock;  	}  	if (adap_layer->receive == NULL) {  		pr_err("adap_layer->receive is NULL\n"); -		return -EINVAL; +		goto unlock;  	}  	if (adap_layer->ctrlcmd == NULL) {  		pr_err("adap_layer->ctrlcmd == NULL\n"); -		return -EINVAL; +		goto unlock;  	} -	frml = cnfg->phy_layers[param->phyid].frm_layer; + +	err = -ENODEV; +	frml = phy->frm_layer;  	if (frml == NULL) {  		pr_err("Specified PHY type does not exist!\n"); -		return -ENODEV; +		goto unlock;  	} -	caif_assert(param->phyid == cnfg->phy_layers[param->phyid].id); -	caif_assert(cnfg->phy_layers[param->phyid].frm_layer->id == -		     param->phyid); -	caif_assert(cnfg->phy_layers[param->phyid].phy_layer->id == -		     param->phyid); - -	*ifindex = cnfg->phy_layers[param->phyid].ifindex; -	*proto_head = -		protohead[param->linktype]+ -		(cnfg->phy_layers[param->phyid].use_stx ? 1 : 0); +	caif_assert(param.phyid == phy->id); +	caif_assert(phy->frm_layer->id == +		     param.phyid); +	caif_assert(phy->phy_layer->id == +		     param.phyid); +	*ifindex = phy->ifindex;  	*proto_tail = 2; +	*proto_head = protohead[param.linktype] + phy->head_room; + +	rcu_read_unlock();  	/* FIXME: ENUMERATE INITIALLY WHEN ACTIVATING PHYSICAL INTERFACE */ -	cfctrl_enum_req(cnfg->ctrl, param->phyid); -	return cfctrl_linkup_request(cnfg->ctrl, param, adap_layer); +	cfctrl_enum_req(cfg->ctrl, param.phyid); +	return cfctrl_linkup_request(cfg->ctrl, ¶m, adap_layer); + +unlock: +	rcu_read_unlock(); +	return err;  } -EXPORT_SYMBOL(cfcnfg_add_adaptation_layer); +EXPORT_SYMBOL(caif_connect_client);  static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id, -			     struct cflayer *adapt_layer) +			      struct cflayer *adapt_layer)  {  	if (adapt_layer != NULL && adapt_layer->ctrlcmd != NULL)  		adapt_layer->ctrlcmd(adapt_layer, @@ -316,32 +372,45 @@ static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id,  static void  cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv, -		 u8 phyid, struct cflayer *adapt_layer) +		  u8 phyid, struct cflayer *adapt_layer)  {  	struct cfcnfg *cnfg = container_obj(layer);  	struct cflayer *servicel = NULL;  	struct cfcnfg_phyinfo *phyinfo;  	struct net_device *netdev; +	if (channel_id == 0) { +		pr_warn("received channel_id zero\n"); +		if (adapt_layer != NULL && adapt_layer->ctrlcmd != NULL) +			adapt_layer->ctrlcmd(adapt_layer, +						CAIF_CTRLCMD_INIT_FAIL_RSP, 0); +		return; +	} + +	rcu_read_lock(); +  	if (adapt_layer == NULL) { -		pr_debug("link setup response but no client exist, send linkdown back\n"); +		pr_debug("link setup response but no client exist," +				"send linkdown back\n");  		cfctrl_linkdown_req(cnfg->ctrl, channel_id, NULL); -		return; +		goto unlock;  	}  	caif_assert(cnfg != NULL);  	caif_assert(phyid != 0); -	phyinfo = &cnfg->phy_layers[phyid]; + +	phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phyid); +	if (phyinfo == NULL) { +		pr_err("ERROR: Link Layer Device disappeared" +				"while connecting\n"); +		goto unlock; +	} + +	caif_assert(phyinfo != NULL);  	caif_assert(phyinfo->id == phyid);  	caif_assert(phyinfo->phy_layer != NULL);  	caif_assert(phyinfo->phy_layer->id == phyid); -	phyinfo->phy_ref_count++; -	if (phyinfo->phy_ref_count == 1 && -	    phyinfo->phy_layer->modemcmd != NULL) { -		phyinfo->phy_layer->modemcmd(phyinfo->phy_layer, -					     _CAIF_MODEMCMD_PHYIF_USEFULL); -	}  	adapt_layer->id = channel_id;  	switch (serv) { @@ -349,7 +418,8 @@ cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv,  		servicel = cfvei_create(channel_id, &phyinfo->dev_info);  		break;  	case CFCTRL_SRV_DATAGRAM: -		servicel = cfdgml_create(channel_id, &phyinfo->dev_info); +		servicel = cfdgml_create(channel_id, +					&phyinfo->dev_info);  		break;  	case CFCTRL_SRV_RFM:  		netdev = phyinfo->dev_info.dev; @@ -366,129 +436,176 @@ cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv,  		servicel = cfdbgl_create(channel_id, &phyinfo->dev_info);  		break;  	default: -		pr_err("Protocol error. Link setup response - unknown channel type\n"); -		return; -	} -	if (!servicel) { -		pr_warn("Out of memory\n"); -		return; +		pr_err("Protocol error. Link setup response " +				"- unknown channel type\n"); +		goto unlock;  	} +	if (!servicel) +		goto unlock;  	layer_set_dn(servicel, cnfg->mux);  	cfmuxl_set_uplayer(cnfg->mux, servicel, channel_id);  	layer_set_up(servicel, adapt_layer);  	layer_set_dn(adapt_layer, servicel); -	cfsrvl_get(servicel); + +	rcu_read_unlock(); +  	servicel->ctrlcmd(servicel, CAIF_CTRLCMD_INIT_RSP, 0); +	return; +unlock: +	rcu_read_unlock();  }  void -cfcnfg_add_phy_layer(struct cfcnfg *cnfg, enum cfcnfg_phy_type phy_type, +cfcnfg_add_phy_layer(struct cfcnfg *cnfg,  		     struct net_device *dev, struct cflayer *phy_layer, -		     u16 *phyid, enum cfcnfg_phy_preference pref, -		     bool fcs, bool stx) +		     enum cfcnfg_phy_preference pref, +		     struct cflayer *link_support, +		     bool fcs, int head_room)  {  	struct cflayer *frml; -	struct cflayer *phy_driver = NULL; +	struct cfcnfg_phyinfo *phyinfo = NULL;  	int i; +	u8 phyid; +	mutex_lock(&cnfg->lock); -	if (cnfg->phy_layers[cnfg->last_phyid].frm_layer == NULL) { -		*phyid = cnfg->last_phyid; +	/* CAIF protocol allow maximum 6 link-layers */ +	for (i = 0; i < 7; i++) { +		phyid = (dev->ifindex + i) & 0x7; +		if (phyid == 0) +			continue; +		if (cfcnfg_get_phyinfo_rcu(cnfg, phyid) == NULL) +			goto got_phyid; +	} +	pr_warn("Too many CAIF Link Layers (max 6)\n"); +	goto out; + +got_phyid: +	phyinfo = kzalloc(sizeof(struct cfcnfg_phyinfo), GFP_ATOMIC); +	if (!phyinfo) +		goto out_err; + +	phy_layer->id = phyid; +	phyinfo->pref = pref; +	phyinfo->id = phyid; +	phyinfo->dev_info.id = phyid; +	phyinfo->dev_info.dev = dev; +	phyinfo->phy_layer = phy_layer; +	phyinfo->ifindex = dev->ifindex; +	phyinfo->head_room = head_room; +	phyinfo->use_fcs = fcs; + +	frml = cffrml_create(phyid, fcs); + +	if (!frml) +		goto out_err; +	phyinfo->frm_layer = frml; +	layer_set_up(frml, cnfg->mux); -		/* range: * 1..(MAX_PHY_LAYERS-1) */ -		cnfg->last_phyid = -		    (cnfg->last_phyid % (MAX_PHY_LAYERS - 1)) + 1; +	if (link_support != NULL) { +		link_support->id = phyid; +		layer_set_dn(frml, link_support); +		layer_set_up(link_support, frml); +		layer_set_dn(link_support, phy_layer); +		layer_set_up(phy_layer, link_support);  	} else { -		*phyid = 0; -		for (i = 1; i < MAX_PHY_LAYERS; i++) { -			if (cnfg->phy_layers[i].frm_layer == NULL) { -				*phyid = i; -				break; -			} -		} -	} -	if (*phyid == 0) { -		pr_err("No Available PHY ID\n"); -		return; +		layer_set_dn(frml, phy_layer); +		layer_set_up(phy_layer, frml);  	} -	switch (phy_type) { -	case CFPHYTYPE_FRAG: -		phy_driver = -		    cfserl_create(CFPHYTYPE_FRAG, *phyid, stx); -		if (!phy_driver) { -			pr_warn("Out of memory\n"); -			return; -		} +	list_add_rcu(&phyinfo->node, &cnfg->phys); +out: +	mutex_unlock(&cnfg->lock); +	return; -		break; -	case CFPHYTYPE_CAIF: -		phy_driver = NULL; -		break; -	default: -		pr_err("%d\n", phy_type); -		return; -		break; +out_err: +	kfree(phyinfo); +	mutex_unlock(&cnfg->lock); +} +EXPORT_SYMBOL(cfcnfg_add_phy_layer); + +int cfcnfg_set_phy_state(struct cfcnfg *cnfg, struct cflayer *phy_layer, +			 bool up) +{ +	struct cfcnfg_phyinfo *phyinfo; + +	rcu_read_lock(); +	phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phy_layer->id); +	if (phyinfo == NULL) { +		rcu_read_unlock(); +		return -ENODEV;  	} -	phy_layer->id = *phyid; -	cnfg->phy_layers[*phyid].pref = pref; -	cnfg->phy_layers[*phyid].id = *phyid; -	cnfg->phy_layers[*phyid].dev_info.id = *phyid; -	cnfg->phy_layers[*phyid].dev_info.dev = dev; -	cnfg->phy_layers[*phyid].phy_layer = phy_layer; -	cnfg->phy_layers[*phyid].phy_ref_count = 0; -	cnfg->phy_layers[*phyid].ifindex = dev->ifindex; -	cnfg->phy_layers[*phyid].use_stx = stx; -	cnfg->phy_layers[*phyid].use_fcs = fcs; - -	phy_layer->type = phy_type; -	frml = cffrml_create(*phyid, fcs); -	if (!frml) { -		pr_warn("Out of memory\n"); -		return; +	if (phyinfo->up == up) { +		rcu_read_unlock(); +		return 0;  	} -	cnfg->phy_layers[*phyid].frm_layer = frml; -	cfmuxl_set_dnlayer(cnfg->mux, frml, *phyid); -	layer_set_up(frml, cnfg->mux); +	phyinfo->up = up; -	if (phy_driver != NULL) { -		phy_driver->id = *phyid; -		layer_set_dn(frml, phy_driver); -		layer_set_up(phy_driver, frml); -		layer_set_dn(phy_driver, phy_layer); -		layer_set_up(phy_layer, phy_driver); +	if (up) { +		cffrml_hold(phyinfo->frm_layer); +		cfmuxl_set_dnlayer(cnfg->mux, phyinfo->frm_layer, +					phy_layer->id);  	} else { -		layer_set_dn(frml, phy_layer); -		layer_set_up(phy_layer, frml); +		cfmuxl_remove_dnlayer(cnfg->mux, phy_layer->id); +		cffrml_put(phyinfo->frm_layer);  	} + +	rcu_read_unlock(); +	return 0;  } -EXPORT_SYMBOL(cfcnfg_add_phy_layer); +EXPORT_SYMBOL(cfcnfg_set_phy_state);  int cfcnfg_del_phy_layer(struct cfcnfg *cnfg, struct cflayer *phy_layer)  {  	struct cflayer *frml, *frml_dn;  	u16 phyid; +	struct cfcnfg_phyinfo *phyinfo; + +	might_sleep(); + +	mutex_lock(&cnfg->lock); +  	phyid = phy_layer->id; -	caif_assert(phyid == cnfg->phy_layers[phyid].id); -	caif_assert(phy_layer == cnfg->phy_layers[phyid].phy_layer); +	phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phyid); + +	if (phyinfo == NULL) { +		mutex_unlock(&cnfg->lock); +		return 0; +	} +	caif_assert(phyid == phyinfo->id); +	caif_assert(phy_layer == phyinfo->phy_layer);  	caif_assert(phy_layer->id == phyid); -	caif_assert(cnfg->phy_layers[phyid].frm_layer->id == phyid); +	caif_assert(phyinfo->frm_layer->id == phyid); + +	list_del_rcu(&phyinfo->node); +	synchronize_rcu(); -	memset(&cnfg->phy_layers[phy_layer->id], 0, -	       sizeof(struct cfcnfg_phyinfo)); -	frml = cfmuxl_remove_dnlayer(cnfg->mux, phy_layer->id); +	/* Fail if reference count is not zero */ +	if (cffrml_refcnt_read(phyinfo->frm_layer) != 0) { +		pr_info("Wait for device inuse\n"); +		list_add_rcu(&phyinfo->node, &cnfg->phys); +		mutex_unlock(&cnfg->lock); +		return -EAGAIN; +	} + +	frml = phyinfo->frm_layer;  	frml_dn = frml->dn;  	cffrml_set_uplayer(frml, NULL);  	cffrml_set_dnlayer(frml, NULL); -	kfree(frml); -  	if (phy_layer != frml_dn) {  		layer_set_up(frml_dn, NULL);  		layer_set_dn(frml_dn, NULL); -		kfree(frml_dn);  	}  	layer_set_up(phy_layer, NULL); + +	if (phyinfo->phy_layer != frml_dn) +		kfree(frml_dn); + +	cffrml_free(frml); +	kfree(phyinfo); +	mutex_unlock(&cnfg->lock); +  	return 0;  }  EXPORT_SYMBOL(cfcnfg_del_phy_layer); diff --git a/net/caif/cfctrl.c b/net/caif/cfctrl.c index 3cd8f978e30..0f455227da8 100644 --- a/net/caif/cfctrl.c +++ b/net/caif/cfctrl.c @@ -1,6 +1,6 @@  /*   * Copyright (C) ST-Ericsson AB 2010 - * Author:	Sjur Brendeland/sjur.brandeland@stericsson.com + * Author:	Sjur Brendeland   * License terms: GNU General Public License (GPL) version 2   */ @@ -9,6 +9,7 @@  #include <linux/stddef.h>  #include <linux/spinlock.h>  #include <linux/slab.h> +#include <linux/pkt_sched.h>  #include <net/caif/caif_layer.h>  #include <net/caif/cfpkt.h>  #include <net/caif/cfctrl.h> @@ -17,15 +18,14 @@  #define UTILITY_NAME_LENGTH 16  #define CFPKT_CTRL_PKT_LEN 20 -  #ifdef CAIF_NO_LOOP  static int handle_loop(struct cfctrl *ctrl, -			      int cmd, struct cfpkt *pkt){ +		       int cmd, struct cfpkt *pkt){  	return -1;  }  #else  static int handle_loop(struct cfctrl *ctrl, -		int cmd, struct cfpkt *pkt); +		       int cmd, struct cfpkt *pkt);  #endif  static int cfctrl_recv(struct cflayer *layr, struct cfpkt *pkt);  static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, @@ -36,29 +36,43 @@ struct cflayer *cfctrl_create(void)  {  	struct dev_info dev_info;  	struct cfctrl *this = -		kmalloc(sizeof(struct cfctrl), GFP_ATOMIC); -	if (!this) { -		pr_warn("Out of memory\n"); +		kzalloc(sizeof(struct cfctrl), GFP_ATOMIC); +	if (!this)  		return NULL; -	}  	caif_assert(offsetof(struct cfctrl, serv.layer) == 0);  	memset(&dev_info, 0, sizeof(dev_info));  	dev_info.id = 0xff; -	memset(this, 0, sizeof(*this));  	cfsrvl_init(&this->serv, 0, &dev_info, false);  	atomic_set(&this->req_seq_no, 1);  	atomic_set(&this->rsp_seq_no, 1);  	this->serv.layer.receive = cfctrl_recv;  	sprintf(this->serv.layer.name, "ctrl");  	this->serv.layer.ctrlcmd = cfctrl_ctrlcmd; +#ifndef CAIF_NO_LOOP  	spin_lock_init(&this->loop_linkid_lock); +	this->loop_linkid = 1; +#endif  	spin_lock_init(&this->info_list_lock);  	INIT_LIST_HEAD(&this->list); -	this->loop_linkid = 1;  	return &this->serv.layer;  } -static bool param_eq(struct cfctrl_link_param *p1, struct cfctrl_link_param *p2) +void cfctrl_remove(struct cflayer *layer) +{ +	struct cfctrl_request_info *p, *tmp; +	struct cfctrl *ctrl = container_obj(layer); + +	spin_lock_bh(&ctrl->info_list_lock); +	list_for_each_entry_safe(p, tmp, &ctrl->list, list) { +		list_del(&p->list); +		kfree(p); +	} +	spin_unlock_bh(&ctrl->info_list_lock); +	kfree(layer); +} + +static bool param_eq(const struct cfctrl_link_param *p1, +		     const struct cfctrl_link_param *p2)  {  	bool eq =  	    p1->linktype == p2->linktype && @@ -100,8 +114,8 @@ static bool param_eq(struct cfctrl_link_param *p1, struct cfctrl_link_param *p2)  	return false;  } -bool cfctrl_req_eq(struct cfctrl_request_info *r1, -		   struct cfctrl_request_info *r2) +static bool cfctrl_req_eq(const struct cfctrl_request_info *r1, +			  const struct cfctrl_request_info *r2)  {  	if (r1->cmd != r2->cmd)  		return false; @@ -112,23 +126,22 @@ bool cfctrl_req_eq(struct cfctrl_request_info *r1,  }  /* Insert request at the end */ -void cfctrl_insert_req(struct cfctrl *ctrl, +static void cfctrl_insert_req(struct cfctrl *ctrl,  			      struct cfctrl_request_info *req)  { -	spin_lock(&ctrl->info_list_lock); +	spin_lock_bh(&ctrl->info_list_lock);  	atomic_inc(&ctrl->req_seq_no);  	req->sequence_no = atomic_read(&ctrl->req_seq_no);  	list_add_tail(&req->list, &ctrl->list); -	spin_unlock(&ctrl->info_list_lock); +	spin_unlock_bh(&ctrl->info_list_lock);  }  /* Compare and remove request */ -struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl, -					      struct cfctrl_request_info *req) +static struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl, +						struct cfctrl_request_info *req)  {  	struct cfctrl_request_info *p, *tmp, *first; -	spin_lock(&ctrl->info_list_lock);  	first = list_first_entry(&ctrl->list, struct cfctrl_request_info, list);  	list_for_each_entry_safe(p, tmp, &ctrl->list, list) { @@ -144,7 +157,6 @@ struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl,  	}  	p = NULL;  out: -	spin_unlock(&ctrl->info_list_lock);  	return p;  } @@ -154,16 +166,6 @@ struct cfctrl_rsp *cfctrl_get_respfuncs(struct cflayer *layer)  	return &this->res;  } -void cfctrl_set_dnlayer(struct cflayer *this, struct cflayer *dn) -{ -	this->dn = dn; -} - -void cfctrl_set_uplayer(struct cflayer *this, struct cflayer *up) -{ -	this->up = up; -} -  static void init_info(struct caif_payload_info *info, struct cfctrl *cfctrl)  {  	info->hdr_len = 0; @@ -173,30 +175,30 @@ static void init_info(struct caif_payload_info *info, struct cfctrl *cfctrl)  void cfctrl_enum_req(struct cflayer *layer, u8 physlinkid)  { +	struct cfpkt *pkt;  	struct cfctrl *cfctrl = container_obj(layer); -	int ret; -	struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN); -	if (!pkt) { -		pr_warn("Out of memory\n"); +	struct cflayer *dn = cfctrl->serv.layer.dn; + +	if (!dn) { +		pr_debug("not able to send enum request\n");  		return;  	} +	pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN); +	if (!pkt) +		return;  	caif_assert(offsetof(struct cfctrl, serv.layer) == 0);  	init_info(cfpkt_info(pkt), cfctrl);  	cfpkt_info(pkt)->dev_info->id = physlinkid;  	cfctrl->serv.dev_info.id = physlinkid;  	cfpkt_addbdy(pkt, CFCTRL_CMD_ENUM);  	cfpkt_addbdy(pkt, physlinkid); -	ret = -	    cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt); -	if (ret < 0) { -		pr_err("Could not transmit enum message\n"); -		cfpkt_destroy(pkt); -	} +	cfpkt_set_prio(pkt, TC_PRIO_CONTROL); +	dn->transmit(dn, pkt);  }  int cfctrl_linkup_request(struct cflayer *layer, -			   struct cfctrl_link_param *param, -			   struct cflayer *user_layer) +			  struct cfctrl_link_param *param, +			  struct cflayer *user_layer)  {  	struct cfctrl *cfctrl = container_obj(layer);  	u32 tmp32; @@ -205,14 +207,27 @@ int cfctrl_linkup_request(struct cflayer *layer,  	struct cfctrl_request_info *req;  	int ret;  	char utility_name[16]; -	struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN); -	if (!pkt) { -		pr_warn("Out of memory\n"); -		return -ENOMEM; +	struct cfpkt *pkt; +	struct cflayer *dn = cfctrl->serv.layer.dn; + +	if (!dn) { +		pr_debug("not able to send linkup request\n"); +		return -ENODEV;  	} + +	if (cfctrl_cancel_req(layer, user_layer) > 0) { +		/* Slight Paranoia, check if already connecting */ +		pr_err("Duplicate connect request for same client\n"); +		WARN_ON(1); +		return -EALREADY; +	} + +	pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN); +	if (!pkt) +		return -ENOMEM;  	cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_SETUP); -	cfpkt_addbdy(pkt, (param->chtype << 4) + param->linktype); -	cfpkt_addbdy(pkt, (param->priority << 3) + param->phyid); +	cfpkt_addbdy(pkt, (param->chtype << 4) | param->linktype); +	cfpkt_addbdy(pkt, (param->priority << 3) | param->phyid);  	cfpkt_addbdy(pkt, param->endpoint & 0x03);  	switch (param->linktype) { @@ -257,10 +272,8 @@ int cfctrl_linkup_request(struct cflayer *layer,  		return -EINVAL;  	}  	req = kzalloc(sizeof(*req), GFP_KERNEL); -	if (!req) { -		pr_warn("Out of memory\n"); +	if (!req)  		return -ENOMEM; -	}  	req->client_layer = user_layer;  	req->cmd = CFCTRL_CMD_LINK_SETUP;  	req->param = *param; @@ -272,105 +285,66 @@ int cfctrl_linkup_request(struct cflayer *layer,  	 *	might arrive with the newly allocated channel ID.  	 */  	cfpkt_info(pkt)->dev_info->id = param->phyid; +	cfpkt_set_prio(pkt, TC_PRIO_CONTROL);  	ret = -	    cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt); +	    dn->transmit(dn, pkt);  	if (ret < 0) { -		pr_err("Could not transmit linksetup request\n"); -		cfpkt_destroy(pkt); -		return -ENODEV; +		int count; + +		count = cfctrl_cancel_req(&cfctrl->serv.layer, +						user_layer); +		if (count != 1) { +			pr_err("Could not remove request (%d)", count); +			return -ENODEV; +		}  	}  	return 0;  }  int cfctrl_linkdown_req(struct cflayer *layer, u8 channelid, -				struct cflayer *client) +			struct cflayer *client)  {  	int ret; +	struct cfpkt *pkt;  	struct cfctrl *cfctrl = container_obj(layer); -	struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN); -	if (!pkt) { -		pr_warn("Out of memory\n"); -		return -ENOMEM; +	struct cflayer *dn = cfctrl->serv.layer.dn; + +	if (!dn) { +		pr_debug("not able to send link-down request\n"); +		return -ENODEV;  	} +	pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN); +	if (!pkt) +		return -ENOMEM;  	cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_DESTROY);  	cfpkt_addbdy(pkt, channelid);  	init_info(cfpkt_info(pkt), cfctrl); +	cfpkt_set_prio(pkt, TC_PRIO_CONTROL);  	ret = -	    cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt); -	if (ret < 0) { -		pr_err("Could not transmit link-down request\n"); -		cfpkt_destroy(pkt); -	} +	    dn->transmit(dn, pkt); +#ifndef CAIF_NO_LOOP +	cfctrl->loop_linkused[channelid] = 0; +#endif  	return ret;  } -void cfctrl_sleep_req(struct cflayer *layer) -{ -	int ret; -	struct cfctrl *cfctrl = container_obj(layer); -	struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN); -	if (!pkt) { -		pr_warn("Out of memory\n"); -		return; -	} -	cfpkt_addbdy(pkt, CFCTRL_CMD_SLEEP); -	init_info(cfpkt_info(pkt), cfctrl); -	ret = -	    cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt); -	if (ret < 0) -		cfpkt_destroy(pkt); -} - -void cfctrl_wake_req(struct cflayer *layer) -{ -	int ret; -	struct cfctrl *cfctrl = container_obj(layer); -	struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN); -	if (!pkt) { -		pr_warn("Out of memory\n"); -		return; -	} -	cfpkt_addbdy(pkt, CFCTRL_CMD_WAKE); -	init_info(cfpkt_info(pkt), cfctrl); -	ret = -	    cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt); -	if (ret < 0) -		cfpkt_destroy(pkt); -} - -void cfctrl_getstartreason_req(struct cflayer *layer) -{ -	int ret; -	struct cfctrl *cfctrl = container_obj(layer); -	struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN); -	if (!pkt) { -		pr_warn("Out of memory\n"); -		return; -	} -	cfpkt_addbdy(pkt, CFCTRL_CMD_START_REASON); -	init_info(cfpkt_info(pkt), cfctrl); -	ret = -	    cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt); -	if (ret < 0) -		cfpkt_destroy(pkt); -} - - -void cfctrl_cancel_req(struct cflayer *layr, struct cflayer *adap_layer) +int cfctrl_cancel_req(struct cflayer *layr, struct cflayer *adap_layer)  {  	struct cfctrl_request_info *p, *tmp;  	struct cfctrl *ctrl = container_obj(layr); -	spin_lock(&ctrl->info_list_lock); +	int found = 0; +	spin_lock_bh(&ctrl->info_list_lock);  	list_for_each_entry_safe(p, tmp, &ctrl->list, list) {  		if (p->client_layer == adap_layer) { -			pr_debug("cancel req :%d\n", p->sequence_no);  			list_del(&p->list);  			kfree(p); +			found++;  		}  	} -	spin_unlock(&ctrl->info_list_lock); +	spin_unlock_bh(&ctrl->info_list_lock); +	return found;  }  static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt) @@ -389,7 +363,8 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)  	cfpkt_extr_head(pkt, &cmdrsp, 1);  	cmd = cmdrsp & CFCTRL_CMD_MASK;  	if (cmd != CFCTRL_CMD_LINK_ERR -	    && CFCTRL_RSP_BIT != (CFCTRL_RSP_BIT & cmdrsp)) { +	    && CFCTRL_RSP_BIT != (CFCTRL_RSP_BIT & cmdrsp) +		&& CFCTRL_ERR_BIT != (CFCTRL_ERR_BIT & cmdrsp)) {  		if (handle_loop(cfctrl, cmd, pkt) != 0)  			cmdrsp |= CFCTRL_ERR_BIT;  	} @@ -515,18 +490,20 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)  				cfpkt_extr_head(pkt, ¶m, len);  				break;  			default: -				pr_warn("Request setup - invalid link type (%d)\n", +				pr_warn("Request setup, invalid type (%d)\n",  					serv);  				goto error;  			}  			rsp.cmd = cmd;  			rsp.param = linkparam; +			spin_lock_bh(&cfctrl->info_list_lock);  			req = cfctrl_remove_req(cfctrl, &rsp);  			if (CFCTRL_ERR_BIT == (CFCTRL_ERR_BIT & cmdrsp) ||  				cfpkt_erroneous(pkt)) { -				pr_err("Invalid O/E bit or parse error on CAIF control channel\n"); +				pr_err("Invalid O/E bit or parse error " +						"on CAIF control channel\n");  				cfctrl->res.reject_rsp(cfctrl->serv.layer.up,  						       0,  						       req ? req->client_layer @@ -539,8 +516,9 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)  							  client_layer : NULL);  			} -			if (req != NULL) -				kfree(req); +			kfree(req); + +			spin_unlock_bh(&cfctrl->info_list_lock);  		}  		break;  	case CFCTRL_CMD_LINK_DESTROY: @@ -578,18 +556,34 @@ error:  }  static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, -			int phyid) +			   int phyid)  {  	struct cfctrl *this = container_obj(layr);  	switch (ctrl) {  	case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND:  	case CAIF_CTRLCMD_FLOW_OFF_IND: -		spin_lock(&this->info_list_lock); -		if (!list_empty(&this->list)) { +		spin_lock_bh(&this->info_list_lock); +		if (!list_empty(&this->list))  			pr_debug("Received flow off in control layer\n"); +		spin_unlock_bh(&this->info_list_lock); +		break; +	case _CAIF_CTRLCMD_PHYIF_DOWN_IND: { +		struct cfctrl_request_info *p, *tmp; + +		/* Find all connect request and report failure */ +		spin_lock_bh(&this->info_list_lock); +		list_for_each_entry_safe(p, tmp, &this->list, list) { +			if (p->param.phyid == phyid) { +				list_del(&p->list); +				p->client_layer->ctrlcmd(p->client_layer, +						CAIF_CTRLCMD_INIT_FAIL_RSP, +						phyid); +				kfree(p); +			}  		} -		spin_unlock(&this->info_list_lock); +		spin_unlock_bh(&this->info_list_lock);  		break; +	}  	default:  		break;  	} @@ -599,27 +593,33 @@ static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,  static int handle_loop(struct cfctrl *ctrl, int cmd, struct cfpkt *pkt)  {  	static int last_linkid; +	static int dec;  	u8 linkid, linktype, tmp;  	switch (cmd) {  	case CFCTRL_CMD_LINK_SETUP: -		spin_lock(&ctrl->loop_linkid_lock); -		for (linkid = last_linkid + 1; linkid < 255; linkid++) -			if (!ctrl->loop_linkused[linkid]) -				goto found; -		for (linkid = last_linkid - 1; linkid > 0; linkid--) +		spin_lock_bh(&ctrl->loop_linkid_lock); +		if (!dec) { +			for (linkid = last_linkid + 1; linkid < 254; linkid++) +				if (!ctrl->loop_linkused[linkid]) +					goto found; +		} +		dec = 1; +		for (linkid = last_linkid - 1; linkid > 1; linkid--)  			if (!ctrl->loop_linkused[linkid])  				goto found; -		spin_unlock(&ctrl->loop_linkid_lock); -		pr_err("Out of link-ids\n"); -		return -EINVAL; +		spin_unlock_bh(&ctrl->loop_linkid_lock); +		return -1;  found: +		if (linkid < 10) +			dec = 0; +  		if (!ctrl->loop_linkused[linkid])  			ctrl->loop_linkused[linkid] = 1;  		last_linkid = linkid;  		cfpkt_add_trail(pkt, &linkid, 1); -		spin_unlock(&ctrl->loop_linkid_lock); +		spin_unlock_bh(&ctrl->loop_linkid_lock);  		cfpkt_peek_head(pkt, &linktype, 1);  		if (linktype ==  CFCTRL_SRV_UTIL) {  			tmp = 0x01; @@ -629,10 +629,10 @@ found:  		break;  	case CFCTRL_CMD_LINK_DESTROY: -		spin_lock(&ctrl->loop_linkid_lock); +		spin_lock_bh(&ctrl->loop_linkid_lock);  		cfpkt_peek_head(pkt, &linkid, 1);  		ctrl->loop_linkused[linkid] = 0; -		spin_unlock(&ctrl->loop_linkid_lock); +		spin_unlock_bh(&ctrl->loop_linkid_lock);  		break;  	default:  		break; diff --git a/net/caif/cfdbgl.c b/net/caif/cfdbgl.c index 11a2af4c162..7aae0b56829 100644 --- a/net/caif/cfdbgl.c +++ b/net/caif/cfdbgl.c @@ -1,6 +1,6 @@  /*   * Copyright (C) ST-Ericsson AB 2010 - * Author:	Sjur Brendeland/sjur.brandeland@stericsson.com + * Author:	Sjur Brendeland   * License terms: GNU General Public License (GPL) version 2   */ @@ -19,13 +19,10 @@ static int cfdbgl_transmit(struct cflayer *layr, struct cfpkt *pkt);  struct cflayer *cfdbgl_create(u8 channel_id, struct dev_info *dev_info)  { -	struct cfsrvl *dbg = kmalloc(sizeof(struct cfsrvl), GFP_ATOMIC); -	if (!dbg) { -		pr_warn("Out of memory\n"); +	struct cfsrvl *dbg = kzalloc(sizeof(struct cfsrvl), GFP_ATOMIC); +	if (!dbg)  		return NULL; -	}  	caif_assert(offsetof(struct cfsrvl, layer) == 0); -	memset(dbg, 0, sizeof(struct cfsrvl));  	cfsrvl_init(dbg, channel_id, dev_info, false);  	dbg->layer.receive = cfdbgl_receive;  	dbg->layer.transmit = cfdbgl_transmit; @@ -44,8 +41,10 @@ static int cfdbgl_transmit(struct cflayer *layr, struct cfpkt *pkt)  	struct caif_payload_info *info;  	int ret; -	if (!cfsrvl_ready(service, &ret)) +	if (!cfsrvl_ready(service, &ret)) { +		cfpkt_destroy(pkt);  		return ret; +	}  	/* Add info for MUX-layer to route the packet out */  	info = cfpkt_info(pkt); diff --git a/net/caif/cfdgml.c b/net/caif/cfdgml.c index d3ed264ad6c..3bdddb32d55 100644 --- a/net/caif/cfdgml.c +++ b/net/caif/cfdgml.c @@ -1,6 +1,6 @@  /*   * Copyright (C) ST-Ericsson AB 2010 - * Author:	Sjur Brendeland/sjur.brandeland@stericsson.com + * Author:	Sjur Brendeland   * License terms: GNU General Public License (GPL) version 2   */ @@ -13,12 +13,12 @@  #include <net/caif/cfsrvl.h>  #include <net/caif/cfpkt.h> +  #define container_obj(layr) ((struct cfsrvl *) layr)  #define DGM_CMD_BIT  0x80  #define DGM_FLOW_OFF 0x81  #define DGM_FLOW_ON  0x80 -#define DGM_CTRL_PKT_SIZE 1  #define DGM_MTU 1500  static int cfdgml_receive(struct cflayer *layr, struct cfpkt *pkt); @@ -26,13 +26,10 @@ static int cfdgml_transmit(struct cflayer *layr, struct cfpkt *pkt);  struct cflayer *cfdgml_create(u8 channel_id, struct dev_info *dev_info)  { -	struct cfsrvl *dgm = kmalloc(sizeof(struct cfsrvl), GFP_ATOMIC); -	if (!dgm) { -		pr_warn("Out of memory\n"); +	struct cfsrvl *dgm = kzalloc(sizeof(struct cfsrvl), GFP_ATOMIC); +	if (!dgm)  		return NULL; -	}  	caif_assert(offsetof(struct cfsrvl, layer) == 0); -	memset(dgm, 0, sizeof(struct cfsrvl));  	cfsrvl_init(dgm, channel_id, dev_info, true);  	dgm->layer.receive = cfdgml_receive;  	dgm->layer.transmit = cfdgml_transmit; @@ -84,18 +81,26 @@ static int cfdgml_receive(struct cflayer *layr, struct cfpkt *pkt)  static int cfdgml_transmit(struct cflayer *layr, struct cfpkt *pkt)  { +	u8 packet_type;  	u32 zero = 0;  	struct caif_payload_info *info;  	struct cfsrvl *service = container_obj(layr);  	int ret; -	if (!cfsrvl_ready(service, &ret)) + +	if (!cfsrvl_ready(service, &ret)) { +		cfpkt_destroy(pkt);  		return ret; +	}  	/* STE Modem cannot handle more than 1500 bytes datagrams */ -	if (cfpkt_getlen(pkt) > DGM_MTU) +	if (cfpkt_getlen(pkt) > DGM_MTU) { +		cfpkt_destroy(pkt);  		return -EMSGSIZE; +	} -	cfpkt_add_head(pkt, &zero, 4); +	cfpkt_add_head(pkt, &zero, 3); +	packet_type = 0x08; /* B9 set - UNCLASSIFIED */ +	cfpkt_add_head(pkt, &packet_type, 1);  	/* Add info for MUX-layer to route the packet out. */  	info = cfpkt_info(pkt); @@ -105,10 +110,5 @@ static int cfdgml_transmit(struct cflayer *layr, struct cfpkt *pkt)  	 */  	info->hdr_len = 4;  	info->dev_info = &service->dev_info; -	ret = layr->dn->transmit(layr->dn, pkt); -	if (ret < 0) { -		u32 tmp32; -		cfpkt_extr_head(pkt, &tmp32, 4); -	} -	return ret; +	return layr->dn->transmit(layr->dn, pkt);  } diff --git a/net/caif/cffrml.c b/net/caif/cffrml.c index a445043931a..8bc7caa28e6 100644 --- a/net/caif/cffrml.c +++ b/net/caif/cffrml.c @@ -2,7 +2,7 @@   * CAIF Framing Layer.   *   * Copyright (C) ST-Ericsson AB 2010 - * Author:	Sjur Brendeland/sjur.brandeland@stericsson.com + * Author:	Sjur Brendeland   * License terms: GNU General Public License (GPL) version 2   */ @@ -12,6 +12,7 @@  #include <linux/spinlock.h>  #include <linux/slab.h>  #include <linux/crc-ccitt.h> +#include <linux/netdevice.h>  #include <net/caif/caif_layer.h>  #include <net/caif/cfpkt.h>  #include <net/caif/cffrml.h> @@ -21,25 +22,29 @@  struct cffrml {  	struct cflayer layer;  	bool dofcs;		/* !< FCS active */ +	int __percpu		*pcpu_refcnt;  };  static int cffrml_receive(struct cflayer *layr, struct cfpkt *pkt);  static int cffrml_transmit(struct cflayer *layr, struct cfpkt *pkt);  static void cffrml_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, -				int phyid); +			   int phyid);  static u32 cffrml_rcv_error;  static u32 cffrml_rcv_checsum_error;  struct cflayer *cffrml_create(u16 phyid, bool use_fcs)  { -	struct cffrml *this = kmalloc(sizeof(struct cffrml), GFP_ATOMIC); -	if (!this) { -		pr_warn("Out of memory\n"); +	struct cffrml *this = kzalloc(sizeof(struct cffrml), GFP_ATOMIC); +	if (!this) +		return NULL; +	this->pcpu_refcnt = alloc_percpu(int); +	if (this->pcpu_refcnt == NULL) { +		kfree(this);  		return NULL;  	} +  	caif_assert(offsetof(struct cffrml, layer) == 0); -	memset(this, 0, sizeof(struct cflayer));  	this->layer.receive = cffrml_receive;  	this->layer.transmit = cffrml_transmit;  	this->layer.ctrlcmd = cffrml_ctrlcmd; @@ -49,6 +54,13 @@ struct cflayer *cffrml_create(u16 phyid, bool use_fcs)  	return (struct cflayer *) this;  } +void cffrml_free(struct cflayer *layer) +{ +	struct cffrml *this = container_obj(layer); +	free_percpu(this->pcpu_refcnt); +	kfree(layer); +} +  void cffrml_set_uplayer(struct cflayer *this, struct cflayer *up)  {  	this->up = up; @@ -112,42 +124,74 @@ static int cffrml_receive(struct cflayer *layr, struct cfpkt *pkt)  		cfpkt_destroy(pkt);  		return -EPROTO;  	} + +	if (layr->up == NULL) { +		pr_err("Layr up is missing!\n"); +		cfpkt_destroy(pkt); +		return -EINVAL; +	} +  	return layr->up->receive(layr->up, pkt);  }  static int cffrml_transmit(struct cflayer *layr, struct cfpkt *pkt)  { -	int tmp;  	u16 chks;  	u16 len; -	int ret; +	__le16 data; +  	struct cffrml *this = container_obj(layr);  	if (this->dofcs) {  		chks = cfpkt_iterate(pkt, cffrml_checksum, 0xffff); -		tmp = cpu_to_le16(chks); -		cfpkt_add_trail(pkt, &tmp, 2); +		data = cpu_to_le16(chks); +		cfpkt_add_trail(pkt, &data, 2);  	} else {  		cfpkt_pad_trail(pkt, 2);  	}  	len = cfpkt_getlen(pkt); -	tmp = cpu_to_le16(len); -	cfpkt_add_head(pkt, &tmp, 2); +	data = cpu_to_le16(len); +	cfpkt_add_head(pkt, &data, 2);  	cfpkt_info(pkt)->hdr_len += 2;  	if (cfpkt_erroneous(pkt)) {  		pr_err("Packet is erroneous!\n"); +		cfpkt_destroy(pkt);  		return -EPROTO;  	} -	ret = layr->dn->transmit(layr->dn, pkt); -	if (ret < 0) { -		/* Remove header on faulty packet. */ -		cfpkt_extr_head(pkt, &tmp, 2); + +	if (layr->dn == NULL) { +		cfpkt_destroy(pkt); +		return -ENODEV; +  	} -	return ret; +	return layr->dn->transmit(layr->dn, pkt);  }  static void cffrml_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, -					int phyid) +			   int phyid)  { -	if (layr->up->ctrlcmd) +	if (layr->up && layr->up->ctrlcmd)  		layr->up->ctrlcmd(layr->up, ctrl, layr->id);  } + +void cffrml_put(struct cflayer *layr) +{ +	struct cffrml *this = container_obj(layr); +	if (layr != NULL && this->pcpu_refcnt != NULL) +		this_cpu_dec(*this->pcpu_refcnt); +} + +void cffrml_hold(struct cflayer *layr) +{ +	struct cffrml *this = container_obj(layr); +	if (layr != NULL && this->pcpu_refcnt != NULL) +		this_cpu_inc(*this->pcpu_refcnt); +} + +int cffrml_refcnt_read(struct cflayer *layr) +{ +	int i, refcnt = 0; +	struct cffrml *this = container_obj(layr); +	for_each_possible_cpu(i) +		refcnt += *per_cpu_ptr(this->pcpu_refcnt, i); +	return refcnt; +} diff --git a/net/caif/cfmuxl.c b/net/caif/cfmuxl.c index 46f34b2e047..8c5d6386319 100644 --- a/net/caif/cfmuxl.c +++ b/net/caif/cfmuxl.c @@ -1,6 +1,6 @@  /*   * Copyright (C) ST-Ericsson AB 2010 - * Author:	Sjur Brendeland/sjur.brandeland@stericsson.com + * Author:	Sjur Brendeland   * License terms: GNU General Public License (GPL) version 2   */ @@ -9,6 +9,7 @@  #include <linux/stddef.h>  #include <linux/spinlock.h>  #include <linux/slab.h> +#include <linux/rculist.h>  #include <net/caif/cfpkt.h>  #include <net/caif/cfmuxl.h>  #include <net/caif/cfsrvl.h> @@ -41,7 +42,7 @@ struct cfmuxl {  static int cfmuxl_receive(struct cflayer *layr, struct cfpkt *pkt);  static int cfmuxl_transmit(struct cflayer *layr, struct cfpkt *pkt);  static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, -				int phyid); +			   int phyid);  static struct cflayer *get_up(struct cfmuxl *muxl, u16 id);  struct cflayer *cfmuxl_create(void) @@ -61,111 +62,88 @@ struct cflayer *cfmuxl_create(void)  	return &this->layer;  } -int cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid) +int cfmuxl_set_dnlayer(struct cflayer *layr, struct cflayer *dn, u8 phyid)  { -	struct cfmuxl *muxl = container_obj(layr); -	spin_lock(&muxl->receive_lock); -	cfsrvl_get(up); -	list_add(&up->node, &muxl->srvl_list); -	spin_unlock(&muxl->receive_lock); +	struct cfmuxl *muxl = (struct cfmuxl *) layr; + +	spin_lock_bh(&muxl->transmit_lock); +	list_add_rcu(&dn->node, &muxl->frml_list); +	spin_unlock_bh(&muxl->transmit_lock);  	return 0;  } -bool cfmuxl_is_phy_inuse(struct cflayer *layr, u8 phyid) +static struct cflayer *get_from_id(struct list_head *list, u16 id)  { -	struct list_head *node; -	struct cflayer *layer; -	struct cfmuxl *muxl = container_obj(layr); -	bool match = false; -	spin_lock(&muxl->receive_lock); - -	list_for_each(node, &muxl->srvl_list) { -		layer = list_entry(node, struct cflayer, node); -		if (cfsrvl_phyid_match(layer, phyid)) { -			match = true; -			break; -		} - +	struct cflayer *lyr; +	list_for_each_entry_rcu(lyr, list, node) { +		if (lyr->id == id) +			return lyr;  	} -	spin_unlock(&muxl->receive_lock); -	return match; + +	return NULL;  } -u8 cfmuxl_get_phyid(struct cflayer *layr, u8 channel_id) +int cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid)  { -	struct cflayer *up; -	int phyid;  	struct cfmuxl *muxl = container_obj(layr); -	spin_lock(&muxl->receive_lock); -	up = get_up(muxl, channel_id); -	if (up != NULL) -		phyid = cfsrvl_getphyid(up); -	else -		phyid = 0; -	spin_unlock(&muxl->receive_lock); -	return phyid; -} +	struct cflayer *old; -int cfmuxl_set_dnlayer(struct cflayer *layr, struct cflayer *dn, u8 phyid) -{ -	struct cfmuxl *muxl = (struct cfmuxl *) layr; -	spin_lock(&muxl->transmit_lock); -	list_add(&dn->node, &muxl->frml_list); -	spin_unlock(&muxl->transmit_lock); -	return 0; -} +	spin_lock_bh(&muxl->receive_lock); -static struct cflayer *get_from_id(struct list_head *list, u16 id) -{ -	struct list_head *node; -	struct cflayer *layer; -	list_for_each(node, list) { -		layer = list_entry(node, struct cflayer, node); -		if (layer->id == id) -			return layer; -	} -	return NULL; +	/* Two entries with same id is wrong, so remove old layer from mux */ +	old = get_from_id(&muxl->srvl_list, linkid); +	if (old != NULL) +		list_del_rcu(&old->node); + +	list_add_rcu(&up->node, &muxl->srvl_list); +	spin_unlock_bh(&muxl->receive_lock); + +	return 0;  }  struct cflayer *cfmuxl_remove_dnlayer(struct cflayer *layr, u8 phyid)  {  	struct cfmuxl *muxl = container_obj(layr);  	struct cflayer *dn; -	spin_lock(&muxl->transmit_lock); -	memset(muxl->dn_cache, 0, sizeof(muxl->dn_cache)); +	int idx = phyid % DN_CACHE_SIZE; + +	spin_lock_bh(&muxl->transmit_lock); +	RCU_INIT_POINTER(muxl->dn_cache[idx], NULL);  	dn = get_from_id(&muxl->frml_list, phyid); -	if (dn == NULL) { -		spin_unlock(&muxl->transmit_lock); -		return NULL; -	} -	list_del(&dn->node); +	if (dn == NULL) +		goto out; + +	list_del_rcu(&dn->node);  	caif_assert(dn != NULL); -	spin_unlock(&muxl->transmit_lock); +out: +	spin_unlock_bh(&muxl->transmit_lock);  	return dn;  } -/* Invariant: lock is taken */  static struct cflayer *get_up(struct cfmuxl *muxl, u16 id)  {  	struct cflayer *up;  	int idx = id % UP_CACHE_SIZE; -	up = muxl->up_cache[idx]; +	up = rcu_dereference(muxl->up_cache[idx]);  	if (up == NULL || up->id != id) { +		spin_lock_bh(&muxl->receive_lock);  		up = get_from_id(&muxl->srvl_list, id); -		muxl->up_cache[idx] = up; +		rcu_assign_pointer(muxl->up_cache[idx], up); +		spin_unlock_bh(&muxl->receive_lock);  	}  	return up;  } -/* Invariant: lock is taken */  static struct cflayer *get_dn(struct cfmuxl *muxl, struct dev_info *dev_info)  {  	struct cflayer *dn;  	int idx = dev_info->id % DN_CACHE_SIZE; -	dn = muxl->dn_cache[idx]; +	dn = rcu_dereference(muxl->dn_cache[idx]);  	if (dn == NULL || dn->id != dev_info->id) { +		spin_lock_bh(&muxl->transmit_lock);  		dn = get_from_id(&muxl->frml_list, dev_info->id); -		muxl->dn_cache[idx] = dn; +		rcu_assign_pointer(muxl->dn_cache[idx], dn); +		spin_unlock_bh(&muxl->transmit_lock);  	}  	return dn;  } @@ -174,15 +152,22 @@ struct cflayer *cfmuxl_remove_uplayer(struct cflayer *layr, u8 id)  {  	struct cflayer *up;  	struct cfmuxl *muxl = container_obj(layr); -	spin_lock(&muxl->receive_lock); -	up = get_up(muxl, id); +	int idx = id % UP_CACHE_SIZE; + +	if (id == 0) { +		pr_warn("Trying to remove control layer\n"); +		return NULL; +	} + +	spin_lock_bh(&muxl->receive_lock); +	up = get_from_id(&muxl->srvl_list, id);  	if (up == NULL)  		goto out; -	memset(muxl->up_cache, 0, sizeof(muxl->up_cache)); -	list_del(&up->node); -	cfsrvl_put(up); + +	RCU_INIT_POINTER(muxl->up_cache[idx], NULL); +	list_del_rcu(&up->node);  out: -	spin_unlock(&muxl->receive_lock); +	spin_unlock_bh(&muxl->receive_lock);  	return up;  } @@ -197,58 +182,86 @@ static int cfmuxl_receive(struct cflayer *layr, struct cfpkt *pkt)  		cfpkt_destroy(pkt);  		return -EPROTO;  	} - -	spin_lock(&muxl->receive_lock); +	rcu_read_lock();  	up = get_up(muxl, id); -	spin_unlock(&muxl->receive_lock); +  	if (up == NULL) { -		pr_info("Received data on unknown link ID = %d (0x%x) up == NULL", -			id, id); +		pr_debug("Received data on unknown link ID = %d (0x%x)" +			" up == NULL", id, id);  		cfpkt_destroy(pkt);  		/*  		 * Don't return ERROR, since modem misbehaves and sends out  		 * flow on before linksetup response.  		 */ + +		rcu_read_unlock();  		return /* CFGLU_EPROT; */ 0;  	} + +	/* We can't hold rcu_lock during receive, so take a ref count instead */  	cfsrvl_get(up); +	rcu_read_unlock(); +  	ret = up->receive(up, pkt); +  	cfsrvl_put(up);  	return ret;  }  static int cfmuxl_transmit(struct cflayer *layr, struct cfpkt *pkt)  { -	int ret;  	struct cfmuxl *muxl = container_obj(layr); +	int err;  	u8 linkid;  	struct cflayer *dn;  	struct caif_payload_info *info = cfpkt_info(pkt); -	dn = get_dn(muxl, cfpkt_info(pkt)->dev_info); +	BUG_ON(!info); + +	rcu_read_lock(); + +	dn = get_dn(muxl, info->dev_info);  	if (dn == NULL) { -		pr_warn("Send data on unknown phy ID = %d (0x%x)\n", +		pr_debug("Send data on unknown phy ID = %d (0x%x)\n",  			info->dev_info->id, info->dev_info->id); +		rcu_read_unlock(); +		cfpkt_destroy(pkt);  		return -ENOTCONN;  	} +  	info->hdr_len += 1;  	linkid = info->channel_id;  	cfpkt_add_head(pkt, &linkid, 1); -	ret = dn->transmit(dn, pkt); -	/* Remove MUX protocol header upon error. */ -	if (ret < 0) -		cfpkt_extr_head(pkt, &linkid, 1); -	return ret; + +	/* We can't hold rcu_lock during receive, so take a ref count instead */ +	cffrml_hold(dn); + +	rcu_read_unlock(); + +	err = dn->transmit(dn, pkt); + +	cffrml_put(dn); +	return err;  }  static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, -				int phyid) +			   int phyid)  {  	struct cfmuxl *muxl = container_obj(layr); -	struct list_head *node;  	struct cflayer *layer; -	list_for_each(node, &muxl->srvl_list) { -		layer = list_entry(node, struct cflayer, node); -		if (cfsrvl_phyid_match(layer, phyid)) + +	rcu_read_lock(); +	list_for_each_entry_rcu(layer, &muxl->srvl_list, node) { + +		if (cfsrvl_phyid_match(layer, phyid) && layer->ctrlcmd) { + +			if ((ctrl == _CAIF_CTRLCMD_PHYIF_DOWN_IND || +				ctrl == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND) && +					layer->id != 0) +				cfmuxl_remove_uplayer(layr, layer->id); + +			/* NOTE: ctrlcmd is not allowed to block */  			layer->ctrlcmd(layer, ctrl, phyid); +		}  	} +	rcu_read_unlock();  } diff --git a/net/caif/cfpkt_skbuff.c b/net/caif/cfpkt_skbuff.c index d7e865e2ff6..1be0b521ac4 100644 --- a/net/caif/cfpkt_skbuff.c +++ b/net/caif/cfpkt_skbuff.c @@ -1,6 +1,6 @@  /*   * Copyright (C) ST-Ericsson AB 2010 - * Author:	Sjur Brendeland/sjur.brandeland@stericsson.com + * Author:	Sjur Brendeland   * License terms: GNU General Public License (GPL) version 2   */ @@ -9,6 +9,7 @@  #include <linux/string.h>  #include <linux/skbuff.h>  #include <linux/hardirq.h> +#include <linux/export.h>  #include <net/caif/cfpkt.h>  #define PKT_PREFIX  48 @@ -42,27 +43,26 @@ struct cfpkt_priv_data {  	bool erronous;  }; -inline struct cfpkt_priv_data *cfpkt_priv(struct cfpkt *pkt) +static inline struct cfpkt_priv_data *cfpkt_priv(struct cfpkt *pkt)  {  	return (struct cfpkt_priv_data *) pkt->skb.cb;  } -inline bool is_erronous(struct cfpkt *pkt) +static inline bool is_erronous(struct cfpkt *pkt)  {  	return cfpkt_priv(pkt)->erronous;  } -inline struct sk_buff *pkt_to_skb(struct cfpkt *pkt) +static inline struct sk_buff *pkt_to_skb(struct cfpkt *pkt)  {  	return &pkt->skb;  } -inline struct cfpkt *skb_to_pkt(struct sk_buff *skb) +static inline struct cfpkt *skb_to_pkt(struct sk_buff *skb)  {  	return (struct cfpkt *) skb;  } -  struct cfpkt *cfpkt_fromnative(enum caif_direction dir, void *nativepkt)  {  	struct cfpkt *pkt = skb_to_pkt(nativepkt); @@ -97,21 +97,18 @@ inline struct cfpkt *cfpkt_create(u16 len)  {  	return cfpkt_create_pfx(len + PKT_POSTFIX, PKT_PREFIX);  } -EXPORT_SYMBOL(cfpkt_create);  void cfpkt_destroy(struct cfpkt *pkt)  {  	struct sk_buff *skb = pkt_to_skb(pkt);  	kfree_skb(skb);  } -EXPORT_SYMBOL(cfpkt_destroy);  inline bool cfpkt_more(struct cfpkt *pkt)  {  	struct sk_buff *skb = pkt_to_skb(pkt);  	return skb->len > 0;  } -EXPORT_SYMBOL(cfpkt_more);  int cfpkt_peek_head(struct cfpkt *pkt, void *data, u16 len)  { @@ -123,7 +120,6 @@ int cfpkt_peek_head(struct cfpkt *pkt, void *data, u16 len)  	return !cfpkt_extr_head(pkt, data, len) &&  	    !cfpkt_add_head(pkt, data, len);  } -EXPORT_SYMBOL(cfpkt_peek_head);  int cfpkt_extr_head(struct cfpkt *pkt, void *data, u16 len)  { @@ -145,7 +141,8 @@ int cfpkt_extr_head(struct cfpkt *pkt, void *data, u16 len)  	}  	from = skb_pull(skb, len);  	from -= len; -	memcpy(data, from, len); +	if (data) +		memcpy(data, from, len);  	return 0;  }  EXPORT_SYMBOL(cfpkt_extr_head); @@ -171,13 +168,11 @@ int cfpkt_extr_trail(struct cfpkt *pkt, void *dta, u16 len)  	memcpy(data, from, len);  	return 0;  } -EXPORT_SYMBOL(cfpkt_extr_trail);  int cfpkt_pad_trail(struct cfpkt *pkt, u16 len)  {  	return cfpkt_add_body(pkt, NULL, len);  } -EXPORT_SYMBOL(cfpkt_pad_trail);  int cfpkt_add_body(struct cfpkt *pkt, const void *data, u16 len)  { @@ -208,31 +203,19 @@ int cfpkt_add_body(struct cfpkt *pkt, const void *data, u16 len)  			PKT_ERROR(pkt, "cow failed\n");  			return -EPROTO;  		} -		/* -		 * Is the SKB non-linear after skb_cow_data()? If so, we are -		 * going to add data to the last SKB, so we need to adjust -		 * lengths of the top SKB. -		 */ -		if (lastskb != skb) { -			pr_warn("Packet is non-linear\n"); -			skb->len += len; -			skb->data_len += len; -		}  	}  	/* All set to put the last SKB and optionally write data there. */ -	to = skb_put(lastskb, len); +	to = pskb_put(skb, lastskb, len);  	if (likely(data))  		memcpy(to, data, len);  	return 0;  } -EXPORT_SYMBOL(cfpkt_add_body);  inline int cfpkt_addbdy(struct cfpkt *pkt, u8 data)  {  	return cfpkt_add_body(pkt, &data, 1);  } -EXPORT_SYMBOL(cfpkt_addbdy);  int cfpkt_add_head(struct cfpkt *pkt, const void *data2, u16 len)  { @@ -265,18 +248,16 @@ inline int cfpkt_add_trail(struct cfpkt *pkt, const void *data, u16 len)  {  	return cfpkt_add_body(pkt, data, len);  } -EXPORT_SYMBOL(cfpkt_add_trail);  inline u16 cfpkt_getlen(struct cfpkt *pkt)  {  	struct sk_buff *skb = pkt_to_skb(pkt);  	return skb->len;  } -EXPORT_SYMBOL(cfpkt_getlen);  inline u16 cfpkt_iterate(struct cfpkt *pkt, -			    u16 (*iter_func)(u16, void *, u16), -			    u16 data) +			 u16 (*iter_func)(u16, void *, u16), +			 u16 data)  {  	/*  	 * Don't care about the performance hit of linearizing, @@ -290,7 +271,6 @@ inline u16 cfpkt_iterate(struct cfpkt *pkt,  	}  	return iter_func(data, pkt->skb.data, cfpkt_getlen(pkt));  } -EXPORT_SYMBOL(cfpkt_iterate);  int cfpkt_setlen(struct cfpkt *pkt, u16 len)  { @@ -315,22 +295,10 @@ int cfpkt_setlen(struct cfpkt *pkt, u16 len)  	return cfpkt_getlen(pkt);  } -EXPORT_SYMBOL(cfpkt_setlen); - -struct cfpkt *cfpkt_create_uplink(const unsigned char *data, unsigned int len) -{ -	struct cfpkt *pkt = cfpkt_create_pfx(len + PKT_POSTFIX, PKT_PREFIX); -	if (!pkt) -		return NULL; -	if (unlikely(data != NULL)) -		cfpkt_add_body(pkt, data, len); -	return pkt; -} -EXPORT_SYMBOL(cfpkt_create_uplink);  struct cfpkt *cfpkt_append(struct cfpkt *dstpkt, -			     struct cfpkt *addpkt, -			     u16 expectlen) +			   struct cfpkt *addpkt, +			   u16 expectlen)  {  	struct sk_buff *dst = pkt_to_skb(dstpkt);  	struct sk_buff *add = pkt_to_skb(addpkt); @@ -368,7 +336,6 @@ struct cfpkt *cfpkt_append(struct cfpkt *dstpkt,  	dst->len += addlen;  	return skb_to_pkt(dst);  } -EXPORT_SYMBOL(cfpkt_append);  struct cfpkt *cfpkt_split(struct cfpkt *pkt, u16 pos)  { @@ -404,176 +371,23 @@ struct cfpkt *cfpkt_split(struct cfpkt *pkt, u16 pos)  	memcpy(skb2->data, split, len2nd);  	skb2->tail += len2nd;  	skb2->len += len2nd; +	skb2->priority = skb->priority;  	return skb_to_pkt(skb2);  } -EXPORT_SYMBOL(cfpkt_split); - -char *cfpkt_log_pkt(struct cfpkt *pkt, char *buf, int buflen) -{ -	struct sk_buff *skb = pkt_to_skb(pkt); -	char *p = buf; -	int i; - -	/* -	 * Sanity check buffer length, it needs to be at least as large as -	 * the header info: ~=50+ bytes -	 */ -	if (buflen < 50) -		return NULL; - -	snprintf(buf, buflen, "%s: pkt:%p len:%ld(%ld+%ld) {%ld,%ld} data: [", -		is_erronous(pkt) ? "ERRONOUS-SKB" : -		 (skb->data_len != 0 ? "COMPLEX-SKB" : "SKB"), -		 skb, -		 (long) skb->len, -		 (long) (skb_tail_pointer(skb) - skb->data), -		 (long) skb->data_len, -		 (long) (skb->data - skb->head), -		 (long) (skb_tail_pointer(skb) - skb->head)); -	p = buf + strlen(buf); - -	for (i = 0; i < skb_tail_pointer(skb) - skb->data && i < 300; i++) { -		if (p > buf + buflen - 10) { -			sprintf(p, "..."); -			p = buf + strlen(buf); -			break; -		} -		sprintf(p, "%02x,", skb->data[i]); -		p = buf + strlen(buf); -	} -	sprintf(p, "]\n"); -	return buf; -} -EXPORT_SYMBOL(cfpkt_log_pkt); - -int cfpkt_raw_append(struct cfpkt *pkt, void **buf, unsigned int buflen) -{ -	struct sk_buff *skb = pkt_to_skb(pkt); -	struct sk_buff *lastskb; - -	caif_assert(buf != NULL); -	if (unlikely(is_erronous(pkt))) -		return -EPROTO; -	/* Make sure SKB is writable */ -	if (unlikely(skb_cow_data(skb, 0, &lastskb) < 0)) { -		PKT_ERROR(pkt, "skb_cow_data failed\n"); -		return -EPROTO; -	} - -	if (unlikely(skb_linearize(skb) != 0)) { -		PKT_ERROR(pkt, "linearize failed\n"); -		return -EPROTO; -	} - -	if (unlikely(skb_tailroom(skb) < buflen)) { -		PKT_ERROR(pkt, "buffer too short - failed\n"); -		return -EPROTO; -	} - -	*buf = skb_put(skb, buflen); -	return 1; -} -EXPORT_SYMBOL(cfpkt_raw_append); - -int cfpkt_raw_extract(struct cfpkt *pkt, void **buf, unsigned int buflen) -{ -	struct sk_buff *skb = pkt_to_skb(pkt); - -	caif_assert(buf != NULL); -	if (unlikely(is_erronous(pkt))) -		return -EPROTO; - -	if (unlikely(buflen > skb->len)) { -		PKT_ERROR(pkt, "buflen too large - failed\n"); -		return -EPROTO; -	} - -	if (unlikely(buflen > skb_headlen(skb))) { -		if (unlikely(skb_linearize(skb) != 0)) { -			PKT_ERROR(pkt, "linearize failed\n"); -			return -EPROTO; -		} -	} - -	*buf = skb->data; -	skb_pull(skb, buflen); - -	return 1; -} -EXPORT_SYMBOL(cfpkt_raw_extract); -inline bool cfpkt_erroneous(struct cfpkt *pkt) +bool cfpkt_erroneous(struct cfpkt *pkt)  {  	return cfpkt_priv(pkt)->erronous;  } -EXPORT_SYMBOL(cfpkt_erroneous); - -struct cfpktq *cfpktq_create(void) -{ -	struct cfpktq *q = kmalloc(sizeof(struct cfpktq), GFP_ATOMIC); -	if (!q) -		return NULL; -	skb_queue_head_init(&q->head); -	atomic_set(&q->count, 0); -	spin_lock_init(&q->lock); -	return q; -} -EXPORT_SYMBOL(cfpktq_create); - -void cfpkt_queue(struct cfpktq *pktq, struct cfpkt *pkt, unsigned short prio) -{ -	atomic_inc(&pktq->count); -	spin_lock(&pktq->lock); -	skb_queue_tail(&pktq->head, pkt_to_skb(pkt)); -	spin_unlock(&pktq->lock); - -} -EXPORT_SYMBOL(cfpkt_queue); - -struct cfpkt *cfpkt_qpeek(struct cfpktq *pktq) -{ -	struct cfpkt *tmp; -	spin_lock(&pktq->lock); -	tmp = skb_to_pkt(skb_peek(&pktq->head)); -	spin_unlock(&pktq->lock); -	return tmp; -} -EXPORT_SYMBOL(cfpkt_qpeek); - -struct cfpkt *cfpkt_dequeue(struct cfpktq *pktq) -{ -	struct cfpkt *pkt; -	spin_lock(&pktq->lock); -	pkt = skb_to_pkt(skb_dequeue(&pktq->head)); -	if (pkt) { -		atomic_dec(&pktq->count); -		caif_assert(atomic_read(&pktq->count) >= 0); -	} -	spin_unlock(&pktq->lock); -	return pkt; -} -EXPORT_SYMBOL(cfpkt_dequeue); - -int cfpkt_qcount(struct cfpktq *pktq) -{ -	return atomic_read(&pktq->count); -} -EXPORT_SYMBOL(cfpkt_qcount); - -struct cfpkt *cfpkt_clone_release(struct cfpkt *pkt) -{ -	struct cfpkt *clone; -	clone  = skb_to_pkt(skb_clone(pkt_to_skb(pkt), GFP_ATOMIC)); -	/* Free original packet. */ -	cfpkt_destroy(pkt); -	if (!clone) -		return NULL; -	return clone; -} -EXPORT_SYMBOL(cfpkt_clone_release);  struct caif_payload_info *cfpkt_info(struct cfpkt *pkt)  {  	return (struct caif_payload_info *)&pkt_to_skb(pkt)->cb;  }  EXPORT_SYMBOL(cfpkt_info); + +void cfpkt_set_prio(struct cfpkt *pkt, int prio) +{ +	pkt_to_skb(pkt)->priority = prio; +} +EXPORT_SYMBOL(cfpkt_set_prio); diff --git a/net/caif/cfrfml.c b/net/caif/cfrfml.c index e2fb5fa7579..61d7617d924 100644 --- a/net/caif/cfrfml.c +++ b/net/caif/cfrfml.c @@ -1,6 +1,6 @@  /*   * Copyright (C) ST-Ericsson AB 2010 - * Author:	Sjur Brendeland/sjur.brandeland@stericsson.com + * Author:	Sjur Brendeland   * License terms: GNU General Public License (GPL) version 2   */ @@ -31,9 +31,9 @@ struct cfrfml {  	spinlock_t sync;  }; -static void cfrfml_release(struct kref *kref) +static void cfrfml_release(struct cflayer *layer)  { -	struct cfsrvl *srvl = container_of(kref, struct cfsrvl, ref); +	struct cfsrvl *srvl = container_of(layer, struct cfsrvl, layer);  	struct cfrfml *rfml = container_obj(&srvl->layer);  	if (rfml->incomplete_frm) @@ -43,16 +43,13 @@ static void cfrfml_release(struct kref *kref)  }  struct cflayer *cfrfml_create(u8 channel_id, struct dev_info *dev_info, -					int mtu_size) +			      int mtu_size)  {  	int tmp; -	struct cfrfml *this = -		kzalloc(sizeof(struct cfrfml), GFP_ATOMIC); +	struct cfrfml *this = kzalloc(sizeof(struct cfrfml), GFP_ATOMIC); -	if (!this) { -		pr_warn("Out of memory\n"); +	if (!this)  		return NULL; -	}  	cfsrvl_init(&this->serv, channel_id, dev_info, false);  	this->serv.release = cfrfml_release; @@ -72,7 +69,7 @@ struct cflayer *cfrfml_create(u8 channel_id, struct dev_info *dev_info,  }  static struct cfpkt *rfm_append(struct cfrfml *rfml, char *seghead, -			struct cfpkt *pkt, int *err) +				struct cfpkt *pkt, int *err)  {  	struct cfpkt *tmppkt;  	*err = -EPROTO; @@ -187,13 +184,18 @@ out:  					rfml->serv.dev_info.id);  	}  	spin_unlock(&rfml->sync); + +	if (unlikely(err == -EAGAIN)) +		/* It is not possible to recover after drop of a fragment */ +		err = -EIO; +  	return err;  }  static int cfrfml_transmit_segment(struct cfrfml *rfml, struct cfpkt *pkt)  { -	caif_assert(cfpkt_getlen(pkt) < rfml->fragment_size); +	caif_assert(cfpkt_getlen(pkt) < rfml->fragment_size + RFM_HEAD_SIZE);  	/* Add info for MUX-layer to route the packet out. */  	cfpkt_info(pkt)->channel_id = rfml->serv.layer.id; @@ -221,7 +223,7 @@ static int cfrfml_transmit(struct cflayer *layr, struct cfpkt *pkt)  	caif_assert(layr->dn->transmit != NULL);  	if (!cfsrvl_ready(&rfml->serv, &err)) -		return err; +		goto out;  	err = -EPROTO;  	if (cfpkt_getlen(pkt) <= RFM_HEAD_SIZE-1) @@ -254,8 +256,11 @@ static int cfrfml_transmit(struct cflayer *layr, struct cfpkt *pkt)  		err = cfrfml_transmit_segment(rfml, frontpkt); -		if (err != 0) +		if (err != 0) { +			frontpkt = NULL;  			goto out; +		} +  		frontpkt = rearpkt;  		rearpkt = NULL; @@ -289,19 +294,8 @@ out:  		if (rearpkt)  			cfpkt_destroy(rearpkt); -		if (frontpkt && frontpkt != pkt) { - +		if (frontpkt)  			cfpkt_destroy(frontpkt); -			/* -			 * Socket layer will free the original packet, -			 * but this packet may already be sent and -			 * freed. So we have to return 0 in this case -			 * to avoid socket layer to re-free this packet. -			 * The return of shutdown indication will -			 * cause connection to be invalidated anyhow. -			 */ -			err = 0; -		}  	}  	return err; diff --git a/net/caif/cfserl.c b/net/caif/cfserl.c index 9297f7dea9d..ce60f06d76d 100644 --- a/net/caif/cfserl.c +++ b/net/caif/cfserl.c @@ -1,6 +1,6 @@  /*   * Copyright (C) ST-Ericsson AB 2010 - * Author:	Sjur Brendeland/sjur.brandeland@stericsson.com + * Author:	Sjur Brendeland   * License terms: GNU General Public License (GPL) version 2   */ @@ -25,26 +25,21 @@ struct cfserl {  	spinlock_t sync;  	bool usestx;  }; -#define STXLEN(layr) (layr->usestx ? 1 : 0)  static int cfserl_receive(struct cflayer *layr, struct cfpkt *pkt);  static int cfserl_transmit(struct cflayer *layr, struct cfpkt *pkt);  static void cfserl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, -				int phyid); +			   int phyid); -struct cflayer *cfserl_create(int type, int instance, bool use_stx) +struct cflayer *cfserl_create(int instance, bool use_stx)  { -	struct cfserl *this = kmalloc(sizeof(struct cfserl), GFP_ATOMIC); -	if (!this) { -		pr_warn("Out of memory\n"); +	struct cfserl *this = kzalloc(sizeof(struct cfserl), GFP_ATOMIC); +	if (!this)  		return NULL; -	}  	caif_assert(offsetof(struct cfserl, layer) == 0); -	memset(this, 0, sizeof(struct cfserl));  	this->layer.receive = cfserl_receive;  	this->layer.transmit = cfserl_transmit;  	this->layer.ctrlcmd = cfserl_ctrlcmd; -	this->layer.type = type;  	this->usestx = use_stx;  	spin_lock_init(&this->sync);  	snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "ser1"); @@ -180,19 +175,14 @@ static int cfserl_receive(struct cflayer *l, struct cfpkt *newpkt)  static int cfserl_transmit(struct cflayer *layer, struct cfpkt *newpkt)  {  	struct cfserl *layr = container_obj(layer); -	int ret;  	u8 tmp8 = CFSERL_STX;  	if (layr->usestx)  		cfpkt_add_head(newpkt, &tmp8, 1); -	ret = layer->dn->transmit(layer->dn, newpkt); -	if (ret < 0) -		cfpkt_extr_head(newpkt, &tmp8, 1); - -	return ret; +	return layer->dn->transmit(layer->dn, newpkt);  }  static void cfserl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, -				int phyid) +			   int phyid)  {  	layr->up->ctrlcmd(layr->up, ctrl, phyid);  } diff --git a/net/caif/cfsrvl.c b/net/caif/cfsrvl.c index ab5e542526b..a6e11546305 100644 --- a/net/caif/cfsrvl.c +++ b/net/caif/cfsrvl.c @@ -1,6 +1,6 @@  /*   * Copyright (C) ST-Ericsson AB 2010 - * Author:	Sjur Brendeland/sjur.brandeland@stericsson.com + * Author:	Sjur Brendeland   * License terms: GNU General Public License (GPL) version 2   */ @@ -10,9 +10,12 @@  #include <linux/types.h>  #include <linux/errno.h>  #include <linux/slab.h> +#include <linux/module.h> +#include <linux/pkt_sched.h>  #include <net/caif/caif_layer.h>  #include <net/caif/cfsrvl.h>  #include <net/caif/cfpkt.h> +#include <net/caif/caif_dev.h>  #define SRVL_CTRL_PKT_SIZE 1  #define SRVL_FLOW_OFF 0x81 @@ -23,12 +26,12 @@  #define container_obj(layr) container_of(layr, struct cfsrvl, layer)  static void cfservl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, -				int phyid) +			    int phyid)  {  	struct cfsrvl *service = container_obj(layr); -	caif_assert(layr->up != NULL); -	caif_assert(layr->up->ctrlcmd != NULL); +	if (layr->up == NULL || layr->up->ctrlcmd == NULL) +		return;  	switch (ctrl) {  	case CAIF_CTRLCMD_INIT_RSP: @@ -107,10 +110,8 @@ static int cfservl_modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl)  			struct caif_payload_info *info;  			u8 flow_on = SRVL_FLOW_ON;  			pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE); -			if (!pkt) { -				pr_warn("Out of memory\n"); +			if (!pkt)  				return -ENOMEM; -			}  			if (cfpkt_add_head(pkt, &flow_on, 1) < 0) {  				pr_err("Packet is erroneous!\n"); @@ -121,6 +122,7 @@ static int cfservl_modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl)  			info->channel_id = service->layer.id;  			info->hdr_len = 1;  			info->dev_info = &service->dev_info; +			cfpkt_set_prio(pkt, TC_PRIO_CONTROL);  			return layr->dn->transmit(layr->dn, pkt);  		}  	case CAIF_MODEMCMD_FLOW_OFF_REQ: @@ -129,10 +131,8 @@ static int cfservl_modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl)  			struct caif_payload_info *info;  			u8 flow_off = SRVL_FLOW_OFF;  			pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE); -			if (!pkt) { -				pr_warn("Out of memory\n"); +			if (!pkt)  				return -ENOMEM; -			}  			if (cfpkt_add_head(pkt, &flow_off, 1) < 0) {  				pr_err("Packet is erroneous!\n"); @@ -143,6 +143,7 @@ static int cfservl_modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl)  			info->channel_id = service->layer.id;  			info->hdr_len = 1;  			info->dev_info = &service->dev_info; +			cfpkt_set_prio(pkt, TC_PRIO_CONTROL);  			return layr->dn->transmit(layr->dn, pkt);  		}  	default: @@ -151,22 +152,16 @@ static int cfservl_modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl)  	return -EINVAL;  } -void cfservl_destroy(struct cflayer *layer) +static void cfsrvl_release(struct cflayer *layer)  { -	kfree(layer); -} - -void cfsrvl_release(struct kref *kref) -{ -	struct cfsrvl *service = container_of(kref, struct cfsrvl, ref); +	struct cfsrvl *service = container_of(layer, struct cfsrvl, layer);  	kfree(service);  }  void cfsrvl_init(struct cfsrvl *service, -			u8 channel_id, -			struct dev_info *dev_info, -			bool supports_flowctrl -			) +		 u8 channel_id, +		 struct dev_info *dev_info, +		 bool supports_flowctrl)  {  	caif_assert(offsetof(struct cfsrvl, layer) == 0);  	service->open = false; @@ -178,22 +173,17 @@ void cfsrvl_init(struct cfsrvl *service,  	service->dev_info = *dev_info;  	service->supports_flowctrl = supports_flowctrl;  	service->release = cfsrvl_release; -	kref_init(&service->ref);  } -  bool cfsrvl_ready(struct cfsrvl *service, int *err)  { -	if (service->open && service->modem_flow_on && service->phy_flow_on) -		return true;  	if (!service->open) {  		*err = -ENOTCONN;  		return false;  	} -	caif_assert(!(service->modem_flow_on && service->phy_flow_on)); -	*err = -EAGAIN; -	return false; +	return true;  } +  u8 cfsrvl_getphyid(struct cflayer *layer)  {  	struct cfsrvl *servl = container_obj(layer); @@ -205,3 +195,27 @@ bool cfsrvl_phyid_match(struct cflayer *layer, int phyid)  	struct cfsrvl *servl = container_obj(layer);  	return servl->dev_info.id == phyid;  } + +void caif_free_client(struct cflayer *adap_layer) +{ +	struct cfsrvl *servl; +	if (adap_layer == NULL || adap_layer->dn == NULL) +		return; +	servl = container_obj(adap_layer->dn); +	servl->release(&servl->layer); +} +EXPORT_SYMBOL(caif_free_client); + +void caif_client_register_refcnt(struct cflayer *adapt_layer, +				 void (*hold)(struct cflayer *lyr), +				 void (*put)(struct cflayer *lyr)) +{ +	struct cfsrvl *service; + +	if (WARN_ON(adapt_layer == NULL || adapt_layer->dn == NULL)) +		return; +	service = container_of(adapt_layer->dn, struct cfsrvl, layer); +	service->hold = hold; +	service->put = put; +} +EXPORT_SYMBOL(caif_client_register_refcnt); diff --git a/net/caif/cfutill.c b/net/caif/cfutill.c index efad410e4c8..1728fa4471c 100644 --- a/net/caif/cfutill.c +++ b/net/caif/cfutill.c @@ -1,6 +1,6 @@  /*   * Copyright (C) ST-Ericsson AB 2010 - * Author:	Sjur Brendeland/sjur.brandeland@stericsson.com + * Author:	Sjur Brendeland   * License terms: GNU General Public License (GPL) version 2   */ @@ -20,19 +20,16 @@  #define UTIL_REMOTE_SHUTDOWN 0x82  #define UTIL_FLOW_OFF 0x81  #define UTIL_FLOW_ON  0x80 -#define UTIL_CTRL_PKT_SIZE 1 +  static int cfutill_receive(struct cflayer *layr, struct cfpkt *pkt);  static int cfutill_transmit(struct cflayer *layr, struct cfpkt *pkt);  struct cflayer *cfutill_create(u8 channel_id, struct dev_info *dev_info)  { -	struct cfsrvl *util = kmalloc(sizeof(struct cfsrvl), GFP_ATOMIC); -	if (!util) { -		pr_warn("Out of memory\n"); +	struct cfsrvl *util = kzalloc(sizeof(struct cfsrvl), GFP_ATOMIC); +	if (!util)  		return NULL; -	}  	caif_assert(offsetof(struct cfsrvl, layer) == 0); -	memset(util, 0, sizeof(struct cfsrvl));  	cfsrvl_init(util, channel_id, dev_info, true);  	util->layer.receive = cfutill_receive;  	util->layer.transmit = cfutill_transmit; @@ -87,8 +84,11 @@ static int cfutill_transmit(struct cflayer *layr, struct cfpkt *pkt)  	caif_assert(layr != NULL);  	caif_assert(layr->dn != NULL);  	caif_assert(layr->dn->transmit != NULL); -	if (!cfsrvl_ready(service, &ret)) + +	if (!cfsrvl_ready(service, &ret)) { +		cfpkt_destroy(pkt);  		return ret; +	}  	cfpkt_add_head(pkt, &zero, 1);  	/* Add info for MUX-layer to route the packet out. */ @@ -100,10 +100,5 @@ static int cfutill_transmit(struct cflayer *layr, struct cfpkt *pkt)  	 */  	info->hdr_len = 1;  	info->dev_info = &service->dev_info; -	ret = layr->dn->transmit(layr->dn, pkt); -	if (ret < 0) { -		u32 tmp32; -		cfpkt_extr_head(pkt, &tmp32, 4); -	} -	return ret; +	return layr->dn->transmit(layr->dn, pkt);  } diff --git a/net/caif/cfveil.c b/net/caif/cfveil.c index 3b425b189a9..262224581ef 100644 --- a/net/caif/cfveil.c +++ b/net/caif/cfveil.c @@ -1,6 +1,6 @@  /*   * Copyright (C) ST-Ericsson AB 2010 - * Author:	Sjur Brendeland/sjur.brandeland@stericsson.com + * Author:	Sjur Brendeland   * License terms: GNU General Public License (GPL) version 2   */ @@ -17,7 +17,7 @@  #define VEI_FLOW_OFF 0x81  #define VEI_FLOW_ON  0x80  #define VEI_SET_PIN  0x82 -#define VEI_CTRL_PKT_SIZE 1 +  #define container_obj(layr) container_of(layr, struct cfsrvl, layer)  static int cfvei_receive(struct cflayer *layr, struct cfpkt *pkt); @@ -25,13 +25,10 @@ static int cfvei_transmit(struct cflayer *layr, struct cfpkt *pkt);  struct cflayer *cfvei_create(u8 channel_id, struct dev_info *dev_info)  { -	struct cfsrvl *vei = kmalloc(sizeof(struct cfsrvl), GFP_ATOMIC); -	if (!vei) { -		pr_warn("Out of memory\n"); +	struct cfsrvl *vei = kzalloc(sizeof(struct cfsrvl), GFP_ATOMIC); +	if (!vei)  		return NULL; -	}  	caif_assert(offsetof(struct cfsrvl, layer) == 0); -	memset(vei, 0, sizeof(struct cfsrvl));  	cfsrvl_init(vei, channel_id, dev_info, true);  	vei->layer.receive = cfvei_receive;  	vei->layer.transmit = cfvei_transmit; @@ -82,13 +79,14 @@ static int cfvei_transmit(struct cflayer *layr, struct cfpkt *pkt)  	int ret;  	struct cfsrvl *service = container_obj(layr);  	if (!cfsrvl_ready(service, &ret)) -		return ret; +		goto err;  	caif_assert(layr->dn != NULL);  	caif_assert(layr->dn->transmit != NULL);  	if (cfpkt_add_head(pkt, &tmp, 1) < 0) {  		pr_err("Packet is erroneous!\n"); -		return -EPROTO; +		ret = -EPROTO; +		goto err;  	}  	/* Add info-> for MUX-layer to route the packet out. */ @@ -96,8 +94,8 @@ static int cfvei_transmit(struct cflayer *layr, struct cfpkt *pkt)  	info->channel_id = service->layer.id;  	info->hdr_len = 1;  	info->dev_info = &service->dev_info; -	ret = layr->dn->transmit(layr->dn, pkt); -	if (ret < 0) -		cfpkt_extr_head(pkt, &tmp, 1); +	return layr->dn->transmit(layr->dn, pkt); +err: +	cfpkt_destroy(pkt);  	return ret;  } diff --git a/net/caif/cfvidl.c b/net/caif/cfvidl.c index bf6fef2a0ef..b3b110e8a35 100644 --- a/net/caif/cfvidl.c +++ b/net/caif/cfvidl.c @@ -1,6 +1,6 @@  /*   * Copyright (C) ST-Ericsson AB 2010 - * Author:	Sjur Brendeland/sjur.brandeland@stericsson.com + * Author:	Sjur Brendeland   * License terms: GNU General Public License (GPL) version 2   */ @@ -21,14 +21,11 @@ static int cfvidl_transmit(struct cflayer *layr, struct cfpkt *pkt);  struct cflayer *cfvidl_create(u8 channel_id, struct dev_info *dev_info)  { -	struct cfsrvl *vid = kmalloc(sizeof(struct cfsrvl), GFP_ATOMIC); -	if (!vid) { -		pr_warn("Out of memory\n"); +	struct cfsrvl *vid = kzalloc(sizeof(struct cfsrvl), GFP_ATOMIC); +	if (!vid)  		return NULL; -	}  	caif_assert(offsetof(struct cfsrvl, layer) == 0); -	memset(vid, 0, sizeof(struct cfsrvl));  	cfsrvl_init(vid, channel_id, dev_info, false);  	vid->layer.receive = cfvidl_receive;  	vid->layer.transmit = cfvidl_transmit; @@ -53,15 +50,16 @@ static int cfvidl_transmit(struct cflayer *layr, struct cfpkt *pkt)  	struct caif_payload_info *info;  	u32 videoheader = 0;  	int ret; -	if (!cfsrvl_ready(service, &ret)) + +	if (!cfsrvl_ready(service, &ret)) { +		cfpkt_destroy(pkt);  		return ret; +	} +  	cfpkt_add_head(pkt, &videoheader, 4);  	/* Add info for MUX-layer to route the packet out */  	info = cfpkt_info(pkt);  	info->channel_id = service->layer.id;  	info->dev_info = &service->dev_info; -	ret = layr->dn->transmit(layr->dn, pkt); -	if (ret < 0) -		cfpkt_extr_head(pkt, &videoheader, 4); -	return ret; +	return layr->dn->transmit(layr->dn, pkt);  } diff --git a/net/caif/chnl_net.c b/net/caif/chnl_net.c index 84a422c9894..4589ff67bfa 100644 --- a/net/caif/chnl_net.c +++ b/net/caif/chnl_net.c @@ -1,14 +1,14 @@  /*   * Copyright (C) ST-Ericsson AB 2010 - * Authors:	Sjur Brendeland/sjur.brandeland@stericsson.com - *		Daniel Martensson / Daniel.Martensson@stericsson.com + * Authors:	Sjur Brendeland + *		Daniel Martensson   * License terms: GNU General Public License (GPL) version 2   */  #define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ -#include <linux/version.h>  #include <linux/fs.h> +#include <linux/hardirq.h>  #include <linux/init.h>  #include <linux/module.h>  #include <linux/netdevice.h> @@ -20,7 +20,6 @@  #include <linux/caif/if_caif.h>  #include <net/rtnetlink.h>  #include <net/caif/caif_layer.h> -#include <net/caif/cfcnfg.h>  #include <net/caif/cfpkt.h>  #include <net/caif/caif_dev.h> @@ -29,6 +28,7 @@  /* 5 sec. connect timeout */  #define CONNECT_TIMEOUT (5 * HZ)  #define CAIF_NET_DEFAULT_QUEUE_LEN 500 +#define UNDEF_CONNID 0xffffffff  /*This list is protected by the rtnl lock. */  static LIST_HEAD(chnl_net_list); @@ -73,24 +73,44 @@ static void robust_list_del(struct list_head *delete_node)  static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt)  {  	struct sk_buff *skb; -	struct chnl_net *priv  = container_of(layr, struct chnl_net, chnl); +	struct chnl_net *priv;  	int pktlen; -	int err = 0; +	const u8 *ip_version; +	u8 buf;  	priv = container_of(layr, struct chnl_net, chnl); -  	if (!priv)  		return -EINVAL; +	skb = (struct sk_buff *) cfpkt_tonative(pkt); +  	/* Get length of CAIF packet. */ -	pktlen = cfpkt_getlen(pkt); +	pktlen = skb->len; -	skb = (struct sk_buff *) cfpkt_tonative(pkt);  	/* Pass some minimum information and  	 * send the packet to the net stack.  	 */  	skb->dev = priv->netdev; -	skb->protocol = htons(ETH_P_IP); + +	/* check the version of IP */ +	ip_version = skb_header_pointer(skb, 0, 1, &buf); +	if (!ip_version) { +		kfree_skb(skb); +		return -EINVAL; +	} + +	switch (*ip_version >> 4) { +	case 4: +		skb->protocol = htons(ETH_P_IP); +		break; +	case 6: +		skb->protocol = htons(ETH_P_IPV6); +		break; +	default: +		kfree_skb(skb); +		priv->netdev->stats.rx_errors++; +		return -EINVAL; +	}  	/* If we change the header in loop mode, the checksum is corrupted. */  	if (priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP) @@ -107,7 +127,7 @@ static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt)  	priv->netdev->stats.rx_packets++;  	priv->netdev->stats.rx_bytes += pktlen; -	return err; +	return 0;  }  static int delete_device(struct chnl_net *dev) @@ -123,22 +143,31 @@ static void close_work(struct work_struct *work)  	struct chnl_net *dev = NULL;  	struct list_head *list_node;  	struct list_head *_tmp; -	/* May be called with or without RTNL lock held */ -	int islocked = rtnl_is_locked(); -	if (!islocked) -		rtnl_lock(); + +	rtnl_lock();  	list_for_each_safe(list_node, _tmp, &chnl_net_list) {  		dev = list_entry(list_node, struct chnl_net, list_field);  		if (dev->state == CAIF_SHUTDOWN)  			dev_close(dev->netdev);  	} -	if (!islocked) -		rtnl_unlock(); +	rtnl_unlock();  }  static DECLARE_WORK(close_worker, close_work); +static void chnl_hold(struct cflayer *lyr) +{ +	struct chnl_net *priv = container_of(lyr, struct chnl_net, chnl); +	dev_hold(priv->netdev); +} + +static void chnl_put(struct cflayer *lyr) +{ +	struct chnl_net *priv = container_of(lyr, struct chnl_net, chnl); +	dev_put(priv->netdev); +} +  static void chnl_flowctrl_cb(struct cflayer *layr, enum caif_ctrlcmd flow, -				int phyid) +			     int phyid)  {  	struct chnl_net *priv = container_of(layr, struct chnl_net, chnl);  	pr_debug("NET flowctrl func called flow: %s\n", @@ -174,6 +203,7 @@ static void chnl_flowctrl_cb(struct cflayer *layr, enum caif_ctrlcmd flow,  		netif_wake_queue(priv->netdev);  		break;  	case CAIF_CTRLCMD_INIT_RSP: +		caif_client_register_refcnt(&priv->chnl, chnl_hold, chnl_put);  		priv->state = CAIF_CONNECTED;  		priv->flowenabled = true;  		netif_wake_queue(priv->netdev); @@ -195,12 +225,16 @@ static int chnl_net_start_xmit(struct sk_buff *skb, struct net_device *dev)  	if (skb->len > priv->netdev->mtu) {  		pr_warn("Size of skb exceeded MTU\n"); -		return -ENOSPC; +		kfree_skb(skb); +		dev->stats.tx_errors++; +		return NETDEV_TX_OK;  	}  	if (!priv->flowenabled) {  		pr_debug("dropping packets flow off\n"); -		return NETDEV_TX_BUSY; +		kfree_skb(skb); +		dev->stats.tx_dropped++; +		return NETDEV_TX_OK;  	}  	if (priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP) @@ -214,9 +248,8 @@ static int chnl_net_start_xmit(struct sk_buff *skb, struct net_device *dev)  	/* Send the packet down the stack. */  	result = priv->chnl.dn->transmit(priv->chnl.dn, pkt);  	if (result) { -		if (result == -EAGAIN) -			result = NETDEV_TX_BUSY; -		return result; +		dev->stats.tx_dropped++; +		return NETDEV_TX_OK;  	}  	/* Update statistics. */ @@ -241,8 +274,9 @@ static int chnl_net_open(struct net_device *dev)  	if (priv->state != CAIF_CONNECTING) {  		priv->state = CAIF_CONNECTING; -		result = caif_connect_client(&priv->conn_req, &priv->chnl, -					&llifindex, &headroom, &tailroom); +		result = caif_connect_client(dev_net(dev), &priv->conn_req, +						&priv->chnl, &llifindex, +						&headroom, &tailroom);  		if (result != 0) {  				pr_debug("err: "  					 "Unable to register and open device," @@ -251,7 +285,7 @@ static int chnl_net_open(struct net_device *dev)  				goto error;  		} -		lldev = dev_get_by_index(dev_net(dev), llifindex); +		lldev = __dev_get_by_index(dev_net(dev), llifindex);  		if (lldev == NULL) {  			pr_debug("no interface?\n"); @@ -273,7 +307,6 @@ static int chnl_net_open(struct net_device *dev)  		mtu = min_t(int, dev->mtu, lldev->mtu - (headroom + tailroom));  		mtu = min_t(int, GPRS_PDP_MTU, mtu);  		dev_set_mtu(dev, mtu); -		dev_put(lldev);  		if (mtu < 100) {  			pr_warn("CAIF Interface MTU too small (%d)\n", mtu); @@ -298,7 +331,7 @@ static int chnl_net_open(struct net_device *dev)  	if (result == 0) {  		pr_debug("connect timeout\n"); -		caif_disconnect_client(&priv->chnl); +		caif_disconnect_client(dev_net(dev), &priv->chnl);  		priv->state = CAIF_DISCONNECTED;  		pr_debug("state disconnected\n");  		result = -ETIMEDOUT; @@ -314,7 +347,7 @@ static int chnl_net_open(struct net_device *dev)  	return 0;  error: -	caif_disconnect_client(&priv->chnl); +	caif_disconnect_client(dev_net(dev), &priv->chnl);  	priv->state = CAIF_DISCONNECTED;  	pr_debug("state disconnected\n");  	return result; @@ -328,7 +361,7 @@ static int chnl_net_stop(struct net_device *dev)  	ASSERT_RTNL();  	priv = netdev_priv(dev);  	priv->state = CAIF_DISCONNECTED; -	caif_disconnect_client(&priv->chnl); +	caif_disconnect_client(dev_net(dev), &priv->chnl);  	return 0;  } @@ -357,11 +390,18 @@ static const struct net_device_ops netdev_ops = {  	.ndo_start_xmit = chnl_net_start_xmit,  }; +static void chnl_net_destructor(struct net_device *dev) +{ +	struct chnl_net *priv = netdev_priv(dev); +	caif_free_client(&priv->chnl); +	free_netdev(dev); +} +  static void ipcaif_net_setup(struct net_device *dev)  {  	struct chnl_net *priv;  	dev->netdev_ops = &netdev_ops; -	dev->destructor = free_netdev; +	dev->destructor = chnl_net_destructor;  	dev->flags |= IFF_NOARP;  	dev->flags |= IFF_POINTOPOINT;  	dev->mtu = GPRS_PDP_MTU; @@ -375,12 +415,10 @@ static void ipcaif_net_setup(struct net_device *dev)  	priv->conn_req.link_selector = CAIF_LINK_HIGH_BANDW;  	priv->conn_req.priority = CAIF_PRIO_LOW;  	/* Insert illegal value */ -	priv->conn_req.sockaddr.u.dgm.connection_id = -1; +	priv->conn_req.sockaddr.u.dgm.connection_id = UNDEF_CONNID;  	priv->flowenabled = false; -	ASSERT_RTNL();  	init_waitqueue_head(&priv->netmgmt_wq); -	list_add(&priv->list_field, &chnl_net_list);  } @@ -389,14 +427,14 @@ static int ipcaif_fill_info(struct sk_buff *skb, const struct net_device *dev)  	struct chnl_net *priv;  	u8 loop;  	priv = netdev_priv(dev); -	NLA_PUT_U32(skb, IFLA_CAIF_IPV4_CONNID, -		    priv->conn_req.sockaddr.u.dgm.connection_id); -	NLA_PUT_U32(skb, IFLA_CAIF_IPV6_CONNID, -		    priv->conn_req.sockaddr.u.dgm.connection_id); +	if (nla_put_u32(skb, IFLA_CAIF_IPV4_CONNID, +			priv->conn_req.sockaddr.u.dgm.connection_id) || +	    nla_put_u32(skb, IFLA_CAIF_IPV6_CONNID, +			priv->conn_req.sockaddr.u.dgm.connection_id)) +		goto nla_put_failure;  	loop = priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP; -	NLA_PUT_U8(skb, IFLA_CAIF_LOOPBACK, loop); - - +	if (nla_put_u8(skb, IFLA_CAIF_LOOPBACK, loop)) +		goto nla_put_failure;  	return 0;  nla_put_failure:  	return -EMSGSIZE; @@ -404,7 +442,7 @@ nla_put_failure:  }  static void caif_netlink_parms(struct nlattr *data[], -				struct caif_connect_request *conn_req) +			       struct caif_connect_request *conn_req)  {  	if (!data) {  		pr_warn("no params data found\n"); @@ -437,11 +475,19 @@ static int ipcaif_newlink(struct net *src_net, struct net_device *dev,  	ret = register_netdevice(dev);  	if (ret)  		pr_warn("device rtml registration failed\n"); +	else +		list_add(&caifdev->list_field, &chnl_net_list); + +	/* Use ifindex as connection id, and use loopback channel default. */ +	if (caifdev->conn_req.sockaddr.u.dgm.connection_id == UNDEF_CONNID) { +		caifdev->conn_req.sockaddr.u.dgm.connection_id = dev->ifindex; +		caifdev->conn_req.protocol = CAIFPROTO_DATAGRAM_LOOP; +	}  	return ret;  }  static int ipcaif_changelink(struct net_device *dev, struct nlattr *tb[], -				struct nlattr *data[]) +			     struct nlattr *data[])  {  	struct chnl_net *caifdev;  	ASSERT_RTNL();  | 
