* Re: [PATCH] bonding: fix arp_validate on bonds inside a bridge
From: Jay Vosburgh @ 2010-04-28 19:05 UTC (permalink / raw)
To: Jiri Bohac; +Cc: bonding-devel, netdev
In-Reply-To: <20100428125940.GB13400@midget.suse.cz>
Jiri Bohac <jbohac@suse.cz> wrote:
>bonding with arp_validate does not currently work when the
>bonding master is part of a bridge. This is because
>bond_arp_rcv() is registered as a packet type handler for ARP,
>but before netif_receive_skb() processes the ptype_base hash
>table, handle_bridge() is called and changes the skb->dev to
>point to the bridge device.
>
>This patch makes bonding_should_drop() call the bonding ARP
>handler directly if a IFF_MASTER_NEEDARP flag is set on the
>bonding master. bond_register_arp() now only needs to set the
>IFF_MASTER_NEEDARP flag.
>
>We ne longer need special ARP handling for inactive slaves, hence
>IFF_SLAVE_NEEDARP is not needed.
>
>skb_reset_network_header() and skb_reset_transport_header() need
>to be called before the call to bonding_should_drop() because
>bond_handle_arp() needs the offsets initialized.
>
>P.S.: bonding_should_drop() should probably be renamed to
>handle_bonding() -- we already have handle_bridge() and
>handle_macvlan(), and bonding_should_drop() has long been doing
>other stuff than deciding which packets to drop...
I agree, and I have code that I've been working on locally that
wraps the "should_drop" into a hook, similar to the bridge and macvlan
hooks that already exist. I used different names, though. I've got
bond_handle_frame (and bond_handle_frame_hook) bond_main.c and
handle_bonding in net/core/dev.c, where the implementation for
handle_bonding parallels that of bridge and macvlan:
#if defined(CONFIG_BONDING) || defined(CONFIG_BONDING_MODULE)
int (*bonding_handle_frame_hook)(struct sk_buff *skb) __read_mostly;
EXPORT_SYMBOL_GPL(bonding_handle_frame_hook);
static inline int handle_bonding(struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
struct net_device *master = dev->master;
if (master->priv_flags & IFF_MASTER_ML)
return bonding_handle_frame_hook(skb);
return skb_bond_should_drop(skb);
}
#else
#define handle_bonding(skb) (skb)
#endif
I think the structure you're using (skb_bond_should_drop calls
the hook as needed) may be better overall, as it doesn't require special
logic in the VLAN cases. The disadvantage is that it's a little uglier
to hide the hook declaration behind #ifdef CONFIG_BONDING (more on that
below).
The code I'm working on now doesn't just hook for ARP (I'm
working on a load-balance by subnet mode that needs to assign skb->dev
by destination to permit the slaves to operate independently from the
master; but that's another topic). I did leave as much as possible to
the priv_flags, since that is less expensive to process than poking
around in the bonding structures (no locks for the priv_flags).
I haven't tested your patch yet, but it looks like it should
work as advertised.
I have a couple of minor comments, below, but nothing
substantive about the core of the changes.
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
>Signed-off-by: Jiri Bohac <jbohac@suse.cz>
>
>diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
>index 0075514..cafd404 100644
>--- a/drivers/net/bonding/bond_main.c
>+++ b/drivers/net/bonding/bond_main.c
>@@ -1940,8 +1940,7 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
> }
>
> slave_dev->priv_flags &= ~(IFF_MASTER_8023AD | IFF_MASTER_ALB |
>- IFF_SLAVE_INACTIVE | IFF_BONDING |
>- IFF_SLAVE_NEEDARP);
>+ IFF_SLAVE_INACTIVE | IFF_BONDING);
>
> kfree(slave);
>
>@@ -2612,11 +2611,12 @@ static void bond_validate_arp(struct bonding *bond, struct slave *slave, __be32
> }
> }
>
>-static int bond_arp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
>+static void bond_handle_arp(struct sk_buff *skb)
> {
> struct arphdr *arp;
> struct slave *slave;
> struct bonding *bond;
>+ struct net_device *dev = skb->dev->master, *orig_dev = skb->dev;
> unsigned char *arp_ptr;
> __be32 sip, tip;
>
>@@ -2637,9 +2637,8 @@ static int bond_arp_rcv(struct sk_buff *skb, struct net_device *dev, struct pack
> bond = netdev_priv(dev);
> read_lock(&bond->lock);
>
>- pr_debug("bond_arp_rcv: bond %s skb->dev %s orig_dev %s\n",
>- bond->dev->name, skb->dev ? skb->dev->name : "NULL",
>- orig_dev ? orig_dev->name : "NULL");
>+ pr_debug("bond_handle_arp: bond: %s, master: %s, slave: %s\n",
>+ bond->dev->name, dev->name, orig_dev->name);
>
> slave = bond_get_slave_by_dev(bond, orig_dev);
> if (!slave || !slave_do_arp_validate(bond, slave))
>@@ -2684,8 +2683,7 @@ static int bond_arp_rcv(struct sk_buff *skb, struct net_device *dev, struct pack
> out_unlock:
> read_unlock(&bond->lock);
> out:
>- dev_kfree_skb(skb);
>- return NET_RX_SUCCESS;
>+ return;
> }
>
> /*
>@@ -3567,23 +3565,12 @@ static void bond_unregister_lacpdu(struct bonding *bond)
>
> void bond_register_arp(struct bonding *bond)
> {
>- struct packet_type *pt = &bond->arp_mon_pt;
>-
>- if (pt->type)
>- return;
>-
>- pt->type = htons(ETH_P_ARP);
>- pt->dev = bond->dev;
>- pt->func = bond_arp_rcv;
>- dev_add_pack(pt);
>+ bond->dev->priv_flags |= IFF_MASTER_NEEDARP;
> }
>
> void bond_unregister_arp(struct bonding *bond)
> {
>- struct packet_type *pt = &bond->arp_mon_pt;
>-
>- dev_remove_pack(pt);
>- pt->type = 0;
>+ bond->dev->priv_flags &= ~IFF_MASTER_NEEDARP;
> }
>
> /*---------------------------- Hashing Policies -----------------------------*/
>@@ -5041,6 +5028,7 @@ static int __init bonding_init(void)
> register_netdevice_notifier(&bond_netdev_notifier);
> register_inetaddr_notifier(&bond_inetaddr_notifier);
> bond_register_ipv6_notifier();
>+ bond_handle_arp_hook = bond_handle_arp;
> out:
> return res;
> err:
>@@ -5061,6 +5049,7 @@ static void __exit bonding_exit(void)
>
> rtnl_link_unregister(&bond_link_ops);
> unregister_pernet_subsys(&bond_net_ops);
>+ bond_handle_arp_hook = NULL;
> }
>
> module_init(bonding_init);
>diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
>index 257a7a4..57adfe5 100644
>--- a/drivers/net/bonding/bonding.h
>+++ b/drivers/net/bonding/bonding.h
>@@ -212,7 +212,6 @@ struct bonding {
> struct bond_params params;
> struct list_head vlan_list;
> struct vlan_group *vlgrp;
>- struct packet_type arp_mon_pt;
> struct workqueue_struct *wq;
> struct delayed_work mii_work;
> struct delayed_work arp_work;
>@@ -292,14 +291,12 @@ static inline void bond_set_slave_inactive_flags(struct slave *slave)
> if (!bond_is_lb(bond))
> slave->state = BOND_STATE_BACKUP;
> slave->dev->priv_flags |= IFF_SLAVE_INACTIVE;
>- if (slave_do_arp_validate(bond, slave))
>- slave->dev->priv_flags |= IFF_SLAVE_NEEDARP;
> }
>
> static inline void bond_set_slave_active_flags(struct slave *slave)
> {
> slave->state = BOND_STATE_ACTIVE;
>- slave->dev->priv_flags &= ~(IFF_SLAVE_INACTIVE | IFF_SLAVE_NEEDARP);
>+ slave->dev->priv_flags &= ~IFF_SLAVE_INACTIVE;
> }
>
> static inline void bond_set_master_3ad_flags(struct bonding *bond)
>diff --git a/include/linux/if.h b/include/linux/if.h
>index 3a9f410..84ab2c8 100644
>--- a/include/linux/if.h
>+++ b/include/linux/if.h
>@@ -63,7 +63,7 @@
> #define IFF_MASTER_8023AD 0x8 /* bonding master, 802.3ad. */
> #define IFF_MASTER_ALB 0x10 /* bonding master, balance-alb. */
> #define IFF_BONDING 0x20 /* bonding master or slave */
>-#define IFF_SLAVE_NEEDARP 0x40 /* need ARPs for validation */
>+#define IFF_MASTER_NEEDARP 0x40 /* need ARPs for validation */
> #define IFF_ISATAP 0x80 /* ISATAP interface (RFC4214) */
> #define IFF_MASTER_ARPMON 0x100 /* bonding master, ARP mon in use */
> #define IFF_WAN_HDLC 0x200 /* WAN HDLC device */
>diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
>index fa8b476..9f82fc6 100644
>--- a/include/linux/netdevice.h
>+++ b/include/linux/netdevice.h
>@@ -2055,6 +2055,8 @@ static inline void skb_bond_set_mac_by_master(struct sk_buff *skb,
> }
> }
>
>+extern void (*bond_handle_arp_hook)(struct sk_buff *skb);
Should this be inside the the skb_bond_should_drop function to
limit its scope? Just wondering if that's a little tidier.
> /* On bonding slaves other than the currently active slave, suppress
> * duplicates except for 802.3ad ETH_P_SLOW, alb non-mcast/bcast, and
> * ARP on active-backup slaves with arp_validate enabled.
>@@ -2076,11 +2078,13 @@ static inline int skb_bond_should_drop(struct sk_buff *skb,
> skb_bond_set_mac_by_master(skb, master);
> }
>
>- if (dev->priv_flags & IFF_SLAVE_INACTIVE) {
>- if ((dev->priv_flags & IFF_SLAVE_NEEDARP) &&
>- skb->protocol == __cpu_to_be16(ETH_P_ARP))
>- return 0;
>+ /* pass ARP frames directly to bonding
>+ before bridging or other hooks change them */
>+ if ((master->priv_flags & IFF_MASTER_NEEDARP) &&
>+ skb->protocol == __cpu_to_be16(ETH_P_ARP))
>+ bond_handle_arp_hook(skb);
>
>+ if (dev->priv_flags & IFF_SLAVE_INACTIVE) {
> if (master->priv_flags & IFF_MASTER_ALB) {
> if (skb->pkt_type != PACKET_BROADCAST &&
> skb->pkt_type != PACKET_MULTICAST)
>diff --git a/net/core/dev.c b/net/core/dev.c
>index f769098..98d85a8 100644
>--- a/net/core/dev.c
>+++ b/net/core/dev.c
>@@ -2314,6 +2314,9 @@ static inline int deliver_skb(struct sk_buff *skb,
> return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
> }
>
>+void (*bond_handle_arp_hook)(struct sk_buff *skb);
>+EXPORT_SYMBOL_GPL(bond_handle_arp_hook);
Should this be hidden by
#if defined(CONFIG_BONDING) || defined(CONFIG_BONDING_MODULE)
with some parallel changes to skb_bond_should_drop so it
vanishes if bonding is not configured? Granted, distros will all turn
it on anyway, but the embedded size might be a bit smaller.
> #if defined(CONFIG_BRIDGE) || defined (CONFIG_BRIDGE_MODULE)
>
> #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
>@@ -2507,6 +2510,10 @@ int netif_receive_skb(struct sk_buff *skb)
> if (!skb->skb_iif)
> skb->skb_iif = skb->dev->ifindex;
>
>+ skb_reset_network_header(skb);
>+ skb_reset_transport_header(skb);
>+ skb->mac_len = skb->network_header - skb->mac_header;
>+
> null_or_orig = NULL;
> orig_dev = skb->dev;
> master = ACCESS_ONCE(orig_dev->master);
>@@ -2519,10 +2526,6 @@ int netif_receive_skb(struct sk_buff *skb)
>
> __get_cpu_var(netdev_rx_stat).total++;
>
>- skb_reset_network_header(skb);
>- skb_reset_transport_header(skb);
>- skb->mac_len = skb->network_header - skb->mac_header;
>-
> pt_prev = NULL;
>
> rcu_read_lock();
-J
---
-Jay Vosburgh, IBM Linux Technology Center, fubar@us.ibm.com
^ permalink raw reply
* [PATCH net-2.6 3/3] sfc: Change falcon_probe_board() to fail for unsupported boards
From: Ben Hutchings @ 2010-04-28 19:01 UTC (permalink / raw)
To: David Miller; +Cc: netdev, linux-net-drivers
In-Reply-To: <1272481235.2549.14.camel@achroite.uk.solarflarecom.com>
The driver needs specific PHY and board support code for each SFC4000
board; there is no point trying to continue if it is missing.
Currently unsupported boards can trigger an 'oops'.
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Cc: stable@kernel.org
---
drivers/net/sfc/falcon.c | 4 +++-
drivers/net/sfc/falcon_boards.c | 13 +++----------
drivers/net/sfc/nic.h | 2 +-
3 files changed, 7 insertions(+), 12 deletions(-)
diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c
index d294d66..08278e7 100644
--- a/drivers/net/sfc/falcon.c
+++ b/drivers/net/sfc/falcon.c
@@ -1320,7 +1320,9 @@ static int falcon_probe_nvconfig(struct efx_nic *efx)
EFX_LOG(efx, "PHY is %d phy_id %d\n", efx->phy_type, efx->mdio.prtad);
- falcon_probe_board(efx, board_rev);
+ rc = falcon_probe_board(efx, board_rev);
+ if (rc)
+ goto fail2;
kfree(nvconfig);
return 0;
diff --git a/drivers/net/sfc/falcon_boards.c b/drivers/net/sfc/falcon_boards.c
index 5712fdd..c7a933a 100644
--- a/drivers/net/sfc/falcon_boards.c
+++ b/drivers/net/sfc/falcon_boards.c
@@ -728,15 +728,7 @@ static const struct falcon_board_type board_types[] = {
},
};
-static const struct falcon_board_type falcon_dummy_board = {
- .init = efx_port_dummy_op_int,
- .init_phy = efx_port_dummy_op_void,
- .fini = efx_port_dummy_op_void,
- .set_id_led = efx_port_dummy_op_set_id_led,
- .monitor = efx_port_dummy_op_int,
-};
-
-void falcon_probe_board(struct efx_nic *efx, u16 revision_info)
+int falcon_probe_board(struct efx_nic *efx, u16 revision_info)
{
struct falcon_board *board = falcon_board(efx);
u8 type_id = FALCON_BOARD_TYPE(revision_info);
@@ -754,8 +746,9 @@ void falcon_probe_board(struct efx_nic *efx, u16 revision_info)
(efx->pci_dev->subsystem_vendor == EFX_VENDID_SFC)
? board->type->ref_model : board->type->gen_type,
'A' + board->major, board->minor);
+ return 0;
} else {
EFX_ERR(efx, "unknown board type %d\n", type_id);
- board->type = &falcon_dummy_board;
+ return -ENODEV;
}
}
diff --git a/drivers/net/sfc/nic.h b/drivers/net/sfc/nic.h
index 9351c03..3166baf 100644
--- a/drivers/net/sfc/nic.h
+++ b/drivers/net/sfc/nic.h
@@ -156,7 +156,7 @@ extern struct efx_nic_type siena_a0_nic_type;
**************************************************************************
*/
-extern void falcon_probe_board(struct efx_nic *efx, u16 revision_info);
+extern int falcon_probe_board(struct efx_nic *efx, u16 revision_info);
/* TX data path */
extern int efx_nic_probe_tx(struct efx_tx_queue *tx_queue);
--
1.6.2.5
--
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply related
* [PATCH net-2.6 2/3] sfc: Always close net device at the end of a disabling reset
From: Ben Hutchings @ 2010-04-28 19:01 UTC (permalink / raw)
To: David Miller; +Cc: netdev, linux-net-drivers
In-Reply-To: <1272481235.2549.14.camel@achroite.uk.solarflarecom.com>
This fixes a regression introduced by commit
eb9f6744cbfa97674c13263802259b5aa0034594 "sfc: Implement ethtool
reset operation".
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Cc: stable@kernel.org
---
drivers/net/sfc/efx.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c
index 6486657..649a264 100644
--- a/drivers/net/sfc/efx.c
+++ b/drivers/net/sfc/efx.c
@@ -1861,6 +1861,7 @@ out:
}
if (disabled) {
+ dev_close(efx->net_dev);
EFX_ERR(efx, "has been disabled\n");
efx->state = STATE_DISABLED;
} else {
@@ -1884,8 +1885,7 @@ static void efx_reset_work(struct work_struct *data)
}
rtnl_lock();
- if (efx_reset(efx, efx->reset_pending))
- dev_close(efx->net_dev);
+ (void)efx_reset(efx, efx->reset_pending);
rtnl_unlock();
}
--
1.6.2.5
--
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply related
* [PATCH net-2.6 1/3] sfc: Wait at most 10ms for the MC to finish reading out MAC statistics
From: Ben Hutchings @ 2010-04-28 19:00 UTC (permalink / raw)
To: David Miller; +Cc: netdev, linux-net-drivers
The original code would wait indefinitely if MAC stats DMA failed.
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Cc: stable@kernel.org
---
drivers/net/sfc/siena.c | 13 +++++++++++--
1 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/net/sfc/siena.c b/drivers/net/sfc/siena.c
index 38dcc42..e0c46f5 100644
--- a/drivers/net/sfc/siena.c
+++ b/drivers/net/sfc/siena.c
@@ -456,8 +456,17 @@ static int siena_try_update_nic_stats(struct efx_nic *efx)
static void siena_update_nic_stats(struct efx_nic *efx)
{
- while (siena_try_update_nic_stats(efx) == -EAGAIN)
- cpu_relax();
+ int retry;
+
+ /* If we're unlucky enough to read statistics wduring the DMA, wait
+ * up to 10ms for it to finish (typically takes <500us) */
+ for (retry = 0; retry < 100; ++retry) {
+ if (siena_try_update_nic_stats(efx) == 0)
+ return;
+ udelay(100);
+ }
+
+ /* Use the old values instead */
}
static void siena_start_nic_stats(struct efx_nic *efx)
--
1.6.2.5
--
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply related
* [PATCH net-next-2.6 7/7] Bugfix: Link selection was swapped in switch.
From: sjur.brandeland @ 2010-04-28 18:54 UTC (permalink / raw)
To: netdev, davem
Cc: marcel, daniel.martensson, sjurbr, linus.walleij,
Sjur Braendeland
In-Reply-To: <1272480880-30672-6-git-send-email-sjur.brandeland@stericsson.com>
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
net/caif/caif_dev.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c
index 0145bae..024fd5b 100644
--- a/net/caif/caif_dev.c
+++ b/net/caif/caif_dev.c
@@ -247,10 +247,10 @@ static int caif_device_notify(struct notifier_block *me, unsigned long what,
switch (caifdev->link_select) {
case CAIF_LINK_HIGH_BANDW:
- pref = CFPHYPREF_LOW_LAT;
+ pref = CFPHYPREF_HIGH_BW;
break;
case CAIF_LINK_LOW_LATENCY:
- pref = CFPHYPREF_HIGH_BW;
+ pref = CFPHYPREF_LOW_LAT;
break;
default:
pref = CFPHYPREF_HIGH_BW;
--
1.6.3.3
^ permalink raw reply related
* [PATCH net-next-2.6 6/7] caif: Bugfixes in CAIF netdevice for close and flow control
From: sjur.brandeland @ 2010-04-28 18:54 UTC (permalink / raw)
To: netdev, davem
Cc: marcel, daniel.martensson, sjurbr, linus.walleij,
Sjur Braendeland
In-Reply-To: <1272480880-30672-5-git-send-email-sjur.brandeland@stericsson.com>
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Changes:
o Bugfix: Flow control was causing the device to be destroyed.
o Bugfix: Handle CAIF channel connect failures.
o If the underlying link layer is gone the net-device is no longer removed,
but closed.
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
net/caif/chnl_net.c | 130 ++++++++++++++++++++++++++++----------------------
1 files changed, 73 insertions(+), 57 deletions(-)
diff --git a/net/caif/chnl_net.c b/net/caif/chnl_net.c
index f622ff1..610966a 100644
--- a/net/caif/chnl_net.c
+++ b/net/caif/chnl_net.c
@@ -22,10 +22,10 @@
#include <net/caif/cfpkt.h>
#include <net/caif/caif_dev.h>
-#define CAIF_CONNECT_TIMEOUT 30
+/* GPRS PDP connection has MTU to 1500 */
#define SIZE_MTU 1500
-#define SIZE_MTU_MAX 4080
-#define SIZE_MTU_MIN 68
+/* 5 sec. connect timeout */
+#define CONNECT_TIMEOUT (5 * HZ)
#define CAIF_NET_DEFAULT_QUEUE_LEN 500
#undef pr_debug
@@ -37,6 +37,13 @@ static LIST_HEAD(chnl_net_list);
MODULE_LICENSE("GPL");
MODULE_ALIAS_RTNL_LINK("caif");
+enum caif_states {
+ CAIF_CONNECTED = 1,
+ CAIF_CONNECTING,
+ CAIF_DISCONNECTED,
+ CAIF_SHUTDOWN
+};
+
struct chnl_net {
struct cflayer chnl;
struct net_device_stats stats;
@@ -47,7 +54,7 @@ struct chnl_net {
wait_queue_head_t netmgmt_wq;
/* Flow status to remember and control the transmission. */
bool flowenabled;
- bool pending_close;
+ enum caif_states state;
};
static void robust_list_del(struct list_head *delete_node)
@@ -58,15 +65,16 @@ static void robust_list_del(struct list_head *delete_node)
list_for_each_safe(list_node, n, &chnl_net_list) {
if (list_node == delete_node) {
list_del(list_node);
- break;
+ return;
}
}
+ WARN_ON(1);
}
static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt)
{
struct sk_buff *skb;
- struct chnl_net *priv = NULL;
+ struct chnl_net *priv = container_of(layr, struct chnl_net, chnl);
int pktlen;
int err = 0;
@@ -91,7 +99,6 @@ static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt)
else
skb->ip_summed = CHECKSUM_NONE;
- /* FIXME: Drivers should call this in tasklet context. */
if (in_interrupt())
netif_rx(skb);
else
@@ -117,23 +124,25 @@ static void close_work(struct work_struct *work)
struct chnl_net *dev = NULL;
struct list_head *list_node;
struct list_head *_tmp;
- rtnl_lock();
+ /* May be called with or without RTNL lock held */
+ int islocked = rtnl_is_locked();
+ if (!islocked)
+ rtnl_lock();
list_for_each_safe(list_node, _tmp, &chnl_net_list) {
dev = list_entry(list_node, struct chnl_net, list_field);
- if (!dev->pending_close)
- continue;
- list_del(list_node);
- delete_device(dev);
+ if (dev->state == CAIF_SHUTDOWN)
+ dev_close(dev->netdev);
}
- rtnl_unlock();
+ if (!islocked)
+ rtnl_unlock();
}
static DECLARE_WORK(close_worker, close_work);
static void chnl_flowctrl_cb(struct cflayer *layr, enum caif_ctrlcmd flow,
int phyid)
{
- struct chnl_net *priv;
- pr_debug("CAIF: %s(): NET flowctrl func called flow: %s.\n",
+ struct chnl_net *priv = container_of(layr, struct chnl_net, chnl);
+ pr_debug("CAIF: %s(): NET flowctrl func called flow: %s\n",
__func__,
flow == CAIF_CTRLCMD_FLOW_ON_IND ? "ON" :
flow == CAIF_CTRLCMD_INIT_RSP ? "INIT" :
@@ -143,21 +152,31 @@ static void chnl_flowctrl_cb(struct cflayer *layr, enum caif_ctrlcmd flow,
flow == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND ?
"REMOTE_SHUTDOWN" : "UKNOWN CTRL COMMAND");
- priv = container_of(layr, struct chnl_net, chnl);
+
switch (flow) {
case CAIF_CTRLCMD_FLOW_OFF_IND:
+ priv->flowenabled = false;
+ netif_stop_queue(priv->netdev);
+ break;
case CAIF_CTRLCMD_DEINIT_RSP:
+ priv->state = CAIF_DISCONNECTED;
+ break;
case CAIF_CTRLCMD_INIT_FAIL_RSP:
+ priv->state = CAIF_DISCONNECTED;
+ wake_up_interruptible(&priv->netmgmt_wq);
+ break;
case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND:
- priv->flowenabled = false;
+ priv->state = CAIF_SHUTDOWN;
netif_tx_disable(priv->netdev);
- pr_warning("CAIF: %s(): done\n", __func__);
- priv->pending_close = 1;
schedule_work(&close_worker);
break;
case CAIF_CTRLCMD_FLOW_ON_IND:
+ priv->flowenabled = true;
+ netif_wake_queue(priv->netdev);
+ break;
case CAIF_CTRLCMD_INIT_RSP:
+ priv->state = CAIF_CONNECTED;
priv->flowenabled = true;
netif_wake_queue(priv->netdev);
wake_up_interruptible(&priv->netmgmt_wq);
@@ -194,9 +213,6 @@ static int chnl_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
pkt = cfpkt_fromnative(CAIF_DIR_OUT, (void *) skb);
- pr_debug("CAIF: %s(): transmit inst %s %d,%p\n",
- __func__, dev->name, priv->chnl.dn->id, &priv->chnl.dn);
-
/* Send the packet down the stack. */
result = priv->chnl.dn->transmit(priv->chnl.dn, pkt);
if (result) {
@@ -217,61 +233,59 @@ static int chnl_net_open(struct net_device *dev)
struct chnl_net *priv = NULL;
int result = -1;
ASSERT_RTNL();
-
priv = netdev_priv(dev);
- pr_debug("CAIF: %s(): dev name: %s\n", __func__, priv->name);
-
if (!priv) {
pr_debug("CAIF: %s(): chnl_net_open: no priv\n", __func__);
return -ENODEV;
}
- result = caif_connect_client(&priv->conn_req, &priv->chnl);
- if (result != 0) {
- pr_debug("CAIF: %s(): err: "
- "Unable to register and open device, Err:%d\n",
- __func__,
- result);
- return -ENODEV;
+
+ if (priv->state != CAIF_CONNECTING) {
+ priv->state = CAIF_CONNECTING;
+ result = caif_connect_client(&priv->conn_req, &priv->chnl);
+ if (result != 0) {
+ priv->state = CAIF_DISCONNECTED;
+ pr_debug("CAIF: %s(): err: "
+ "Unable to register and open device,"
+ " Err:%d\n",
+ __func__,
+ result);
+ return result;
+ }
}
- result = wait_event_interruptible(priv->netmgmt_wq, priv->flowenabled);
+
+ result = wait_event_interruptible_timeout(priv->netmgmt_wq,
+ priv->state != CAIF_CONNECTING,
+ CONNECT_TIMEOUT);
if (result == -ERESTARTSYS) {
pr_debug("CAIF: %s(): wait_event_interruptible"
" woken by a signal\n", __func__);
return -ERESTARTSYS;
- } else
- pr_debug("CAIF: %s(): Flow on recieved\n", __func__);
+ }
+ if (result == 0) {
+ pr_debug("CAIF: %s(): connect timeout\n", __func__);
+ caif_disconnect_client(&priv->chnl);
+ priv->state = CAIF_DISCONNECTED;
+ pr_debug("CAIF: %s(): state disconnected\n", __func__);
+ return -ETIMEDOUT;
+ }
+ if (priv->state != CAIF_CONNECTED) {
+ pr_debug("CAIF: %s(): connect failed\n", __func__);
+ return -ECONNREFUSED;
+ }
+ pr_debug("CAIF: %s(): CAIF Netdevice connected\n", __func__);
return 0;
}
static int chnl_net_stop(struct net_device *dev)
{
struct chnl_net *priv;
- int result = -1;
+
ASSERT_RTNL();
priv = netdev_priv(dev);
-
- result = caif_disconnect_client(&priv->chnl);
- if (result != 0) {
- pr_debug("CAIF: %s(): chnl_net_stop: err: "
- "Unable to STOP device, Err:%d\n",
- __func__, result);
- return -EBUSY;
- }
- result = wait_event_interruptible(priv->netmgmt_wq,
- !priv->flowenabled);
-
- if (result == -ERESTARTSYS) {
- pr_debug("CAIF: %s(): wait_event_interruptible woken by"
- " signal, signal_pending(current) = %d\n",
- __func__,
- signal_pending(current));
- } else {
- pr_debug("CAIF: %s(): disconnect received\n", __func__);
-
- }
-
+ priv->state = CAIF_DISCONNECTED;
+ caif_disconnect_client(&priv->chnl);
return 0;
}
@@ -377,6 +391,8 @@ static int ipcaif_newlink(struct net *src_net, struct net_device *dev,
ASSERT_RTNL();
caifdev = netdev_priv(dev);
caif_netlink_parms(data, &caifdev->conn_req);
+ dev_net_set(caifdev->netdev, src_net);
+
ret = register_netdevice(dev);
if (ret)
pr_warning("CAIF: %s(): device rtml registration failed\n",
--
1.6.3.3
^ permalink raw reply related
* [PATCH net-next-2.6 5/7] caif: Rewritten socket implementation
From: sjur.brandeland @ 2010-04-28 18:54 UTC (permalink / raw)
To: netdev, davem
Cc: marcel, daniel.martensson, sjurbr, linus.walleij,
Sjur Braendeland
In-Reply-To: <1272480880-30672-4-git-send-email-sjur.brandeland@stericsson.com>
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Changes:
This is a complete re-write of the socket layer. Making the socket
implementation more aligned with the other socket layers and using more
of the support functions available in sock.c. Lots of code is copied
from af_unix (and some from af_irda).
Non-blocking mode should be working as well.
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
include/linux/caif/caif_socket.h | 5 +-
net/caif/caif_socket.c | 2643 ++++++++++++++++++--------------------
2 files changed, 1255 insertions(+), 1393 deletions(-)
rewrite net/caif/caif_socket.c (76%)
diff --git a/include/linux/caif/caif_socket.h b/include/linux/caif/caif_socket.h
index 8e5c844..2a61eb1 100644
--- a/include/linux/caif/caif_socket.h
+++ b/include/linux/caif/caif_socket.h
@@ -16,7 +16,6 @@
#include <sys/socket.h>
#endif
-
/**
* enum caif_link_selector - Physical Link Selection.
* @CAIF_LINK_HIGH_BANDW: Physical interface for high-bandwidth
@@ -59,7 +58,7 @@ enum caif_channel_priority {
/**
* enum caif_protocol_type - CAIF Channel type.
* @CAIFPROTO_AT: Classic AT channel.
- * @CAIFPROTO_DATAGRAM: Datagram channel.
+ * @CAIFPROTO_DATAGRAM: Datagram channel.
* @CAIFPROTO_DATAGRAM_LOOP: Datagram loopback channel, used for testing.
* @CAIFPROTO_UTIL: Utility (Psock) channel.
* @CAIFPROTO_RFM: Remote File Manager
@@ -87,6 +86,7 @@ enum caif_at_type {
/**
* struct sockaddr_caif - the sockaddr structure for CAIF sockets.
+ * @family: Address family number, must be AF_CAIF.
* @u: Union of address data 'switched' by family.
* :
* @u.at: Applies when family = CAIFPROTO_AT.
@@ -153,6 +153,7 @@ struct sockaddr_caif {
*
*
* This enum defines the CAIF Socket options to be used on a socket
+ * of type PF_CAIF.
*
*/
enum caif_socket_opts {
diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c
dissimilarity index 76%
index d455375..c3a70c5 100644
--- a/net/caif/caif_socket.c
+++ b/net/caif/caif_socket.c
@@ -1,1391 +1,1252 @@
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland sjur.brandeland@stericsson.com
- * Per Sigmond per.sigmond@stericsson.com
- * License terms: GNU General Public License (GPL) version 2
- */
-
-#include <linux/fs.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/spinlock.h>
-#include <linux/mutex.h>
-#include <linux/list.h>
-#include <linux/wait.h>
-#include <linux/poll.h>
-#include <linux/tcp.h>
-#include <linux/uaccess.h>
-#include <asm/atomic.h>
-
-#include <linux/caif/caif_socket.h>
-#include <net/caif/caif_layer.h>
-#include <net/caif/caif_dev.h>
-#include <net/caif/cfpkt.h>
-
-MODULE_LICENSE("GPL");
-
-#define CHNL_SKT_READ_QUEUE_HIGH 200
-#define CHNL_SKT_READ_QUEUE_LOW 100
-
-static int caif_sockbuf_size = 40000;
-static atomic_t caif_nr_socks = ATOMIC_INIT(0);
-
-#define CONN_STATE_OPEN_BIT 1
-#define CONN_STATE_PENDING_BIT 2
-#define CONN_STATE_PEND_DESTROY_BIT 3
-#define CONN_REMOTE_SHUTDOWN_BIT 4
-
-#define TX_FLOW_ON_BIT 1
-#define RX_FLOW_ON_BIT 2
-
-#define STATE_IS_OPEN(cf_sk) test_bit(CONN_STATE_OPEN_BIT,\
- (void *) &(cf_sk)->conn_state)
-#define STATE_IS_REMOTE_SHUTDOWN(cf_sk) test_bit(CONN_REMOTE_SHUTDOWN_BIT,\
- (void *) &(cf_sk)->conn_state)
-#define STATE_IS_PENDING(cf_sk) test_bit(CONN_STATE_PENDING_BIT,\
- (void *) &(cf_sk)->conn_state)
-#define STATE_IS_PENDING_DESTROY(cf_sk) test_bit(CONN_STATE_PEND_DESTROY_BIT,\
- (void *) &(cf_sk)->conn_state)
-
-#define SET_STATE_PENDING_DESTROY(cf_sk) set_bit(CONN_STATE_PEND_DESTROY_BIT,\
- (void *) &(cf_sk)->conn_state)
-#define SET_STATE_OPEN(cf_sk) set_bit(CONN_STATE_OPEN_BIT,\
- (void *) &(cf_sk)->conn_state)
-#define SET_STATE_CLOSED(cf_sk) clear_bit(CONN_STATE_OPEN_BIT,\
- (void *) &(cf_sk)->conn_state)
-#define SET_PENDING_ON(cf_sk) set_bit(CONN_STATE_PENDING_BIT,\
- (void *) &(cf_sk)->conn_state)
-#define SET_PENDING_OFF(cf_sk) clear_bit(CONN_STATE_PENDING_BIT,\
- (void *) &(cf_sk)->conn_state)
-#define SET_REMOTE_SHUTDOWN(cf_sk) set_bit(CONN_REMOTE_SHUTDOWN_BIT,\
- (void *) &(cf_sk)->conn_state)
-
-#define SET_REMOTE_SHUTDOWN_OFF(dev) clear_bit(CONN_REMOTE_SHUTDOWN_BIT,\
- (void *) &(dev)->conn_state)
-#define RX_FLOW_IS_ON(cf_sk) test_bit(RX_FLOW_ON_BIT,\
- (void *) &(cf_sk)->flow_state)
-#define TX_FLOW_IS_ON(cf_sk) test_bit(TX_FLOW_ON_BIT,\
- (void *) &(cf_sk)->flow_state)
-
-#define SET_RX_FLOW_OFF(cf_sk) clear_bit(RX_FLOW_ON_BIT,\
- (void *) &(cf_sk)->flow_state)
-#define SET_RX_FLOW_ON(cf_sk) set_bit(RX_FLOW_ON_BIT,\
- (void *) &(cf_sk)->flow_state)
-#define SET_TX_FLOW_OFF(cf_sk) clear_bit(TX_FLOW_ON_BIT,\
- (void *) &(cf_sk)->flow_state)
-#define SET_TX_FLOW_ON(cf_sk) set_bit(TX_FLOW_ON_BIT,\
- (void *) &(cf_sk)->flow_state)
-
-#define SKT_READ_FLAG 0x01
-#define SKT_WRITE_FLAG 0x02
-static struct dentry *debugfsdir;
-#include <linux/debugfs.h>
-
-#ifdef CONFIG_DEBUG_FS
-struct debug_fs_counter {
- atomic_t num_open;
- atomic_t num_close;
- atomic_t num_init;
- atomic_t num_init_resp;
- atomic_t num_init_fail_resp;
- atomic_t num_deinit;
- atomic_t num_deinit_resp;
- 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;
- atomic_t skb_in_use;
- atomic_t skb_alloc;
- atomic_t skb_free;
-};
-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
-
-/* The AF_CAIF socket */
-struct caifsock {
- /* NOTE: sk has to be the first member */
- struct sock sk;
- struct cflayer layer;
- char name[CAIF_LAYER_NAME_SZ];
- u32 conn_state;
- u32 flow_state;
- struct cfpktq *pktq;
- int file_mode;
- struct caif_connect_request conn_req;
- int read_queue_len;
- /* protect updates of read_queue_len */
- spinlock_t read_queue_len_lock;
- struct dentry *debugfs_socket_dir;
-};
-
-static void drain_queue(struct caifsock *cf_sk);
-
-/* Packet Receive Callback function called from CAIF Stack */
-static int caif_sktrecv_cb(struct cflayer *layr, struct cfpkt *pkt)
-{
- struct caifsock *cf_sk;
- int read_queue_high;
- cf_sk = container_of(layr, struct caifsock, layer);
-
- if (!STATE_IS_OPEN(cf_sk)) {
- /*FIXME: This should be allowed finally!*/
- pr_debug("CAIF: %s(): called after close request\n", __func__);
- cfpkt_destroy(pkt);
- return 0;
- }
- /* NOTE: This function may be called in Tasklet context! */
-
- /* The queue has its own lock */
- cfpkt_queue(cf_sk->pktq, pkt, 0);
-
- spin_lock(&cf_sk->read_queue_len_lock);
- cf_sk->read_queue_len++;
-
- read_queue_high = (cf_sk->read_queue_len > CHNL_SKT_READ_QUEUE_HIGH);
- spin_unlock(&cf_sk->read_queue_len_lock);
-
- if (RX_FLOW_IS_ON(cf_sk) && read_queue_high) {
- dbfs_atomic_inc(&cnt.num_rx_flow_off);
- SET_RX_FLOW_OFF(cf_sk);
-
- /* Send flow off (NOTE: must not sleep) */
- pr_debug("CAIF: %s():"
- " sending flow OFF (queue len = %d)\n",
- __func__,
- cf_sk->read_queue_len);
- caif_assert(cf_sk->layer.dn);
- caif_assert(cf_sk->layer.dn->ctrlcmd);
-
- (void) cf_sk->layer.dn->modemcmd(cf_sk->layer.dn,
- CAIF_MODEMCMD_FLOW_OFF_REQ);
- }
-
- /* Signal reader that data is available. */
-
- wake_up_interruptible(sk_sleep(&cf_sk->sk));
-
- return 0;
-}
-
-/* Packet Flow Control Callback function called from CAIF */
-static void caif_sktflowctrl_cb(struct cflayer *layr,
- enum caif_ctrlcmd flow,
- int phyid)
-{
- struct caifsock *cf_sk;
-
- /* NOTE: This function may be called in Tasklet context! */
- pr_debug("CAIF: %s(): flowctrl func called: %s.\n",
- __func__,
- flow == CAIF_CTRLCMD_FLOW_ON_IND ? "ON" :
- flow == CAIF_CTRLCMD_FLOW_OFF_IND ? "OFF" :
- flow == CAIF_CTRLCMD_INIT_RSP ? "INIT_RSP" :
- flow == CAIF_CTRLCMD_DEINIT_RSP ? "DEINIT_RSP" :
- flow == CAIF_CTRLCMD_INIT_FAIL_RSP ? "INIT_FAIL_RSP" :
- flow ==
- CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND ? "REMOTE_SHUTDOWN" :
- "UKNOWN CTRL COMMAND");
-
- if (layr == NULL)
- return;
-
- cf_sk = container_of(layr, struct caifsock, layer);
-
- switch (flow) {
- case CAIF_CTRLCMD_FLOW_ON_IND:
- dbfs_atomic_inc(&cnt.num_tx_flow_on_ind);
- /* Signal reader that data is available. */
- SET_TX_FLOW_ON(cf_sk);
- wake_up_interruptible(sk_sleep(&cf_sk->sk));
- break;
-
- case CAIF_CTRLCMD_FLOW_OFF_IND:
- dbfs_atomic_inc(&cnt.num_tx_flow_off_ind);
- SET_TX_FLOW_OFF(cf_sk);
- break;
-
- case CAIF_CTRLCMD_INIT_RSP:
- dbfs_atomic_inc(&cnt.num_init_resp);
- /* Signal reader that data is available. */
- caif_assert(STATE_IS_OPEN(cf_sk));
- SET_PENDING_OFF(cf_sk);
- SET_TX_FLOW_ON(cf_sk);
- wake_up_interruptible(sk_sleep(&cf_sk->sk));
- break;
-
- case CAIF_CTRLCMD_DEINIT_RSP:
- dbfs_atomic_inc(&cnt.num_deinit_resp);
- caif_assert(!STATE_IS_OPEN(cf_sk));
- SET_PENDING_OFF(cf_sk);
- if (!STATE_IS_PENDING_DESTROY(cf_sk)) {
- if (sk_sleep(&cf_sk->sk) != NULL)
- wake_up_interruptible(sk_sleep(&cf_sk->sk));
- }
- dbfs_atomic_inc(&cnt.num_deinit);
- sock_put(&cf_sk->sk);
- break;
-
- case CAIF_CTRLCMD_INIT_FAIL_RSP:
- dbfs_atomic_inc(&cnt.num_init_fail_resp);
- caif_assert(STATE_IS_OPEN(cf_sk));
- SET_STATE_CLOSED(cf_sk);
- SET_PENDING_OFF(cf_sk);
- SET_TX_FLOW_OFF(cf_sk);
- wake_up_interruptible(sk_sleep(&cf_sk->sk));
- break;
-
- case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND:
- dbfs_atomic_inc(&cnt.num_remote_shutdown_ind);
- SET_REMOTE_SHUTDOWN(cf_sk);
- /* Use sk_shutdown to indicate remote shutdown indication */
- cf_sk->sk.sk_shutdown |= RCV_SHUTDOWN;
- cf_sk->file_mode = 0;
- wake_up_interruptible(sk_sleep(&cf_sk->sk));
- break;
-
- default:
- pr_debug("CAIF: %s(): Unexpected flow command %d\n",
- __func__, flow);
- }
-}
-
-static void skb_destructor(struct sk_buff *skb)
-{
- dbfs_atomic_inc(&cnt.skb_free);
- dbfs_atomic_dec(&cnt.skb_in_use);
-}
-
-
-static int caif_recvmsg(struct kiocb *iocb, struct socket *sock,
- struct msghdr *m, size_t buf_len, int flags)
-
-{
- struct sock *sk = sock->sk;
- struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
- struct cfpkt *pkt = NULL;
- size_t len;
- int result;
- struct sk_buff *skb;
- ssize_t ret = -EIO;
- int read_queue_low;
-
- if (cf_sk == NULL) {
- pr_debug("CAIF: %s(): private_data not set!\n",
- __func__);
- ret = -EBADFD;
- goto read_error;
- }
-
- /* Don't do multiple iovec entries yet */
- if (m->msg_iovlen != 1)
- return -EOPNOTSUPP;
-
- if (unlikely(!buf_len))
- return -EINVAL;
-
- lock_sock(&(cf_sk->sk));
-
- caif_assert(cf_sk->pktq);
-
- if (!STATE_IS_OPEN(cf_sk)) {
- /* Socket is closed or closing. */
- if (!STATE_IS_PENDING(cf_sk)) {
- pr_debug("CAIF: %s(): socket is closed (by remote)\n",
- __func__);
- ret = -EPIPE;
- } else {
- pr_debug("CAIF: %s(): socket is closing..\n", __func__);
- ret = -EBADF;
- }
- goto read_error;
- }
- /* Socket is open or opening. */
- if (STATE_IS_PENDING(cf_sk)) {
- pr_debug("CAIF: %s(): socket is opening...\n", __func__);
-
- if (flags & MSG_DONTWAIT) {
- /* We can't block. */
- pr_debug("CAIF: %s():state pending and MSG_DONTWAIT\n",
- __func__);
- ret = -EAGAIN;
- goto read_error;
- }
-
- /*
- * Blocking mode; state is pending and we need to wait
- * for its conclusion.
- */
- release_sock(&cf_sk->sk);
-
- result =
- wait_event_interruptible(*sk_sleep(&cf_sk->sk),
- !STATE_IS_PENDING(cf_sk));
-
- lock_sock(&(cf_sk->sk));
-
- if (result == -ERESTARTSYS) {
- pr_debug("CAIF: %s(): wait_event_interruptible"
- " woken by a signal (1)", __func__);
- ret = -ERESTARTSYS;
- goto read_error;
- }
- }
-
- if (STATE_IS_REMOTE_SHUTDOWN(cf_sk) ||
- !STATE_IS_OPEN(cf_sk) ||
- STATE_IS_PENDING(cf_sk)) {
-
- pr_debug("CAIF: %s(): socket closed\n",
- __func__);
- ret = -ESHUTDOWN;
- goto read_error;
- }
-
- /*
- * Block if we don't have any received buffers.
- * The queue has its own lock.
- */
- while ((pkt = cfpkt_qpeek(cf_sk->pktq)) == NULL) {
-
- if (flags & MSG_DONTWAIT) {
- pr_debug("CAIF: %s(): MSG_DONTWAIT\n", __func__);
- ret = -EAGAIN;
- goto read_error;
- }
- trace_printk("CAIF: %s() wait_event\n", __func__);
-
- /* Let writers in. */
- release_sock(&cf_sk->sk);
-
- /* Block reader until data arrives or socket is closed. */
- if (wait_event_interruptible(*sk_sleep(&cf_sk->sk),
- cfpkt_qpeek(cf_sk->pktq)
- || STATE_IS_REMOTE_SHUTDOWN(cf_sk)
- || !STATE_IS_OPEN(cf_sk)) ==
- -ERESTARTSYS) {
- pr_debug("CAIF: %s():"
- " wait_event_interruptible woken by "
- "a signal, signal_pending(current) = %d\n",
- __func__,
- signal_pending(current));
- return -ERESTARTSYS;
- }
-
- trace_printk("CAIF: %s() awake\n", __func__);
- if (STATE_IS_REMOTE_SHUTDOWN(cf_sk)) {
- pr_debug("CAIF: %s(): "
- "received remote_shutdown indication\n",
- __func__);
- ret = -ESHUTDOWN;
- goto read_error_no_unlock;
- }
-
- /* I want to be alone on cf_sk (except status and queue). */
- lock_sock(&(cf_sk->sk));
-
- if (!STATE_IS_OPEN(cf_sk)) {
- /* Someone closed the link, report error. */
- pr_debug("CAIF: %s(): remote end shutdown!\n",
- __func__);
- ret = -EPIPE;
- goto read_error;
- }
- }
-
- /* The queue has its own lock. */
- len = cfpkt_getlen(pkt);
-
- /* Check max length that can be copied. */
- if (len <= buf_len)
- pkt = cfpkt_dequeue(cf_sk->pktq);
- else {
- pr_debug("CAIF: %s(): user buffer too small (%ld,%ld)\n",
- __func__, (long) len, (long) buf_len);
- if (sock->type == SOCK_SEQPACKET) {
- ret = -EMSGSIZE;
- goto read_error;
- }
- len = buf_len;
- }
-
-
- spin_lock(&cf_sk->read_queue_len_lock);
- cf_sk->read_queue_len--;
- read_queue_low = (cf_sk->read_queue_len < CHNL_SKT_READ_QUEUE_LOW);
- spin_unlock(&cf_sk->read_queue_len_lock);
-
- if (!RX_FLOW_IS_ON(cf_sk) && read_queue_low) {
- dbfs_atomic_inc(&cnt.num_rx_flow_on);
- SET_RX_FLOW_ON(cf_sk);
-
- /* Send flow on. */
- pr_debug("CAIF: %s(): sending flow ON (queue len = %d)\n",
- __func__, cf_sk->read_queue_len);
- caif_assert(cf_sk->layer.dn);
- caif_assert(cf_sk->layer.dn->ctrlcmd);
- (void) cf_sk->layer.dn->modemcmd(cf_sk->layer.dn,
- CAIF_MODEMCMD_FLOW_ON_REQ);
-
- caif_assert(cf_sk->read_queue_len >= 0);
- }
-
- skb = cfpkt_tonative(pkt);
- result = skb_copy_datagram_iovec(skb, 0, m->msg_iov, len);
- skb_pull(skb, len);
-
- if (result) {
- pr_debug("CAIF: %s(): copy to_iovec failed\n", __func__);
- cfpkt_destroy(pkt);
- ret = -EFAULT;
- goto read_error;
- }
-
- /* Free packet and remove from queue */
- if (skb->len == 0)
- skb_free_datagram(sk, skb);
-
- /* Let the others in. */
- release_sock(&cf_sk->sk);
- return len;
-
-read_error:
- release_sock(&cf_sk->sk);
-read_error_no_unlock:
- return ret;
-}
-
-/* Send a signal as a consequence of sendmsg, sendto or caif_sendmsg. */
-static int caif_sendmsg(struct kiocb *kiocb, struct socket *sock,
- struct msghdr *msg, size_t len)
-{
-
- struct sock *sk = sock->sk;
- struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
- size_t payload_size = msg->msg_iov->iov_len;
- struct cfpkt *pkt = NULL;
- struct caif_payload_info info;
- unsigned char *txbuf;
- ssize_t ret = -EIO;
- int result;
- struct sk_buff *skb;
- caif_assert(msg->msg_iovlen == 1);
-
- if (cf_sk == NULL) {
- pr_debug("CAIF: %s(): private_data not set!\n",
- __func__);
- ret = -EBADFD;
- goto write_error_no_unlock;
- }
-
- if (unlikely(msg->msg_iov->iov_base == NULL)) {
- pr_warning("CAIF: %s(): Buffer is NULL.\n", __func__);
- ret = -EINVAL;
- goto write_error_no_unlock;
- }
-
- if (payload_size > CAIF_MAX_PAYLOAD_SIZE) {
- pr_debug("CAIF: %s(): buffer too long\n", __func__);
- if (sock->type == SOCK_SEQPACKET) {
- ret = -EINVAL;
- goto write_error_no_unlock;
- }
- payload_size = CAIF_MAX_PAYLOAD_SIZE;
- }
-
- /* I want to be alone on cf_sk (except status and queue) */
- lock_sock(&(cf_sk->sk));
-
- caif_assert(cf_sk->pktq);
-
- if (!STATE_IS_OPEN(cf_sk)) {
- /* Socket is closed or closing */
- if (!STATE_IS_PENDING(cf_sk)) {
- pr_debug("CAIF: %s(): socket is closed (by remote)\n",
- __func__);
- ret = -EPIPE;
- } else {
- pr_debug("CAIF: %s(): socket is closing...\n",
- __func__);
- ret = -EBADF;
- }
- goto write_error;
- }
-
- /* Socket is open or opening */
- if (STATE_IS_PENDING(cf_sk)) {
- pr_debug("CAIF: %s(): socket is opening...\n", __func__);
-
- if (msg->msg_flags & MSG_DONTWAIT) {
- /* We can't block */
- trace_printk("CAIF: %s():state pending:"
- "state=MSG_DONTWAIT\n", __func__);
- ret = -EAGAIN;
- goto write_error;
- }
- /* Let readers in */
- release_sock(&cf_sk->sk);
-
- /*
- * Blocking mode; state is pending and we need to wait
- * for its conclusion.
- */
- result =
- wait_event_interruptible(*sk_sleep(&cf_sk->sk),
- !STATE_IS_PENDING(cf_sk));
- /* I want to be alone on cf_sk (except status and queue) */
- lock_sock(&(cf_sk->sk));
-
- if (result == -ERESTARTSYS) {
- pr_debug("CAIF: %s(): wait_event_interruptible"
- " woken by a signal (1)", __func__);
- ret = -ERESTARTSYS;
- goto write_error;
- }
- }
- if (STATE_IS_REMOTE_SHUTDOWN(cf_sk) ||
- !STATE_IS_OPEN(cf_sk) ||
- STATE_IS_PENDING(cf_sk)) {
-
- pr_debug("CAIF: %s(): socket closed\n",
- __func__);
- ret = -ESHUTDOWN;
- goto write_error;
- }
-
- if (!TX_FLOW_IS_ON(cf_sk)) {
-
- /* Flow is off. Check non-block flag */
- if (msg->msg_flags & MSG_DONTWAIT) {
- trace_printk("CAIF: %s(): MSG_DONTWAIT and tx flow off",
- __func__);
- ret = -EAGAIN;
- goto write_error;
- }
-
- /* release lock before waiting */
- release_sock(&cf_sk->sk);
-
- /* Wait until flow is on or socket is closed */
- if (wait_event_interruptible(*sk_sleep(&cf_sk->sk),
- TX_FLOW_IS_ON(cf_sk)
- || !STATE_IS_OPEN(cf_sk)
- || STATE_IS_REMOTE_SHUTDOWN(cf_sk)
- ) == -ERESTARTSYS) {
- pr_debug("CAIF: %s():"
- " wait_event_interruptible woken by a signal",
- __func__);
- ret = -ERESTARTSYS;
- goto write_error_no_unlock;
- }
-
- /* I want to be alone on cf_sk (except status and queue) */
- lock_sock(&(cf_sk->sk));
-
- if (!STATE_IS_OPEN(cf_sk)) {
- /* someone closed the link, report error */
- pr_debug("CAIF: %s(): remote end shutdown!\n",
- __func__);
- ret = -EPIPE;
- goto write_error;
- }
-
- if (STATE_IS_REMOTE_SHUTDOWN(cf_sk)) {
- pr_debug("CAIF: %s(): "
- "received remote_shutdown indication\n",
- __func__);
- ret = -ESHUTDOWN;
- goto write_error;
- }
- }
-
- pkt = cfpkt_create(payload_size);
- skb = (struct sk_buff *)pkt;
- skb->destructor = skb_destructor;
- skb->sk = sk;
- dbfs_atomic_inc(&cnt.skb_alloc);
- dbfs_atomic_inc(&cnt.skb_in_use);
- if (cfpkt_raw_append(pkt, (void **) &txbuf, payload_size) < 0) {
- pr_debug("CAIF: %s(): cfpkt_raw_append failed\n", __func__);
- cfpkt_destroy(pkt);
- ret = -EINVAL;
- goto write_error;
- }
-
- /* Copy data into buffer. */
- if (copy_from_user(txbuf, msg->msg_iov->iov_base, payload_size)) {
- pr_debug("CAIF: %s(): copy_from_user returned non zero.\n",
- __func__);
- cfpkt_destroy(pkt);
- ret = -EINVAL;
- goto write_error;
- }
- memset(&info, 0, sizeof(info));
-
- /* Send the packet down the stack. */
- caif_assert(cf_sk->layer.dn);
- caif_assert(cf_sk->layer.dn->transmit);
-
- do {
- ret = cf_sk->layer.dn->transmit(cf_sk->layer.dn, pkt);
-
- if (likely((ret >= 0) || (ret != -EAGAIN)))
- break;
-
- /* EAGAIN - retry */
- if (msg->msg_flags & MSG_DONTWAIT) {
- pr_debug("CAIF: %s(): NONBLOCK and transmit failed,"
- " error = %ld\n", __func__, (long) ret);
- ret = -EAGAIN;
- goto write_error;
- }
-
- /* Let readers in */
- release_sock(&cf_sk->sk);
-
- /* Wait until flow is on or socket is closed */
- if (wait_event_interruptible(*sk_sleep(&cf_sk->sk),
- TX_FLOW_IS_ON(cf_sk)
- || !STATE_IS_OPEN(cf_sk)
- || STATE_IS_REMOTE_SHUTDOWN(cf_sk)
- ) == -ERESTARTSYS) {
- pr_debug("CAIF: %s(): wait_event_interruptible"
- " woken by a signal", __func__);
- ret = -ERESTARTSYS;
- goto write_error_no_unlock;
- }
-
- /* I want to be alone on cf_sk (except status and queue) */
- lock_sock(&(cf_sk->sk));
-
- } while (ret == -EAGAIN);
-
- if (ret < 0) {
- cfpkt_destroy(pkt);
- pr_debug("CAIF: %s(): transmit failed, error = %ld\n",
- __func__, (long) ret);
-
- goto write_error;
- }
-
- release_sock(&cf_sk->sk);
- return payload_size;
-
-write_error:
- release_sock(&cf_sk->sk);
-write_error_no_unlock:
- return ret;
-}
-
-static unsigned int caif_poll(struct file *file, struct socket *sock,
- poll_table *wait)
-{
- struct sock *sk = sock->sk;
- struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
- u32 mask = 0;
- poll_wait(file, sk_sleep(sk), wait);
- lock_sock(&(cf_sk->sk));
- if (!STATE_IS_OPEN(cf_sk)) {
- if (!STATE_IS_PENDING(cf_sk))
- mask |= POLLHUP;
- } else {
- if (cfpkt_qpeek(cf_sk->pktq) != NULL)
- mask |= (POLLIN | POLLRDNORM);
- if (TX_FLOW_IS_ON(cf_sk))
- mask |= (POLLOUT | POLLWRNORM);
- }
- release_sock(&cf_sk->sk);
- trace_printk("CAIF: %s(): poll mask=0x%04x\n",
- __func__, mask);
- return mask;
-}
-
-static void drain_queue(struct caifsock *cf_sk)
-{
- struct cfpkt *pkt = NULL;
-
- /* Empty the queue */
- do {
- /* The queue has its own lock */
- if (!cf_sk->pktq)
- break;
-
- pkt = cfpkt_dequeue(cf_sk->pktq);
- if (!pkt)
- break;
- pr_debug("CAIF: %s(): freeing packet from read queue\n",
- __func__);
- cfpkt_destroy(pkt);
-
- } while (1);
-
- cf_sk->read_queue_len = 0;
-}
-
-static int setsockopt(struct socket *sock,
- 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);
- int prio, linksel;
- struct ifreq ifreq;
-
- if (STATE_IS_OPEN(cf_sk)) {
- pr_debug("CAIF: %s(): setsockopt "
- "cannot be done on a connected socket\n",
- __func__);
- return -ENOPROTOOPT;
- }
- switch (opt) {
- case CAIFSO_LINK_SELECT:
- if (ol < sizeof(int)) {
- pr_debug("CAIF: %s(): setsockopt"
- " CAIFSO_CHANNEL_CONFIG bad size\n", __func__);
- return -EINVAL;
- }
- if (lvl != SOL_CAIF)
- goto bad_sol;
- if (copy_from_user(&linksel, ov, sizeof(int)))
- return -EINVAL;
- lock_sock(&(cf_sk->sk));
- cf_sk->conn_req.link_selector = linksel;
- release_sock(&cf_sk->sk);
- return 0;
-
- case SO_PRIORITY:
- if (lvl != SOL_SOCKET)
- goto bad_sol;
- if (ol < sizeof(int)) {
- pr_debug("CAIF: %s(): setsockopt"
- " SO_PRIORITY bad size\n", __func__);
- return -EINVAL;
- }
- if (copy_from_user(&prio, ov, sizeof(int)))
- return -EINVAL;
- lock_sock(&(cf_sk->sk));
- cf_sk->conn_req.priority = prio;
- pr_debug("CAIF: %s(): Setting sockopt priority=%d\n", __func__,
- cf_sk->conn_req.priority);
- release_sock(&cf_sk->sk);
- return 0;
-
- case SO_BINDTODEVICE:
- if (lvl != SOL_SOCKET)
- goto bad_sol;
- if (ol < sizeof(struct ifreq)) {
- pr_debug("CAIF: %s(): setsockopt"
- " SO_PRIORITY bad size\n", __func__);
- return -EINVAL;
- }
- if (copy_from_user(&ifreq, ov, sizeof(ifreq)))
- return -EFAULT;
- lock_sock(&(cf_sk->sk));
- strncpy(cf_sk->conn_req.link_name, ifreq.ifr_name,
- sizeof(cf_sk->conn_req.link_name));
- cf_sk->conn_req.link_name
- [sizeof(cf_sk->conn_req.link_name)-1] = 0;
- release_sock(&cf_sk->sk);
- return 0;
-
- case CAIFSO_REQ_PARAM:
- if (lvl != SOL_CAIF)
- goto bad_sol;
- if (cf_sk->sk.sk_protocol != CAIFPROTO_UTIL)
- return -ENOPROTOOPT;
- if (ol > sizeof(cf_sk->conn_req.param.data))
- goto req_param_bad_size;
-
- lock_sock(&(cf_sk->sk));
- cf_sk->conn_req.param.size = ol;
- if (copy_from_user(&cf_sk->conn_req.param.data, ov, ol)) {
- release_sock(&cf_sk->sk);
-req_param_bad_size:
- pr_debug("CAIF: %s(): setsockopt"
- " CAIFSO_CHANNEL_CONFIG bad size\n", __func__);
- return -EINVAL;
- }
-
- release_sock(&cf_sk->sk);
- return 0;
-
- default:
- pr_debug("CAIF: %s(): unhandled option %d\n", __func__, opt);
- return -EINVAL;
- }
-
- return 0;
-bad_sol:
- pr_debug("CAIF: %s(): setsockopt bad level\n", __func__);
- return -ENOPROTOOPT;
-
-}
-
-static int caif_connect(struct socket *sock, struct sockaddr *uservaddr,
- int sockaddr_len, int flags)
-{
- struct caifsock *cf_sk = NULL;
- int result = -1;
- int mode = 0;
- int ret = -EIO;
- struct sock *sk = sock->sk;
- BUG_ON(sk == NULL);
-
- cf_sk = container_of(sk, struct caifsock, sk);
-
- trace_printk("CAIF: %s(): cf_sk=%p OPEN=%d, TX_FLOW=%d, RX_FLOW=%d\n",
- __func__, cf_sk,
- STATE_IS_OPEN(cf_sk),
- TX_FLOW_IS_ON(cf_sk), RX_FLOW_IS_ON(cf_sk));
-
-
- if (sock->type == SOCK_SEQPACKET || sock->type == SOCK_STREAM)
- sock->state = SS_CONNECTING;
- else
- goto out;
-
- /* I want to be alone on cf_sk (except status and queue) */
- lock_sock(&(cf_sk->sk));
-
- if (sockaddr_len != sizeof(struct sockaddr_caif)) {
- pr_debug("CAIF: %s(): Bad address len (%ld,%lu)\n",
- __func__, (long) sockaddr_len,
- (long unsigned) sizeof(struct sockaddr_caif));
- ret = -EINVAL;
- goto open_error;
- }
-
- if (uservaddr->sa_family != AF_CAIF) {
- pr_debug("CAIF: %s(): Bad address family (%d)\n",
- __func__, uservaddr->sa_family);
- ret = -EAFNOSUPPORT;
- goto open_error;
- }
-
- memcpy(&cf_sk->conn_req.sockaddr, uservaddr,
- sizeof(struct sockaddr_caif));
-
- dbfs_atomic_inc(&cnt.num_open);
- mode = SKT_READ_FLAG | SKT_WRITE_FLAG;
-
- /* If socket is not open, make sure socket is in fully closed state */
- if (!STATE_IS_OPEN(cf_sk)) {
- /* Has link close response been received (if we ever sent it)?*/
- if (STATE_IS_PENDING(cf_sk)) {
- /*
- * Still waiting for close response from remote.
- * If opened non-blocking, report "would block"
- */
- if (flags & O_NONBLOCK) {
- pr_debug("CAIF: %s(): O_NONBLOCK"
- " && close pending\n", __func__);
- ret = -EAGAIN;
- goto open_error;
- }
-
- pr_debug("CAIF: %s(): Wait for close response"
- " from remote...\n", __func__);
-
- release_sock(&cf_sk->sk);
-
- /*
- * Blocking mode; close is pending and we need to wait
- * for its conclusion.
- */
- result =
- wait_event_interruptible(*sk_sleep(&cf_sk->sk),
- !STATE_IS_PENDING(cf_sk));
-
- lock_sock(&(cf_sk->sk));
- if (result == -ERESTARTSYS) {
- pr_debug("CAIF: %s(): wait_event_interruptible"
- "woken by a signal (1)", __func__);
- ret = -ERESTARTSYS;
- goto open_error;
- }
- }
- }
-
- /* socket is now either closed, pending open or open */
- if (STATE_IS_OPEN(cf_sk) && !STATE_IS_PENDING(cf_sk)) {
- /* Open */
- pr_debug("CAIF: %s(): Socket is already opened (cf_sk=%p)"
- " check access f_flags = 0x%x file_mode = 0x%x\n",
- __func__, cf_sk, mode, cf_sk->file_mode);
-
- } else {
- /* We are closed or pending open.
- * If closed: send link setup
- * If pending open: link setup already sent (we could have been
- * interrupted by a signal last time)
- */
- if (!STATE_IS_OPEN(cf_sk)) {
- /* First opening of file; connect lower layers: */
- /* Drain queue (very unlikely) */
- drain_queue(cf_sk);
-
- cf_sk->layer.receive = caif_sktrecv_cb;
- SET_STATE_OPEN(cf_sk);
- SET_PENDING_ON(cf_sk);
-
- /* Register this channel. */
- result =
- caif_connect_client(&cf_sk->conn_req,
- &cf_sk->layer);
- if (result < 0) {
- pr_debug("CAIF: %s(): can't register channel\n",
- __func__);
- ret = -EIO;
- SET_STATE_CLOSED(cf_sk);
- SET_PENDING_OFF(cf_sk);
- goto open_error;
- }
- dbfs_atomic_inc(&cnt.num_init);
- }
-
- /* If opened non-blocking, report "success".
- */
- if (flags & O_NONBLOCK) {
- pr_debug("CAIF: %s(): O_NONBLOCK success\n",
- __func__);
- ret = -EINPROGRESS;
- cf_sk->sk.sk_err = -EINPROGRESS;
- goto open_error;
- }
-
- trace_printk("CAIF: %s(): Wait for connect response\n",
- __func__);
-
- /* release lock before waiting */
- release_sock(&cf_sk->sk);
-
- result =
- wait_event_interruptible(*sk_sleep(&cf_sk->sk),
- !STATE_IS_PENDING(cf_sk));
-
- lock_sock(&(cf_sk->sk));
-
- if (result == -ERESTARTSYS) {
- pr_debug("CAIF: %s(): wait_event_interruptible"
- "woken by a signal (2)", __func__);
- ret = -ERESTARTSYS;
- goto open_error;
- }
-
- if (!STATE_IS_OPEN(cf_sk)) {
- /* Lower layers said "no" */
- pr_debug("CAIF: %s(): Closed received\n", __func__);
- ret = -EPIPE;
- goto open_error;
- }
-
- trace_printk("CAIF: %s(): Connect received\n", __func__);
- }
- /* Open is ok */
- cf_sk->file_mode |= mode;
-
- trace_printk("CAIF: %s(): Connected - file mode = %x\n",
- __func__, cf_sk->file_mode);
-
- release_sock(&cf_sk->sk);
- return 0;
-open_error:
- sock->state = SS_UNCONNECTED;
- release_sock(&cf_sk->sk);
-out:
- return ret;
-}
-
-static int caif_shutdown(struct socket *sock, int how)
-{
- struct caifsock *cf_sk = NULL;
- int result = 0;
- int tx_flow_state_was_on;
- struct sock *sk = sock->sk;
-
- trace_printk("CAIF: %s(): enter\n", __func__);
- pr_debug("f_flags=%x\n", sock->file->f_flags);
-
- if (how != SHUT_RDWR)
- return -EOPNOTSUPP;
-
- cf_sk = container_of(sk, struct caifsock, sk);
- if (cf_sk == NULL) {
- pr_debug("CAIF: %s(): COULD NOT FIND SOCKET\n", __func__);
- return -EBADF;
- }
-
- /* I want to be alone on cf_sk (except status queue) */
- lock_sock(&(cf_sk->sk));
- sock_hold(&cf_sk->sk);
-
- /* IS_CLOSED have double meaning:
- * 1) Spontanous Remote Shutdown Request.
- * 2) Ack on a channel teardown(disconnect)
- * Must clear bit in case we previously received
- * remote shudown request.
- */
- if (STATE_IS_OPEN(cf_sk) && !STATE_IS_PENDING(cf_sk)) {
- SET_STATE_CLOSED(cf_sk);
- SET_PENDING_ON(cf_sk);
- tx_flow_state_was_on = TX_FLOW_IS_ON(cf_sk);
- SET_TX_FLOW_OFF(cf_sk);
-
- /* Hold the socket until DEINIT_RSP is received */
- sock_hold(&cf_sk->sk);
- result = caif_disconnect_client(&cf_sk->layer);
-
- if (result < 0) {
- pr_debug("CAIF: %s(): "
- "caif_disconnect_client() failed\n",
- __func__);
- SET_STATE_CLOSED(cf_sk);
- SET_PENDING_OFF(cf_sk);
- SET_TX_FLOW_OFF(cf_sk);
- release_sock(&cf_sk->sk);
- sock_put(&cf_sk->sk);
- return -EIO;
- }
-
- }
- if (STATE_IS_REMOTE_SHUTDOWN(cf_sk)) {
- SET_PENDING_OFF(cf_sk);
- SET_REMOTE_SHUTDOWN_OFF(cf_sk);
- }
-
- /*
- * Socket is no longer in state pending close,
- * and we can release the reference.
- */
-
- dbfs_atomic_inc(&cnt.num_close);
- drain_queue(cf_sk);
- SET_RX_FLOW_ON(cf_sk);
- cf_sk->file_mode = 0;
- sock_put(&cf_sk->sk);
- release_sock(&cf_sk->sk);
- if (!result && (sock->file->f_flags & O_NONBLOCK)) {
- pr_debug("nonblocking shutdown returing -EAGAIN\n");
- return -EAGAIN;
- } else
- return result;
-}
-
-static ssize_t caif_sock_no_sendpage(struct socket *sock,
- struct page *page,
- int offset, size_t size, int flags)
-{
- return -EOPNOTSUPP;
-}
-
-/* This function is called as part of close. */
-static int caif_release(struct socket *sock)
-{
- struct sock *sk = sock->sk;
- struct caifsock *cf_sk = NULL;
- int res;
- caif_assert(sk != NULL);
- cf_sk = container_of(sk, struct caifsock, sk);
-
- if (cf_sk->debugfs_socket_dir != NULL)
- debugfs_remove_recursive(cf_sk->debugfs_socket_dir);
-
- res = caif_shutdown(sock, SHUT_RDWR);
- if (res && res != -EINPROGRESS)
- return res;
-
- /*
- * FIXME: Shutdown should probably be possible to do async
- * without flushing queues, allowing reception of frames while
- * waiting for DEINIT_IND.
- * Release should always block, to allow secure decoupling of
- * CAIF stack.
- */
- if (!(sock->file->f_flags & O_NONBLOCK)) {
- res = wait_event_interruptible(*sk_sleep(&cf_sk->sk),
- !STATE_IS_PENDING(cf_sk));
-
- if (res == -ERESTARTSYS) {
- pr_debug("CAIF: %s(): wait_event_interruptible"
- "woken by a signal (1)", __func__);
- }
- }
- lock_sock(&(cf_sk->sk));
-
- sock->sk = NULL;
-
- /* Detach the socket from its process context by making it orphan. */
- sock_orphan(sk);
-
- /*
- * Setting SHUTDOWN_MASK means that both send and receive are shutdown
- * for the socket.
- */
- sk->sk_shutdown = SHUTDOWN_MASK;
-
- /*
- * Set the socket state to closed, the TCP_CLOSE macro is used when
- * closing any socket.
- */
-
- /* Flush out this sockets receive queue. */
- drain_queue(cf_sk);
-
- /* Finally release the socket. */
- SET_STATE_PENDING_DESTROY(cf_sk);
-
- release_sock(&cf_sk->sk);
-
- sock_put(sk);
-
- /*
- * The rest of the cleanup will be handled from the
- * caif_sock_destructor
- */
- return res;
-}
-
-static const struct proto_ops caif_ops = {
- .family = PF_CAIF,
- .owner = THIS_MODULE,
- .release = caif_release,
- .bind = sock_no_bind,
- .connect = caif_connect,
- .socketpair = sock_no_socketpair,
- .accept = sock_no_accept,
- .getname = sock_no_getname,
- .poll = caif_poll,
- .ioctl = sock_no_ioctl,
- .listen = sock_no_listen,
- .shutdown = caif_shutdown,
- .setsockopt = setsockopt,
- .getsockopt = sock_no_getsockopt,
- .sendmsg = caif_sendmsg,
- .recvmsg = caif_recvmsg,
- .mmap = sock_no_mmap,
- .sendpage = caif_sock_no_sendpage,
-};
-
-/* This function is called when a socket is finally destroyed. */
-static void caif_sock_destructor(struct sock *sk)
-{
- struct caifsock *cf_sk = NULL;
- cf_sk = container_of(sk, struct caifsock, sk);
- /* Error checks. */
- caif_assert(!atomic_read(&sk->sk_wmem_alloc));
- caif_assert(sk_unhashed(sk));
- caif_assert(!sk->sk_socket);
- if (!sock_flag(sk, SOCK_DEAD)) {
- pr_debug("CAIF: %s(): 0x%p", __func__, sk);
- return;
- }
-
- if (STATE_IS_OPEN(cf_sk)) {
- pr_debug("CAIF: %s(): socket is opened (cf_sk=%p)"
- " file_mode = 0x%x\n", __func__,
- cf_sk, cf_sk->file_mode);
- return;
- }
- drain_queue(cf_sk);
- kfree(cf_sk->pktq);
-
- trace_printk("CAIF: %s(): caif_sock_destructor: Removing socket %s\n",
- __func__, cf_sk->name);
- atomic_dec(&caif_nr_socks);
-}
-
-static int caif_create(struct net *net, struct socket *sock, int protocol,
- int kern)
-{
- struct sock *sk = NULL;
- struct caifsock *cf_sk = NULL;
- int result = 0;
- static struct proto prot = {.name = "PF_CAIF",
- .owner = THIS_MODULE,
- .obj_size = sizeof(struct caifsock),
- };
-
- /*
- * The sock->type specifies the socket type to use.
- * in SEQPACKET mode packet boundaries are enforced.
- */
- if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM)
- return -ESOCKTNOSUPPORT;
-
- if (net != &init_net)
- return -EAFNOSUPPORT;
-
- if (protocol < 0 || protocol >= CAIFPROTO_MAX)
- return -EPROTONOSUPPORT;
- /*
- * Set the socket state to unconnected. The socket state is really
- * not used at all in the net/core or socket.c but the
- * initialization makes sure that sock->state is not uninitialized.
- */
- sock->state = SS_UNCONNECTED;
-
- sk = sk_alloc(net, PF_CAIF, GFP_KERNEL, &prot);
- if (!sk)
- return -ENOMEM;
-
- cf_sk = container_of(sk, struct caifsock, sk);
-
- /* Store the protocol */
- sk->sk_protocol = (unsigned char) protocol;
-
- spin_lock_init(&cf_sk->read_queue_len_lock);
-
- /* Fill in some information concerning the misc socket. */
- snprintf(cf_sk->name, sizeof(cf_sk->name), "cf_sk%d",
- atomic_read(&caif_nr_socks));
-
- /*
- * Lock in order to try to stop someone from opening the socket
- * too early.
- */
- lock_sock(&(cf_sk->sk));
-
- /* Initialize the nozero default sock structure data. */
- sock_init_data(sock, sk);
- sock->ops = &caif_ops;
- sk->sk_destruct = caif_sock_destructor;
- sk->sk_sndbuf = caif_sockbuf_size;
- sk->sk_rcvbuf = caif_sockbuf_size;
-
- cf_sk->pktq = cfpktq_create();
-
- if (!cf_sk->pktq) {
- pr_err("CAIF: %s(): queue create failed.\n", __func__);
- result = -ENOMEM;
- release_sock(&cf_sk->sk);
- goto err_failed;
- }
- cf_sk->layer.ctrlcmd = caif_sktflowctrl_cb;
- SET_STATE_CLOSED(cf_sk);
- SET_PENDING_OFF(cf_sk);
- SET_TX_FLOW_OFF(cf_sk);
- SET_RX_FLOW_ON(cf_sk);
-
- /* Set default options on configuration */
- cf_sk->conn_req.priority = CAIF_PRIO_NORMAL;
- cf_sk->conn_req.link_selector = CAIF_LINK_HIGH_BANDW;
- cf_sk->conn_req.protocol = protocol;
- /* Increase the number of sockets created. */
- atomic_inc(&caif_nr_socks);
- if (!IS_ERR(debugfsdir)) {
- cf_sk->debugfs_socket_dir =
- debugfs_create_dir(cf_sk->name, debugfsdir);
- debugfs_create_u32("conn_state", S_IRUSR | S_IWUSR,
- cf_sk->debugfs_socket_dir, &cf_sk->conn_state);
- debugfs_create_u32("flow_state", S_IRUSR | S_IWUSR,
- cf_sk->debugfs_socket_dir, &cf_sk->flow_state);
- debugfs_create_u32("read_queue_len", S_IRUSR | S_IWUSR,
- cf_sk->debugfs_socket_dir,
- (u32 *) &cf_sk->read_queue_len);
- debugfs_create_u32("identity", S_IRUSR | S_IWUSR,
- cf_sk->debugfs_socket_dir,
- (u32 *) &cf_sk->layer.id);
- }
- release_sock(&cf_sk->sk);
- return 0;
-err_failed:
- sk_free(sk);
- return result;
-}
-
-static struct net_proto_family caif_family_ops = {
- .family = PF_CAIF,
- .create = caif_create,
- .owner = THIS_MODULE,
-};
-
-static int af_caif_init(void)
-{
- int err;
- err = sock_register(&caif_family_ops);
-
- if (!err)
- return err;
-
- return 0;
-}
-
-static int __init caif_sktinit_module(void)
-{
- int stat;
-#ifdef CONFIG_DEBUG_FS
- debugfsdir = debugfs_create_dir("chnl_skt", NULL);
- if (!IS_ERR(debugfsdir)) {
- debugfs_create_u32("skb_inuse", S_IRUSR | S_IWUSR,
- debugfsdir,
- (u32 *) &cnt.skb_in_use);
- debugfs_create_u32("skb_alloc", S_IRUSR | S_IWUSR,
- debugfsdir,
- (u32 *) &cnt.skb_alloc);
- debugfs_create_u32("skb_free", S_IRUSR | S_IWUSR,
- debugfsdir,
- (u32 *) &cnt.skb_free);
- debugfs_create_u32("num_sockets", S_IRUSR | S_IWUSR,
- debugfsdir,
- (u32 *) &caif_nr_socks);
- debugfs_create_u32("num_open", S_IRUSR | S_IWUSR,
- debugfsdir,
- (u32 *) &cnt.num_open);
- debugfs_create_u32("num_close", S_IRUSR | S_IWUSR,
- debugfsdir,
- (u32 *) &cnt.num_close);
- debugfs_create_u32("num_init", S_IRUSR | S_IWUSR,
- debugfsdir,
- (u32 *) &cnt.num_init);
- debugfs_create_u32("num_init_resp", S_IRUSR | S_IWUSR,
- debugfsdir,
- (u32 *) &cnt.num_init_resp);
- debugfs_create_u32("num_init_fail_resp", S_IRUSR | S_IWUSR,
- debugfsdir,
- (u32 *) &cnt.num_init_fail_resp);
- debugfs_create_u32("num_deinit", S_IRUSR | S_IWUSR,
- debugfsdir,
- (u32 *) &cnt.num_deinit);
- debugfs_create_u32("num_deinit_resp", S_IRUSR | S_IWUSR,
- debugfsdir,
- (u32 *) &cnt.num_deinit_resp);
- 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
- stat = af_caif_init();
- if (stat) {
- pr_err("CAIF: %s(): Failed to initialize CAIF socket layer.",
- __func__);
- return stat;
- }
- return 0;
-}
-
-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);
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/poll.h>
+#include <linux/tcp.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+#include <linux/debugfs.h>
+#include <linux/caif/caif_socket.h>
+#include <asm/atomic.h>
+#include <net/sock.h>
+#include <net/tcp_states.h>
+#include <net/caif/caif_layer.h>
+#include <net/caif/caif_dev.h>
+#include <net/caif/cfpkt.h>
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_NETPROTO(AF_CAIF);
+
+#define CAIF_DEF_SNDBUF (CAIF_MAX_PAYLOAD_SIZE*10)
+#define CAIF_DEF_RCVBUF (CAIF_MAX_PAYLOAD_SIZE*100)
+
+/*
+ * CAIF state is re-using the TCP socket states.
+ * caif_states stored in sk_state reflect the state as reported by
+ * the CAIF stack, while sk_socket->state is the state of the socket.
+ */
+enum caif_states {
+ CAIF_CONNECTED = TCP_ESTABLISHED,
+ CAIF_CONNECTING = TCP_SYN_SENT,
+ CAIF_DISCONNECTED = TCP_CLOSE
+};
+
+#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;
+};
+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;
+ struct dentry *debugfs_socket_dir;
+};
+
+static int rx_flow_is_on(struct caifsock *cf_sk)
+{
+ return test_bit(RX_FLOW_ON_BIT,
+ (void *) &cf_sk->flow_state);
+}
+
+static int tx_flow_is_on(struct caifsock *cf_sk)
+{
+ return test_bit(TX_FLOW_ON_BIT,
+ (void *) &cf_sk->flow_state);
+}
+
+static void set_rx_flow_off(struct caifsock *cf_sk)
+{
+ clear_bit(RX_FLOW_ON_BIT,
+ (void *) &cf_sk->flow_state);
+}
+
+static void set_rx_flow_on(struct caifsock *cf_sk)
+{
+ set_bit(RX_FLOW_ON_BIT,
+ (void *) &cf_sk->flow_state);
+}
+
+static void set_tx_flow_off(struct caifsock *cf_sk)
+{
+ clear_bit(TX_FLOW_ON_BIT,
+ (void *) &cf_sk->flow_state);
+}
+
+static void set_tx_flow_on(struct caifsock *cf_sk)
+{
+ set_bit(TX_FLOW_ON_BIT,
+ (void *) &cf_sk->flow_state);
+}
+
+static void caif_read_lock(struct sock *sk)
+{
+ struct caifsock *cf_sk;
+ cf_sk = container_of(sk, struct caifsock, sk);
+ mutex_lock(&cf_sk->readlock);
+}
+
+static void caif_read_unlock(struct sock *sk)
+{
+ struct caifsock *cf_sk;
+ cf_sk = container_of(sk, struct caifsock, sk);
+ mutex_unlock(&cf_sk->readlock);
+}
+
+int sk_rcvbuf_lowwater(struct caifsock *cf_sk)
+{
+ /* A quarter of full buffer is used a low water mark */
+ return cf_sk->sk.sk_rcvbuf / 4;
+}
+
+void caif_flow_ctrl(struct sock *sk, int mode)
+{
+ struct caifsock *cf_sk;
+ cf_sk = container_of(sk, struct caifsock, sk);
+ if (cf_sk->layer.dn)
+ cf_sk->layer.dn->modemcmd(cf_sk->layer.dn, mode);
+}
+
+/*
+ * Copied from sock.c:sock_queue_rcv_skb(), but changed so packets are
+ * not dropped, but CAIF is sending flow off instead.
+ */
+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)) {
+ trace_printk("CAIF: %s():"
+ " sending flow OFF (queue len = %d %d)\n",
+ __func__,
+ atomic_read(&cf_sk->sk.sk_rmem_alloc),
+ sk_rcvbuf_lowwater(cf_sk));
+ set_rx_flow_off(cf_sk);
+ if (cf_sk->layer.dn)
+ cf_sk->layer.dn->modemcmd(cf_sk->layer.dn,
+ 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)) {
+ set_rx_flow_off(cf_sk);
+ trace_printk("CAIF: %s():"
+ " sending flow OFF due to rmem_schedule\n",
+ __func__);
+ if (cf_sk->layer.dn)
+ cf_sk->layer.dn->modemcmd(cf_sk->layer.dn,
+ CAIF_MODEMCMD_FLOW_OFF_REQ);
+ }
+ skb->dev = NULL;
+ skb_set_owner_r(skb, sk);
+ /* Cache the SKB length before we tack it onto the receive
+ * queue. Once it is added it no longer belongs to us and
+ * 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);
+ else
+ kfree_skb(skb);
+ return 0;
+}
+
+/* Packet Receive Callback function called from CAIF Stack */
+static int caif_sktrecv_cb(struct cflayer *layr, struct cfpkt *pkt)
+{
+ struct caifsock *cf_sk;
+ struct sk_buff *skb;
+
+ cf_sk = container_of(layr, struct caifsock, layer);
+ skb = cfpkt_tonative(pkt);
+
+ if (unlikely(cf_sk->sk.sk_state != CAIF_CONNECTED)) {
+ cfpkt_destroy(pkt);
+ return 0;
+ }
+ caif_queue_rcv_skb(&cf_sk->sk, skb);
+ return 0;
+}
+
+/* Packet Control Callback function called from CAIF */
+static void caif_ctrl_cb(struct cflayer *layr,
+ 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);
+ cf_sk->sk.sk_state = CAIF_CONNECTED;
+ set_tx_flow_on(cf_sk);
+ cf_sk->sk.sk_state_change(&cf_sk->sk);
+ break;
+
+ case CAIF_CTRLCMD_DEINIT_RSP:
+ /* 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;
+ /*
+ * Socket "standards" seems to require POLLOUT to
+ * be set at connect failure.
+ */
+ set_tx_flow_on(cf_sk);
+ cf_sk->sk.sk_state_change(&cf_sk->sk);
+ break;
+
+ 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);
+ cf_sk->sk.sk_error_report(&cf_sk->sk);
+ break;
+
+ default:
+ pr_debug("CAIF: %s(): Unexpected flow command %d\n",
+ __func__, flow);
+ }
+}
+
+static void caif_check_flow_release(struct sock *sk)
+{
+ struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
+
+ if (cf_sk->layer.dn == NULL || cf_sk->layer.dn->modemcmd == NULL)
+ return;
+ if (rx_flow_is_on(cf_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);
+ cf_sk->layer.dn->modemcmd(cf_sk->layer.dn,
+ CAIF_MODEMCMD_FLOW_ON_REQ);
+ }
+}
+/*
+ * Copied from sock.c:sock_queue_rcv_skb(), and added check that user buffer
+ * has sufficient size.
+ */
+
+static int caif_seqpkt_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *m, size_t buf_len, int flags)
+
+{
+ struct sock *sk = sock->sk;
+ struct sk_buff *skb;
+ int ret = 0;
+ int len;
+
+ if (unlikely(!buf_len))
+ return -EINVAL;
+
+ skb = skb_recv_datagram(sk, flags, 0 , &ret);
+ if (!skb)
+ goto read_error;
+
+ len = skb->len;
+
+ if (skb && skb->len > buf_len && !(flags & MSG_PEEK)) {
+ len = buf_len;
+ /*
+ * Push skb back on receive queue if buffer too small.
+ * This has a built-in race where multi-threaded receive
+ * may get packet in wrong order, but multiple read does
+ * not really guarantee ordered delivery anyway.
+ * Let's optimize for speed without taking locks.
+ */
+
+ skb_queue_head(&sk->sk_receive_queue, skb);
+ ret = -EMSGSIZE;
+ goto read_error;
+ }
+
+ ret = skb_copy_datagram_iovec(skb, 0, m->msg_iov, len);
+ if (ret)
+ goto read_error;
+
+ skb_free_datagram(sk, skb);
+
+ caif_check_flow_release(sk);
+
+ return len;
+
+read_error:
+ return ret;
+}
+
+
+/* Copied from unix_stream_wait_data, identical except for lock call. */
+static long caif_stream_data_wait(struct sock *sk, long timeo)
+{
+ DEFINE_WAIT(wait);
+ lock_sock(sk);
+
+ for (;;) {
+ prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
+
+ if (!skb_queue_empty(&sk->sk_receive_queue) ||
+ sk->sk_err ||
+ sk->sk_state != CAIF_CONNECTED ||
+ sock_flag(sk, SOCK_DEAD) ||
+ (sk->sk_shutdown & RCV_SHUTDOWN) ||
+ signal_pending(current) ||
+ !timeo)
+ break;
+
+ set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
+ release_sock(sk);
+ timeo = schedule_timeout(timeo);
+ lock_sock(sk);
+ clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
+ }
+
+ finish_wait(sk_sleep(sk), &wait);
+ release_sock(sk);
+ return timeo;
+}
+
+
+/*
+ * Copied from unix_stream_recvmsg, but removed credit checks,
+ * 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 sock *sk = sock->sk;
+ int copied = 0;
+ int target;
+ int err = 0;
+ long timeo;
+
+ err = -EOPNOTSUPP;
+ if (flags&MSG_OOB)
+ goto out;
+
+ msg->msg_namelen = 0;
+
+ /*
+ * Lock the socket to prevent queue disordering
+ * while sleeps in memcpy_tomsg
+ */
+ err = -EAGAIN;
+ if (sk->sk_state == CAIF_CONNECTING)
+ goto out;
+
+ caif_read_lock(sk);
+ target = sock_rcvlowat(sk, flags&MSG_WAITALL, size);
+ timeo = sock_rcvtimeo(sk, flags&MSG_DONTWAIT);
+
+ do {
+ int chunk;
+ struct sk_buff *skb;
+
+ lock_sock(sk);
+ skb = skb_dequeue(&sk->sk_receive_queue);
+ caif_check_flow_release(sk);
+
+ if (skb == NULL) {
+ if (copied >= target)
+ goto unlock;
+ /*
+ * POSIX 1003.1g mandates this order.
+ */
+ err = sock_error(sk);
+ if (err)
+ goto unlock;
+ err = -ECONNRESET;
+ if (sk->sk_shutdown & RCV_SHUTDOWN)
+ goto unlock;
+
+ err = -EPIPE;
+ if (sk->sk_state != CAIF_CONNECTED)
+ goto unlock;
+ if (sock_flag(sk, SOCK_DEAD))
+ goto unlock;
+
+ release_sock(sk);
+
+ err = -EAGAIN;
+ if (!timeo)
+ break;
+
+ caif_read_unlock(sk);
+
+ timeo = caif_stream_data_wait(sk, timeo);
+
+ if (signal_pending(current)) {
+ err = sock_intr_errno(timeo);
+ goto out;
+ }
+ caif_read_lock(sk);
+ continue;
+unlock:
+ release_sock(sk);
+ break;
+ }
+ release_sock(sk);
+ chunk = min_t(unsigned int, skb->len, size);
+ if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) {
+ skb_queue_head(&sk->sk_receive_queue, skb);
+ if (copied == 0)
+ copied = -EFAULT;
+ break;
+ }
+ copied += chunk;
+ size -= chunk;
+
+ /* Mark read part of skb as used */
+ if (!(flags & MSG_PEEK)) {
+ skb_pull(skb, chunk);
+
+ /* put the skb back if we didn't use it up. */
+ if (skb->len) {
+ skb_queue_head(&sk->sk_receive_queue, skb);
+ break;
+ }
+ kfree_skb(skb);
+
+ } else {
+ /*
+ * It is questionable, see note in unix_dgram_recvmsg.
+ */
+ /* put message back and return */
+ skb_queue_head(&sk->sk_receive_queue, skb);
+ break;
+ }
+ } while (size);
+ caif_read_unlock(sk);
+
+out:
+ return copied ? : err;
+}
+
+/*
+ * Copied from sock.c:sock_wait_for_wmem, but change to wait for
+ * CAIF flow-on and sock_writable.
+ */
+static long caif_wait_for_flow_on(struct caifsock *cf_sk,
+ int wait_writeable, long timeo, int *err)
+{
+ struct sock *sk = &cf_sk->sk;
+ DEFINE_WAIT(wait);
+ for (;;) {
+ *err = 0;
+ if (tx_flow_is_on(cf_sk) &&
+ (!wait_writeable || sock_writeable(&cf_sk->sk)))
+ break;
+ *err = -ETIMEDOUT;
+ if (!timeo)
+ break;
+ *err = -ERESTARTSYS;
+ if (signal_pending(current))
+ break;
+ prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
+ *err = -ECONNRESET;
+ if (sk->sk_shutdown & SHUTDOWN_MASK)
+ break;
+ *err = -sk->sk_err;
+ if (sk->sk_err)
+ break;
+ *err = -EPIPE;
+ if (cf_sk->sk.sk_state != CAIF_CONNECTED)
+ break;
+ timeo = schedule_timeout(timeo);
+ }
+ finish_wait(sk_sleep(sk), &wait);
+ return timeo;
+}
+
+/*
+ * Transmit a SKB. The device may temporarily request re-transmission
+ * by returning EAGAIN.
+ */
+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;
+
+ /* Slight paranoia, probably not needed. */
+ if (unlikely(loopcnt++ > 1000)) {
+ pr_warning("CAIF: %s(): transmit retries failed,"
+ " error = %d\n", __func__, ret);
+ break;
+ }
+
+ 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;
+}
+
+/* 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 sock *sk = sock->sk;
+ struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
+ int buffer_size;
+ int ret = 0;
+ struct sk_buff *skb = NULL;
+ int noblock;
+ long timeo;
+ caif_assert(cf_sk);
+ ret = sock_error(sk);
+ if (ret)
+ goto err;
+
+ ret = -EOPNOTSUPP;
+ if (msg->msg_flags&MSG_OOB)
+ goto err;
+
+ ret = -EOPNOTSUPP;
+ if (msg->msg_namelen)
+ goto err;
+
+ ret = -EINVAL;
+ if (unlikely(msg->msg_iov->iov_base == NULL))
+ goto err;
+ noblock = msg->msg_flags & MSG_DONTWAIT;
+
+ buffer_size = len + CAIF_NEEDED_HEADROOM + CAIF_NEEDED_TAILROOM;
+
+ ret = -EMSGSIZE;
+ if (buffer_size > CAIF_MAX_PAYLOAD_SIZE)
+ goto err;
+
+ timeo = sock_sndtimeo(sk, noblock);
+ timeo = caif_wait_for_flow_on(container_of(sk, struct caifsock, sk),
+ 1, timeo, &ret);
+
+ ret = -EPIPE;
+ if (cf_sk->sk.sk_state != CAIF_CONNECTED ||
+ sock_flag(sk, SOCK_DEAD) ||
+ (sk->sk_shutdown & RCV_SHUTDOWN))
+ goto err;
+
+ ret = -ENOMEM;
+ skb = sock_alloc_send_skb(sk, buffer_size, noblock, &ret);
+ if (!skb)
+ goto err;
+ skb_reserve(skb, CAIF_NEEDED_HEADROOM);
+
+ ret = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
+
+ if (ret)
+ goto err;
+ ret = transmit_skb(skb, cf_sk, noblock, timeo);
+ if (ret < 0)
+ goto err;
+ return len;
+err:
+ kfree_skb(skb);
+ return ret;
+}
+
+/*
+ * Copied from unix_stream_sendmsg and adapted to CAIF:
+ * Changed removed permission handling and added waiting for flow on
+ * and other minor adaptations.
+ */
+static int caif_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
+ struct msghdr *msg, size_t len)
+{
+ struct sock *sk = sock->sk;
+ struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
+ int err, size;
+ struct sk_buff *skb;
+ int sent = 0;
+ long timeo;
+
+ err = -EOPNOTSUPP;
+
+ if (unlikely(msg->msg_flags&MSG_OOB))
+ goto out_err;
+
+ if (unlikely(msg->msg_namelen))
+ goto out_err;
+
+ timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
+ timeo = caif_wait_for_flow_on(cf_sk, 1, timeo, &err);
+
+ if (unlikely(sk->sk_shutdown & SEND_SHUTDOWN))
+ goto pipe_err;
+
+ while (sent < len) {
+
+ size = len-sent;
+
+ if (size > CAIF_MAX_PAYLOAD_SIZE)
+ size = CAIF_MAX_PAYLOAD_SIZE;
+
+ /* If size is more than half of sndbuf, chop up message */
+ if (size > ((sk->sk_sndbuf >> 1) - 64))
+ size = (sk->sk_sndbuf >> 1) - 64;
+
+ if (size > SKB_MAX_ALLOC)
+ size = SKB_MAX_ALLOC;
+
+ skb = sock_alloc_send_skb(sk,
+ size + CAIF_NEEDED_HEADROOM
+ + CAIF_NEEDED_TAILROOM,
+ msg->msg_flags&MSG_DONTWAIT,
+ &err);
+ if (skb == NULL)
+ goto out_err;
+
+ skb_reserve(skb, CAIF_NEEDED_HEADROOM);
+ /*
+ * If you pass two values to the sock_alloc_send_skb
+ * it tries to grab the large buffer with GFP_NOFS
+ * (which can fail easily), and if it fails grab the
+ * fallback size buffer which is under a page and will
+ * succeed. [Alan]
+ */
+ size = min_t(int, size, skb_tailroom(skb));
+
+ err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
+ if (err) {
+ kfree_skb(skb);
+ goto out_err;
+ }
+ err = transmit_skb(skb, cf_sk,
+ msg->msg_flags&MSG_DONTWAIT, timeo);
+ if (err < 0) {
+ kfree_skb(skb);
+ goto pipe_err;
+ }
+ sent += size;
+ }
+
+ return sent;
+
+pipe_err:
+ if (sent == 0 && !(msg->msg_flags&MSG_NOSIGNAL))
+ send_sig(SIGPIPE, current, 0);
+ err = -EPIPE;
+out_err:
+ return sent ? : err;
+}
+
+static int setsockopt(struct socket *sock,
+ 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);
+ int prio, linksel;
+ struct ifreq ifreq;
+
+ if (cf_sk->sk.sk_socket->state != SS_UNCONNECTED)
+ return -ENOPROTOOPT;
+
+ switch (opt) {
+ case CAIFSO_LINK_SELECT:
+ if (ol < sizeof(int))
+ return -EINVAL;
+ if (lvl != SOL_CAIF)
+ goto bad_sol;
+ if (copy_from_user(&linksel, ov, sizeof(int)))
+ return -EINVAL;
+ lock_sock(&(cf_sk->sk));
+ cf_sk->conn_req.link_selector = linksel;
+ release_sock(&cf_sk->sk);
+ return 0;
+
+ case SO_PRIORITY:
+ if (lvl != SOL_SOCKET)
+ goto bad_sol;
+ if (ol < sizeof(int))
+ return -EINVAL;
+ if (copy_from_user(&prio, ov, sizeof(int)))
+ return -EINVAL;
+ lock_sock(&(cf_sk->sk));
+ cf_sk->conn_req.priority = prio;
+ release_sock(&cf_sk->sk);
+ return 0;
+
+ case SO_BINDTODEVICE:
+ if (lvl != SOL_SOCKET)
+ goto bad_sol;
+ if (ol < sizeof(struct ifreq))
+ return -EINVAL;
+ if (copy_from_user(&ifreq, ov, sizeof(ifreq)))
+ return -EFAULT;
+ lock_sock(&(cf_sk->sk));
+ strncpy(cf_sk->conn_req.link_name, ifreq.ifr_name,
+ sizeof(cf_sk->conn_req.link_name));
+ cf_sk->conn_req.link_name
+ [sizeof(cf_sk->conn_req.link_name)-1] = 0;
+ release_sock(&cf_sk->sk);
+ return 0;
+
+ case CAIFSO_REQ_PARAM:
+ if (lvl != SOL_CAIF)
+ goto bad_sol;
+ if (cf_sk->sk.sk_protocol != CAIFPROTO_UTIL)
+ return -ENOPROTOOPT;
+ lock_sock(&(cf_sk->sk));
+ cf_sk->conn_req.param.size = ol;
+ if (ol > sizeof(cf_sk->conn_req.param.data) ||
+ copy_from_user(&cf_sk->conn_req.param.data, ov, ol)) {
+ release_sock(&cf_sk->sk);
+ return -EINVAL;
+ }
+ release_sock(&cf_sk->sk);
+ return 0;
+
+ default:
+ return -ENOPROTOOPT;
+ }
+
+ return 0;
+bad_sol:
+ return -ENOPROTOOPT;
+
+}
+
+/*
+ * caif_connect() - Connect a CAIF Socket
+ * Copied and modified af_irda.c:irda_connect().
+ *
+ * Note : by consulting "errno", the user space caller may learn the cause
+ * of the failure. Most of them are visible in the function, others may come
+ * from subroutines called and are listed here :
+ * o -EAFNOSUPPORT: bad socket family or type.
+ * o -ESOCKTNOSUPPORT: bad socket type or protocol
+ * o -EINVAL: bad socket address, or CAIF link type
+ * o -ECONNREFUSED: remote end refused the connection.
+ * o -EINPROGRESS: connect request sent but timed out (or non-blocking)
+ * o -EISCONN: already connected.
+ * o -ETIMEDOUT: Connection timed out (send timeout)
+ * o -ENODEV: No link layer to send request
+ * o -ECONNRESET: Received Shutdown indication or lost link layer
+ * o -ENOMEM: Out of memory
+ *
+ * State Strategy:
+ * o sk_state: holds the CAIF_* protocol state, it's updated by
+ * caif_ctrl_cb.
+ * o sock->state: holds the SS_* socket state and is updated by connect and
+ * disconnect.
+ */
+static int caif_connect(struct socket *sock, struct sockaddr *uaddr,
+ int addr_len, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
+ long timeo;
+ int err;
+ lock_sock(sk);
+
+ err = -EAFNOSUPPORT;
+ if (uaddr->sa_family != AF_CAIF)
+ goto out;
+
+ err = -ESOCKTNOSUPPORT;
+ if (unlikely(!(sk->sk_type == SOCK_STREAM &&
+ cf_sk->sk.sk_protocol == CAIFPROTO_AT) &&
+ sk->sk_type != SOCK_SEQPACKET))
+ goto out;
+ switch (sock->state) {
+ case SS_UNCONNECTED:
+ /* Normal case, a fresh connect */
+ caif_assert(sk->sk_state == CAIF_DISCONNECTED);
+ break;
+ case SS_CONNECTING:
+ switch (sk->sk_state) {
+ case CAIF_CONNECTED:
+ sock->state = SS_CONNECTED;
+ err = -EISCONN;
+ goto out;
+ case CAIF_DISCONNECTED:
+ /* Reconnect allowed */
+ break;
+ case CAIF_CONNECTING:
+ err = -EALREADY;
+ if (flags & O_NONBLOCK)
+ goto out;
+ goto wait_connect;
+ }
+ break;
+ case SS_CONNECTED:
+ caif_assert(sk->sk_state == CAIF_CONNECTED ||
+ sk->sk_state == CAIF_DISCONNECTED);
+ if (sk->sk_shutdown & SHUTDOWN_MASK) {
+ /* Allow re-connect after SHUTDOWN_IND */
+ caif_disconnect_client(&cf_sk->layer);
+ break;
+ }
+ /* No reconnect on a seqpacket socket */
+ err = -EISCONN;
+ goto out;
+ case SS_DISCONNECTING:
+ case SS_FREE:
+ caif_assert(1); /*Should never happen */
+ break;
+ }
+ sk->sk_state = CAIF_DISCONNECTED;
+ sock->state = SS_UNCONNECTED;
+ sk_stream_kill_queues(&cf_sk->sk);
+
+ err = -EINVAL;
+ if (addr_len != sizeof(struct sockaddr_caif) ||
+ !uaddr)
+ goto out;
+
+ memcpy(&cf_sk->conn_req.sockaddr, uaddr,
+ sizeof(struct sockaddr_caif));
+
+ /* Move to connecting socket, start sending Connect Requests */
+ sock->state = SS_CONNECTING;
+ sk->sk_state = CAIF_CONNECTING;
+
+ dbfs_atomic_inc(&cnt.num_connect_req);
+ cf_sk->layer.receive = caif_sktrecv_cb;
+ err = caif_connect_client(&cf_sk->conn_req,
+ &cf_sk->layer);
+ if (err < 0) {
+ cf_sk->sk.sk_socket->state = SS_UNCONNECTED;
+ cf_sk->sk.sk_state = CAIF_DISCONNECTED;
+ goto out;
+ }
+
+ err = -EINPROGRESS;
+wait_connect:
+
+ if (sk->sk_state != CAIF_CONNECTED && (flags & O_NONBLOCK))
+ goto out;
+
+ timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
+
+ release_sock(sk);
+ err = wait_event_interruptible_timeout(*sk_sleep(sk),
+ sk->sk_state != CAIF_CONNECTING,
+ timeo);
+ lock_sock(sk);
+ if (err < 0)
+ goto out; /* -ERESTARTSYS */
+ if (err == 0 && sk->sk_state != CAIF_CONNECTED) {
+ err = -ETIMEDOUT;
+ goto out;
+ }
+
+ if (sk->sk_state != CAIF_CONNECTED) {
+ sock->state = SS_UNCONNECTED;
+ err = sock_error(sk);
+ if (!err)
+ err = -ECONNREFUSED;
+ goto out;
+ }
+ sock->state = SS_CONNECTED;
+ err = 0;
+out:
+ release_sock(sk);
+ return err;
+}
+
+
+/*
+ * caif_release() - Disconnect a CAIF Socket
+ * Copied and modified af_irda.c:irda_release().
+ */
+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;
+
+ set_tx_flow_off(cf_sk);
+
+ /*
+ * Ensure that packets are not queued after this point in time.
+ * 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);
+ sock_set_flag(sk, SOCK_DEAD);
+ spin_unlock(&sk->sk_receive_queue.lock);
+ sock->sk = NULL;
+
+ dbfs_atomic_inc(&cnt.num_disconnect);
+
+ if (cf_sk->debugfs_socket_dir != NULL)
+ debugfs_remove_recursive(cf_sk->debugfs_socket_dir);
+
+ lock_sock(&(cf_sk->sk));
+ 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);
+
+ 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;
+}
+
+/* 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 sock *sk = sock->sk;
+ unsigned int mask;
+ struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
+
+ sock_poll_wait(file, sk_sleep(sk), wait);
+ mask = 0;
+
+ /* exceptional events? */
+ if (sk->sk_err)
+ mask |= POLLERR;
+ if (sk->sk_shutdown == SHUTDOWN_MASK)
+ mask |= POLLHUP;
+ if (sk->sk_shutdown & RCV_SHUTDOWN)
+ mask |= POLLRDHUP;
+
+ /* readable? */
+ if (!skb_queue_empty(&sk->sk_receive_queue) ||
+ (sk->sk_shutdown & RCV_SHUTDOWN))
+ mask |= POLLIN | POLLRDNORM;
+
+ /* Connection-based need to check for termination and startup */
+ if (sk->sk_state == CAIF_DISCONNECTED)
+ mask |= POLLHUP;
+
+ /*
+ * we set writable also when the other side has shut down the
+ * connection. This prevents stuck sockets.
+ */
+ if (sock_writeable(sk) && tx_flow_is_on(cf_sk))
+ mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
+
+ return mask;
+}
+
+static const struct proto_ops caif_seqpacket_ops = {
+ .family = PF_CAIF,
+ .owner = THIS_MODULE,
+ .release = caif_release,
+ .bind = sock_no_bind,
+ .connect = caif_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = sock_no_getname,
+ .poll = caif_poll,
+ .ioctl = sock_no_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = setsockopt,
+ .getsockopt = sock_no_getsockopt,
+ .sendmsg = caif_seqpkt_sendmsg,
+ .recvmsg = caif_seqpkt_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+};
+
+static const struct proto_ops caif_stream_ops = {
+ .family = PF_CAIF,
+ .owner = THIS_MODULE,
+ .release = caif_release,
+ .bind = sock_no_bind,
+ .connect = caif_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = sock_no_getname,
+ .poll = caif_poll,
+ .ioctl = sock_no_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = setsockopt,
+ .getsockopt = sock_no_getsockopt,
+ .sendmsg = caif_stream_sendmsg,
+ .recvmsg = caif_stream_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+};
+
+/* This function is called when a socket is finally destroyed. */
+static void caif_sock_destructor(struct sock *sk)
+{
+ struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
+ caif_assert(!atomic_read(&sk->sk_wmem_alloc));
+ 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);
+ return;
+ }
+ sk_stream_kill_queues(&cf_sk->sk);
+ dbfs_atomic_dec(&cnt.caif_nr_socks);
+}
+
+static int caif_create(struct net *net, struct socket *sock, int protocol,
+ int kern)
+{
+ struct sock *sk = NULL;
+ struct caifsock *cf_sk = NULL;
+ static struct proto prot = {.name = "PF_CAIF",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct caifsock),
+ };
+
+ if (!capable(CAP_SYS_ADMIN) && !capable(CAP_NET_ADMIN))
+ return -EPERM;
+ /*
+ * The sock->type specifies the socket type to use.
+ * The CAIF socket is a packet stream in the sense
+ * that it is packet based. CAIF trusts the reliability
+ * of the link, no resending is implemented.
+ */
+ if (sock->type == SOCK_SEQPACKET)
+ sock->ops = &caif_seqpacket_ops;
+ else if (sock->type == SOCK_STREAM)
+ sock->ops = &caif_stream_ops;
+ else
+ return -ESOCKTNOSUPPORT;
+
+ if (protocol < 0 || protocol >= CAIFPROTO_MAX)
+ return -EPROTONOSUPPORT;
+ /*
+ * Set the socket state to unconnected. The socket state
+ * is really not used at all in the net/core or socket.c but the
+ * initialization makes sure that sock->state is not uninitialized.
+ */
+ sk = sk_alloc(net, PF_CAIF, GFP_KERNEL, &prot);
+ if (!sk)
+ return -ENOMEM;
+
+ cf_sk = container_of(sk, struct caifsock, sk);
+
+ /* Store the protocol */
+ sk->sk_protocol = (unsigned char) protocol;
+
+ /* Sendbuf dictates the amount of outbound packets not yet sent */
+ sk->sk_sndbuf = CAIF_DEF_SNDBUF;
+ sk->sk_rcvbuf = CAIF_DEF_RCVBUF;
+
+ /*
+ * Lock in order to try to stop someone from opening the socket
+ * too early.
+ */
+ lock_sock(&(cf_sk->sk));
+
+ /* Initialize the nozero default sock structure data. */
+ sock_init_data(sock, sk);
+ sk->sk_destruct = caif_sock_destructor;
+
+ mutex_init(&cf_sk->readlock); /* single task reading lock */
+ cf_sk->layer.ctrlcmd = caif_ctrl_cb;
+ cf_sk->sk.sk_socket->state = SS_UNCONNECTED;
+ cf_sk->sk.sk_state = CAIF_DISCONNECTED;
+
+ set_tx_flow_off(cf_sk);
+ set_rx_flow_on(cf_sk);
+
+ /* Set default options on configuration */
+ cf_sk->conn_req.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;
+}
+
+
+static struct net_proto_family caif_family_ops = {
+ .family = PF_CAIF,
+ .create = caif_create,
+ .owner = THIS_MODULE,
+};
+
+int af_caif_init(void)
+{
+ int err = sock_register(&caif_family_ops);
+ if (!err)
+ return err;
+ 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);
--
1.6.3.3
^ permalink raw reply related
* [PATCH net-next-2.6 4/7] caif: Disconnect without waiting for response
From: sjur.brandeland @ 2010-04-28 18:54 UTC (permalink / raw)
To: netdev, davem
Cc: marcel, daniel.martensson, sjurbr, linus.walleij,
Sjur Braendeland
In-Reply-To: <1272480880-30672-3-git-send-email-sjur.brandeland@stericsson.com>
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Changes:
o Function cfcnfg_disconn_adapt_layer is changed to do asynchronous
disconnect, not waiting for any response from the modem. Due to this
the function cfcnfg_linkdestroy_rsp does nothing anymore.
o Because disconnect may take down a connection before a connect response
is received the function cfcnfg_linkup_rsp is checking if the client is
still waiting for the response, if not a disconnect request is sent to
the modem.
o cfctrl is no longer keeping track of pending disconnect requests.
o Added function cfctrl_cancel_req, which is used for deleting a pending
connect request if disconnect is done before connect response is received.
o Removed unused function cfctrl_insert_req2
o Added better handling of connect reject from modem.
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
include/net/caif/cfctrl.h | 7 +-
net/caif/cfcnfg.c | 155 +++++++++++++--------------------------------
net/caif/cfctrl.c | 95 ++++++++++++++++++----------
3 files changed, 111 insertions(+), 146 deletions(-)
diff --git a/include/net/caif/cfctrl.h b/include/net/caif/cfctrl.h
index dee25b8..997603f 100644
--- a/include/net/caif/cfctrl.h
+++ b/include/net/caif/cfctrl.h
@@ -43,8 +43,7 @@ struct cfctrl_rsp {
void (*linksetup_rsp)(struct cflayer *layer, u8 linkid,
enum cfctrl_srv serv, u8 phyid,
struct cflayer *adapt_layer);
- void (*linkdestroy_rsp)(struct cflayer *layer, u8 linkid,
- struct cflayer *client_layer);
+ void (*linkdestroy_rsp)(struct cflayer *layer, u8 linkid);
void (*linkerror_ind)(void);
void (*enum_rsp)(void);
void (*sleep_rsp)(void);
@@ -117,7 +116,7 @@ struct cfctrl {
};
void cfctrl_enum_req(struct cflayer *cfctrl, u8 physlinkid);
-void cfctrl_linkup_request(struct cflayer *cfctrl,
+int cfctrl_linkup_request(struct cflayer *cfctrl,
struct cfctrl_link_param *param,
struct cflayer *user_layer);
int cfctrl_linkdown_req(struct cflayer *cfctrl, u8 linkid,
@@ -135,4 +134,6 @@ void cfctrl_insert_req(struct cfctrl *ctrl,
struct cfctrl_request_info *req);
struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl,
struct cfctrl_request_info *req);
+void cfctrl_cancel_req(struct cflayer *layr, struct cflayer *adap_layer);
+
#endif /* CFCTRL_H_ */
diff --git a/net/caif/cfcnfg.c b/net/caif/cfcnfg.c
index f94f3df..471c629 100644
--- a/net/caif/cfcnfg.c
+++ b/net/caif/cfcnfg.c
@@ -54,8 +54,7 @@ struct cfcnfg {
static void cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id,
enum cfctrl_srv serv, u8 phyid,
struct cflayer *adapt_layer);
-static void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id,
- struct cflayer *client_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);
static void cfctrl_resp_func(void);
@@ -175,73 +174,65 @@ int cfcnfg_get_named(struct cfcnfg *cnfg, char *name)
return 0;
}
-/*
- * NOTE: What happens on destroy failure:
- * 1a) No response - Too early
- * This will not happen because enumerate has already
- * completed.
- * 1b) No response - FATAL
- * Not handled, but this should be a CAIF PROTOCOL ERROR
- * Modem error, response is really expected - this
- * case is not really handled.
- * 2) O/E-bit indicate error
- * Ignored - this link is destroyed anyway.
- * 3) Not able to match on request
- * Not handled, but this should be a CAIF PROTOCOL ERROR
- * 4) Link-Error - (no response)
- * Not handled, but this should be a CAIF PROTOCOL ERROR
- */
int cfcnfg_disconn_adapt_layer(struct cfcnfg *cnfg, struct cflayer *adap_layer)
{
u8 channel_id = 0;
int ret = 0;
+ struct cflayer *servl = NULL;
struct cfcnfg_phyinfo *phyinfo = NULL;
u8 phyid = 0;
-
caif_assert(adap_layer != NULL);
channel_id = adap_layer->id;
- if (channel_id == 0) {
+ if (adap_layer->dn == NULL || channel_id == 0) {
pr_err("CAIF: %s():adap_layer->id is 0\n", __func__);
ret = -ENOTCONN;
goto end;
}
-
- if (adap_layer->dn == NULL) {
- pr_err("CAIF: %s():adap_layer->dn is NULL\n", __func__);
- ret = -ENODEV;
- goto end;
- }
-
- if (adap_layer->dn != NULL)
- phyid = cfsrvl_getphyid(adap_layer->dn);
-
- phyinfo = cfcnfg_get_phyinfo(cnfg, phyid);
- if (phyinfo == NULL) {
- pr_warning("CAIF: %s(): No interface to send disconnect to\n",
- __func__);
- ret = -ENODEV;
+ servl = cfmuxl_remove_uplayer(cnfg->mux, channel_id);
+ if (servl == NULL)
goto end;
- }
-
- if (phyinfo->id != phyid
- || phyinfo->phy_layer->id != phyid
- || phyinfo->frm_layer->id != phyid) {
-
- pr_err("CAIF: %s(): Inconsistency in phy registration\n",
- __func__);
+ layer_set_up(servl, NULL);
+ ret = cfctrl_linkdown_req(cnfg->ctrl, channel_id, adap_layer);
+ if (servl == NULL) {
+ pr_err("CAIF: %s(): PROTOCOL ERROR "
+ "- Error removing service_layer Channel_Id(%d)",
+ __func__, channel_id);
ret = -EINVAL;
goto end;
}
+ caif_assert(channel_id == servl->id);
+ if (adap_layer->dn != NULL) {
+ phyid = cfsrvl_getphyid(adap_layer->dn);
- ret = cfctrl_linkdown_req(cnfg->ctrl, channel_id, adap_layer);
-
-end:
+ phyinfo = cfcnfg_get_phyinfo(cnfg, phyid);
+ if (phyinfo == NULL) {
+ pr_warning("CAIF: %s(): "
+ "No interface to send disconnect to\n",
+ __func__);
+ ret = -ENODEV;
+ goto end;
+ }
+ if (phyinfo->id != phyid ||
+ phyinfo->phy_layer->id != phyid ||
+ phyinfo->frm_layer->id != phyid) {
+ pr_err("CAIF: %s(): "
+ "Inconsistency in phy registration\n",
+ __func__);
+ 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 (adap_layer->ctrlcmd != NULL)
+ adap_layer->ctrlcmd(adap_layer, CAIF_CTRLCMD_DEINIT_RSP, 0);
return ret;
}
@@ -254,69 +245,11 @@ void cfcnfg_release_adap_layer(struct cflayer *adap_layer)
}
EXPORT_SYMBOL(cfcnfg_release_adap_layer);
-static void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id,
- struct cflayer *client_layer)
+static void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id)
{
- struct cfcnfg *cnfg = container_obj(layer);
- struct cflayer *servl;
-
- /*
- * 1) Remove service from the MUX layer. The MUX must
- * guarante that no more payload sent "upwards" (receive)
- */
- servl = cfmuxl_remove_uplayer(cnfg->mux, channel_id);
-
- if (servl == NULL) {
- pr_err("CAIF: %s(): PROTOCOL ERROR "
- "- Error removing service_layer Channel_Id(%d)",
- __func__, channel_id);
- return;
- }
- caif_assert(channel_id == servl->id);
-
- if (servl != client_layer && servl->up != client_layer) {
- pr_err("CAIF: %s(): Error removing service_layer "
- "Channel_Id(%d) %p %p",
- __func__, channel_id, (void *) servl,
- (void *) client_layer);
- return;
- }
-
- /*
- * 2) DEINIT_RSP must guarantee that no more packets are transmitted
- * from client (adap_layer) when it returns.
- */
-
- if (servl->ctrlcmd == NULL) {
- pr_err("CAIF: %s(): Error servl->ctrlcmd == NULL", __func__);
- return;
- }
-
- servl->ctrlcmd(servl, CAIF_CTRLCMD_DEINIT_RSP, 0);
-
- /* 3) It is now safe to destroy the service layer. */
- cfservl_destroy(servl);
}
-/*
- * NOTE: What happens on linksetup failure:
- * 1a) No response - Too early
- * This will not happen because enumerate is secured
- * before using interface.
- * 1b) No response - FATAL
- * Not handled, but this should be a CAIF PROTOCOL ERROR
- * Modem error, response is really expected - this case is
- * not really handled.
- * 2) O/E-bit indicate error
- * Handled in cnfg_reject_rsp
- * 3) Not able to match on request
- * Not handled, but this should be a CAIF PROTOCOL ERROR
- * 4) Link-Error - (no response)
- * Not handled, but this should be a CAIF PROTOCOL ERROR
- */
-
-int
-cfcnfg_add_adaptation_layer(struct cfcnfg *cnfg,
+int cfcnfg_add_adaptation_layer(struct cfcnfg *cnfg,
struct cfctrl_link_param *param,
struct cflayer *adap_layer)
{
@@ -346,8 +279,7 @@ cfcnfg_add_adaptation_layer(struct cfcnfg *cnfg,
param->phyid);
/* FIXME: ENUMERATE INITIALLY WHEN ACTIVATING PHYSICAL INTERFACE */
cfctrl_enum_req(cnfg->ctrl, param->phyid);
- cfctrl_linkup_request(cnfg->ctrl, param, adap_layer);
- return 0;
+ return cfctrl_linkup_request(cnfg->ctrl, param, adap_layer);
}
EXPORT_SYMBOL(cfcnfg_add_adaptation_layer);
@@ -367,8 +299,10 @@ cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv,
struct cflayer *servicel = NULL;
struct cfcnfg_phyinfo *phyinfo;
if (adapt_layer == NULL) {
- pr_err("CAIF: %s(): PROTOCOL ERROR "
- "- LinkUp Request/Response did not match\n", __func__);
+ pr_debug("CAIF: %s(): link setup response "
+ "but no client exist, send linkdown back\n",
+ __func__);
+ cfctrl_linkdown_req(cnfg->ctrl, channel_id, NULL);
return;
}
@@ -424,6 +358,7 @@ cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv,
cfmuxl_set_uplayer(cnfg->mux, servicel, channel_id);
layer_set_up(servicel, adapt_layer);
layer_set_dn(adapt_layer, servicel);
+ cfsrvl_get(servicel);
servicel->ctrlcmd(servicel, CAIF_CTRLCMD_INIT_RSP, 0);
}
diff --git a/net/caif/cfctrl.c b/net/caif/cfctrl.c
index 11f8014..a521d32 100644
--- a/net/caif/cfctrl.c
+++ b/net/caif/cfctrl.c
@@ -32,6 +32,7 @@ static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
struct cflayer *cfctrl_create(void)
{
+ struct dev_info dev_info;
struct cfctrl *this =
kmalloc(sizeof(struct cfctrl), GFP_ATOMIC);
if (!this) {
@@ -39,12 +40,13 @@ struct cflayer *cfctrl_create(void)
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);
spin_lock_init(&this->info_list_lock);
atomic_set(&this->req_seq_no, 1);
atomic_set(&this->rsp_seq_no, 1);
- this->serv.dev_info.id = 0xff;
- this->serv.layer.id = 0;
this->serv.layer.receive = cfctrl_recv;
sprintf(this->serv.layer.name, "ctrl");
this->serv.layer.ctrlcmd = cfctrl_ctrlcmd;
@@ -127,20 +129,6 @@ void cfctrl_insert_req(struct cfctrl *ctrl,
spin_unlock(&ctrl->info_list_lock);
}
-static void cfctrl_insert_req2(struct cfctrl *ctrl, enum cfctrl_cmd cmd,
- u8 linkid, struct cflayer *user_layer)
-{
- struct cfctrl_request_info *req = kmalloc(sizeof(*req), GFP_KERNEL);
- if (!req) {
- pr_warning("CAIF: %s(): Out of memory\n", __func__);
- return;
- }
- req->client_layer = user_layer;
- req->cmd = cmd;
- req->channel_id = linkid;
- cfctrl_insert_req(ctrl, req);
-}
-
/* Compare and remove request */
struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl,
struct cfctrl_request_info *req)
@@ -234,7 +222,7 @@ void cfctrl_enum_req(struct cflayer *layer, u8 physlinkid)
}
}
-void cfctrl_linkup_request(struct cflayer *layer,
+int cfctrl_linkup_request(struct cflayer *layer,
struct cfctrl_link_param *param,
struct cflayer *user_layer)
{
@@ -248,7 +236,7 @@ void cfctrl_linkup_request(struct cflayer *layer,
struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
if (!pkt) {
pr_warning("CAIF: %s(): Out of memory\n", __func__);
- return;
+ return -ENOMEM;
}
cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_SETUP);
cfpkt_addbdy(pkt, (param->chtype << 4) + param->linktype);
@@ -294,11 +282,12 @@ void cfctrl_linkup_request(struct cflayer *layer,
default:
pr_warning("CAIF: %s():Request setup of bad link type = %d\n",
__func__, param->linktype);
+ return -EINVAL;
}
req = kmalloc(sizeof(*req), GFP_KERNEL);
if (!req) {
pr_warning("CAIF: %s(): Out of memory\n", __func__);
- return;
+ return -ENOMEM;
}
memset(req, 0, sizeof(*req));
req->client_layer = user_layer;
@@ -306,6 +295,11 @@ void cfctrl_linkup_request(struct cflayer *layer,
req->param = *param;
cfctrl_insert_req(cfctrl, req);
init_info(cfpkt_info(pkt), cfctrl);
+ /*
+ * NOTE:Always send linkup and linkdown request on the same
+ * device as the payload. Otherwise old queued up payload
+ * might arrive with the newly allocated channel ID.
+ */
cfpkt_info(pkt)->dev_info->id = param->phyid;
ret =
cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
@@ -313,7 +307,9 @@ void cfctrl_linkup_request(struct cflayer *layer,
pr_err("CAIF: %s(): Could not transmit linksetup request\n",
__func__);
cfpkt_destroy(pkt);
+ return -ENODEV;
}
+ return 0;
}
int cfctrl_linkdown_req(struct cflayer *layer, u8 channelid,
@@ -326,7 +322,6 @@ int cfctrl_linkdown_req(struct cflayer *layer, u8 channelid,
pr_warning("CAIF: %s(): Out of memory\n", __func__);
return -ENOMEM;
}
- cfctrl_insert_req2(cfctrl, CFCTRL_CMD_LINK_DESTROY, channelid, client);
cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_DESTROY);
cfpkt_addbdy(pkt, channelid);
init_info(cfpkt_info(pkt), cfctrl);
@@ -392,6 +387,38 @@ void cfctrl_getstartreason_req(struct cflayer *layer)
}
+void cfctrl_cancel_req(struct cflayer *layr, struct cflayer *adap_layer)
+{
+ struct cfctrl_request_info *p, *req;
+ struct cfctrl *ctrl = container_obj(layr);
+ spin_lock(&ctrl->info_list_lock);
+
+ if (ctrl->first_req == NULL) {
+ spin_unlock(&ctrl->info_list_lock);
+ return;
+ }
+
+ if (ctrl->first_req->client_layer == adap_layer) {
+
+ req = ctrl->first_req;
+ ctrl->first_req = ctrl->first_req->next;
+ kfree(req);
+ }
+
+ p = ctrl->first_req;
+ while (p != NULL && p->next != NULL) {
+ if (p->next->client_layer == adap_layer) {
+
+ req = p->next;
+ p->next = p->next->next;
+ kfree(p->next);
+ }
+ p = p->next;
+ }
+
+ spin_unlock(&ctrl->info_list_lock);
+}
+
static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)
{
u8 cmdrsp;
@@ -409,11 +436,8 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)
cmd = cmdrsp & CFCTRL_CMD_MASK;
if (cmd != CFCTRL_CMD_LINK_ERR
&& CFCTRL_RSP_BIT != (CFCTRL_RSP_BIT & cmdrsp)) {
- if (handle_loop(cfctrl, cmd, pkt) == CAIF_FAILURE) {
- pr_info("CAIF: %s() CAIF Protocol error:"
- "Response bit not set\n", __func__);
- goto error;
- }
+ if (handle_loop(cfctrl, cmd, pkt) == CAIF_FAILURE)
+ cmdrsp |= CFCTRL_ERR_BIT;
}
switch (cmd) {
@@ -451,12 +475,16 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)
switch (serv) {
case CFCTRL_SRV_VEI:
case CFCTRL_SRV_DBG:
+ if (CFCTRL_ERR_BIT & cmdrsp)
+ break;
/* Link ID */
cfpkt_extr_head(pkt, &linkid, 1);
break;
case CFCTRL_SRV_VIDEO:
cfpkt_extr_head(pkt, &tmp, 1);
linkparam.u.video.connid = tmp;
+ if (CFCTRL_ERR_BIT & cmdrsp)
+ break;
/* Link ID */
cfpkt_extr_head(pkt, &linkid, 1);
break;
@@ -465,6 +493,8 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)
cfpkt_extr_head(pkt, &tmp32, 4);
linkparam.u.datagram.connid =
le32_to_cpu(tmp32);
+ if (CFCTRL_ERR_BIT & cmdrsp)
+ break;
/* Link ID */
cfpkt_extr_head(pkt, &linkid, 1);
break;
@@ -483,6 +513,8 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)
*cp++ = tmp;
*cp = '\0';
+ if (CFCTRL_ERR_BIT & cmdrsp)
+ break;
/* Link ID */
cfpkt_extr_head(pkt, &linkid, 1);
@@ -519,6 +551,8 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)
cfpkt_extr_head(pkt, &tmp, 1);
*cp++ = tmp;
}
+ if (CFCTRL_ERR_BIT & cmdrsp)
+ break;
/* Link ID */
cfpkt_extr_head(pkt, &linkid, 1);
/* Length */
@@ -560,13 +594,7 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)
break;
case CFCTRL_CMD_LINK_DESTROY:
cfpkt_extr_head(pkt, &linkid, 1);
- rsp.cmd = cmd;
- rsp.channel_id = linkid;
- req = cfctrl_remove_req(cfctrl, &rsp);
- cfctrl->res.linkdestroy_rsp(cfctrl->serv.layer.up, linkid,
- req ? req->client_layer : NULL);
- if (req != NULL)
- kfree(req);
+ cfctrl->res.linkdestroy_rsp(cfctrl->serv.layer.up, linkid);
break;
case CFCTRL_CMD_LINK_ERR:
pr_err("CAIF: %s(): Frame Error Indication received\n",
@@ -608,7 +636,7 @@ static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
case CAIF_CTRLCMD_FLOW_OFF_IND:
spin_lock(&this->info_list_lock);
if (this->first_req != NULL) {
- pr_warning("CAIF: %s(): Received flow off in "
+ pr_debug("CAIF: %s(): Received flow off in "
"control layer", __func__);
}
spin_unlock(&this->info_list_lock);
@@ -633,6 +661,7 @@ static int handle_loop(struct cfctrl *ctrl, int cmd, struct cfpkt *pkt)
if (!ctrl->loop_linkused[linkid])
goto found;
spin_unlock(&ctrl->loop_linkid_lock);
+ pr_err("CAIF: %s(): Out of link-ids\n", __func__);
return -EINVAL;
found:
if (!ctrl->loop_linkused[linkid])
--
1.6.3.3
^ permalink raw reply related
* [PATCH net-next-2.6 3/7] caif: Add reference counting to service layer
From: sjur.brandeland @ 2010-04-28 18:54 UTC (permalink / raw)
To: netdev, davem
Cc: marcel, daniel.martensson, sjurbr, linus.walleij,
Sjur Braendeland
In-Reply-To: <1272480880-30672-2-git-send-email-sjur.brandeland@stericsson.com>
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Changes:
o Added functions cfsrvl_get and cfsrvl_put.
o Added support release_client to use by socket and net device.
o Increase reference counting for in-flight packets from cfmuxl
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
include/net/caif/caif_dev.h | 11 +++++++++++
include/net/caif/cfcnfg.h | 7 +++++++
include/net/caif/cfsrvl.h | 22 ++++++++++++++++++++++
net/caif/caif_dev.c | 6 ++++++
net/caif/cfcnfg.c | 7 +++++++
net/caif/cfmuxl.c | 7 ++++++-
net/caif/cfsrvl.c | 7 +++++++
7 files changed, 66 insertions(+), 1 deletions(-)
diff --git a/include/net/caif/caif_dev.h b/include/net/caif/caif_dev.h
index 3aa1ff6..318ab94 100644
--- a/include/net/caif/caif_dev.h
+++ b/include/net/caif/caif_dev.h
@@ -70,6 +70,17 @@ int caif_connect_client(struct caif_connect_request *config,
int caif_disconnect_client(struct cflayer *client_layer);
/**
+ * caif_release_client - Release adaptation layer reference to client.
+ *
+ * @client_layer: Client layer.
+ *
+ * Releases a client/adaptation layer use of the caif stack.
+ * This function must be used after caif_disconnect_client to
+ * decrease the reference count of the service layer.
+ */
+void caif_release_client(struct cflayer *client_layer);
+
+/**
* connect_req_to_link_param - Translate configuration parameters
* from socket format to internal format.
* @cnfg: Pointer to configuration handler
diff --git a/include/net/caif/cfcnfg.h b/include/net/caif/cfcnfg.h
index f16b875..9fc2fc2 100644
--- a/include/net/caif/cfcnfg.h
+++ b/include/net/caif/cfcnfg.h
@@ -97,6 +97,13 @@ int cfcnfg_disconn_adapt_layer(struct cfcnfg *cnfg,
struct cflayer *adap_layer);
/**
+ * cfcnfg_release_adap_layer - Used by client to release the adaptation layer.
+ *
+ * @adap_layer: Adaptation layer.
+ */
+void cfcnfg_release_adap_layer(struct cflayer *adap_layer);
+
+/**
* cfcnfg_add_adaptation_layer - Add an adaptation layer to the CAIF stack.
*
* The adaptation Layer is where the interface to application or higher-level
diff --git a/include/net/caif/cfsrvl.h b/include/net/caif/cfsrvl.h
index b2a12db..2dc9eb1 100644
--- a/include/net/caif/cfsrvl.h
+++ b/include/net/caif/cfsrvl.h
@@ -9,14 +9,18 @@
#include <linux/list.h>
#include <linux/stddef.h>
#include <linux/types.h>
+#include <linux/kref.h>
+
struct cfsrvl {
struct cflayer layer;
bool open;
bool phy_flow_on;
bool modem_flow_on;
struct dev_info dev_info;
+ struct kref ref;
};
+void cfsrvl_release(struct kref *kref);
struct cflayer *cfvei_create(u8 linkid, struct dev_info *dev_info);
struct cflayer *cfdgml_create(u8 linkid, struct dev_info *dev_info);
struct cflayer *cfutill_create(u8 linkid, struct dev_info *dev_info);
@@ -31,4 +35,22 @@ void cfsrvl_init(struct cfsrvl *service,
bool cfsrvl_ready(struct cfsrvl *service, int *err);
u8 cfsrvl_getphyid(struct cflayer *layer);
+static inline void cfsrvl_get(struct cflayer *layr)
+{
+ struct cfsrvl *s;
+ if (layr == NULL)
+ return;
+ s = container_of(layr, struct cfsrvl, layer);
+ kref_get(&s->ref);
+}
+
+static inline void cfsrvl_put(struct cflayer *layr)
+{
+ struct cfsrvl *s;
+ if (layr == NULL)
+ return;
+ s = container_of(layr, struct cfsrvl, layer);
+ kref_put(&s->ref, cfsrvl_release);
+}
+
#endif /* CFSRVL_H_ */
diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c
index be1f674..0145bae 100644
--- a/net/caif/caif_dev.c
+++ b/net/caif/caif_dev.c
@@ -346,6 +346,12 @@ int caif_disconnect_client(struct cflayer *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)
{
diff --git a/net/caif/cfcnfg.c b/net/caif/cfcnfg.c
index d52f256..f94f3df 100644
--- a/net/caif/cfcnfg.c
+++ b/net/caif/cfcnfg.c
@@ -247,6 +247,13 @@ end:
}
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);
+
static void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id,
struct cflayer *client_layer)
{
diff --git a/net/caif/cfmuxl.c b/net/caif/cfmuxl.c
index 6fb9f9e..7372f27 100644
--- a/net/caif/cfmuxl.c
+++ b/net/caif/cfmuxl.c
@@ -62,6 +62,7 @@ int cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid)
{
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);
return 0;
@@ -172,8 +173,11 @@ struct cflayer *cfmuxl_remove_uplayer(struct cflayer *layr, u8 id)
struct cfmuxl *muxl = container_obj(layr);
spin_lock(&muxl->receive_lock);
up = get_up(muxl, id);
+ if (up == NULL)
+ return NULL;
memset(muxl->up_cache, 0, sizeof(muxl->up_cache));
list_del(&up->node);
+ cfsrvl_put(up);
spin_unlock(&muxl->receive_lock);
return up;
}
@@ -203,8 +207,9 @@ static int cfmuxl_receive(struct cflayer *layr, struct cfpkt *pkt)
*/
return /* CFGLU_EPROT; */ 0;
}
-
+ cfsrvl_get(up);
ret = up->receive(up, pkt);
+ cfsrvl_put(up);
return ret;
}
diff --git a/net/caif/cfsrvl.c b/net/caif/cfsrvl.c
index d470c51..aff31f3 100644
--- a/net/caif/cfsrvl.c
+++ b/net/caif/cfsrvl.c
@@ -158,6 +158,13 @@ void cfsrvl_init(struct cfsrvl *service,
service->layer.ctrlcmd = cfservl_ctrlcmd;
service->layer.modemcmd = cfservl_modemcmd;
service->dev_info = *dev_info;
+ kref_init(&service->ref);
+}
+
+void cfsrvl_release(struct kref *kref)
+{
+ struct cfsrvl *service = container_of(kref, struct cfsrvl, ref);
+ kfree(service);
}
bool cfsrvl_ready(struct cfsrvl *service, int *err)
--
1.6.3.3
^ permalink raw reply related
* [PATCH net-next-2.6 2/7] caif: Rename functions in cfcnfg and caif_dev
From: sjur.brandeland @ 2010-04-28 18:54 UTC (permalink / raw)
To: netdev, davem
Cc: marcel, daniel.martensson, sjurbr, linus.walleij,
Sjur Braendeland
In-Reply-To: <1272480880-30672-1-git-send-email-sjur.brandeland@stericsson.com>
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Changes:
o Renamed cfcnfg_del_adapt_layer to cfcnfg_disconn_adapt_layer
o Fixed typo cfcfg to cfcnfg
o Renamed linkid to channel_id
o Updated documentation in caif_dev.h
o Minor formatting changes
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
include/net/caif/caif_dev.h | 6 +++-
include/net/caif/cfcnfg.h | 10 ++++----
net/caif/caif_dev.c | 15 ++++++------
net/caif/cfcnfg.c | 51 +++++++++++++++++++++----------------------
4 files changed, 41 insertions(+), 41 deletions(-)
diff --git a/include/net/caif/caif_dev.h b/include/net/caif/caif_dev.h
index 42a7c78..3aa1ff6 100644
--- a/include/net/caif/caif_dev.h
+++ b/include/net/caif/caif_dev.h
@@ -23,17 +23,19 @@ struct caif_param {
};
/**
- * caif_connect_request - Request data for CAIF channel setup.
+ * struct caif_connect_request - Request data for CAIF channel setup.
+ * @protocol: Type of CAIF protocol to use (at, datagram etc)
* @sockaddr: Socket address to connect.
* @priority: Priority of the connection.
* @link_selector: Link selector (high bandwidth or low latency)
* @link_name: Name of the CAIF Link Layer to use.
+ * @param: Connect Request parameters (CAIF_SO_REQ_PARAM).
*
* This struct is used when connecting a CAIF channel.
* It contains all CAIF channel configuration options.
*/
struct caif_connect_request {
- int protocol;
+ enum caif_protocol_type protocol;
struct sockaddr_caif sockaddr;
enum caif_channel_priority priority;
enum caif_link_selector link_selector;
diff --git a/include/net/caif/cfcnfg.h b/include/net/caif/cfcnfg.h
index 366082c..f16b875 100644
--- a/include/net/caif/cfcnfg.h
+++ b/include/net/caif/cfcnfg.h
@@ -87,13 +87,14 @@ cfcnfg_add_phy_layer(struct cfcnfg *cnfg, enum cfcnfg_phy_type phy_type,
int cfcnfg_del_phy_layer(struct cfcnfg *cnfg, struct cflayer *phy_layer);
/**
- * cfcnfg_del_adapt_layer - Deletes an adaptation layer from the CAIF stack.
+ * cfcnfg_disconn_adapt_layer - Disconnects an adaptation layer.
*
* @cnfg: Pointer to a CAIF configuration object, created by
* cfcnfg_create().
* @adap_layer: Adaptation layer to be removed.
*/
-int cfcnfg_del_adapt_layer(struct cfcnfg *cnfg, struct cflayer *adap_layer);
+int cfcnfg_disconn_adapt_layer(struct cfcnfg *cnfg,
+ struct cflayer *adap_layer);
/**
* cfcnfg_add_adaptation_layer - Add an adaptation layer to the CAIF stack.
@@ -102,14 +103,13 @@ int cfcnfg_del_adapt_layer(struct cfcnfg *cnfg, struct cflayer *adap_layer);
* driver functionality is implemented.
*
* @cnfg: Pointer to a CAIF configuration object, created by
- * cfcnfg_create().
+ * cfcnfg_create().
* @param: Link setup parameters.
* @adap_layer: Specify the adaptation layer; the receive and
* flow-control functions MUST be set in the structure.
*
*/
-int
-cfcnfg_add_adaptation_layer(struct cfcnfg *cnfg,
+int cfcnfg_add_adaptation_layer(struct cfcnfg *cnfg,
struct cfctrl_link_param *param,
struct cflayer *adap_layer);
diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c
index e84837e..be1f674 100644
--- a/net/caif/caif_dev.c
+++ b/net/caif/caif_dev.c
@@ -330,20 +330,19 @@ int caif_connect_client(struct caif_connect_request *conn_req,
struct cflayer *client_layer)
{
struct cfctrl_link_param param;
- if (connect_req_to_link_param(get_caif_conf(), conn_req, ¶m) == 0)
- /* Hook up the adaptation layer. */
- return cfcnfg_add_adaptation_layer(get_caif_conf(),
+ 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);
-
- return -EINVAL;
-
- caif_assert(0);
}
EXPORT_SYMBOL(caif_connect_client);
int caif_disconnect_client(struct cflayer *adap_layer)
{
- return cfcnfg_del_adapt_layer(get_caif_conf(), adap_layer);
+ return cfcnfg_disconn_adapt_layer(get_caif_conf(), adap_layer);
}
EXPORT_SYMBOL(caif_disconnect_client);
diff --git a/net/caif/cfcnfg.c b/net/caif/cfcnfg.c
index c873e3d..d52f256 100644
--- a/net/caif/cfcnfg.c
+++ b/net/caif/cfcnfg.c
@@ -51,12 +51,12 @@ struct cfcnfg {
struct cfcnfg_phyinfo phy_layers[MAX_PHY_LAYERS];
};
-static void cncfg_linkup_rsp(struct cflayer *layer, u8 linkid,
+static void cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id,
enum cfctrl_srv serv, u8 phyid,
struct cflayer *adapt_layer);
-static void cncfg_linkdestroy_rsp(struct cflayer *layer, u8 linkid,
+static void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id,
struct cflayer *client_layer);
-static void cncfg_reject_rsp(struct cflayer *layer, u8 linkid,
+static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id,
struct cflayer *adapt_layer);
static void cfctrl_resp_func(void);
static void cfctrl_enum_resp(void);
@@ -82,13 +82,13 @@ struct cfcnfg *cfcnfg_create(void)
resp = cfctrl_get_respfuncs(this->ctrl);
resp->enum_rsp = cfctrl_enum_resp;
resp->linkerror_ind = cfctrl_resp_func;
- resp->linkdestroy_rsp = cncfg_linkdestroy_rsp;
+ resp->linkdestroy_rsp = cfcnfg_linkdestroy_rsp;
resp->sleep_rsp = cfctrl_resp_func;
resp->wake_rsp = cfctrl_resp_func;
resp->restart_rsp = cfctrl_resp_func;
resp->radioset_rsp = cfctrl_resp_func;
- resp->linksetup_rsp = cncfg_linkup_rsp;
- resp->reject_rsp = cncfg_reject_rsp;
+ resp->linksetup_rsp = cfcnfg_linkup_rsp;
+ resp->reject_rsp = cfcnfg_reject_rsp;
this->last_phyid = 1;
@@ -191,8 +191,7 @@ int cfcnfg_get_named(struct cfcnfg *cnfg, char *name)
* 4) Link-Error - (no response)
* Not handled, but this should be a CAIF PROTOCOL ERROR
*/
-
-int cfcnfg_del_adapt_layer(struct cfcnfg *cnfg, struct cflayer *adap_layer)
+int cfcnfg_disconn_adapt_layer(struct cfcnfg *cnfg, struct cflayer *adap_layer)
{
u8 channel_id = 0;
int ret = 0;
@@ -246,9 +245,9 @@ end:
return ret;
}
-EXPORT_SYMBOL(cfcnfg_del_adapt_layer);
+EXPORT_SYMBOL(cfcnfg_disconn_adapt_layer);
-static void cncfg_linkdestroy_rsp(struct cflayer *layer, u8 linkid,
+static void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id,
struct cflayer *client_layer)
{
struct cfcnfg *cnfg = container_obj(layer);
@@ -258,20 +257,20 @@ static void cncfg_linkdestroy_rsp(struct cflayer *layer, u8 linkid,
* 1) Remove service from the MUX layer. The MUX must
* guarante that no more payload sent "upwards" (receive)
*/
- servl = cfmuxl_remove_uplayer(cnfg->mux, linkid);
+ servl = cfmuxl_remove_uplayer(cnfg->mux, channel_id);
if (servl == NULL) {
pr_err("CAIF: %s(): PROTOCOL ERROR "
- "- Error removing service_layer Linkid(%d)",
- __func__, linkid);
+ "- Error removing service_layer Channel_Id(%d)",
+ __func__, channel_id);
return;
}
- caif_assert(linkid == servl->id);
+ caif_assert(channel_id == servl->id);
if (servl != client_layer && servl->up != client_layer) {
pr_err("CAIF: %s(): Error removing service_layer "
- "Linkid(%d) %p %p",
- __func__, linkid, (void *) servl,
+ "Channel_Id(%d) %p %p",
+ __func__, channel_id, (void *) servl,
(void *) client_layer);
return;
}
@@ -345,7 +344,7 @@ cfcnfg_add_adaptation_layer(struct cfcnfg *cnfg,
}
EXPORT_SYMBOL(cfcnfg_add_adaptation_layer);
-static void cncfg_reject_rsp(struct cflayer *layer, u8 linkid,
+static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id,
struct cflayer *adapt_layer)
{
if (adapt_layer != NULL && adapt_layer->ctrlcmd != NULL)
@@ -354,7 +353,7 @@ static void cncfg_reject_rsp(struct cflayer *layer, u8 linkid,
}
static void
-cncfg_linkup_rsp(struct cflayer *layer, u8 linkid, enum cfctrl_srv serv,
+cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv,
u8 phyid, struct cflayer *adapt_layer)
{
struct cfcnfg *cnfg = container_obj(layer);
@@ -383,26 +382,26 @@ cncfg_linkup_rsp(struct cflayer *layer, u8 linkid, enum cfctrl_srv serv,
_CAIF_MODEMCMD_PHYIF_USEFULL);
}
- adapt_layer->id = linkid;
+ adapt_layer->id = channel_id;
switch (serv) {
case CFCTRL_SRV_VEI:
- servicel = cfvei_create(linkid, &phyinfo->dev_info);
+ servicel = cfvei_create(channel_id, &phyinfo->dev_info);
break;
case CFCTRL_SRV_DATAGRAM:
- servicel = cfdgml_create(linkid, &phyinfo->dev_info);
+ servicel = cfdgml_create(channel_id, &phyinfo->dev_info);
break;
case CFCTRL_SRV_RFM:
- servicel = cfrfml_create(linkid, &phyinfo->dev_info);
+ servicel = cfrfml_create(channel_id, &phyinfo->dev_info);
break;
case CFCTRL_SRV_UTIL:
- servicel = cfutill_create(linkid, &phyinfo->dev_info);
+ servicel = cfutill_create(channel_id, &phyinfo->dev_info);
break;
case CFCTRL_SRV_VIDEO:
- servicel = cfvidl_create(linkid, &phyinfo->dev_info);
+ servicel = cfvidl_create(channel_id, &phyinfo->dev_info);
break;
case CFCTRL_SRV_DBG:
- servicel = cfdbgl_create(linkid, &phyinfo->dev_info);
+ servicel = cfdbgl_create(channel_id, &phyinfo->dev_info);
break;
default:
pr_err("CAIF: %s(): Protocol error. "
@@ -415,7 +414,7 @@ cncfg_linkup_rsp(struct cflayer *layer, u8 linkid, enum cfctrl_srv serv,
return;
}
layer_set_dn(servicel, cnfg->mux);
- cfmuxl_set_uplayer(cnfg->mux, servicel, linkid);
+ cfmuxl_set_uplayer(cnfg->mux, servicel, channel_id);
layer_set_up(servicel, adapt_layer);
layer_set_dn(adapt_layer, servicel);
servicel->ctrlcmd(servicel, CAIF_CTRLCMD_INIT_RSP, 0);
--
1.6.3.3
^ permalink raw reply related
* [PATCH net-next-2.6 1/7] caif: Ldisc add permission check and mem-alloc error check
From: sjur.brandeland @ 2010-04-28 18:54 UTC (permalink / raw)
To: netdev, davem
Cc: marcel, daniel.martensson, sjurbr, linus.walleij,
Sjur Braendeland
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Changes:
o Added permission checks for installing. CAP_SYS_ADMIN and
CAP_SYS_TTY_CONFIG can install the ldisc.
o Check if allocation of skb was successful.
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
drivers/net/caif/caif_serial.c | 5 ++++-
1 files changed, 4 insertions(+), 1 deletions(-)
diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c
index 38c0186..09257ca 100644
--- a/drivers/net/caif/caif_serial.c
+++ b/drivers/net/caif/caif_serial.c
@@ -197,7 +197,8 @@ static void ldisc_receive(struct tty_struct *tty, const u8 *data,
/* Get a suitable caif packet and copy in data. */
skb = netdev_alloc_skb(ser->dev, count+1);
- BUG_ON(skb == NULL);
+ if (skb == NULL)
+ return;
p = skb_put(skb, count);
memcpy(p, data, count);
@@ -315,6 +316,8 @@ static int ldisc_open(struct tty_struct *tty)
/* No write no play */
if (tty->ops->write == NULL)
return -EOPNOTSUPP;
+ if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_TTY_CONFIG))
+ return -EPERM;
sprintf(name, "cf%s", tty->name);
dev = alloc_netdev(sizeof(*ser), name, caifdev_setup);
--
1.6.3.3
^ permalink raw reply related
* Re: [net-next-2.6 PATCH 1/2] Add netdev port-profile support (take III, was iovnl)
From: Scott Feldman @ 2010-04-28 18:54 UTC (permalink / raw)
To: Arnd Bergmann; +Cc: davem, netdev, chrisw
In-Reply-To: <201004281513.58879.arnd@arndb.de>
On 4/28/10 6:13 AM, "Arnd Bergmann" <arnd@arndb.de> wrote:
> What I could imagine to unify this is something like
>
> ip port_profile set DEVICE [ { pre_associate | pre_associate_rr } ]
> { name PORT-PROFILE | vsi MGR:VTID:VER }
> mac LLADDR
> [ vlan VID ]
> [ host_uuid HOST_UUID ]
> [ client_uuid CLIENT_UUID ]
> [ client_name CLIENT_NAME ]
> ip port_profile del DEVICE [ mac LLADDR [ vlan VID ] ]
> ip port_profile show DEVICE
Arnd, can someone test this with VDP today? I don't have access to that
equipment so it's difficult for me to fully test the unified patch. I can
test the previous patch with enic easily because I have access to production
systems. I'd like to make sure someone can test this with VDP before I
respin the patch one more time.
-scott
^ permalink raw reply
* Re: [PATCH]: sctp: Fix skb_over_panic resulting from multiple invalid parameter errors (CVE-2010-1173)
From: Neil Horman @ 2010-04-28 18:52 UTC (permalink / raw)
To: Vlad Yasevich; +Cc: sri, linux-sctp, eteo, netdev, davem, security
In-Reply-To: <4BD87DFF.6080502@hp.com>
On Wed, Apr 28, 2010 at 02:27:11PM -0400, Vlad Yasevich wrote:
>
>
> Neil Horman wrote:
> > On Wed, Apr 28, 2010 at 01:52:05PM -0400, Vlad Yasevich wrote:
> >>
> >> Vlad Yasevich wrote:
> >>> Neil Horman wrote:
> >>>> On Wed, Apr 28, 2010 at 10:00:37AM -0400, Vlad Yasevich wrote:
> >>>>> I have this patch and a few others already queued.
> >>>>>
> >>>>> I was planning on sending these today for stable.
> >>>>>
> >>>>> Here is the full list of stable patches I have:
> >>>>>
> >>>>> sctp: Fix oops when sending queued ASCONF chunks
> >>>>> sctp: fix to calc the INIT/INIT-ACK chunk length correctly is set
> >>>>> sctp: per_cpu variables should be in bh_disabled section
> >>>>> sctp: fix potential reference of a freed pointer
> >>>>> sctp: avoid irq lock inversion while call sk->sk_data_ready()
> >>>>>
> >>>>> -vlad
> >>>>>
> >>>> Are you sure? this oops looks _very_ simmilar to the INIT/INIT-ACK length
> >>>> calculation oops described above, but is in fact different, and requires this
> >>>> patch, from what I can see. The right fix might be in the ASCONF chunk patch
> >>>> you list above, but I don't see that in your tree at the moment, so I can't be
> >>>> sure.
> >>> As I said, I totally goofed when reading the description and I apologize.
> >>> However, I do one comment regarding the patch.
> >>>
> >>> If the bad packet is REALLY long (I mean close to 65K IP limit), then
> >>> we'll end up allocating a supper huge skb in this case and potentially exceed
> >>> the IP length limitation. Section 11.4 of rfc 4960 allows us to omit some
> >>> errors and limit the size of the packet.
> >>>
> >>> I would recommend limiting this to MTU worth of potentiall errors. This is
> >>> on top of what the INIT-ACK is going to carry, so at most we'll sent 2 MTUs
> >>> worth. That's still a potential by amplification attack, but it's somewhat
> >>> mitigated.
> >>>
> >>> Of course now we have to handle the case of checking for space before adding
> >>> an error cause. :)
> >>>
> >> Hi Neil
> >>
> >> I am also not crazy about the pre-allocation scheme. In the case where you have
> >> say 100 parameters that are all 'skip' parameters, you'd end up pre-allocating a
> >> huge buffer for absolutely nothing.
> >>
> > Would have been nice if you'd made your opinion known 4 hours ago when I was
> > testing version 2 of this. :)
> >
>
> sorry, fighting a head cold and need drugs to think clearly... ;)
>
Its ok, I'm apparently just feeling a bit short tempered today. Apologies, hope
your feeling better soon :)
>
> >> This is another point toward a fixed error chunk size and let parameter
> >> processing allocate it when it reaches a parameter that needs an error.
> >>
> > Hmm, ok, what would you say to a pathmtu sized chunk allocation in parameter
> > processing that drops errors beyond its capacity
> > Neil
>
> Here is my quick take on this. Haven't tested it at all.
>
I think somthing like this will work, I've got a variant that uses some helper
functions to create and manipulate fixed length op error chunks going right now.
It does basically the same thing that your doing, but consolidates the checking
of remaining space to a central place. I think that might be better, as during
my looking at this version, I found two other points that might be vulnerable to
this error (haven't tested to confirm yet though). I'll post shortly.
Thanks!
Neil
^ permalink raw reply
* [PATCH 5/5] sctp: Fix oops when sending queued ASCONF chunks
From: Vlad Yasevich @ 2010-04-28 18:47 UTC (permalink / raw)
To: netdev; +Cc: davem, linux-sctp, Vlad Yasevich, Yuansong Qiao, Shuaijun Zhang
In-Reply-To: <1272480442-32673-1-git-send-email-vladislav.yasevich@hp.com>
When we finish processing ASCONF_ACK chunk, we try to send
the next queued ASCONF. This action runs the sctp state
machine recursively and it's not prepared to do so.
kernel BUG at kernel/timer.c:790!
invalid opcode: 0000 [#1] SMP
last sysfs file: /sys/module/ipv6/initstate
Modules linked in: sha256_generic sctp libcrc32c ipv6 dm_multipath
uinput 8139too i2c_piix4 8139cp mii i2c_core pcspkr virtio_net joydev
floppy virtio_blk virtio_pci [last unloaded: scsi_wait_scan]
Pid: 0, comm: swapper Not tainted 2.6.34-rc4 #15 /Bochs
EIP: 0060:[<c044a2ef>] EFLAGS: 00010286 CPU: 0
EIP is at add_timer+0xd/0x1b
EAX: cecbab14 EBX: 000000f0 ECX: c0957b1c EDX: 03595cf4
ESI: cecba800 EDI: cf276f00 EBP: c0957aa0 ESP: c0957aa0
DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068
Process swapper (pid: 0, ti=c0956000 task=c0988ba0 task.ti=c0956000)
Stack:
c0957ae0 d1851214 c0ab62e4 c0ab5f26 0500ffff 00000004 00000005 00000004
<0> 00000000 d18694fd 00000004 1666b892 cecba800 cecba800 c0957b14
00000004
<0> c0957b94 d1851b11 ceda8b00 cecba800 cf276f00 00000001 c0957b14
000000d0
Call Trace:
[<d1851214>] ? sctp_side_effects+0x607/0xdfc [sctp]
[<d1851b11>] ? sctp_do_sm+0x108/0x159 [sctp]
[<d1863386>] ? sctp_pname+0x0/0x1d [sctp]
[<d1861a56>] ? sctp_primitive_ASCONF+0x36/0x3b [sctp]
[<d185657c>] ? sctp_process_asconf_ack+0x2a4/0x2d3 [sctp]
[<d184e35c>] ? sctp_sf_do_asconf_ack+0x1dd/0x2b4 [sctp]
[<d1851ac1>] ? sctp_do_sm+0xb8/0x159 [sctp]
[<d1863334>] ? sctp_cname+0x0/0x52 [sctp]
[<d1854377>] ? sctp_assoc_bh_rcv+0xac/0xe1 [sctp]
[<d1858f0f>] ? sctp_inq_push+0x2d/0x30 [sctp]
[<d186329d>] ? sctp_rcv+0x797/0x82e [sctp]
Tested-by: Wei Yongjun <yjwei@cn.fujitsu.com>
Signed-off-by: Yuansong Qiao <ysqiao@research.ait.ie>
Signed-off-by: Shuaijun Zhang <szhang@research.ait.ie>
Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
---
include/net/sctp/command.h | 1 +
net/sctp/sm_make_chunk.c | 15 ---------------
net/sctp/sm_sideeffect.c | 26 ++++++++++++++++++++++++++
net/sctp/sm_statefuns.c | 8 +++++++-
4 files changed, 34 insertions(+), 16 deletions(-)
diff --git a/include/net/sctp/command.h b/include/net/sctp/command.h
index 8be5135..2c55a7e 100644
--- a/include/net/sctp/command.h
+++ b/include/net/sctp/command.h
@@ -107,6 +107,7 @@ typedef enum {
SCTP_CMD_T1_RETRAN, /* Mark for retransmission after T1 timeout */
SCTP_CMD_UPDATE_INITTAG, /* Update peer inittag */
SCTP_CMD_SEND_MSG, /* Send the whole use message */
+ SCTP_CMD_SEND_NEXT_ASCONF, /* Send the next ASCONF after ACK */
SCTP_CMD_LAST
} sctp_verb_t;
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index f6fc5c1..0fd5b4c 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -3318,21 +3318,6 @@ int sctp_process_asconf_ack(struct sctp_association *asoc,
sctp_chunk_free(asconf);
asoc->addip_last_asconf = NULL;
- /* Send the next asconf chunk from the addip chunk queue. */
- if (!list_empty(&asoc->addip_chunk_list)) {
- struct list_head *entry = asoc->addip_chunk_list.next;
- asconf = list_entry(entry, struct sctp_chunk, list);
-
- list_del_init(entry);
-
- /* Hold the chunk until an ASCONF_ACK is received. */
- sctp_chunk_hold(asconf);
- if (sctp_primitive_ASCONF(asoc, asconf))
- sctp_chunk_free(asconf);
- else
- asoc->addip_last_asconf = asconf;
- }
-
return retval;
}
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index 4c5bed9..d5ae450 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -962,6 +962,29 @@ static int sctp_cmd_send_msg(struct sctp_association *asoc,
}
+/* Sent the next ASCONF packet currently stored in the association.
+ * This happens after the ASCONF_ACK was succeffully processed.
+ */
+static void sctp_cmd_send_asconf(struct sctp_association *asoc)
+{
+ /* Send the next asconf chunk from the addip chunk
+ * queue.
+ */
+ if (!list_empty(&asoc->addip_chunk_list)) {
+ struct list_head *entry = asoc->addip_chunk_list.next;
+ struct sctp_chunk *asconf = list_entry(entry,
+ struct sctp_chunk, list);
+ list_del_init(entry);
+
+ /* Hold the chunk until an ASCONF_ACK is received. */
+ sctp_chunk_hold(asconf);
+ if (sctp_primitive_ASCONF(asoc, asconf))
+ sctp_chunk_free(asconf);
+ else
+ asoc->addip_last_asconf = asconf;
+ }
+}
+
/* These three macros allow us to pull the debugging code out of the
* main flow of sctp_do_sm() to keep attention focused on the real
@@ -1617,6 +1640,9 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
}
error = sctp_cmd_send_msg(asoc, cmd->obj.msg);
break;
+ case SCTP_CMD_SEND_NEXT_ASCONF:
+ sctp_cmd_send_asconf(asoc);
+ break;
default:
printk(KERN_WARNING "Impossible command: %u, %p\n",
cmd->verb, cmd->obj.ptr);
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index abf601a..24b2cd5 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -3676,8 +3676,14 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep,
SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO));
if (!sctp_process_asconf_ack((struct sctp_association *)asoc,
- asconf_ack))
+ asconf_ack)) {
+ /* Successfully processed ASCONF_ACK. We can
+ * release the next asconf if we have one.
+ */
+ sctp_add_cmd_sf(commands, SCTP_CMD_SEND_NEXT_ASCONF,
+ SCTP_NULL());
return SCTP_DISPOSITION_CONSUME;
+ }
abort = sctp_make_abort(asoc, asconf_ack,
sizeof(sctp_errhdr_t));
--
1.6.0.4
^ permalink raw reply related
* [PATCH 4/5] sctp: fix to calc the INIT/INIT-ACK chunk length correctly is set
From: Vlad Yasevich @ 2010-04-28 18:47 UTC (permalink / raw)
To: netdev; +Cc: davem, linux-sctp, Wei Yongjun, Vlad Yasevich
In-Reply-To: <1272480442-32673-1-git-send-email-vladislav.yasevich@hp.com>
From: Wei Yongjun <yjwei@cn.fujitsu.com>
When calculating the INIT/INIT-ACK chunk length, we should not
only account the length of parameters, but also the parameters
zero padding length, such as AUTH HMACS parameter and CHUNKS
parameter. Without the parameters zero padding length we may get
following oops.
skb_over_panic: text:ce2068d2 len:130 put:6 head:cac3fe00 data:cac3fe00 tail:0xcac3fe82 end:0xcac3fe80 dev:<NULL>
------------[ cut here ]------------
kernel BUG at net/core/skbuff.c:127!
invalid opcode: 0000 [#2] SMP
last sysfs file: /sys/module/aes_generic/initstate
Modules linked in: authenc ......
Pid: 4102, comm: sctp_darn Tainted: G D 2.6.34-rc2 #6
EIP: 0060:[<c0607630>] EFLAGS: 00010282 CPU: 0
EIP is at skb_over_panic+0x37/0x3e
EAX: 00000078 EBX: c07c024b ECX: c07c02b9 EDX: cb607b78
ESI: 00000000 EDI: cac3fe7a EBP: 00000002 ESP: cb607b74
DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068
Process sctp_darn (pid: 4102, ti=cb607000 task=cabdc990 task.ti=cb607000)
Stack:
c07c02b9 ce2068d2 00000082 00000006 cac3fe00 cac3fe00 cac3fe82 cac3fe80
<0> c07c024b cac3fe7c cac3fe7a c0608dec ca986e80 ce2068d2 00000006 0000007a
<0> cb8120ca ca986e80 cb812000 00000003 cb8120c4 ce208a25 cb8120ca cadd9400
Call Trace:
[<ce2068d2>] ? sctp_addto_chunk+0x45/0x85 [sctp]
[<c0608dec>] ? skb_put+0x2e/0x32
[<ce2068d2>] ? sctp_addto_chunk+0x45/0x85 [sctp]
[<ce208a25>] ? sctp_make_init+0x279/0x28c [sctp]
[<c0686a92>] ? apic_timer_interrupt+0x2a/0x30
[<ce1fdc0b>] ? sctp_sf_do_prm_asoc+0x2b/0x7b [sctp]
[<ce202823>] ? sctp_do_sm+0xa0/0x14a [sctp]
[<ce2133b9>] ? sctp_pname+0x0/0x14 [sctp]
[<ce211d72>] ? sctp_primitive_ASSOCIATE+0x2b/0x31 [sctp]
[<ce20f3cf>] ? sctp_sendmsg+0x7a0/0x9eb [sctp]
[<c064eb1e>] ? inet_sendmsg+0x3b/0x43
[<c04244b7>] ? task_tick_fair+0x2d/0xd9
[<c06031e1>] ? sock_sendmsg+0xa7/0xc1
[<c0416afe>] ? smp_apic_timer_interrupt+0x6b/0x75
[<c0425123>] ? dequeue_task_fair+0x34/0x19b
[<c0446abb>] ? sched_clock_local+0x17/0x11e
[<c052ea87>] ? _copy_from_user+0x2b/0x10c
[<c060ab3a>] ? verify_iovec+0x3c/0x6a
[<c06035ca>] ? sys_sendmsg+0x186/0x1e2
[<c042176b>] ? __wake_up_common+0x34/0x5b
[<c04240c2>] ? __wake_up+0x2c/0x3b
[<c057e35c>] ? tty_wakeup+0x43/0x47
[<c04430f2>] ? remove_wait_queue+0x16/0x24
[<c0580c94>] ? n_tty_read+0x5b8/0x65e
[<c042be02>] ? default_wake_function+0x0/0x8
[<c0604e0e>] ? sys_socketcall+0x17f/0x1cd
[<c040264c>] ? sysenter_do_call+0x12/0x22
Code: 0f 45 de 53 ff b0 98 00 00 00 ff b0 94 ......
EIP: [<c0607630>] skb_over_panic+0x37/0x3e SS:ESP 0068:cb607b74
To reproduce:
# modprobe sctp
# echo 1 > /proc/sys/net/sctp/addip_enable
# echo 1 > /proc/sys/net/sctp/auth_enable
# sctp_test -H 3ffe:501:ffff:100:20c:29ff:fe4d:f37e -P 800 -l
# sctp_darn -H 3ffe:501:ffff:100:20c:29ff:fe4d:f37e -P 900 -h 192.168.0.21 -p 800 -I -s -t
sctp_darn ready to send...
3ffe:501:ffff:100:20c:29ff:fe4d:f37e:900-192.168.0.21:800 Interactive mode> bindx-add=192.168.0.21
3ffe:501:ffff:100:20c:29ff:fe4d:f37e:900-192.168.0.21:800 Interactive mode> bindx-add=192.168.1.21
3ffe:501:ffff:100:20c:29ff:fe4d:f37e:900-192.168.0.21:800 Interactive mode> snd=10
------------------------------------------------------------------
eth0 has addresses: 3ffe:501:ffff:100:20c:29ff:fe4d:f37e and 192.168.0.21
eth1 has addresses: 192.168.1.21
------------------------------------------------------------------
Reported-by: George Cheimonidis <gchimon@gmail.com>
Signed-off-by: Wei Yongjun <yjwei@cn.fujitsu.com>
Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
---
net/sctp/sm_make_chunk.c | 17 ++++++++++-------
1 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 17cb400..f6fc5c1 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -208,7 +208,8 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc,
sp = sctp_sk(asoc->base.sk);
num_types = sp->pf->supported_addrs(sp, types);
- chunksize = sizeof(init) + addrs_len + SCTP_SAT_LEN(num_types);
+ chunksize = sizeof(init) + addrs_len;
+ chunksize += WORD_ROUND(SCTP_SAT_LEN(num_types));
chunksize += sizeof(ecap_param);
if (sctp_prsctp_enable)
@@ -238,14 +239,14 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc,
/* Add HMACS parameter length if any were defined */
auth_hmacs = (sctp_paramhdr_t *)asoc->c.auth_hmacs;
if (auth_hmacs->length)
- chunksize += ntohs(auth_hmacs->length);
+ chunksize += WORD_ROUND(ntohs(auth_hmacs->length));
else
auth_hmacs = NULL;
/* Add CHUNKS parameter length */
auth_chunks = (sctp_paramhdr_t *)asoc->c.auth_chunks;
if (auth_chunks->length)
- chunksize += ntohs(auth_chunks->length);
+ chunksize += WORD_ROUND(ntohs(auth_chunks->length));
else
auth_chunks = NULL;
@@ -255,7 +256,8 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc,
/* If we have any extensions to report, account for that */
if (num_ext)
- chunksize += sizeof(sctp_supported_ext_param_t) + num_ext;
+ chunksize += WORD_ROUND(sizeof(sctp_supported_ext_param_t) +
+ num_ext);
/* RFC 2960 3.3.2 Initiation (INIT) (1)
*
@@ -397,13 +399,13 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc,
auth_hmacs = (sctp_paramhdr_t *)asoc->c.auth_hmacs;
if (auth_hmacs->length)
- chunksize += ntohs(auth_hmacs->length);
+ chunksize += WORD_ROUND(ntohs(auth_hmacs->length));
else
auth_hmacs = NULL;
auth_chunks = (sctp_paramhdr_t *)asoc->c.auth_chunks;
if (auth_chunks->length)
- chunksize += ntohs(auth_chunks->length);
+ chunksize += WORD_ROUND(ntohs(auth_chunks->length));
else
auth_chunks = NULL;
@@ -412,7 +414,8 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc,
}
if (num_ext)
- chunksize += sizeof(sctp_supported_ext_param_t) + num_ext;
+ chunksize += WORD_ROUND(sizeof(sctp_supported_ext_param_t) +
+ num_ext);
/* Now allocate and fill out the chunk. */
retval = sctp_make_chunk(asoc, SCTP_CID_INIT_ACK, 0, chunksize);
--
1.6.0.4
^ permalink raw reply related
* [PATCH 3/5] sctp: per_cpu variables should be in bh_disabled section
From: Vlad Yasevich @ 2010-04-28 18:47 UTC (permalink / raw)
To: netdev; +Cc: davem, linux-sctp, Vlad Yasevich
In-Reply-To: <1272480442-32673-1-git-send-email-vladislav.yasevich@hp.com>
Since the change of the atomics to percpu variables, we now
have to disable BH in process context when touching percpu variables.
Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
---
net/sctp/socket.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index efa2bc3..44a1ab0 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -3719,12 +3719,12 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
sp->hmac = NULL;
SCTP_DBG_OBJCNT_INC(sock);
- percpu_counter_inc(&sctp_sockets_allocated);
/* Set socket backlog limit. */
sk->sk_backlog.limit = sysctl_sctp_rmem[1];
local_bh_disable();
+ percpu_counter_inc(&sctp_sockets_allocated);
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
local_bh_enable();
@@ -3741,8 +3741,8 @@ SCTP_STATIC void sctp_destroy_sock(struct sock *sk)
/* Release our hold on the endpoint. */
ep = sctp_sk(sk)->ep;
sctp_endpoint_free(ep);
- percpu_counter_dec(&sctp_sockets_allocated);
local_bh_disable();
+ percpu_counter_dec(&sctp_sockets_allocated);
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
local_bh_enable();
}
--
1.6.0.4
^ permalink raw reply related
* [PATCH 1/5] sctp: avoid irq lock inversion while call sk->sk_data_ready()
From: Vlad Yasevich @ 2010-04-28 18:47 UTC (permalink / raw)
To: netdev; +Cc: davem, linux-sctp, Wei Yongjun, Vlad Yasevich
In-Reply-To: <1272480442-32673-1-git-send-email-vladislav.yasevich@hp.com>
From: Wei Yongjun <yjwei@cn.fujitsu.com>
sk->sk_data_ready() of sctp socket can be called from both BH and non-BH
contexts, but the default sk->sk_data_ready(), sock_def_readable(), can
not be used in this case. Therefore, we have to make a new function
sctp_data_ready() to grab sk->sk_data_ready() with BH disabling.
=========================================================
[ INFO: possible irq lock inversion dependency detected ]
2.6.33-rc6 #129
---------------------------------------------------------
sctp_darn/1517 just changed the state of lock:
(clock-AF_INET){++.?..}, at: [<c06aab60>] sock_def_readable+0x20/0x80
but this lock took another, SOFTIRQ-unsafe lock in the past:
(slock-AF_INET){+.-...}
and interrupts could create inverse lock ordering between them.
other info that might help us debug this:
1 lock held by sctp_darn/1517:
#0: (sk_lock-AF_INET){+.+.+.}, at: [<cdfe363d>] sctp_sendmsg+0x23d/0xc00 [sctp]
Signed-off-by: Wei Yongjun <yjwei@cn.fujitsu.com>
Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
---
include/net/sctp/sctp.h | 1 +
net/sctp/endpointola.c | 1 +
net/sctp/socket.c | 10 ++++++++++
3 files changed, 12 insertions(+), 0 deletions(-)
diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h
index 78740ec..fa6cde5 100644
--- a/include/net/sctp/sctp.h
+++ b/include/net/sctp/sctp.h
@@ -128,6 +128,7 @@ extern int sctp_register_pf(struct sctp_pf *, sa_family_t);
int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb);
int sctp_inet_listen(struct socket *sock, int backlog);
void sctp_write_space(struct sock *sk);
+void sctp_data_ready(struct sock *sk, int len);
unsigned int sctp_poll(struct file *file, struct socket *sock,
poll_table *wait);
void sctp_sock_rfree(struct sk_buff *skb);
diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c
index 905fda5..7ec09ba 100644
--- a/net/sctp/endpointola.c
+++ b/net/sctp/endpointola.c
@@ -144,6 +144,7 @@ static struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep,
/* Use SCTP specific send buffer space queues. */
ep->sndbuf_policy = sctp_sndbuf_policy;
+ sk->sk_data_ready = sctp_data_ready;
sk->sk_write_space = sctp_write_space;
sock_set_flag(sk, SOCK_USE_WRITE_QUEUE);
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 007e8ba..efa2bc3 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -6189,6 +6189,16 @@ do_nonblock:
goto out;
}
+void sctp_data_ready(struct sock *sk, int len)
+{
+ read_lock_bh(&sk->sk_callback_lock);
+ if (sk_has_sleeper(sk))
+ wake_up_interruptible_sync_poll(sk->sk_sleep, POLLIN |
+ POLLRDNORM | POLLRDBAND);
+ sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN);
+ read_unlock_bh(&sk->sk_callback_lock);
+}
+
/* If socket sndbuf has changed, wake up all per association waiters. */
void sctp_write_space(struct sock *sk)
{
--
1.6.0.4
^ permalink raw reply related
* [PATCH 2/5] sctp: fix potential reference of a freed pointer
From: Vlad Yasevich @ 2010-04-28 18:47 UTC (permalink / raw)
To: netdev; +Cc: davem, linux-sctp, Vlad Yasevich
In-Reply-To: <1272480442-32673-1-git-send-email-vladislav.yasevich@hp.com>
When sctp attempts to update an assocition, it removes any
addresses that were not in the updated INITs. However, the loop
may attempt to refrence a transport with address after removing it.
Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
---
net/sctp/associola.c | 6 ++++--
1 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index df5abbf..99c93ee 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -1194,8 +1194,10 @@ void sctp_assoc_update(struct sctp_association *asoc,
/* Remove any peer addresses not present in the new association. */
list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) {
trans = list_entry(pos, struct sctp_transport, transports);
- if (!sctp_assoc_lookup_paddr(new, &trans->ipaddr))
- sctp_assoc_del_peer(asoc, &trans->ipaddr);
+ if (!sctp_assoc_lookup_paddr(new, &trans->ipaddr)) {
+ sctp_assoc_rm_peer(asoc, trans);
+ continue;
+ }
if (asoc->state >= SCTP_STATE_ESTABLISHED)
sctp_transport_reset(trans);
--
1.6.0.4
^ permalink raw reply related
* sctp patches for net-2.6
From: Vlad Yasevich @ 2010-04-28 18:47 UTC (permalink / raw)
To: netdev; +Cc: davem, linux-sctp
Hi David
The following are the patches for the current net-2.6 tree that
solve some critical issues. Please consider pushing them to
stable as well.
Thanks
-vlad
^ permalink raw reply
* Re: [net-next-2.6 PATCH 2/2] add ndo_set_port_profile op support for enic dynamic vnics
From: Scott Feldman @ 2010-04-28 18:39 UTC (permalink / raw)
To: Arnd Bergmann; +Cc: davem, netdev, chrisw
In-Reply-To: <201004281532.47875.arnd@arndb.de>
On 4/28/10 6:32 AM, "Arnd Bergmann" <arnd@arndb.de> wrote:
> On Wednesday 28 April 2010, Scott Feldman wrote:
>> +static int enic_set_port_profile(struct net_device *netdev,
>> + struct ifla_port_profile *ipp)
>> +{
>> + struct enic *enic = netdev_priv(netdev);
>> + struct vic_provinfo *vp;
>> + u8 oui[3] = VIC_PROVINFO_CISCO_OUI;
>> + u8 *mac = ipp->mac;
>> + int err;
>> +
>> + memset(&enic->port_profile, 0, sizeof(enic->port_profile));
>> +
>> + if (!enic_is_dynamic(enic))
>> + return -EOPNOTSUPP;
>
> Not sure I understand how this fits together. You said in an earlier mail:
>
>>> Anything that ties port profiles to VFs seems fundamentally flawed AFAICT,
>>> at least when we want to extend this to adapters that don't do it in
>>> firmware.
>>
>> Ya, I tend I agree. Let's just make port-profile a setting of any netdev,
>> an eth, macvtap, eth.x, bond, etc. That's probably what I should have done
>> in the first place. Something like:
>
> I thought you had meant that we can do the association of attached interfaces
> through any interface, rather than tying it to the slave interface. While I'm
> not sure I read your code correctly, it seems like you now only talk to the
> slave interface, not to the master at all!
>
> At least the check above should be 'if (enic_is_dynamic(enic)) return
> -EOPNOTSUPP', not the other way round.
> Moreover, if the netdev is the master here, you only allow a single slave,
> which is not enough for larger setups (n > 1), though that could be a
> limitation of your first version.
The code is correct. I probably confused you with earlier patches trying to
accommodate master/slave devices and you might have assumed enic was such a
device. But it's not. For enic, there are two device IDs, let's call one
"static" and the other "dynamic". The only difference between the two is
static enics load up fully ready to go just like a normal nic, whereas
dynamic enics load up but can't yet pass traffic because they're not
"plugged in" to the network. To plug them in, you need to associate a
port-profile. The physical analogy is this: server admin tells network
admin: plug my nic into a switch port with these characteristics. Here, the
port-profile describes those switch port characteristics. Now, there is no
master/slave relationship between static and dynamic enics. There could be
with a simple firmware update, but it's not there today. Also, I want to
point out that a single phys Cisco nic can be provisioned to expose many
static and/or many dynamic enics to the host. On the order of 100s. The
code above is to block port-profile association on static enics. Static
enics where already provisioned on the network when created so there is no
need for a port-profile push from the host.
> Passing just the slave device however would not work in the general case, as I
> tried to point out in the mail you replied to. If the slave interface is owned
> by a guest using PCI passthrough, or it sits below a stack of nested
> interfaces
> (vlan, bridge, tap, vhost, ...), it's impossible to know what interface is
> responsible for setting up the slave.
For port-profile, we want to pass the device that is to be "plugged-in" to
the network based on port-profile association. This is the device that
gives basic connectivity to the guest interface, regardless of how the guest
interface is wired to the device. It could be direct PCI pass-thru, macvtap
stack, some yet-to-be-invented kernel-bypass stack, etc.
> Note that you cannot perform the association
> through the slave interface itself because the remote switch would discard any
> traffic originating from an unassociated interface.
That's not a limitation of our device/switch.
-scott
^ permalink raw reply
* Re: [PATCH]: sctp: Fix skb_over_panic resulting from multiple invalid parameter errors (CVE-2010-1173)
From: Vlad Yasevich @ 2010-04-28 18:27 UTC (permalink / raw)
To: Neil Horman; +Cc: sri, linux-sctp, eteo, netdev, davem, security
In-Reply-To: <20100428181645.GD4818@hmsreliant.think-freely.org>
[-- Attachment #1: Type: text/plain, Size: 2614 bytes --]
Neil Horman wrote:
> On Wed, Apr 28, 2010 at 01:52:05PM -0400, Vlad Yasevich wrote:
>>
>> Vlad Yasevich wrote:
>>> Neil Horman wrote:
>>>> On Wed, Apr 28, 2010 at 10:00:37AM -0400, Vlad Yasevich wrote:
>>>>> I have this patch and a few others already queued.
>>>>>
>>>>> I was planning on sending these today for stable.
>>>>>
>>>>> Here is the full list of stable patches I have:
>>>>>
>>>>> sctp: Fix oops when sending queued ASCONF chunks
>>>>> sctp: fix to calc the INIT/INIT-ACK chunk length correctly is set
>>>>> sctp: per_cpu variables should be in bh_disabled section
>>>>> sctp: fix potential reference of a freed pointer
>>>>> sctp: avoid irq lock inversion while call sk->sk_data_ready()
>>>>>
>>>>> -vlad
>>>>>
>>>> Are you sure? this oops looks _very_ simmilar to the INIT/INIT-ACK length
>>>> calculation oops described above, but is in fact different, and requires this
>>>> patch, from what I can see. The right fix might be in the ASCONF chunk patch
>>>> you list above, but I don't see that in your tree at the moment, so I can't be
>>>> sure.
>>> As I said, I totally goofed when reading the description and I apologize.
>>> However, I do one comment regarding the patch.
>>>
>>> If the bad packet is REALLY long (I mean close to 65K IP limit), then
>>> we'll end up allocating a supper huge skb in this case and potentially exceed
>>> the IP length limitation. Section 11.4 of rfc 4960 allows us to omit some
>>> errors and limit the size of the packet.
>>>
>>> I would recommend limiting this to MTU worth of potentiall errors. This is
>>> on top of what the INIT-ACK is going to carry, so at most we'll sent 2 MTUs
>>> worth. That's still a potential by amplification attack, but it's somewhat
>>> mitigated.
>>>
>>> Of course now we have to handle the case of checking for space before adding
>>> an error cause. :)
>>>
>> Hi Neil
>>
>> I am also not crazy about the pre-allocation scheme. In the case where you have
>> say 100 parameters that are all 'skip' parameters, you'd end up pre-allocating a
>> huge buffer for absolutely nothing.
>>
> Would have been nice if you'd made your opinion known 4 hours ago when I was
> testing version 2 of this. :)
>
sorry, fighting a head cold and need drugs to think clearly... ;)
>> This is another point toward a fixed error chunk size and let parameter
>> processing allocate it when it reaches a parameter that needs an error.
>>
> Hmm, ok, what would you say to a pathmtu sized chunk allocation in parameter
> processing that drops errors beyond its capacity
> Neil
Here is my quick take on this. Haven't tested it at all.
-vlad
[-- Attachment #2: neil --]
[-- Type: text/plain, Size: 3987 bytes --]
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 0fd5b4c..74d8d21 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -1959,8 +1959,10 @@ static void sctp_process_ext_param(struct sctp_association *asoc,
static sctp_ierror_t sctp_process_unk_param(const struct sctp_association *asoc,
union sctp_params param,
struct sctp_chunk *chunk,
- struct sctp_chunk **errp)
+ struct sctp_chunk **errp,
+ unsigned int param_cnt)
{
+ unsigned int needed_bytes;
int retval = SCTP_IERROR_NO_ERROR;
switch (param.p->type & SCTP_PARAM_ACTION_MASK) {
@@ -1976,11 +1978,41 @@ static sctp_ierror_t sctp_process_unk_param(const struct sctp_association *asoc,
/* Make an ERROR chunk, preparing enough room for
* returning multiple unknown parameters.
*/
- if (NULL == *errp)
- *errp = sctp_make_op_error_space(asoc, chunk,
- ntohs(chunk->chunk_hdr->length));
+ if (NULL == *errp) {
+ unsigned int len;
+
+ /* Reserver space for the worst possible case
+ * at this time. We count incomming chunk length
+ * since error parameters carry the bad parameter
+ * inself, plus have space for error headers for
+ * the remaining number of parameters.
+ */
+ len = ntohs(chunk->chunk_hdr->length);
+ len += sizeof(sctp_errhdr_t) * param_cnt;
+
+ /* We need to prevent amplification attacks that
+ * result from sending 65K init chunks with all bad
+ * params maliciously, so lets limit our error response
+ * to 1 MTU worth of errors, but at least 1500 bytes
+ * in case our pathmtu hasn't been updated yet.
+ */
+ len = min(len, asoc ? asoc->pathmtu :
+ SCTP_DEFAULT_MAXSEGMENT);
+ *errp = sctp_make_op_error_space(asoc, chunk, len);
+ }
if (*errp) {
+ needed_bytes = sizeof(sctp_errhdr_t) +
+ WORD_ROUND(ntohs(param.p->length));
+
+ if (skb_tailroom((*errp)->skb) < needed_bytes)
+ /*
+ * If we're over our packet size here, don't add
+ * the error, this is allowed by section 11.4 of
+ * RFC 4960
+ */
+ break;
+
sctp_init_cause(*errp, SCTP_ERROR_UNKNOWN_PARAM,
WORD_ROUND(ntohs(param.p->length)));
sctp_addto_chunk(*errp,
@@ -2013,7 +2045,8 @@ static sctp_ierror_t sctp_verify_param(const struct sctp_association *asoc,
union sctp_params param,
sctp_cid_t cid,
struct sctp_chunk *chunk,
- struct sctp_chunk **err_chunk)
+ struct sctp_chunk **err_chunk,
+ unsigned int param_cnt)
{
struct sctp_hmac_algo_param *hmacs;
int retval = SCTP_IERROR_NO_ERROR;
@@ -2119,7 +2152,8 @@ fallthrough:
default:
SCTP_DEBUG_PRINTK("Unrecognized param: %d for chunk %d.\n",
ntohs(param.p->type), cid);
- retval = sctp_process_unk_param(asoc, param, chunk, err_chunk);
+ retval = sctp_process_unk_param(asoc, param, chunk, err_chunk,
+ param_cnt);
break;
}
return retval;
@@ -2135,6 +2169,7 @@ int sctp_verify_init(const struct sctp_association *asoc,
union sctp_params param;
int has_cookie = 0;
int result;
+ unsigned int param_cnt = 0;
/* Verify stream values are non-zero. */
if ((0 == peer_init->init_hdr.num_outbound_streams) ||
@@ -2150,6 +2185,7 @@ int sctp_verify_init(const struct sctp_association *asoc,
if (SCTP_PARAM_STATE_COOKIE == param.p->type)
has_cookie = 1;
+ param_cnt++;
} /* for (loop through all parameters) */
@@ -2173,7 +2209,8 @@ int sctp_verify_init(const struct sctp_association *asoc,
/* Verify all the variable length parameters */
sctp_walk_params(param, peer_init, init_hdr.params) {
- result = sctp_verify_param(asoc, param, cid, chunk, errp);
+ result = sctp_verify_param(asoc, param, cid, chunk, errp,
+ param_cnt);
switch (result) {
case SCTP_IERROR_ABORT:
case SCTP_IERROR_NOMEM:
@@ -2184,6 +2221,7 @@ int sctp_verify_init(const struct sctp_association *asoc,
default:
break;
}
+ param_cnt--;
} /* for (loop through all parameters) */
^ permalink raw reply related
* Re: [PATCH] forcedeth: Stay in NAPI as long as there's work
From: Stephen Hemminger @ 2010-04-28 18:25 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: Joe Perches, Tom Herbert, netdev, aabdulla, davem
In-Reply-To: <20100428111354.49a6ec1a@nehalam>
The following does the same thing without the extra overhead
of testing all the registers. It also handles the out of memory
case.
Compile tested only...
Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
--- a/drivers/net/forcedeth.c 2010-04-28 11:14:29.784799317 -0700
+++ b/drivers/net/forcedeth.c 2010-04-28 11:23:53.254354744 -0700
@@ -3743,23 +3743,26 @@ static int nv_napi_poll(struct napi_stru
u8 __iomem *base = get_hwbase(dev);
unsigned long flags;
int retcode;
- int tx_work, rx_work;
+ int rx_count, tx_work=0, rx_work=0;
- if (!nv_optimized(np)) {
- spin_lock_irqsave(&np->lock, flags);
- tx_work = nv_tx_done(dev, np->tx_ring_size);
- spin_unlock_irqrestore(&np->lock, flags);
+ do {
+ if (!nv_optimized(np)) {
+ spin_lock_irqsave(&np->lock, flags);
+ tx_work += nv_tx_done(dev, np->tx_ring_size);
+ spin_unlock_irqrestore(&np->lock, flags);
- rx_work = nv_rx_process(dev, budget);
- retcode = nv_alloc_rx(dev);
- } else {
- spin_lock_irqsave(&np->lock, flags);
- tx_work = nv_tx_done_optimized(dev, np->tx_ring_size);
- spin_unlock_irqrestore(&np->lock, flags);
+ rx_count = nv_rx_process(dev, budget);
+ retcode = nv_alloc_rx(dev);
+ } else {
+ spin_lock_irqsave(&np->lock, flags);
+ tx_work += nv_tx_done_optimized(dev, np->tx_ring_size);
+ spin_unlock_irqrestore(&np->lock, flags);
- rx_work = nv_rx_process_optimized(dev, budget);
- retcode = nv_alloc_rx_optimized(dev);
- }
+ rx_count = nv_rx_process_optimized(dev, budget);
+ retcode = nv_alloc_rx_optimized(dev);
+ }
+ } while (retcode == 0 &&
+ rx_count > 0 && (rx_work += rx_count) < budget);
if (retcode) {
spin_lock_irqsave(&np->lock, flags);
^ permalink raw reply
* Re: [PATCH] forcedeth: Stay in NAPI as long as there's work
From: Tom Herbert @ 2010-04-28 18:18 UTC (permalink / raw)
To: Joe Perches; +Cc: netdev, aabdulla, davem
In-Reply-To: <g2t65634d661004281107j676369c9ge379434723478c1@mail.gmail.com>
On Wed, Apr 28, 2010 at 11:07 AM, Tom Herbert <therbert@google.com> wrote:
>>
>> It might be better to test the comparisons using
>> a cpu_to_le32 of the constants.
>>
> Yes. Probably should also be changed in nv_tx_done{_optimized} and
> nv_rx_process{_optimized}
>
Scratch that. flags are checked all over the place in those other functions.
>> static inline int nv_has_work(struct fe_priv *np)
>> {
>> if (nv_optimized(np))
>> return ((np->get_rx.ex != np->put_rx.ex) &&
>> !(np->get_rx.ex->flaglen & cpu_to_le32(NV_RX2_AVAIL))) ||
>> ((np->get_tx.ex != np->put_tx.ex) &&
>> !(np->get_tx.ex->flaglen & cpu_to_le32(NV_TX_VALID)));
>>
>> return ((np->get_rx.orig != np->put_rx.orig) &&
>> !(np->get_rx.orig->flaglen & cpu_to_le32(NV_RX_AVAIL))) ||
>> ((np->get_tx.orig != np->put_tx.orig) &&
>> !(np->get_tx.orig->flaglen & cpu_to_le32(NV_TX_VALID)));
>> }
>>
>>
>>
>
^ permalink raw reply
* Re: [PATCH]: sctp: Fix skb_over_panic resulting from multiple invalid parameter errors (CVE-2010-1173)
From: Neil Horman @ 2010-04-28 18:16 UTC (permalink / raw)
To: Vlad Yasevich; +Cc: sri, linux-sctp, eteo, netdev, davem, security
In-Reply-To: <4BD875C5.9000907@hp.com>
On Wed, Apr 28, 2010 at 01:52:05PM -0400, Vlad Yasevich wrote:
>
>
> Vlad Yasevich wrote:
> >
> > Neil Horman wrote:
> >> On Wed, Apr 28, 2010 at 10:00:37AM -0400, Vlad Yasevich wrote:
> >>> I have this patch and a few others already queued.
> >>>
> >>> I was planning on sending these today for stable.
> >>>
> >>> Here is the full list of stable patches I have:
> >>>
> >>> sctp: Fix oops when sending queued ASCONF chunks
> >>> sctp: fix to calc the INIT/INIT-ACK chunk length correctly is set
> >>> sctp: per_cpu variables should be in bh_disabled section
> >>> sctp: fix potential reference of a freed pointer
> >>> sctp: avoid irq lock inversion while call sk->sk_data_ready()
> >>>
> >>> -vlad
> >>>
> >> Are you sure? this oops looks _very_ simmilar to the INIT/INIT-ACK length
> >> calculation oops described above, but is in fact different, and requires this
> >> patch, from what I can see. The right fix might be in the ASCONF chunk patch
> >> you list above, but I don't see that in your tree at the moment, so I can't be
> >> sure.
> >
> > As I said, I totally goofed when reading the description and I apologize.
> > However, I do one comment regarding the patch.
> >
> > If the bad packet is REALLY long (I mean close to 65K IP limit), then
> > we'll end up allocating a supper huge skb in this case and potentially exceed
> > the IP length limitation. Section 11.4 of rfc 4960 allows us to omit some
> > errors and limit the size of the packet.
> >
> > I would recommend limiting this to MTU worth of potentiall errors. This is
> > on top of what the INIT-ACK is going to carry, so at most we'll sent 2 MTUs
> > worth. That's still a potential by amplification attack, but it's somewhat
> > mitigated.
> >
> > Of course now we have to handle the case of checking for space before adding
> > an error cause. :)
> >
>
> Hi Neil
>
> I am also not crazy about the pre-allocation scheme. In the case where you have
> say 100 parameters that are all 'skip' parameters, you'd end up pre-allocating a
> huge buffer for absolutely nothing.
>
Would have been nice if you'd made your opinion known 4 hours ago when I was
testing version 2 of this. :)
> This is another point toward a fixed error chunk size and let parameter
> processing allocate it when it reaches a parameter that needs an error.
>
Hmm, ok, what would you say to a pathmtu sized chunk allocation in parameter
processing that drops errors beyond its capacity
Neil
>
^ permalink raw reply
* Re: [PATCH] forcedeth: Stay in NAPI as long as there's work
From: Stephen Hemminger @ 2010-04-28 18:13 UTC (permalink / raw)
To: Joe Perches; +Cc: Tom Herbert, netdev, aabdulla, davem
In-Reply-To: <1272477251.18228.36.camel@Joe-Laptop.home>
On Wed, 28 Apr 2010 10:54:11 -0700
Joe Perches <joe@perches.com> wrote:
> On Tue, 2010-04-27 at 23:36 -0700, Tom Herbert wrote:
> > Add loop in NAPI poll routine to keep processing RX and TX as long as
> > there is more work to do. This is similar to what tg3 and some other
> > drivers do.
> > Signed-off-by: Tom Herbert <therbert@google.com>
> > ---
> > diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c
> > index a1c0e7b..1e4de7b 100644
> > --- a/drivers/net/forcedeth.c
> > +++ b/drivers/net/forcedeth.c
> > @@ -3736,6 +3736,23 @@ static irqreturn_t nv_nic_irq_tx(int foo, void *data)
> > }
> >
> > #ifdef CONFIG_FORCEDETH_NAPI
> > +static inline int nv_has_work(struct fe_priv *np)
> > +{
> > + if (nv_optimized(np)) {
> > + return (
> > + ((np->get_rx.ex != np->put_rx.ex) &&
> > + !(le32_to_cpu(np->get_rx.ex->flaglen) & NV_RX2_AVAIL)) ||
> > + ((np->get_tx.ex != np->put_tx.ex) &&
> > + !(le32_to_cpu(np->get_tx.ex->flaglen) & NV_TX_VALID)));
> > + } else {
> > + return (
> > + ((np->get_rx.orig != np->put_rx.orig) &&
> > + !(le32_to_cpu(np->get_rx.orig->flaglen) & NV_RX_AVAIL)) ||
> > + ((np->get_tx.orig != np->put_tx.orig) &&
> > + !(le32_to_cpu(np->get_tx.orig->flaglen) & NV_TX_VALID)));
> > + }
> > +}
Why than adding another check step, why not just keep going until
rx_done returns 0?
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox