Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH] can: flexcan: fix irq flooding by clearing all interrupt sources
From: Wolfgang Grandegger @ 2011-12-14 13:21 UTC (permalink / raw)
  To: Marc Kleine-Budde; +Cc: netdev, linux-can, Reuben Dowle, Lothar Waßmann
In-Reply-To: <4EE77BAE.5040702@grandegger.com>

Hi Marc,

On 12/13/2011 05:22 PM, Wolfgang Grandegger wrote:
> On 12/13/2011 01:59 PM, Marc Kleine-Budde wrote:
>> On 12/13/2011 01:53 PM, Wolfgang Grandegger wrote:
>>>> I got my MX35PDK board working and can confirm, that the patch works on
>>>> a i.mx35 as well. My testing also confirms, that the ESR TWRN_INT,
>>>> RWRN_INT, FLEXCAN_ESR_BOFF_INT do not function as documented. These
>>>> flags do show up once, together with ERR_INT, and then, after clearing,
>>>> never again. Obviously a bug in the Flexcan logic. From the feedback we
>>>> can say, that only the i.MX28 does behave differently (==correctly). All
>>>> other seem to work with the current code:
>>>>
>>>>  Flexcan on
>>>>  - i.mx25
>>>>  - i.mx35
>>>>  - i.mx53
>>>>  - P1010/P1020
>>>
>>> But unfortunately, state change reporting looks different with this patch :)
>>
>> Hmm - so not schedule for stable. What about your buf-off-handling, will
>> this change the reporting again?
> 
> Well, as it is a serious problem on i.MX28, I would schedule this patch
> for stable as well. The error and state change reporting is bogus on the
> Flexcan anyhow. Without this patch, I get "active->warning->passive" if
> I send a message with cable disconnect (no ack). With patch just
> "active->warning". That's the same behaviour
> as on the i.MX28, also with my new state and bus-off handling. See:

I now understand the difference. I "flexcan_irq" we have:

	/*
	 * schedule NAPI in case of:
	 * - rx IRQ
	 * - state change IRQ
	 * - bus error IRQ and bus error reporting is activated
	 */
	if ((reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) ||
	    (reg_esr & FLEXCAN_ESR_ERR_STATE) ||
	    flexcan_has_and_handle_berr(priv, reg_esr)) {

Without this patch, "reg_esr & FLEXCAN_ESR_ERR_STATE" is *always* true
because the RWRN_INT is never cleared. This means, *any* message is
scheduled. As a nice side effect, this patch fixes this bug a well.

Only because any message is scheduled, the state change to error passive
is recognized in flexcan_poll(). The state change to error passive is
not signaled by an extra interrupt and is therefore only visible
together with RX, TX done or bus-error events.

Wolfgang.


^ permalink raw reply

* Re: kenel level packet capturing
From: Jan Engelhardt @ 2011-12-14 13:31 UTC (permalink / raw)
  To: raviraj joshi; +Cc: David Miller, netdev, netfilter-devel, netfilter
In-Reply-To: <CAKj1SsNO_uJuVtKs_-tygcd=QDesKkYa=9DVeajhK73LFV-opA@mail.gmail.com>


On Wednesday 2011-12-14 11:46, raviraj joshi wrote:
>On Wed, Dec 14, 2011 at 12:06 AM, David Miller wrote:
>>
>>>We have decided to use PF_RING(a kernel module to capture packets)
>>>for the same due to the number of advantages.
>>
>>What "advantages"?  The AF_PACKET socket layer already upstream in
>>the kernel supports every relevant performance feature PF_RING
>>does, and then some.
>
>I refered to the document on "A Measurement Study of Packet
>Reception using Linux"[1] which said pf_ring maintains a ring
>buffer, so we dont have to issue a receive system call for each
>packet in contrast to AF_PACKET which issues a system call for each
>packet(pls correct me if i am wrong).

AF_PACKET has had mmap support for a loooong time. The initial code
traces back to 2.3.15pre3 (1999-Aug-23).
So much for $academia.

>[1]
>http://www.cs.wustl.edu/~jain/cse567-11/ftp/pkt_recp/index.html
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH net-next] net: ping: remove some sparse errors
From: Eric Dumazet @ 2011-12-14 13:51 UTC (permalink / raw)
  To: David Miller; +Cc: netdev

net/ipv4/sysctl_net_ipv4.c:78:6: warning: symbol 'inet_get_ping_group_range_table'
was not declared. Should it be static?

net/ipv4/sysctl_net_ipv4.c:119:31: warning: incorrect type in argument 2
(different signedness)
net/ipv4/sysctl_net_ipv4.c:119:31: expected int *range
net/ipv4/sysctl_net_ipv4.c:119:31: got unsigned int *<noident>

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
 net/ipv4/sysctl_net_ipv4.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index fe9bf91..4aa7e9d 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -75,7 +75,7 @@ static int ipv4_local_port_range(ctl_table *table, int write,
 }
 
 
-void inet_get_ping_group_range_table(struct ctl_table *table, gid_t *low, gid_t *high)
+static void inet_get_ping_group_range_table(struct ctl_table *table, gid_t *low, gid_t *high)
 {
 	gid_t *data = table->data;
 	unsigned seq;
@@ -88,7 +88,7 @@ void inet_get_ping_group_range_table(struct ctl_table *table, gid_t *low, gid_t
 }
 
 /* Update system visible IP port range */
-static void set_ping_group_range(struct ctl_table *table, int range[2])
+static void set_ping_group_range(struct ctl_table *table, gid_t range[2])
 {
 	gid_t *data = table->data;
 	write_seqlock(&sysctl_local_ports.lock);

^ permalink raw reply related

* RE: kenel level packet capturing
From: chetan loke @ 2011-12-14 14:55 UTC (permalink / raw)
  To: spaans, David Miller; +Cc: raviraj.j1991, netdev

> Some documentation for fanout is one feature that would be nice. (How
> to
> use it from userspace - after 5 minutes of googling, the first place
> where I could find an example is in the suricata source code[1]).

How about netdev's email archives ;) ? You would have noticed Dave's
sample fanout code. you can take that email and easily churn it into
20-50 lines of doc.

^ permalink raw reply

* [PATCH net-next] inet: remove rcu protection on tw_net
From: Eric Dumazet @ 2011-12-14 14:58 UTC (permalink / raw)
  To: David Miller; +Cc: netdev, Eric W. Biederman

commit b099ce2602d806 (net: Batch inet_twsk_purge) added rcu protection
on tw_net for no obvious reason.

struct net are refcounted anyway since timewait sockets escape from rcu
protected sections. tw_net stay valid for the whole timwait lifetime.

This also removes a lot of sparse errors.

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
CC: Eric W. Biederman <ebiederm@xmission.com>
---
 include/net/inet_timewait_sock.h |   12 ++----------
 1 file changed, 2 insertions(+), 10 deletions(-)

diff --git a/include/net/inet_timewait_sock.h b/include/net/inet_timewait_sock.h
index e8c25b9..ba52c83 100644
--- a/include/net/inet_timewait_sock.h
+++ b/include/net/inet_timewait_sock.h
@@ -218,20 +218,12 @@ extern void inet_twsk_purge(struct inet_hashinfo *hashinfo,
 static inline
 struct net *twsk_net(const struct inet_timewait_sock *twsk)
 {
-#ifdef CONFIG_NET_NS
-	return rcu_dereference_raw(twsk->tw_net); /* protected by locking, */
-						  /* reference counting, */
-						  /* initialization, or RCU. */
-#else
-	return &init_net;
-#endif
+	return read_pnet(&twsk->tw_net);
 }
 
 static inline
 void twsk_net_set(struct inet_timewait_sock *twsk, struct net *net)
 {
-#ifdef CONFIG_NET_NS
-	rcu_assign_pointer(twsk->tw_net, net);
-#endif
+	write_pnet(&twsk->tw_net, net);
 }
 #endif	/* _INET_TIMEWAIT_SOCK_ */

^ permalink raw reply related

* Re: kenel level packet capturing
From: Jasper Spaans @ 2011-12-14 15:04 UTC (permalink / raw)
  To: chetan loke; +Cc: David Miller, netdev@vger.kernel.org
In-Reply-To: <CAAsGZS4k8EASbB+1Edc=9VSzZ2sUXgZLFzt9f=egh1BLat+ZZg@mail.gmail.com>

On 14/12/11 15:55, chetan loke wrote:
>> Some documentation for fanout is one feature that would be nice. (How
>> to
>> use it from userspace - after 5 minutes of googling, the first place
>> where I could find an example is in the suricata source code[1]).
> How about netdev's email archives ;) ? You would have noticed Dave's
> sample fanout code. you can take that email and easily churn it into
> 20-50 lines of doc.
Hmm google didn't tell me about those, so they don't exist (...)

I'll make some time to write up some documentation, as I'm interested in
using fanout.

-- 
 /\____/\   Ir. Jasper Spaans      
 \   (_)/   Fox-IT Experts in IT Security!
  \    X    T: +31-15-2847999
   \  / \   M: +31-6-41588725   
    \/      KvK Haaglanden 27301624

^ permalink raw reply

* RE: kenel level packet capturing
From: Eric Dumazet @ 2011-12-14 15:07 UTC (permalink / raw)
  To: chetan loke; +Cc: spaans, David Miller, raviraj.j1991, netdev
In-Reply-To: <CAAsGZS4k8EASbB+1Edc=9VSzZ2sUXgZLFzt9f=egh1BLat+ZZg@mail.gmail.com>

Le mercredi 14 décembre 2011 à 09:55 -0500, chetan loke a écrit :
> > Some documentation for fanout is one feature that would be nice. (How
> > to
> > use it from userspace - after 5 minutes of googling, the first place
> > where I could find an example is in the suricata source code[1]).
> 
> How about netdev's email archives ;) ? You would have noticed Dave's
> sample fanout code. you can take that email and easily churn it into
> 20-50 lines of doc.

Contributions to Documentation/network are higly welcomed.

Thanks !

^ permalink raw reply

* [PATCH] libcxgbi: do not print a message when memory allocation fails
From: Thadeu Lima de Souza Cascardo @ 2011-12-14 15:46 UTC (permalink / raw)
  To: linux-scsi
  Cc: netdev, kxie, michaelc, davem, JBottomley, linux-kernel,
	Thadeu Lima de Souza Cascardo

In alloc_pdu, libcxgbi tries to allocate a skb with GFP_ATOMIC, which
may potentially fail. When it happens, the current code prints a warning
message.

When the system is under IO stress, this failure may happen lots of
times and it usually scares users.

Instead of printing the warning message, the code now increases the
tx_dropped statistics for the ethernet interface wich is doing the iscsi
task.

Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@linux.vnet.ibm.com>
---
 drivers/scsi/cxgbi/libcxgbi.c |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c
index c10f74a..3422bc2 100644
--- a/drivers/scsi/cxgbi/libcxgbi.c
+++ b/drivers/scsi/cxgbi/libcxgbi.c
@@ -1862,8 +1862,9 @@ int cxgbi_conn_alloc_pdu(struct iscsi_task *task, u8 opcode)
 
 	tdata->skb = alloc_skb(cdev->skb_tx_rsvd + headroom, GFP_ATOMIC);
 	if (!tdata->skb) {
-		pr_warn("alloc skb %u+%u, opcode 0x%x failed.\n",
-			cdev->skb_tx_rsvd, headroom, opcode);
+		struct cxgbi_sock *csk = cconn->cep->csk;
+		struct net_device *ndev = cdev->ports[csk->port_id];
+		ndev->stats.tx_dropped++;
 		return -ENOMEM;
 	}
 
-- 
1.7.4.4

^ permalink raw reply related

* linux-3.0.x regression with ipv4 routes having mtu
From: Timo Teräs @ 2011-12-14 15:54 UTC (permalink / raw)
  To: netdev

Hi,

I'm testing linux-3.0.10 and it appears to have a mtu bug with ipv4.
There was recent funniness in ipv6 side as well [1,2], but this seems
quite different.

Basically, doing something like (ipA being the IPv4 host):
 1. do a tcp session to ipA (telnet to open ipv4 port, chat, close)
 2. "ip route get ipA" to see the route cache entry
 3. "ip route add ipA via (whatever was the router) mtu 1400"
 4. "ip route flush cache"
 5. "ip route get ipA" and you still get the old same info as in step 2:
    the mtu was not updated
    (btw. how long the tcp info from step 1 gets cached? is it tunable?)

So something is does not get updated here. This used to work though.
The current production boxes where I know this work is 2.6.38.8.

Sounds like the TCP info is used if it exists. But it never gets updated
after it's gone, and the new MTU is not being updated.

- Timo

[1] http://www.spinics.net/lists/netdev/msg174468.html
[2] http://marc.info/?l=linux-netdev&m=131529445904858

^ permalink raw reply

* Re: kenel level packet capturing
From: David Miller @ 2011-12-14 16:09 UTC (permalink / raw)
  To: raviraj.j1991; +Cc: netdev, netfilter-devel, netfilter
In-Reply-To: <CAKj1SsNO_uJuVtKs_-tygcd=QDesKkYa=9DVeajhK73LFV-opA@mail.gmail.com>

From: raviraj joshi <raviraj.j1991@gmail.com>
Date: Wed, 14 Dec 2011 16:16:01 +0530

> On Wed, Dec 14, 2011 at 12:06 AM, David Miller <davem@davemloft.net> wrote:
>> From: raviraj joshi <raviraj.j1991@gmail.com>
>> Date: Tue, 13 Dec 2011 17:34:22 +0530
>>
>>> We have decided to use PF_RING(a kernel module to capture packets) for
>>> the same due to the number of advantages.
>>
>> What "advantages"?  The AF_PACKET socket layer already upstream in the
>> kernel supports every relevant performance feature PF_RING does, and
>> then some.
> I refered to the document on "A Measurement Study of Packet Reception
> using Linux"[1]  which said pf_ring maintains
> a ring buffer, so we dont have to issue a receive system call for each
> packet in contrast to AF_PACKET which issues a system call for each
> packet(pls correct me if i am wrong).

AF_PACKET supports mmap()'d packet rings, and even supports variable
packet lengths within those rings.

AF_PACKET supports all the worthwhile performance features of PR_RING
and it's upstream, stop kidding yourself.

I'm really sick and tired of people saying PF_RING is better than
what we have upstream, it really isn't.
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* nonlocal_bind and IPv6
From: Vincent Bernat @ 2011-12-14 16:20 UTC (permalink / raw)
  To: netdev; +Cc: davem, yoshfuji

This is a second tentative to port ip_nonlocal_bind to IPv6. The two
patches are independant. The first patch enables
net.ipv6.ip_nonlocal_bind and is "namespace aware". The second patch
modifies net.ipv4.ip_nonlocal_bind to also be "namespace aware". I
don't know if this is something important.

I did not test the SCTP part of the second patch (but it compiles).

 Documentation/networking/ip-sysctl.txt |    5 +++++
 include/net/netns/ipv4.h               |    1 +
 include/net/netns/ipv6.h               |    1 +
 net/ipv4/af_inet.c                     |    6 +-----
 net/ipv4/ping.c                        |    2 +-
 net/ipv4/sysctl_net_ipv4.c             |   16 +++++++++-------
 net/ipv6/af_inet6.c                    |    6 ++++--
 net/ipv6/sysctl_net_ipv6.c             |    8 ++++++++
 net/sctp/protocol.c                    |    2 +-
 9 files changed, 31 insertions(+), 16 deletions(-)

^ permalink raw reply

* [PATCH 1/2] net/ipv6: add ip_nonlocal_bind sysctl for IPv6
From: Vincent Bernat @ 2011-12-14 16:20 UTC (permalink / raw)
  To: netdev; +Cc: davem, yoshfuji, Vincent Bernat
In-Reply-To: <1323879648-419-1-git-send-email-bernat@luffy.cx>

net.ipv4.ip_nonlocal_bind sysctl allows applications to bind to non
local IPv4 addresses (for example, dynamic addresses that do not exist
yet). This modification introduces net.ipv6.ip_nonlocal_bind which has
the same effect for IPv6.

However, contrary to net.ipv4.ip_nonlocal_bind, this settings is bound
to the current namespace.

Signed-off-by: Vincent Bernat <bernat@luffy.cx>
---
 Documentation/networking/ip-sysctl.txt |    5 +++++
 include/net/netns/ipv6.h               |    1 +
 net/ipv6/af_inet6.c                    |    4 +++-
 net/ipv6/sysctl_net_ipv6.c             |    8 ++++++++
 4 files changed, 17 insertions(+), 1 deletions(-)

diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index ad3e80e..be8a80e 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -1007,6 +1007,11 @@ bindv6only - BOOLEAN
 
 	Default: FALSE (as specified in RFC3493)
 
+ip_nonlocal_bind - BOOLEAN
+	If set, allows processes to bind() to non-local IP addresses,
+	which can be quite useful - but may break some applications.
+	Default: FALSE
+
 IPv6 Fragmentation:
 
 ip6frag_high_thresh - INTEGER
diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h
index 81abfcb..f9326cc 100644
--- a/include/net/netns/ipv6.h
+++ b/include/net/netns/ipv6.h
@@ -16,6 +16,7 @@ struct netns_sysctl_ipv6 {
 	struct ctl_table_header *frags_hdr;
 #endif
 	int bindv6only;
+	int ip6_nonlocal_bind;
 	int flush_delay;
 	int ip6_rt_max_size;
 	int ip6_rt_gc_min_interval;
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 273f48d..27c32f387 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -347,7 +347,8 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 			 */
 			v4addr = LOOPBACK4_IPV6;
 			if (!(addr_type & IPV6_ADDR_MULTICAST))	{
-				if (!(inet->freebind || inet->transparent) &&
+				if (!net->ipv6.sysctl.ip6_nonlocal_bind &&
+				    !(inet->freebind || inet->transparent) &&
 				    !ipv6_chk_addr(net, &addr->sin6_addr,
 						   dev, 0)) {
 					err = -EADDRNOTAVAIL;
@@ -1017,6 +1018,7 @@ static int __net_init inet6_net_init(struct net *net)
 	int err = 0;
 
 	net->ipv6.sysctl.bindv6only = 0;
+	net->ipv6.sysctl.ip6_nonlocal_bind = 0;
 	net->ipv6.sysctl.icmpv6_time = 1*HZ;
 
 	err = ipv6_init_mibs(net);
diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c
index 166a57c..42f0cf0 100644
--- a/net/ipv6/sysctl_net_ipv6.c
+++ b/net/ipv6/sysctl_net_ipv6.c
@@ -48,6 +48,13 @@ static ctl_table ipv6_table_template[] = {
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec
 	},
+	{
+		.procname	= "ip_nonlocal_bind",
+		.data		= &init_net.ipv6.sysctl.ip6_nonlocal_bind,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec
+	},
 	{ }
 };
 
@@ -93,6 +100,7 @@ static int __net_init ipv6_sysctl_net_init(struct net *net)
 	ipv6_table[1].child = ipv6_icmp_table;
 
 	ipv6_table[2].data = &net->ipv6.sysctl.bindv6only;
+	ipv6_table[3].data = &net->ipv6.sysctl.ip6_nonlocal_bind;
 
 	net->ipv6.sysctl.table = register_net_sysctl_table(net, net_ipv6_ctl_path,
 							   ipv6_table);
-- 
1.7.7.3

^ permalink raw reply related

* [PATCH 2/2] net/ipv4: bind ip_nonlocal_bind to current netns
From: Vincent Bernat @ 2011-12-14 16:20 UTC (permalink / raw)
  To: netdev; +Cc: davem, yoshfuji, Vincent Bernat
In-Reply-To: <1323879648-419-1-git-send-email-bernat@luffy.cx>

net.ipv4.ip_nonlocal_bind sysctl was global to all network
namespaces. This patch allows to set a different value for each
network namespace.

Signed-off-by: Vincent Bernat <bernat@luffy.cx>
---
 include/net/netns/ipv4.h   |    1 +
 net/ipv4/af_inet.c         |    6 +-----
 net/ipv4/ping.c            |    2 +-
 net/ipv4/sysctl_net_ipv4.c |   16 +++++++++-------
 net/ipv6/af_inet6.c        |    2 +-
 net/sctp/protocol.c        |    2 +-
 6 files changed, 14 insertions(+), 15 deletions(-)

diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h
index bbd023a..ad8587c 100644
--- a/include/net/netns/ipv4.h
+++ b/include/net/netns/ipv4.h
@@ -51,6 +51,7 @@ struct netns_ipv4 {
 	int sysctl_icmp_ratelimit;
 	int sysctl_icmp_ratemask;
 	int sysctl_icmp_errors_use_inbound_ifaddr;
+	int sysctl_ip_nonlocal_bind;
 	int sysctl_rt_cache_rebuild_count;
 	int current_rt_cache_rebuild_count;
 
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index f7b5670..4fe4a7b 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -443,10 +443,6 @@ int inet_release(struct socket *sock)
 }
 EXPORT_SYMBOL(inet_release);
 
-/* It is off by default, see below. */
-int sysctl_ip_nonlocal_bind __read_mostly;
-EXPORT_SYMBOL(sysctl_ip_nonlocal_bind);
-
 int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 {
 	struct sockaddr_in *addr = (struct sockaddr_in *)uaddr;
@@ -485,7 +481,7 @@ int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 	 *  is temporarily down)
 	 */
 	err = -EADDRNOTAVAIL;
-	if (!sysctl_ip_nonlocal_bind &&
+	if (!sock_net(sk)->ipv4.sysctl_ip_nonlocal_bind &&
 	    !(inet->freebind || inet->transparent) &&
 	    addr->sin_addr.s_addr != htonl(INADDR_ANY) &&
 	    chk_addr_ret != RTN_LOCAL &&
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
index 43d4c3b..01cf59d 100644
--- a/net/ipv4/ping.c
+++ b/net/ipv4/ping.c
@@ -257,7 +257,7 @@ static int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
 	if (addr->sin_addr.s_addr == INADDR_ANY)
 		chk_addr_ret = RTN_LOCAL;
 
-	if ((sysctl_ip_nonlocal_bind == 0 &&
+	if ((sock_net(sk)->ipv4.sysctl_ip_nonlocal_bind == 0 &&
 	    isk->freebind == 0 && isk->transparent == 0 &&
 	     chk_addr_ret != RTN_LOCAL) ||
 	    chk_addr_ret == RTN_MULTICAST ||
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index fe9bf91..15e4534 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -265,13 +265,6 @@ static struct ctl_table ipv4_table[] = {
 		.proc_handler	= proc_dointvec
 	},
 	{
-		.procname	= "ip_nonlocal_bind",
-		.data		= &sysctl_ip_nonlocal_bind,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec
-	},
-	{
 		.procname	= "tcp_syn_retries",
 		.data		= &sysctl_tcp_syn_retries,
 		.maxlen		= sizeof(int),
@@ -765,6 +758,13 @@ static struct ctl_table ipv4_net_table[] = {
 		.mode		= 0644,
 		.proc_handler	= ipv4_tcp_mem,
 	},
+	{
+		.procname	= "ip_nonlocal_bind",
+		.data		= &init_net.ipv4.sysctl_ip_nonlocal_bind,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec
+	},
 	{ }
 };
 
@@ -802,6 +802,8 @@ static __net_init int ipv4_sysctl_init_net(struct net *net)
 			&net->ipv4.sysctl_rt_cache_rebuild_count;
 		table[7].data =
 			&net->ipv4.sysctl_ping_group_range;
+		table[9].data =
+			&net->ipv4.sysctl_ip_nonlocal_bind;
 
 	}
 
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 27c32f387..76b970d 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -307,7 +307,7 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 		/* Reproduce AF_INET checks to make the bindings consistent */
 		v4addr = addr->sin6_addr.s6_addr32[3];
 		chk_addr_ret = inet_addr_type(net, v4addr);
-		if (!sysctl_ip_nonlocal_bind &&
+		if (!net->ipv4.sysctl_ip_nonlocal_bind &&
 		    !(inet->freebind || inet->transparent) &&
 		    v4addr != htonl(INADDR_ANY) &&
 		    chk_addr_ret != RTN_LOCAL &&
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 544a9b6..8c9c6bf 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -403,7 +403,7 @@ static int sctp_v4_available(union sctp_addr *addr, struct sctp_sock *sp)
 	if (addr->v4.sin_addr.s_addr != htonl(INADDR_ANY) &&
 	   ret != RTN_LOCAL &&
 	   !sp->inet.freebind &&
-	   !sysctl_ip_nonlocal_bind)
+	    !sock_net(sctp_opt2sk(sp))->ipv4.sysctl_ip_nonlocal_bind)
 		return 0;
 
 	if (ipv6_only_sock(sctp_opt2sk(sp)))
-- 
1.7.7.3

^ permalink raw reply related

* Re: [PATCH v6 01/10] Basic kernel memory functionality for the Memory Controller
From: Michal Hocko @ 2011-12-14 16:38 UTC (permalink / raw)
  To: Glauber Costa
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA, lizf-BthXqXjhjHXQFUHtdCDX3A,
	kamezawa.hiroyu-+CUm20s59erQFUHtdCDX3A,
	ebiederm-aS9lmoZGLiVWk0Htik3J/w, davem-fT/PcQaiUtIeIZ0/mPfg9Q,
	paul-inf54ven1CmVyaH7bEyXVA, gthelen-hpIqsD4AKlfQT0dZR+AlfA,
	netdev-u79uwXL29TY76Z2rM5mHXA, linux-mm-Bw31MaZKKs3YtjvyW6yDsg,
	kirill-oKw7cIdHH8eLwutG50LtGA, avagin-bzQdu9zFT3WakBO8gow8eQ,
	devel-GEFAQzZX7r8dnm+yROfE0A, eric.dumazet-Re5JQEeQqe8AvxtiuMwx3w,
	cgroups-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1322242696-27682-2-git-send-email-glommer-bzQdu9zFT3WakBO8gow8eQ@public.gmane.org>

Sorry for jumping in that late but I was busy recently...

On Fri 25-11-11 15:38:07, Glauber Costa wrote:
> This patch lays down the foundation for the kernel memory component
> of the Memory Controller.
> 
> As of today, I am only laying down the following files:
> 
>  * memory.independent_kmem_limit

Maybe has been already discussed but the name is rather awkward and it
would deserve more clarification. It is independent in the way that it
doesn't add up to the standard (user) allocations or it enables/disables
accounting?

>  * memory.kmem.limit_in_bytes (currently ignored)

What happens if we reach the limit? Are all kernel allocations
considered or only selected caches? How do I find out which are those?

AFAIU you have implemented it for network buffers at this stage but I
guess that dentries will follow...

>  * memory.kmem.usage_in_bytes (always zero)
> 
> Signed-off-by: Glauber Costa <glommer-bzQdu9zFT3WakBO8gow8eQ@public.gmane.org>
> Reviewed-by: Kirill A. Shutemov <kirill-oKw7cIdHH8eLwutG50LtGA@public.gmane.org>
> CC: Paul Menage <paul-inf54ven1CmVyaH7bEyXVA@public.gmane.org>
> CC: Greg Thelen <gthelen-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
> ---
>  Documentation/cgroups/memory.txt |   36 ++++++++++++-
>  init/Kconfig                     |   14 +++++
>  mm/memcontrol.c                  |  107 ++++++++++++++++++++++++++++++++++++--
>  3 files changed, 150 insertions(+), 7 deletions(-)
> 
> diff --git a/Documentation/cgroups/memory.txt b/Documentation/cgroups/memory.txt
> index 06eb6d9..bf00cd2 100644
> --- a/Documentation/cgroups/memory.txt
> +++ b/Documentation/cgroups/memory.txt
> @@ -44,8 +44,9 @@ Features:
>   - oom-killer disable knob and oom-notifier
>   - Root cgroup has no limit controls.
>  
> - Kernel memory and Hugepages are not under control yet. We just manage
> - pages on LRU. To add more controls, we have to take care of performance.
> + Hugepages is not under control yet. We just manage pages on LRU. To add more

Hugepages are not
Anyway this sounds outdated as we track both THP and hugetlb, right?

> + controls, we have to take care of performance. Kernel memory support is work
> + in progress, and the current version provides basically functionality.

s/basically/basic/

>  
>  Brief summary of control files.
>  
> @@ -56,8 +57,11 @@ Brief summary of control files.
>  				 (See 5.5 for details)
>   memory.memsw.usage_in_bytes	 # show current res_counter usage for memory+Swap
>  				 (See 5.5 for details)
> + memory.kmem.usage_in_bytes	 # show current res_counter usage for kmem only.
> +				 (See 2.7 for details)
>   memory.limit_in_bytes		 # set/show limit of memory usage
>   memory.memsw.limit_in_bytes	 # set/show limit of memory+Swap usage
> + memory.kmem.limit_in_bytes	 # if allowed, set/show limit of kernel memory
>   memory.failcnt			 # show the number of memory usage hits limits
>   memory.memsw.failcnt		 # show the number of memory+Swap hits limits
>   memory.max_usage_in_bytes	 # show max memory usage recorded
> @@ -72,6 +76,9 @@ Brief summary of control files.
>   memory.oom_control		 # set/show oom controls.
>   memory.numa_stat		 # show the number of memory usage per numa node
>  
> + memory.independent_kmem_limit	 # select whether or not kernel memory limits are
> +				   independent of user limits

It is not clear to me what happens in enabled/disabled cases. Let's say
they are not independent. Do they form a single limit or it toggles kmem
charging on/off.

> +
>  1. History
>  
>  The memory controller has a long history. A request for comments for the memory
> @@ -255,6 +262,31 @@ When oom event notifier is registered, event will be delivered.
>    per-zone-per-cgroup LRU (cgroup's private LRU) is just guarded by
>    zone->lru_lock, it has no lock of its own.
>  
> +2.7 Kernel Memory Extension (CONFIG_CGROUP_MEM_RES_CTLR_KMEM)
> +
> + With the Kernel memory extension, the Memory Controller is able to limit
> +the amount of kernel memory used by the system. 

Per kmem cache? Or what is the granularity?

> Kernel memory is fundamentally
> +different than user memory, since it can't be swapped out, which makes it
> +possible to DoS the system by consuming too much of this precious resource.
> +Kernel memory limits are not imposed for the root cgroup.
> +
> +Memory limits as specified by the standard Memory Controller may or may not
> +take kernel memory into consideration. This is achieved through the file
> +memory.independent_kmem_limit. A Value different than 0 will allow for kernel
> +memory to be controlled separately.
> +
> +When kernel memory limits are not independent, the limit values set in
> +memory.kmem files are ignored.

This suggests that the independent_kmem_limit is toggle to
enable/disable accounting. Wouldn't kmem_limit_enabled (0/1 or on/off)
be more obvious in that case?

Also a description what happens when the limit is reached (in both
cases) would be helpful.

[...]
> @@ -343,9 +352,14 @@ enum charge_type {
>  };
>  
>  /* for encoding cft->private value on file */
> -#define _MEM			(0)
> -#define _MEMSWAP		(1)
> -#define _OOM_TYPE		(2)
> +
> +enum mem_type {
> +	_MEM = 0,
> +	_MEMSWAP,
> +	_OOM_TYPE,
> +	_KMEM,
> +};

Probably a separate (cleanup) patch?

> +
>  #define MEMFILE_PRIVATE(x, val)	(((x) << 16) | (val))
>  #define MEMFILE_TYPE(val)	(((val) >> 16) & 0xffff)
>  #define MEMFILE_ATTR(val)	((val) & 0xffff)
> @@ -3838,10 +3852,17 @@ static inline u64 mem_cgroup_usage(struct mem_cgroup *mem, bool swap)
>  	u64 val;
>  
>  	if (!mem_cgroup_is_root(mem)) {
> +		val = 0;
> +#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM
> +		if (!mem->kmem_independent_accounting)
> +			val = res_counter_read_u64(&mem->kmem, RES_USAGE);
> +#endif

OK so this suggests that independent accounting really means
kmem+user_usage.


>  		if (!swap)
> -			return res_counter_read_u64(&mem->res, RES_USAGE);
> +			val += res_counter_read_u64(&mem->res, RES_USAGE);
>  		else
> -			return res_counter_read_u64(&mem->memsw, RES_USAGE);
> +			val += res_counter_read_u64(&mem->memsw, RES_USAGE);
> +
> +		return val;
>  	}
>  
>  	val = mem_cgroup_recursive_stat(mem, MEM_CGROUP_STAT_CACHE);
[...]
-- 
Michal Hocko
SUSE Labs
SUSE LINUX s.r.o.
Lihovarska 1060/12
190 00 Praha 9    
Czech Republic
--
To unsubscribe from this list: send the line "unsubscribe cgroups" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v6 01/10] Basic kernel memory functionality for the Memory Controller
From: Michal Hocko @ 2011-12-14 16:45 UTC (permalink / raw)
  To: Glauber Costa
  Cc: linux-kernel, lizf, kamezawa.hiroyu, ebiederm, davem, paul,
	gthelen, netdev, linux-mm, kirill, avagin, devel, eric.dumazet,
	cgroups
In-Reply-To: <20111214163844.GA4856@tiehlicka.suse.cz>

On Wed 14-12-11 17:38:44, Michal Hocko wrote:
> Sorry for jumping in that late but I was busy recently...

And obviously an old version of the patch in my mailbox. I will comment
on the v9 now. Sorry...
[...]
-- 
Michal Hocko
SUSE Labs
SUSE LINUX s.r.o.
Lihovarska 1060/12
190 00 Praha 9    
Czech Republic

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply

* Re: [PATCH v9 1/9] Basic kernel memory functionality for the Memory Controller
From: Michal Hocko @ 2011-12-14 17:04 UTC (permalink / raw)
  To: Glauber Costa
  Cc: davem, linux-kernel, paul, lizf, kamezawa.hiroyu, ebiederm,
	gthelen, netdev, linux-mm, kirill, avagin, devel, eric.dumazet,
	cgroups, Johannes Weiner
In-Reply-To: <1323676029-5890-2-git-send-email-glommer@parallels.com>

[Now with the current patch version, I hope]

On Mon 12-12-11 11:47:01, Glauber Costa wrote:
> This patch lays down the foundation for the kernel memory component
> of the Memory Controller.
> 
> As of today, I am only laying down the following files:
> 
>  * memory.independent_kmem_limit

Maybe has been already discussed but the name is rather awkward and it
would deserve more clarification. It is independent in the way that it
doesn't add up to the standard (user) allocations or it enables/disables
accounting?

>  * memory.kmem.limit_in_bytes (currently ignored)

What happens if we reach the limit? Are all kernel allocations
considered or only selected caches? How do I find out which are those?

AFAIU you have implemented it for network buffers at this stage but I
guess that dentries will follow...

>  * memory.kmem.usage_in_bytes (always zero)
> 
> Signed-off-by: Glauber Costa <glommer@parallels.com>
> CC: Kirill A. Shutemov <kirill@shutemov.name>
> CC: Paul Menage <paul@paulmenage.org>
> CC: Greg Thelen <gthelen@google.com>
> CC: Johannes Weiner <jweiner@redhat.com>
> CC: Michal Hocko <mhocko@suse.cz>
> ---
>  Documentation/cgroups/memory.txt |   40 ++++++++++++++-
>  init/Kconfig                     |   11 ++++
>  mm/memcontrol.c                  |  105 ++++++++++++++++++++++++++++++++++++--
>  3 files changed, 149 insertions(+), 7 deletions(-)
> 
> diff --git a/Documentation/cgroups/memory.txt b/Documentation/cgroups/memory.txt
> index cc0ebc5..f245324 100644
> --- a/Documentation/cgroups/memory.txt
> +++ b/Documentation/cgroups/memory.txt
> @@ -44,8 +44,9 @@ Features:
>   - oom-killer disable knob and oom-notifier
>   - Root cgroup has no limit controls.
>  
> - Kernel memory and Hugepages are not under control yet. We just manage
> - pages on LRU. To add more controls, we have to take care of performance.
> + Hugepages is not under control yet. We just manage pages on LRU. To add more

Hugepages are not
Anyway this sounds outdated as we track both THP and hugetlb, right?

> + controls, we have to take care of performance. Kernel memory support is work
> + in progress, and the current version provides basically functionality.

s/basically/basic/

>  
>  Brief summary of control files.
>  
> @@ -56,8 +57,11 @@ Brief summary of control files.
>  				 (See 5.5 for details)
>   memory.memsw.usage_in_bytes	 # show current res_counter usage for memory+Swap
>  				 (See 5.5 for details)
> + memory.kmem.usage_in_bytes	 # show current res_counter usage for kmem only.
> +				 (See 2.7 for details)
>   memory.limit_in_bytes		 # set/show limit of memory usage
>   memory.memsw.limit_in_bytes	 # set/show limit of memory+Swap usage
> + memory.kmem.limit_in_bytes	 # if allowed, set/show limit of kernel memory
>   memory.failcnt			 # show the number of memory usage hits limits
>   memory.memsw.failcnt		 # show the number of memory+Swap hits limits
>   memory.max_usage_in_bytes	 # show max memory usage recorded
> @@ -72,6 +76,9 @@ Brief summary of control files.
>   memory.oom_control		 # set/show oom controls.
>   memory.numa_stat		 # show the number of memory usage per numa node
>  
> + memory.independent_kmem_limit	 # select whether or not kernel memory limits are
> +				   independent of user limits
> +

It is not clear what happens in enabled/disabled cases. Let's say they
are not independent. Does it form a single limit with user charges or it
toggles kmem charging on/off.

>  1. History
>  
>  The memory controller has a long history. A request for comments for the memory
> @@ -255,6 +262,35 @@ When oom event notifier is registered, event will be delivered.
>    per-zone-per-cgroup LRU (cgroup's private LRU) is just guarded by
>    zone->lru_lock, it has no lock of its own.
>  
> +2.7 Kernel Memory Extension (CONFIG_CGROUP_MEM_RES_CTLR_KMEM)
> +
> +With the Kernel memory extension, the Memory Controller is able to limit
> +the amount of kernel memory used by the system. Kernel memory is fundamentally
> +different than user memory, since it can't be swapped out, which makes it
> +possible to DoS the system by consuming too much of this precious resource.
> +
> +Some kernel memory resources may be accounted and limited separately from the
> +main "kmem" resource. For instance, a slab cache that is considered important
> +enough to be limited separately may have its own knobs.

How do you tell which are those that are accounted to the "main kmem"?

> +
> +Kernel memory limits are not imposed for the root cgroup. Usage for the root
> +cgroup may or may not be accounted.
> +
> +Memory limits as specified by the standard Memory Controller may or may not
> +take kernel memory into consideration. This is achieved through the file
> +memory.independent_kmem_limit. A Value different than 0 will allow for kernel
> +memory to be controlled separately.

Separately from user space allocations, right?
What happens if we reach the limit in both cases?

> @@ -344,9 +353,14 @@ enum charge_type {
>  };
>  
>  /* for encoding cft->private value on file */
> -#define _MEM			(0)
> -#define _MEMSWAP		(1)
> -#define _OOM_TYPE		(2)
> +
> +enum mem_type {
> +	_MEM = 0,
> +	_MEMSWAP,
> +	_OOM_TYPE,
> +	_KMEM,
> +};
> +

Probably in a separate (cleanup) patch?

>  #define MEMFILE_PRIVATE(x, val)	(((x) << 16) | (val))
>  #define MEMFILE_TYPE(val)	(((val) >> 16) & 0xffff)
>  #define MEMFILE_ATTR(val)	((val) & 0xffff)
> @@ -3848,10 +3862,17 @@ static inline u64 mem_cgroup_usage(struct mem_cgroup *memcg, bool swap)
>  	u64 val;
>  
>  	if (!mem_cgroup_is_root(memcg)) {
> +		val = 0;
> +#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM
> +		if (!memcg->kmem_independent_accounting)
> +			val = res_counter_read_u64(&memcg->kmem, RES_USAGE);
> +#endif
>  		if (!swap)
> -			return res_counter_read_u64(&memcg->res, RES_USAGE);
> +			val += res_counter_read_u64(&memcg->res, RES_USAGE);
>  		else
> -			return res_counter_read_u64(&memcg->memsw, RES_USAGE);
> +			val += res_counter_read_u64(&memcg->memsw, RES_USAGE);
> +
> +		return val;
>  	}

So you report kmem+user but we do not consider kmem during charge so one
can easily end up with usage_in_bytes over limit but no reclaim is going
on. Not good, I would say.

OK, so to sum it up. The biggest problem I see is the (non)independent
accounting. We simply cannot mix user+kernel limits otherwise we would
see issues (like kernel resource hog would force memcg-oom and innocent
members would die because their rss is much bigger).
It is also not clear to me what should happen when we hit the kmem
limit. I guess it will be kmem cache dependent.
-- 
Michal Hocko
SUSE Labs
SUSE LINUX s.r.o.
Lihovarska 1060/12
190 00 Praha 9    
Czech Republic

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply

* Re: [PATCH 3/3] qmi_wwan: Driver for WWAN devices requiring use of the QMI protocol
From: Dan Williams @ 2011-12-14 17:08 UTC (permalink / raw)
  To: Bjorn Mork
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1323750784-32608-4-git-send-email-bjorn-yOkvZcmFvRU@public.gmane.org>

On Tue, 2011-12-13 at 05:33 +0100, Bjorn Mork wrote:
> From: Bjørn Mork <bjorn-yOkvZcmFvRU@public.gmane.org>
> 
> Some WWAN LTE/3G devices based on chipsets from Qualcomm provide
> near standard CDC ECM interfaces in addition to the usual serial
> interfaces.  But they cannot be fully configured using AT commands
> over a serial interface.  It is necessary to speak the proprietary
> Qualcomm Messaging Interface (QMI) protocol to the device to
> enable the ethernet proxy functionality.
> 
> This protocol is exposed by the device through the standard CDC
> USB_CDC_SEND_ENCAPSULATED_COMMAND and USB_CDC_GET_ENCAPSULATED_RESPONSE
> control messages, and using USB_CDC_NOTIFY_RESPONSE_AVAILABLE
> notifications.
> 
> This driver adds a usbnet minidriver with very rudimentary QMI
> support.
> 
> The layout of the usb interfaces can vary a lot within a single
> device, depending on which "modeswitch" command has been used to
> switch it from usb-storage mode.  Control and data interfaces
> can be combined, or they can look like standard CDC ECM interfaces
> with the appropriate descriptors but with vendor specific class
> code. This driver is attempting to support them all, by reusing
> as much as possible of the existing usbnet infrastructure.

So I thought the protocol acronym stood for "Qualcomm MSM/Modem
Interface"  (see
https://www.codeaurora.org/gitweb/quic/le/?p=kernel/msm.git;a=commitdiff;h=5f6f87b51184e13b6c493012de787895d5d18765)

In any case, great work here.  But I'm a bit concerned about how all
this should fit together.  QMI is a huge protocol that does everything a
modem would ever want to do, and we'd likely want to be able to speak
QMI from userspace to the modem too so that modem managers can expose
the full functionality of the modems.  For example Gobi modems have a
very minimal AT command set and most of the functionality is exposed
over QMI or DM.  That's why the Qualcomm GobiNet driver also
exposed /dev/qmi for userspace access.

Second, does the modem actually respond to DHCP over the ECM interface?
If you take a look at the Android rmnet code (which is similar to your
driver) you'll see they extract the IP address and DNS details from the
QMI response.  Do we need to do that here too?

Dan

> Signed-off-by: Bjørn Mork <bjorn-yOkvZcmFvRU@public.gmane.org>
> ---
>  drivers/net/usb/Kconfig         |   13 +
>  drivers/net/usb/Makefile        |    2 +
>  drivers/net/usb/qmi.c           |  730 +++++++++++++++++++++++++++++++++++++++
>  drivers/net/usb/qmi.h           |  101 ++++++
>  drivers/net/usb/qmi_wwan_core.c |  206 +++++++++++
>  5 files changed, 1052 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/net/usb/qmi.c
>  create mode 100644 drivers/net/usb/qmi.h
>  create mode 100644 drivers/net/usb/qmi_wwan_core.c
> 
> diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
> index 2335761..eed9a37 100644
> --- a/drivers/net/usb/Kconfig
> +++ b/drivers/net/usb/Kconfig
> @@ -461,4 +461,17 @@ config USB_VL600
>  
>  	  http://ubuntuforums.org/showpost.php?p=10589647&postcount=17
>  
> +config USB_NET_QMI_WWAN
> +	tristate "USB-to-WWAN driver for QMI based 3G and LTE modems"
> +	depends on USB_NET_CDCETHER
> +	help
> +	  Support WWAN LTE/3G devices based on chipsets from Qualcomm,
> +	  using the Qualcomm Messaging Interface (QMI) protocol to
> +	  configure the device.
> +
> +	  Note that it is still necessary to configure some aspects of
> +	  the device using AT commands on a tty.  Select the option
> +	  driver to get this support.
> +
> +
>  endmenu
> diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
> index c203fa2..ed715da 100644
> --- a/drivers/net/usb/Makefile
> +++ b/drivers/net/usb/Makefile
> @@ -29,4 +29,6 @@ obj-$(CONFIG_USB_SIERRA_NET)	+= sierra_net.o
>  obj-$(CONFIG_USB_NET_CX82310_ETH)	+= cx82310_eth.o
>  obj-$(CONFIG_USB_NET_CDC_NCM)	+= cdc_ncm.o
>  obj-$(CONFIG_USB_VL600)		+= lg-vl600.o
> +obj-$(CONFIG_USB_NET_QMI_WWAN)	+= qmi_wwan.o
> +qmi_wwan-objs := qmi_wwan_core.o qmi.o
>  
> diff --git a/drivers/net/usb/qmi.c b/drivers/net/usb/qmi.c
> new file mode 100644
> index 0000000..b4f6a4b
> --- /dev/null
> +++ b/drivers/net/usb/qmi.c
> @@ -0,0 +1,730 @@
> +/*
> + * Copyright (c) 2011  Bjørn Mork <bjorn-yOkvZcmFvRU@public.gmane.org>
> + * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2 as published by the Free Software Foundation.
> + */
> +
> +/*
> + * Dealing with devices using Qualcomm Messaging Interface (QMI) for
> + * configuration.  Full documentation of the protocol can be obtained
> + * from http://developer.qualcomm.com/
> + *
> + * Some of this code may be inspired by code from the Qualcomm Gobi
> + * 2000 and Gobi 3000 drivers, available at http://www.codeaurora.org/
> + */
> +
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/usb.h>
> +#include <linux/usb/cdc.h>
> +#include "qmi.h"
> +
> +
> +/* QMI protocol debug output */
> +static int qmi_debug;
> +module_param(qmi_debug, int, 0);
> +MODULE_PARM_DESC(qmi_debug, "Enable QMI protocol debugging");
> +
> +/* find and return a pointer to the requested tlv */
> +struct qmi_tlv *qmi_get_tlv(u8 type, u8 *buf, size_t len)
> +{
> +	u8 *p;
> +	struct qmi_tlv *t;
> +
> +	for (p = buf; p < buf + len; p += t->len + sizeof(struct qmi_tlv)) {
> +		t = (struct qmi_tlv *)p;
> +		if (t->type == type)
> +			return (p + t->len <= buf + len) ? t : NULL;
> +	}
> +	return NULL;
> +}
> +
> +
> +/* TLV 0x02 is status: return a negative QMI error code or 0 if OK */
> +int qmi_verify_status_tlv(u8 *buf, size_t len)
> +{
> +	struct qmi_tlv *tlv = (void *)qmi_get_tlv(0x02, buf, len);
> +	struct qmi_tlv_response_data *r = (void *)tlv->bytes;
> +
> +	if (!tlv || tlv->len != 2 * sizeof(__le16))
> +		return -1; /* QMI_ERR_MALFORMED_MSG */
> +
> +	return r->error ? -r->code : 0;
> +}
> +
> +static char *decode_trans_flags(const u8 flags)
> +{
> +	switch (flags) {
> +	case 0: return "request";
> +	case 2:	return "response";
> +	case 4:	return "indication";
> +	case 6:	return "reserved";
> +	default: return "invalid";
> +	}
> +}
> +
> +static char *qmi_system(u8 system)
> +{
> +	switch (system) {
> +	case 0:	return "QMI_CTL";
> +	case 1:	return "QMI_WDS";
> +	case 2:	return "QMI_DMS";
> +	default: return "(unknown)";
> +	}
> +}
> +
> +
> +/* debug print TLVs from a QMI message */
> +static void qmi_tlv_dump(struct qmi_state *qmi, u8 *data, size_t len, u8 system, __le16 msgid)
> +{
> +	u8 *p;
> +	struct qmi_tlv *t;
> +	char linebuf[100];
> +
> +	for (p = data; p < data + len; p += t->len + sizeof(struct qmi_tlv)) {
> +		t = (struct qmi_tlv *)p;
> +
> +		/* mark an empty string */
> +		linebuf[0] = 0;
> +		if (t->type == 0x02) { /* status response */
> +			struct qmi_tlv_response_data *r = (void *)t->bytes;
> +			snprintf(linebuf, sizeof(linebuf), "%s (0x%04x)",
> +				r->error ? "FAILED" : "SUCCESS", r->code);
> +		} else {
> +			switch (system) {
> +			case QMI_WDS:
> +				switch (msgid) {
> +				case 0x0001:
> +					switch (t->type) {
> +					case 0x16:
> +						snprintf(linebuf, sizeof(linebuf), "tx: %d bps, rx: %d bps",
> +							*(__le32 *)t->bytes, *(__le32 *)(t->bytes + 4));
> +					}
> +				}
> +				break;
> +			case QMI_DMS:
> +				switch (msgid) {
> +				case 0x002b:
> +					if (t->type == 0x11 || t->type == 0x12)
> +						snprintf(linebuf, sizeof(linebuf),
> +							"PIN%d status=%d, %d verify retries left, %d unblock retries left",
> +							t->type & 0x3, t->bytes[0], t->bytes[1], t->bytes[2]);
> +				}
> +			}
> +		}
> +
> +		/* do a default byte dump if the linebuf is empty */
> +		if (linebuf[0] == 0)
> +			hex_dump_to_buffer(t->bytes, t->len, 16, 1, linebuf, sizeof(linebuf), true);
> +
> +		dev_info(&qmi->dev->dev, "[0x%02x] (%d) %s\n", t->type, t->len, linebuf);
> +	}
> +}
> +
> +/* verify QMUX message header and return pointer to the (first) message if OK */
> +static struct qmi_msg *qmi_qmux_verify(u8 *data, size_t len)
> +{
> +	struct qmux *h =  (void *)data;
> +	struct qmi_msg *m;
> +	ssize_t hsize = sizeof(struct qmux);
> +
> +	if (len < sizeof(struct qmux) || /* short packet */
> +		h->tf != 0x01 || h->len != (len - 1)) /* invalid QMUX packet! */
> +		return NULL;
> +
> +	/* tid has a different size for QMI_CTL for some fucking stupid reason */
> +	if (h->service == QMI_CTL)
> +		hsize--;
> +
> +	m = (struct qmi_msg *)(data + hsize);
> +
> +	/* insanity checking before any further dereferencing... */
> +	if (hsize + sizeof(struct qmi_msg) + m->len > len)
> +		return NULL;
> +
> +	return m;
> +}
> +
> +/* debug print a QMUX packet */
> +static void qmi_dump_qmux(struct qmi_state *qmi, u8 *data, size_t len)
> +{
> +	struct qmux *h =  (void *)data;
> +	struct qmi_msg *m;
> +
> +	m = qmi_qmux_verify(data, len);
> +	if (!m) {
> +		dev_info(&qmi->dev->dev, "invalid QMUX packet (%zu bytes)\n", len);
> +		return;
> +	}
> +
> +	/* dump parts of the QMUX header and the message ID */
> +	dev_info(&qmi->dev->dev, "%s %s: msg 0x%04x (len %d) from \"%s\" with cid=0x%02x%s and TLVs:\n",
> +		qmi_system(h->service),
> +		decode_trans_flags(h->service == QMI_CTL ? h->flags << 1 : h->flags),
> +		m->msgid,
> +		h->len,
> +		h->ctrl & 0x80 ? "service" : "control point",
> +		h->qmicid, h->qmicid == 0xff ? " (broadcast)" : "");
> +
> +	/* dump the TLVs */
> +	qmi_tlv_dump(qmi, m->tlv, m->len, h->service, m->msgid);
> +}
> +
> +static int qmi_dms_parse(struct qmi_state *qmi, struct qmi_msg *m)
> +{
> +	int status;
> +	struct qmi_tlv *tlv;
> +
> +	if (!m)
> +		return -1;
> +
> +	/* check and save status TLV */
> +	status = qmi_verify_status_tlv(m->tlv, m->len);
> +
> +	/* no QMI_DMS messages requires parsing unless status is OK */
> +	if (status < 0)
> +		return status;
> +
> +	switch (m->msgid) {
> +	case 0x002b: /* QMI_DMS_UIM_GET_PIN_STATUS */
> +		if (status == 0) {
> +			/* PIN1 status */
> +			tlv = qmi_get_tlv(0x11, m->tlv, m->len);
> +			if (tlv && tlv->len == 3) {
> +				switch (tlv->bytes[0]) {
> +				case 2: /* PIN enabled, verified */
> +				case 3: /* PIN disabled */
> +					qmi->flags |= QMI_FLAG_PINOK;
> +				}
> +			}
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int qmi_ctl_parse(struct qmi_state *qmi, struct qmi_msg *m)
> +{
> +	int status;
> +	struct qmi_tlv *tlv;
> +
> +	if (!m)
> +		return -1;
> +
> +	/* check and save status TLV */
> +	status = qmi_verify_status_tlv(m->tlv, m->len);
> +
> +	if (status < 0)
> +		return status;
> +
> +	/* TLVs are message dependent */
> +	switch (m->msgid) {
> +	case 0x0022: /* allocate cid */
> +		/* TLV 0x01 is system + cid */
> +		tlv = qmi_get_tlv(0x01, m->tlv, m->len);
> +
> +		/* can only save CID if there is a slot for the subsystem */
> +		if (tlv && tlv->len == 2 && tlv->bytes[0] < sizeof(qmi->cid))
> +			qmi->cid[tlv->bytes[0]] = tlv->bytes[1];
> +		else
> +			return -1;
> +	}
> +
> +	return status;
> +}
> +
> +static int qmi_wds_parse(struct qmi_state *qmi, struct qmi_msg *m)
> +{
> +	int status;
> +	struct qmi_tlv *tlv;
> +
> +	if (!m)
> +		return -1;
> +
> +	/* check and save status TLV */
> +	status = qmi_verify_status_tlv(m->tlv, m->len);
> +
> +	if (m->msgid == 0x0020 && -status == 0x001A) { /* QMI_ERR_NO_EFFECT */
> +		memset(qmi->handle, 0xff, sizeof(qmi->handle)); /* global handle */
> +		return 0;
> +	}
> +
> +	if (status < 0)
> +		return status;
> +
> +	switch (m->msgid) {
> +	case 0x0020: /* QMI_WDS_START_NETWORK_INTERFACE */
> +		/* TLV 0x01 is a 4 byte packet data handle we need to keep */
> +		tlv = qmi_get_tlv(0x01, m->tlv, m->len);
> +		if (tlv && tlv->len == sizeof(qmi->handle))
> +			memcpy(qmi->handle, tlv->bytes, sizeof(qmi->handle));
> +
> +		break;
> +
> +	case 0x0022: /* QMI_WDS_GET_PKT_SRVC_STATUS */
> +		/* TLV 0x01 is a 1 byte connection status */
> +		tlv = qmi_get_tlv(0x01, m->tlv, m->len);
> +		if (tlv && tlv->len == 1)
> +			qmi->wds_status = tlv->bytes[0];
> +
> +	}
> +
> +	return status;
> +}
> +
> +static void qmi_read_callback(struct urb *urb)
> +{
> +	struct qmi_state *qmi = (void *)urb->context;
> +
> +	if (urb->status == 0) {
> +		struct qmux *h = (void *)urb->transfer_buffer;
> +		struct qmi_msg *m = qmi_qmux_verify(urb->transfer_buffer, urb->actual_length);
> +
> +		switch (h->service) {
> +		case QMI_CTL:
> +			qmi_ctl_parse(qmi, m);
> +			break;
> +		case QMI_WDS:
> +			qmi_wds_parse(qmi, m);
> +			break;
> +		case QMI_DMS:
> +			qmi_dms_parse(qmi, m);
> +		}
> +
> +		if (qmi_debug)
> +			qmi_dump_qmux(qmi, urb->transfer_buffer, urb->actual_length);
> +	}
> +}
> +
> +static int qmi_recv_sync(struct qmi_state *qmi)
> +{
> +	int status;
> +
> +	status = usb_control_msg(qmi->dev,
> +			       usb_rcvctrlpipe(qmi->dev, 0),
> +			       USB_CDC_GET_ENCAPSULATED_RESPONSE, USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE,
> +			       0, qmi->intfnr,
> +			       qmi->rcvbuf, QMI_BUFLEN, 1000);
> +
> +	if (status > 0 && qmi_debug)
> +		qmi_dump_qmux(qmi, qmi->rcvbuf, status);
> +
> +	return status;
> +}
> +
> +static int qmi_send_sync(struct qmi_state *qmi, unsigned char *msg, size_t len)
> +{
> +	int status;
> +
> +	status = usb_control_msg(qmi->dev,
> +				usb_sndctrlpipe(qmi->dev, 0),
> +				USB_CDC_SEND_ENCAPSULATED_COMMAND, USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE,
> +				0, qmi->intfnr,
> +				msg, len, 1000);
> +
> +	if (qmi_debug) /* NOTE: regardless of status! */
> +		qmi_dump_qmux(qmi, msg, len);
> +
> +	return status;
> +}
> +
> +/* send message and wait for the reply - returns pointer to reply message */
> +struct qmi_msg *qmi_send_and_wait_for_ack(struct qmi_state *qmi, unsigned char *data, size_t len, int timeout)
> +{
> +	int ret;
> +	u8 system;
> +	__le16 msgid;
> +	struct qmi_msg *msg = qmi_qmux_verify(data, len);
> +	struct qmux *h = (void *)data;
> +
> +	/* should never happen? */
> +	if (!msg)
> +		return NULL;
> +	msgid = msg->msgid;
> +	system = h->service;
> +
> +	/* flush any pending messages  */
> +	do {
> +		ret = qmi_recv_sync(qmi);
> +	} while (ret > 0);
> +
> +	/* send our message */
> +	ret = qmi_send_sync(qmi, data, len);
> +	if (ret != len)
> +		return NULL;
> +
> +	/* wait a while for the (correct) reply */
> +	do {
> +		ret = qmi_recv_sync(qmi);
> +		msg = qmi_qmux_verify(qmi->rcvbuf, ret);
> +		if (msg) {
> +			h = (struct qmux *)qmi->rcvbuf;
> +			if (msg->msgid == msgid && h->service == system) /* found it */
> +				return msg;
> +
> +		}
> +		if (timeout--)
> +			msleep(100);
> +	} while (timeout > 0);
> +
> +	/* no reply */
> +	return NULL;
> +}
> +
> +/* add a TLV to an existing QMUX */
> +static size_t qmi_add_tlv(u8 *buf, size_t buflen, u8 type, const u8 *tlvdata, size_t datalen)
> +{
> +	struct qmux *h =  (void *)buf;
> +	struct qmi_msg *m = (void *)(buf + sizeof(struct qmux) - (h->service == QMI_CTL));
> +	struct qmi_tlv *tlv = (void *)(buf + h->len + 1);
> +	size_t tlvlen = sizeof(struct qmi_tlv) + datalen;
> +
> +	if (buflen < h->len + tlvlen - 1)
> +		return -1;
> +
> +	tlv->type = type;
> +	tlv->len = datalen;
> +	memcpy(tlv->bytes, tlvdata, datalen);
> +	h->len += tlvlen;
> +	m->len += tlvlen;
> +	return h->len + 1;
> +}
> +
> +/* get a new transaction id */
> +static __le16 new_tid(void)
> +{
> +	static __le16 tid;
> +	return ++tid;
> +}
> +
> +/* assemble a complete QMUX packet with one QMI message */
> +static size_t qmi_create_msg(char *buf, size_t buflen, u8 cid, u8 system, __le16 msgid)
> +{
> +	struct qmux *h =  (void *)buf;
> +	struct qmi_msg *m;
> +	size_t hsize = sizeof(struct qmux);
> +
> +	/* this is weird, but it seems as though QMI_CTL uses 1 byte
> +	   transaction ids while the other systems use 2 bytes as specified
> +	   in the standard */
> +	if (system == QMI_CTL)
> +		hsize--;
> +
> +	/* sanity check */
> +	if (buflen < hsize + sizeof(struct qmi_msg))
> +		return -1;
> +
> +	h->tf = 1;     /* always 1 */
> +	h->ctrl = 0;   /* 0 - control point */
> +	h->service = system;
> +	h->qmicid = cid;
> +	h->flags = 0;  /* 0 - request */
> +
> +	/* see comment above */
> +	if (system == QMI_CTL)
> +		h->tid.b = new_tid() & 0xff;
> +	else
> +		h->tid.w = new_tid();
> +
> +	m = (struct qmi_msg *)(buf + hsize);
> +	m->msgid = msgid;
> +
> +	h->len = hsize + sizeof(struct qmi_msg)  - 1;
> +	m->len = 0;
> +	return h->len + 1;
> +}
> +
> +/* QMI_CTL msg 0x0022 is "request cid", TLV 0x01 is system */
> +static int qmi_ctl_request_cid(struct qmi_state *qmi, u8 system)
> +{
> +	size_t len;
> +	u8 tlvdata[1] = { system };
> +	char buf[32];
> +
> +	/* return immediately if a CID is already allocated */
> +	if (qmi->cid[system])
> +		return qmi->cid[system];
> +
> +	/* else request a new one */
> +	len = qmi_create_msg(buf, sizeof(buf), 0, QMI_CTL, 0x0022);
> +	len = qmi_add_tlv(buf, sizeof(buf), 0x01, tlvdata, sizeof(tlvdata));
> +
> +	/* send message */
> +	return qmi_ctl_parse(qmi, qmi_send_and_wait_for_ack(qmi, buf, len, 30));
> +}
> +
> +/* QMI_WDS msg 0x0001 is "" */
> +static int qmi_wds_report(struct qmi_state *qmi)
> +{
> +	size_t len;
> +	u8 cid = qmi->cid[QMI_WDS];
> +	u8 tlvdata[1] = { 1 };	/* generic "enable" */
> +	char buf[32];
> +
> +	/* enable connection status reports */
> +	len = qmi_create_msg(buf, sizeof(buf), cid, QMI_WDS, 0x0001);
> +	len = qmi_add_tlv(buf, sizeof(buf), 0x10, tlvdata, sizeof(tlvdata));	/* current channel rate */
> +	len = qmi_add_tlv(buf, sizeof(buf), 0x15, tlvdata, sizeof(tlvdata));	/* current data bearer tech */
> +
> +	/* send message */
> +	return qmi_wds_parse(qmi, qmi_send_and_wait_for_ack(qmi, buf, len, 30));
> +}
> +
> +/* QMI_WDS msg 0x0020 is "" */
> +static int qmi_wds_start(struct qmi_state *qmi)
> +{
> +	size_t len;
> +	u8 cid = qmi->cid[QMI_WDS];
> +	char buf[32];
> +
> +	if (!cid)
> +		return -1;
> +
> +	len = qmi_create_msg(buf, sizeof(buf), cid, QMI_WDS, 0x0020);
> +
> +	/* send message */
> +	return qmi_wds_parse(qmi, qmi_send_and_wait_for_ack(qmi, buf, len, 30));
> +}
> +
> +/* QMI_WDS msg 0x0021 is "" */
> +static int qmi_wds_stop(struct qmi_state *qmi)
> +{
> +	size_t len;
> +	u8 cid = qmi->cid[QMI_WDS];
> +	char buf[32];
> +
> +	if (!cid)
> +		return -1;
> +
> +	len = qmi_create_msg(buf, sizeof(buf), cid, QMI_WDS, 0x0021);
> +	len = qmi_add_tlv(buf, sizeof(buf), 0x01, qmi->handle, sizeof(qmi->handle));	 /* packet data handle */
> +
> +	return qmi_wds_parse(qmi, qmi_send_and_wait_for_ack(qmi, buf, len, 30));
> +}
> +
> +/* QMI_WDS msg 0x0022 is "" */
> +static int qmi_wds_status(struct qmi_state *qmi)
> +{
> +	int ret;
> +	size_t len;
> +	u8 cid = qmi->cid[QMI_WDS];
> +	char buf[32];
> +
> +	if (!cid)
> +		return -1;
> +
> +	len = qmi_create_msg(buf, sizeof(buf), cid, QMI_WDS, 0x0022);
> +
> +	ret = qmi_wds_parse(qmi, qmi_send_and_wait_for_ack(qmi, buf, len, 30));
> +	if (ret < 0)
> +		return ret;
> +
> +	return qmi->wds_status;
> +}
> +
> +/* QMI_CTL msg 0x0023 is "release cid", TLV 0x01 is system + cid */
> +static int qmi_ctl_release_cid(struct qmi_state *qmi, u8 system)
> +{
> +	char buf[32];
> +	size_t len = qmi_create_msg(buf, sizeof(buf), 0, QMI_CTL, 0x0023);
> +
> +	if (system < sizeof(qmi->cid)) {
> +		u8 tlvdata[2] = { system, qmi->cid[system] };
> +		len = qmi_add_tlv(buf, sizeof(buf), 0x01, tlvdata, sizeof(tlvdata));
> +
> +	} else {
> +		return -1;
> +	}
> +
> +	/* no need to parse result - cant reuse CID in any case */
> +	qmi_send_and_wait_for_ack(qmi, buf, len, 30);
> +	qmi->cid[system] = 0;
> +
> +	return 1;
> +}
> +
> +static int qmi_dms_verify_pin(struct qmi_state *qmi)
> +{
> +	int ret;
> +	size_t len;
> +	u8 cid = qmi->cid[QMI_DMS];
> +	char buf[32];
> +
> +	if (!cid)
> +		return -1;
> +
> +	len = qmi_create_msg(buf, sizeof(buf), cid, QMI_DMS, 0x002b);
> +	ret = qmi_dms_parse(qmi, qmi_send_and_wait_for_ack(qmi, buf, len, 30));
> +	return qmi->flags && QMI_FLAG_PINOK;
> +}
> +
> +
> +/* ----- exported API ----- */
> +
> +int qmi_reset(struct qmi_state *qmi)
> +{
> +	/* NOTE:  We do NOT clear the allocated CIDs! */
> +	qmi->state = QMI_STATE_INIT;
> +	qmi->wds_status = 0;
> +	qmi->flags = 0;
> +	return 1;
> +}
> +
> +int qmi_disconnect(struct qmi_state *qmi)
> +{
> +	qmi->flags &= ~QMI_FLAG_RECV;         /* disable async receiving */
> +	qmi_wds_stop(qmi);
> +	qmi_wds_status(qmi);
> +	qmi->state = QMI_STATE_DOWN;
> +	return 1;
> +}
> +
> +/* receive a QMUX can be called in interrupt context! */
> +int qmi_recv(struct qmi_state *qmi)
> +{
> +	if (qmi->flags & QMI_FLAG_RECV) /* async receiving enabled? */
> +		return usb_submit_urb(qmi->urb, GFP_ATOMIC);
> +	else
> +		return 0;
> +}
> +
> +/*
> + * return current connection state with side effects: will run through
> + *  the states from INIT to CONN if possible
> + */
> +int qmi_conn_state(struct qmi_state *qmi)
> +{
> +	int ret;
> +
> +	dev_dbg(&qmi->dev->dev, ".state=%d, .cid[QMI_WDS]=0x%02x, .cid[QMI_DMS]=0x%02x, .flags=0x%08lx, .handle=0x%08x\n",
> +		qmi->state,
> +		qmi->cid[QMI_WDS],
> +		qmi->cid[QMI_DMS],
> +		qmi->flags,
> +		*(__le32 *)qmi->handle);
> +
> +	switch (qmi->state) {
> +	/* these states don't trigger any action */
> +	case QMI_STATE_DOWN:
> +		return 1;
> +	case QMI_STATE_UP:
> +		qmi->flags |= QMI_FLAG_RECV;  /* enable async receiving  */
> +		return 0;
> +	case QMI_STATE_ERR:
> +		return -1;
> +
> +	case QMI_STATE_INIT:
> +		qmi->flags &= ~QMI_FLAG_RECV;  /* disable async receiving */
> +
> +		/* request CIDs */
> +		if (qmi_ctl_request_cid(qmi, QMI_WDS) < 0 || qmi_ctl_request_cid(qmi, QMI_DMS) < 0) {
> +			qmi->state = QMI_STATE_ERR; /* FATAL - no way out except for unplugging the device */
> +			return -1;
> +		}
> +
> +		/* stay in this state until PIN code is entered */
> +		if (!qmi_dms_verify_pin(qmi)) {
> +			dev_notice(&qmi->dev->dev, "please enter PIN code using AT+CPIN=\"xxxx\" on a ttyUSB0 device\n");
> +			return 1;
> +		}
> +		/* check status */
> +		switch (qmi_wds_status(qmi)) {
> +		case 1: /* DISCONNECTED - attempt to connect */
> +			ret = qmi_wds_start(qmi);
> +			if (ret < 0) {
> +				dev_notice(&qmi->dev->dev, "connection failed: 0x%04x\n", -ret);
> +				break;
> +			}
> +
> +			/* fallthrough */
> +		case 2: /* CONNECTED - note that */
> +			qmi_wds_report(qmi); /* enable status reports */
> +			qmi->state = QMI_STATE_UP;
> +
> +		/* do nothing for the other states */
> +		}
> +		break;
> +
> +	default:
> +		dev_dbg(&qmi->dev->dev, "%s() unknown state=%d\n", __func__, qmi->state);
> +	}
> +	return 1;  /* not connected yet - */
> +}
> +
> +
> +/* allocate and initiate a QMI state device */
> +struct qmi_state *qmi_init(struct usb_interface *intf)
> +{
> +	struct qmi_state *qmi = kmalloc(sizeof(struct qmi_state), GFP_KERNEL);
> +
> +	if (!qmi)
> +		return NULL;
> +
> +	memset(qmi, 0, sizeof(*qmi));
> +
> +	/*
> +	 * keep track of the device receiving the control messages and the
> +	 * number of the CDC (like) control interface which is our target.
> +	 * Note that the interface might be disguised as vendor specific,
> +	 * and be a combined CDC control/data interface
> +	 */
> +	qmi->dev = interface_to_usbdev(intf);
> +	qmi->intfnr = intf->cur_altsetting->desc.bInterfaceNumber;
> +	/* FIXME: it would be useful to verify that this interface actually talks CDC */
> +
> +	/* create async receive URB */
> +	qmi->urb = usb_alloc_urb(0, GFP_KERNEL);
> +	if (!qmi->urb) {
> +		kfree(qmi);
> +		return NULL;
> +	}
> +
> +	/* pre-allocate buffer for receiving */
> +	qmi->rcvbuf = kmalloc(QMI_BUFLEN, GFP_KERNEL);
> +
> +	if (!qmi->rcvbuf) {
> +		usb_free_urb(qmi->urb);
> +		kfree(qmi);
> +		return NULL;
> +	}
> +
> +	/* usb control setup */
> +	qmi->setup.bRequestType = USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE;
> +	qmi->setup.bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
> +	qmi->setup.wValue = 0; /* zero */
> +	qmi->setup.wIndex = qmi->intfnr;
> +	qmi->setup.wLength = QMI_BUFLEN;
> +
> +	/* prepare the async receive URB */
> +	usb_fill_control_urb(qmi->urb, qmi->dev,
> +			usb_rcvctrlpipe(qmi->dev, 0),
> +			(char *)&qmi->setup,
> +			qmi->rcvbuf,
> +			QMI_BUFLEN,
> +			qmi_read_callback, qmi);
> +
> +	return qmi;
> +}
> +
> +/* disable and free a QMI state device */
> +int qmi_exit(struct qmi_state *qmi)
> +{
> +	int i;
> +
> +	/* release all CIDs - may need to sleep */
> +	for (i = 1; i < sizeof(qmi->cid); i++)
> +		qmi_ctl_release_cid(qmi, i);
> +
> +	/* free all buffers */
> +	kfree(qmi->rcvbuf);
> +
> +	/* free URBs */
> +	usb_free_urb(qmi->urb);
> +
> +	kfree(qmi);
> +
> +	return 0;
> +}
> diff --git a/drivers/net/usb/qmi.h b/drivers/net/usb/qmi.h
> new file mode 100644
> index 0000000..bff94f1
> --- /dev/null
> +++ b/drivers/net/usb/qmi.h
> @@ -0,0 +1,101 @@
> +/*
> + * Copyright (c) 2011  Bjørn Mork <bjorn-yOkvZcmFvRU@public.gmane.org>
> + * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2 as published by the Free Software Foundation.
> + */
> +
> +/*
> + * Dealing with devices using Qualcomm Messaging Interface (QMI) for
> + * configuration.  Full documentation of the protocol can be obtained
> + * from http://developer.qualcomm.com/
> + *
> + * Some of this code may be inspired by code from the Qualcomm Gobi
> + * 2000 and Gobi 3000 drivers, available at http://www.codeaurora.org/
> + */
> +
> +#define QMI_BUFLEN 128
> +
> +enum qmi_device_state {
> +	QMI_STATE_INIT = 0, /* allocate CIDs, verify pin code */
> +	QMI_STATE_DOWN,    /* connection is down */
> +	QMI_STATE_UP,      /* connection is up */
> +	QMI_STATE_ERR,     /* fatal and final error state, e.g. no CID available - no way out of here! */
> +};
> +
> +/* the different QMI subsystems we are using */
> +enum qmi_subsystems {
> +	QMI_CTL = 0,
> +	QMI_WDS,
> +	QMI_DMS,
> +	QMI_SYSMAX,
> +};
> +
> +#define QMI_FLAG_RECV		0x00000001
> +#define QMI_FLAG_PINOK		0x00000002
> +
> +struct qmi_state {
> +	struct usb_device *dev;
> +	struct urb *urb;		/* receive urb */
> +	unsigned char *rcvbuf;		/* pre-allocated receive buffer */
> +	struct usb_ctrlrequest setup;	/* the receive setup - 8 bytes */
> +	unsigned long flags;		/* used for en/dis-abling functionality on the fly */
> +	u8 state;			/* for connection state machine */
> +	u8 wds_status;			/* current value for QMI_WDS message 0x0022 or 0 if uninitialized */
> +	__le16 intfnr;			/* keeping track of the interface we are referring to */
> +	u8 handle[4];			/* connection handle needed for disconnect */
> +	u8 cid[QMI_SYSMAX];		/* keeping track of cid per subsystem */
> +};
> +
> +/* combined QMUX and SDU */
> +struct qmux {
> +	u8 tf;		/* always 1 */
> +	__le16 len;	/* excluding tf */
> +	u8 ctrl;	/* b7: sendertype 1 => service, 0 => control point */
> +	u8 service;	/* 0 => QMI_CTL, 1 => QMI_WDS, .. */
> +	u8 qmicid;	/* client id or 0xff for broadcast */
> +	u8 flags;	/* always 0 for req */
> +	union {
> +		__le16 w;  /* each control point maintains a transaction id counter - non-zero */
> +		u8 b;	/* system QMI_CTL uses one byte transaction ids! */
> +	} tid;
> +	u8 msg[];	/* one or more messages */
> +} __packed;
> +
> +struct qmi_msg {
> +	__le16 msgid;
> +	__le16 len;
> +	u8 tlv[];	/* zero or more tlvs */
> +} __packed;
> +
> +struct qmi_tlv {
> +	u8 type;
> +	__le16 len;
> +	u8 bytes[];
> +} __packed;
> +
> +struct qmi_tlv_response_data {
> +	__le16 error;
> +	__le16 code;
> +} __packed;
> +
> +
> +/* reset state to INIT */
> +extern int qmi_reset(struct qmi_state *qmi);
> +
> +/* disconnect WWAN connection */
> +extern int qmi_disconnect(struct qmi_state *qmi);
> +
> +/* must be called whenever USB_CDC_NOTIFY_RESPONSE_AVAILABLE is received */
> +extern int qmi_recv(struct qmi_state *qmi);
> +
> +/* called periodically, whenever a state transition is wanted */
> +extern int qmi_conn_state(struct qmi_state *qmi);
> +
> +/* initialize */
> +extern struct qmi_state *qmi_init(struct usb_interface *intf);
> +
> +/* clean up */
> +extern int qmi_exit(struct qmi_state *qmi);
> diff --git a/drivers/net/usb/qmi_wwan_core.c b/drivers/net/usb/qmi_wwan_core.c
> new file mode 100644
> index 0000000..bf25e33
> --- /dev/null
> +++ b/drivers/net/usb/qmi_wwan_core.c
> @@ -0,0 +1,206 @@
> +/*
> + * Copyright (c) 2011  Bjørn Mork <bjorn-yOkvZcmFvRU@public.gmane.org>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2 as published by the Free Software Foundation.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/netdevice.h>
> +#include <linux/ethtool.h>
> +#include <linux/mii.h>
> +#include <linux/usb.h>
> +#include <linux/usb/cdc.h>
> +#include <linux/usb/usbnet.h>
> +#include "qmi.h"
> +
> +/* overloading the cdc_state structure */
> +struct qmi_wwan_state {
> +	struct qmi_state		*qmi;
> +	struct usb_cdc_union_desc	*u;
> +	struct usb_cdc_ether_desc	*ether;
> +	struct usb_interface		*control;
> +	struct usb_interface		*data;
> +};
> +
> +
> +static void qmi_wwan_cdc_status(struct usbnet *dev, struct urb *urb)
> +{
> +	struct usb_cdc_notification	*event;
> +	struct qmi_wwan_state *info = (void *)&dev->data;
> +
> +	if (urb->actual_length < sizeof *event) {
> +		netdev_dbg(dev->net, "short status urb rcvd: %d bytes\n", urb->actual_length);
> +		return;
> +	}
> +
> +	event = urb->transfer_buffer;
> +	switch (event->bNotificationType) {
> +	case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
> +		qmi_recv(info->qmi);	/* request response */
> +		break;
> +	default:
> +		/* reuse usbnet() for handling other messages */
> +		usbnet_cdc_status(dev, urb);
> +	}
> +}
> +
> +static int qmi_wwan_cdc_ecmlike_bind(struct usbnet *dev, struct usb_interface *intf)
> +{
> +	int status;
> +	struct qmi_wwan_state *info = (void *)&dev->data;
> +
> +	status = usbnet_generic_cdc_bind(dev, intf);
> +	if (status < 0)
> +		return status;
> +
> +	/* initialize QMI data */
> +	info->qmi = qmi_init(intf);
> +	if (!info->qmi) {
> +		usbnet_cdc_unbind(dev, intf);
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +static int qmi_wwan_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
> +{
> +	int status;
> +	struct qmi_wwan_state *info = (void *)&dev->data;
> +
> +	memset(info, 0, sizeof(*info));
> +	status = usbnet_get_endpoints(dev, intf);
> +	if (status < 0)
> +		return status;
> +
> +	/* initialize QMI data */
> +	info->qmi = qmi_init(intf);
> +	if (!info->qmi)
> +		return -1;
> +
> +	info->control = intf;
> +	return 0;
> +}
> +
> +static void qmi_wwan_cdc_unbind(struct usbnet *dev, struct usb_interface *intf)
> +{
> +	struct qmi_wwan_state *info = (void *)&dev->data;
> +
> +	/* release our private structure */
> +	qmi_exit(info->qmi);
> +	info->qmi = NULL;
> +
> +	/* disconnect */
> +	usbnet_cdc_unbind(dev, intf);
> +}
> +
> +static int qmi_wwan_reset(struct usbnet *dev)
> +{
> +	struct qmi_wwan_state *info = (void *)&dev->data;
> +
> +	qmi_reset(info->qmi);
> +	return 1;
> +}
> +
> +static int qmi_wwan_stop(struct usbnet *dev)
> +{
> +	struct qmi_wwan_state *info = (void *)&dev->data;
> +
> +	qmi_disconnect(info->qmi);
> +	return 1;
> +}
> +
> +/* abusing check_connect for triggering QMI state transitions */
> +static int qmi_wwan_check_connect(struct usbnet *dev)
> +{
> +	struct qmi_wwan_state *info = (void *)&dev->data;
> +	return qmi_conn_state(info->qmi);
> +}
> +
> +static const struct driver_info	qmi_wwan_ecm_info = {
> +	.description =	"QMI speaking CDC ECM like wwan device",
> +	.flags =	 FLAG_WWAN,
> +	.bind =		 qmi_wwan_cdc_ecmlike_bind,
> +	.unbind =	 qmi_wwan_cdc_unbind,
> +	.status =	 qmi_wwan_cdc_status,
> +	.check_connect = qmi_wwan_check_connect,
> +	.stop =          qmi_wwan_stop,
> +	.reset =         qmi_wwan_reset,
> +};
> +
> +static const struct driver_info	qmi_wwan_info = {
> +	.description =	"QMI speaking wwan device",
> +	.flags =	 FLAG_WWAN,
> +	.bind =		 qmi_wwan_cdc_bind,
> +	.unbind =	 qmi_wwan_cdc_unbind,
> +	.status =	 qmi_wwan_cdc_status,
> +	.check_connect = qmi_wwan_check_connect,
> +	.stop =          qmi_wwan_stop,
> +	.reset =         qmi_wwan_reset,
> +};
> +
> +#define HUAWEI_VENDOR_ID	0x12D1
> +
> +static const struct usb_device_id products[] = {
> +{
> +	/* Qmi_Wwan E392, E398, ++? */
> +	.match_flags    =   USB_DEVICE_ID_MATCH_VENDOR
> +		 | USB_DEVICE_ID_MATCH_INT_INFO,
> +	.idVendor               = HUAWEI_VENDOR_ID,
> +	.bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
> +	.bInterfaceSubClass	= 1,
> +	.bInterfaceProtocol	= 9,
> +	.driver_info = (unsigned long)&qmi_wwan_ecm_info,
> +}, {
> +	/* Qmi_Wwan device id 1413 ++? */
> +	.match_flags    =   USB_DEVICE_ID_MATCH_VENDOR
> +		 | USB_DEVICE_ID_MATCH_INT_INFO,
> +	.idVendor               = HUAWEI_VENDOR_ID,
> +	.bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
> +	.bInterfaceSubClass	= 6,
> +	.bInterfaceProtocol	= 255,
> +	.driver_info = (unsigned long)&qmi_wwan_ecm_info,
> +}, {
> +	/* Qmi_Wwan E392, E398, ++? "Windows mode" */
> +	.match_flags    =   USB_DEVICE_ID_MATCH_VENDOR
> +		 | USB_DEVICE_ID_MATCH_INT_INFO,
> +	.idVendor               = HUAWEI_VENDOR_ID,
> +	.bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
> +	.bInterfaceSubClass	= 1,
> +	.bInterfaceProtocol	= 17,
> +	.driver_info = (unsigned long)&qmi_wwan_info,
> +},
> +{ },		/* END */
> +};
> +MODULE_DEVICE_TABLE(usb, products);
> +
> +static struct usb_driver qmi_wwan_driver = {
> +	.name =		"qmi_wwan",
> +	.id_table =	products,
> +	.probe =	usbnet_probe,
> +	.disconnect =	usbnet_disconnect,
> +	.suspend =	usbnet_suspend,
> +	.resume =	usbnet_resume,
> +	.reset_resume =	usbnet_resume,
> +	.supports_autosuspend = 1,
> +};
> +
> +static int __init qmi_wwan_init(void)
> +{
> +	/* we remap struct (cdc_state) so we should be compatible */
> +	BUILD_BUG_ON(sizeof(struct cdc_state) != sizeof(struct qmi_wwan_state));
> +
> +	return usb_register(&qmi_wwan_driver);
> +}
> +module_init(qmi_wwan_init);
> +
> +static void __exit qmi_wwan_exit(void)
> +{
> +	usb_deregister(&qmi_wwan_driver);
> +}
> +module_exit(qmi_wwan_exit);
> +
> +MODULE_AUTHOR("Bjørn Mork <bjorn-yOkvZcmFvRU@public.gmane.org>");
> +MODULE_DESCRIPTION("Qualcomm Messaging Interface (QMI) WWAN driver");
> +MODULE_LICENSE("GPL");



--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: linux-3.0.x regression with ipv4 routes having mtu
From: David Miller @ 2011-12-14 17:50 UTC (permalink / raw)
  To: timo.teras; +Cc: netdev
In-Reply-To: <4EE8C6A8.3060308@iki.fi>

From: Timo Teräs <timo.teras@iki.fi>
Date: Wed, 14 Dec 2011 17:54:16 +0200

> So something is does not get updated here. This used to work though.
> The current production boxes where I know this work is 2.6.38.8.

We store the PMTU externally in inetpeer entries, Eric Dumazet
fixed recently but that fix hasn't been submitted to -stable
yet.

I'd recommend using 3.1.x if possible if you can't wait.

^ permalink raw reply

* Re: linux-3.0.x regression with ipv4 routes having mtu
From: Eric Dumazet @ 2011-12-14 17:57 UTC (permalink / raw)
  To: David Miller; +Cc: timo.teras, netdev
In-Reply-To: <20111214.125010.82857701285437834.davem@davemloft.net>

Le mercredi 14 décembre 2011 à 12:50 -0500, David Miller a écrit :
> From: Timo Teräs <timo.teras@iki.fi>
> Date: Wed, 14 Dec 2011 17:54:16 +0200
> 
> > So something is does not get updated here. This used to work though.
> > The current production boxes where I know this work is 2.6.38.8.
> 
> We store the PMTU externally in inetpeer entries, Eric Dumazet
> fixed recently but that fix hasn't been submitted to -stable
> yet.
> 
> I'd recommend using 3.1.x if possible if you can't wait.

I am wondering if this not another problem ?

I did a quick check on net-next, and it seems to have same problem.

Sorry, I have to run right now and wont be able to full check before 4/5
hours.

^ permalink raw reply

* Re: linux-3.0.x regression with ipv4 routes having mtu
From: Timo Teräs @ 2011-12-14 18:22 UTC (permalink / raw)
  To: David Miller; +Cc: netdev
In-Reply-To: <20111214.125010.82857701285437834.davem@davemloft.net>

On 12/14/2011 07:50 PM, David Miller wrote:
> From: Timo Teräs <timo.teras@iki.fi>
> Date: Wed, 14 Dec 2011 17:54:16 +0200
> 
>> So something is does not get updated here. This used to work though.
>> The current production boxes where I know this work is 2.6.38.8.
> 
> We store the PMTU externally in inetpeer entries, Eric Dumazet
> fixed recently but that fix hasn't been submitted to -stable
> yet.
> 
> I'd recommend using 3.1.x if possible if you can't wait.

I'll test 3.1.x though to verify that this is fixed.

I'd prefer 3.0.x for now as we have some other patchsets on production
kernels that are not yet available for 3.1.x. And this does sound like
-stable material anyway. If it's just a few commits, I could backport
and cherry-pick them. Could some point me the commits in question?

Thanks,
 Timo

^ permalink raw reply

* Re: [PATCH net-next 1/1] bnx2x: handle vpd data longer than 128 bytes
From: David Miller @ 2011-12-14 18:35 UTC (permalink / raw)
  To: barak; +Cc: netdev, eilong
In-Reply-To: <1323857693-24527-1-git-send-email-barak@broadcom.com>

From: "Barak Witkowski" <barak@broadcom.com>
Date: Wed, 14 Dec 2011 12:14:53 +0200

> Signed-off-by: Barak Witkowski <barak@broadcom.com>
> Signed-off-by: Eilon Greenstein <eilong@broadcom.com>

Applied.

^ permalink raw reply

* Re: [PATCH net-next] cls_flow: remove one dynamic array
From: David Miller @ 2011-12-14 18:35 UTC (permalink / raw)
  To: eric.dumazet; +Cc: netdev
In-Reply-To: <1323865800.2334.15.camel@edumazet-HP-Compaq-6005-Pro-SFF-PC>

From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Wed, 14 Dec 2011 13:30:00 +0100

> Its better to use a predefined size for this small automatic variable.
> 
> Removes a sparse error as well :
> 
> net/sched/cls_flow.c:288:13: error: bad constant expression
> 
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>

Applied.

^ permalink raw reply

* Re: [PATCH net-next] net: ping: remove some sparse errors
From: David Miller @ 2011-12-14 18:35 UTC (permalink / raw)
  To: eric.dumazet; +Cc: netdev
In-Reply-To: <1323870688.2334.24.camel@edumazet-HP-Compaq-6005-Pro-SFF-PC>

From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Wed, 14 Dec 2011 14:51:28 +0100

> net/ipv4/sysctl_net_ipv4.c:78:6: warning: symbol 'inet_get_ping_group_range_table'
> was not declared. Should it be static?
> 
> net/ipv4/sysctl_net_ipv4.c:119:31: warning: incorrect type in argument 2
> (different signedness)
> net/ipv4/sysctl_net_ipv4.c:119:31: expected int *range
> net/ipv4/sysctl_net_ipv4.c:119:31: got unsigned int *<noident>
> 
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>

Applied.

^ permalink raw reply

* Re: [PATCH net-next] inet: remove rcu protection on tw_net
From: David Miller @ 2011-12-14 18:35 UTC (permalink / raw)
  To: eric.dumazet; +Cc: netdev, ebiederm
In-Reply-To: <1323874713.2334.33.camel@edumazet-HP-Compaq-6005-Pro-SFF-PC>

From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Wed, 14 Dec 2011 15:58:33 +0100

> commit b099ce2602d806 (net: Batch inet_twsk_purge) added rcu protection
> on tw_net for no obvious reason.
> 
> struct net are refcounted anyway since timewait sockets escape from rcu
> protected sections. tw_net stay valid for the whole timwait lifetime.
> 
> This also removes a lot of sparse errors.
> 
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
> CC: Eric W. Biederman <ebiederm@xmission.com>

Applied.

^ permalink raw reply

* Re: linux-next: build failure after merge of the final tree (net-next tree related)
From: David Miller @ 2011-12-14 18:35 UTC (permalink / raw)
  To: sfr; +Cc: netdev, linux-next, linux-kernel, glommer, tj, akpm
In-Reply-To: <20111214180752.7893d5460c6fca84d63cab0e@canb.auug.org.au>

From: Stephen Rothwell <sfr@canb.auug.org.au>
Date: Wed, 14 Dec 2011 18:07:52 +1100

> After merging the final tree, today's linux-next build (powerpc
> allnoconfig) failed like this:
> 
> In file included from include/linux/tcp.h:211:0,
>                  from include/linux/ipv6.h:221,
>                  from include/net/ipv6.h:16,
>                  from include/linux/sunrpc/clnt.h:26,
>                  from include/linux/nfs_fs.h:50,
>                  from init/do_mounts.c:20:
> include/net/sock.h: In function 'memcg_memory_allocated_add':
> include/net/sock.h:1000:20: error: 'OVER_LIMIT' undeclared (first use in this function)
> 
> And several other similar ones.
> 
> Caused by commit e1aab161e013 ("socket: initial cgroup code") from the
> net-next tree. OVER_LIMIT is only defined it CONFIG_INET is set.
> 
> I applied the following (stupid) patch for today (just because reverting
> the above commit would probably be too hard - we have cgroup stuff all
> over the place :-( ):

Glauber, please fix this properly.

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox