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 | 255 | ||||
| -rw-r--r-- | net/caif/caif_usb.c | 204 | ||||
| -rw-r--r-- | net/caif/cfcnfg.c | 590 | ||||
| -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 | 112 |
20 files changed, 1532 insertions, 1366 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 8184c031d02..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 { - - ret = -ETIMEDOUT; + memset(skb->cb, 0, sizeof(struct caif_payload_info)); + cfpkt_set_prio(pkt, cf_sk->sk.sk_priority); - /* 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); @@ -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 c665de778b6..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,132 +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) { - pr_err("PROTOCOL ERROR - Error removing service_layer Channel_Id(%d)", - channel_id); - ret = -EINVAL; - goto end; - } - layer_set_up(servl, NULL); - ret = cfctrl_linkdown_req(cnfg->ctrl, channel_id, adap_layer); - if (ret) - 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; + return 0; } -EXPORT_SYMBOL(cfcnfg_disconn_adapt_layer); - -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, @@ -263,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, @@ -317,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) { @@ -350,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; @@ -367,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 6008d6dc18a..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,21 +73,20 @@ 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. */ @@ -95,8 +94,11 @@ static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt) /* check the version of IP */ ip_version = skb_header_pointer(skb, 0, 1, &buf); - if (!ip_version) + if (!ip_version) { + kfree_skb(skb); return -EINVAL; + } + switch (*ip_version >> 4) { case 4: skb->protocol = htons(ETH_P_IP); @@ -105,6 +107,8 @@ static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt) skb->protocol = htons(ETH_P_IPV6); break; default: + kfree_skb(skb); + priv->netdev->stats.rx_errors++; return -EINVAL; } @@ -123,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) @@ -139,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", @@ -190,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); @@ -211,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) @@ -230,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. */ @@ -257,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," @@ -267,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"); @@ -289,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); @@ -314,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; @@ -330,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; @@ -344,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; } @@ -373,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; @@ -391,7 +415,7 @@ 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; init_waitqueue_head(&priv->netmgmt_wq); @@ -403,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; @@ -418,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"); @@ -453,11 +477,17 @@ static int ipcaif_newlink(struct net *src_net, struct net_device *dev, 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(); |
