* [PATCH net-next] ipv4: tcp: ip_send_unicast_reply() is not BH safe
From: Eric Dumazet @ 2016-05-06 16:46 UTC (permalink / raw)
To: David Miller; +Cc: netdev, Andres Lagar-Cavilla, edumazet
From: Eric Dumazet <edumazet@google.com>
I forgot that ip_send_unicast_reply() is not BH safe (yet).
Disabling preemption before calling it was not a good move.
Fixes: c10d9310edf5 ("tcp: do not assume TCP code is non preemptible")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: Andres Lagar-Cavilla <andreslc@google.com>
---
net/ipv4/tcp_ipv4.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index a7ab9472d64560d86ea24ac1b6e1a7800f89989d..8219d0d8dc8370d0d3e6fc4cd17b4925617968ab 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -692,7 +692,7 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
offsetof(struct inet_timewait_sock, tw_bound_dev_if));
arg.tos = ip_hdr(skb)->tos;
- preempt_disable();
+ local_bh_disable();
ip_send_unicast_reply(*this_cpu_ptr(net->ipv4.tcp_sk),
skb, &TCP_SKB_CB(skb)->header.h4.opt,
ip_hdr(skb)->saddr, ip_hdr(skb)->daddr,
@@ -700,7 +700,7 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
__TCP_INC_STATS(net, TCP_MIB_OUTSEGS);
__TCP_INC_STATS(net, TCP_MIB_OUTRSTS);
- preempt_enable();
+ local_bh_enable();
#ifdef CONFIG_TCP_MD5SIG
out:
@@ -776,14 +776,14 @@ static void tcp_v4_send_ack(struct net *net,
if (oif)
arg.bound_dev_if = oif;
arg.tos = tos;
- preempt_disable();
+ local_bh_disable();
ip_send_unicast_reply(*this_cpu_ptr(net->ipv4.tcp_sk),
skb, &TCP_SKB_CB(skb)->header.h4.opt,
ip_hdr(skb)->saddr, ip_hdr(skb)->daddr,
&arg, arg.iov[0].iov_len);
__TCP_INC_STATS(net, TCP_MIB_OUTSEGS);
- preempt_enable();
+ local_bh_enable();
}
static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb)
^ permalink raw reply related
* Re: [PATCH] Documentation/networking: more accurate LCO explanation
From: Alexander Duyck @ 2016-05-06 16:29 UTC (permalink / raw)
To: Shmulik Ladkani; +Cc: David S . Miller, Netdev, Edward Cree
In-Reply-To: <1462550263-9742-1-git-send-email-shmulik.ladkani@gmail.com>
On Fri, May 6, 2016 at 8:57 AM, Shmulik Ladkani
<shmulik.ladkani@gmail.com> wrote:
> In few places the term "ones-complement sum" was used but the actual
> meaning is "the complement of the ones-complement sum".
Looks like there might still be a few minor corrections needed.
Comments inline below.
>
> Signed-off-by: Shmulik Ladkani <shmulik.ladkani@gmail.com>
> ---
>
> I assume readers interpret the term "ones-complement sum" as the sum
> using one's complement arithmentic, without the final bitwise
> complement of sum's result.
> Hence I added "the complement of" where applicable.
>
> Documentation/networking/checksum-offloads.txt | 10 +++++-----
> 1 file changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/Documentation/networking/checksum-offloads.txt b/Documentation/networking/checksum-offloads.txt
> index de2a327766..9567200e1f 100644
> --- a/Documentation/networking/checksum-offloads.txt
> +++ b/Documentation/networking/checksum-offloads.txt
> @@ -69,17 +69,17 @@ LCO: Local Checksum Offload
> LCO is a technique for efficiently computing the outer checksum of an
> encapsulated datagram when the inner checksum is due to be offloaded.
> The ones-complement sum of a correctly checksummed TCP or UDP packet is
> - equal to the sum of the pseudo header, because everything else gets
> - 'cancelled out' by the checksum field. This is because the sum was
> + equal to the complement of the sum of the pseudo header, because everything
> + else gets 'cancelled out' by the checksum field. This is because the sum was
> complemented before being written to the checksum field.
> More generally, this holds in any case where the 'IP-style' ones complement
> checksum is used, and thus any checksum that TX Checksum Offload supports.
> That is, if we have set up TX Checksum Offload with a start/offset pair, we
> know that _after the device has filled in that checksum_, the ones
> complement sum from csum_start to the end of the packet will be equal to
> - _whatever value we put in the checksum field beforehand_. This allows us
> - to compute the outer checksum without looking at the payload: we simply
> - stop summing when we get to csum_start, then add the 16-bit word at
> + the complement of _whatever value we put in the checksum field beforehand_.
I don't really see the point of using an underscore before and after
that statement. If it was only one or two words it might work for
emphasis but the statement is large enough that starting it with an
underscore just makes it harder to read.
> + This allows us to compute the outer checksum without looking at the payload:
> + we simply stop summing when we get to csum_start, then add the 16-bit word at
> (csum_start + csum_offset).
You don't add the 16-bit word you add the compliment of the 16 bit word.
^ permalink raw reply
* RE: [Intel-wired-lan] NULL dereference on v4.1.x while enabling VF
From: Skidmore, Donald C @ 2016-05-06 16:26 UTC (permalink / raw)
To: William Dauchy; +Cc: NETDEV, intel-wired-lan@lists.osuosl.org, Alex Duyck
In-Reply-To: <CAJ75kXY_PMK0iTyM5bbhQoAS9je5AqpW67jyZpJEUoNw0BPcqw@mail.gmail.com>
Hey William,
My validation hasn't be able to recreate the dereference on v4.1.x, v4.5.x or net_next. Where exactly did you place the two line script in your rc scripts. Our validation was able to run it as soon as ~14 second after the first boot message logged in dmesg. Is this anywhere close to where you were executing it? Likewise he has attempted running at both run level 3 and 5 in case that changed any of the times of how soon the rc scripts are executed. So the more detail you can give of setup the more it might help us recreate what you are seeing.
Thanks,
-Don Skidmore <donald.c.skidmore>
> -----Original Message-----
> From: William Dauchy [mailto:wdauchy@gmail.com]
> Sent: Tuesday, May 03, 2016 5:33 AM
> To: Skidmore, Donald C <donald.c.skidmore@intel.com>
> Cc: NETDEV <netdev@vger.kernel.org>; intel-wired-lan@lists.osuosl.org;
> Alex Duyck <aduyck@mirantis.com>
> Subject: Re: [Intel-wired-lan] NULL dereference on v4.1.x while enabling VF
>
> Hello Don,
>
> Thank you for your reply.
>
> On Mon, May 2, 2016 at 11:33 PM, Skidmore, Donald C
> <donald.c.skidmore@intel.com> wrote:
> > Thanks for reporting the dereference. Could you provide a little more
> detail on how you created this issue? Are you just running the two
> commands (ip, sriov_numvfs) in some rc script and if you put a few second
> sleep in front of it you don't see the failure?
>
> Your understanding is correct; a rc script is run with ip and echo in numvfs
> commands. I tried to reduce it to the minimum. If I put a sleep
> 20 in front of it, it does not crash. I also forgot to add I did not had the issue
> in 3.14.x with the same script.
>
> Best,
> --
> William
^ permalink raw reply
* Re: [PATCH] Documentation/networking: more accurate LCO explanation
From: Edward Cree @ 2016-05-06 16:12 UTC (permalink / raw)
To: Shmulik Ladkani, David S . Miller, netdev
In-Reply-To: <1462550263-9742-1-git-send-email-shmulik.ladkani@gmail.com>
On 06/05/16 16:57, Shmulik Ladkani wrote:
> In few places the term "ones-complement sum" was used but the actual
> meaning is "the complement of the ones-complement sum".
>
> Signed-off-by: Shmulik Ladkani <shmulik.ladkani@gmail.com>
Acked-by: Edward Cree <ecree@solarflare.com>
^ permalink raw reply
* Re: [PATCH] rtlwifi: pci: use dev_kfree_skb_irq instead of kfree_skb in rtl_pci_reset_trx_ring
From: Larry Finger @ 2016-05-06 16:12 UTC (permalink / raw)
To: Wang YanQing, chaoming_li, kvalo, linux-wireless, netdev,
linux-kernel
In-Reply-To: <20160505171904.GA16619@udknight>
On 05/05/2016 12:19 PM, Wang YanQing wrote:
> We can't use kfree_skb in irq disable context, because spin_lock_irqsave
> make sure we are always in irq disable context, use dev_kfree_skb_irq
> instead of kfree_skb is better than dev_kfree_skb_any.
>
> This patch fix below kernel warning:
> [ 7612.095528] ------------[ cut here ]------------
> [ 7612.095546] WARNING: CPU: 3 PID: 4460 at kernel/softirq.c:150 __local_bh_enable_ip+0x58/0x80()
> [ 7612.095550] Modules linked in: rtl8723be x86_pkg_temp_thermal btcoexist rtl_pci rtlwifi rtl8723_common
> [ 7612.095567] CPU: 3 PID: 4460 Comm: ifconfig Tainted: G W 4.4.0+ #4
> [ 7612.095570] Hardware name: LENOVO 20DFA04FCD/20DFA04FCD, BIOS J5ET48WW (1.19 ) 08/27/2015
> [ 7612.095574] 00000000 00000000 da37fc70 c12ce7c5 00000000 da37fca0 c104cc59 c19d4454
> [ 7612.095584] 00000003 0000116c c19d4784 00000096 c10508a8 c10508a8 00000200 c1b42400
> [ 7612.095594] f29be780 da37fcb0 c104ccad 00000009 00000000 da37fcbc c10508a8 f21f08b8
> [ 7612.095604] Call Trace:
> [ 7612.095614] [<c12ce7c5>] dump_stack+0x41/0x5c
> [ 7612.095620] [<c104cc59>] warn_slowpath_common+0x89/0xc0
> [ 7612.095628] [<c10508a8>] ? __local_bh_enable_ip+0x58/0x80
> [ 7612.095634] [<c10508a8>] ? __local_bh_enable_ip+0x58/0x80
> [ 7612.095640] [<c104ccad>] warn_slowpath_null+0x1d/0x20
> [ 7612.095646] [<c10508a8>] __local_bh_enable_ip+0x58/0x80
> [ 7612.095653] [<c16b7d34>] destroy_conntrack+0x64/0xa0
> [ 7612.095660] [<c16b300f>] nf_conntrack_destroy+0xf/0x20
> [ 7612.095665] [<c1677565>] skb_release_head_state+0x55/0xa0
> [ 7612.095670] [<c16775bb>] skb_release_all+0xb/0x20
> [ 7612.095674] [<c167760b>] __kfree_skb+0xb/0x60
> [ 7612.095679] [<c16776f0>] kfree_skb+0x30/0x70
> [ 7612.095686] [<f81b869d>] ? rtl_pci_reset_trx_ring+0x22d/0x370 [rtl_pci]
> [ 7612.095692] [<f81b869d>] rtl_pci_reset_trx_ring+0x22d/0x370 [rtl_pci]
> [ 7612.095698] [<f81b87f9>] rtl_pci_start+0x19/0x190 [rtl_pci]
> [ 7612.095705] [<f81970e6>] rtl_op_start+0x56/0x90 [rtlwifi]
> [ 7612.095712] [<c17e3f16>] drv_start+0x36/0xc0
> [ 7612.095717] [<c17f5ab3>] ieee80211_do_open+0x2d3/0x890
> [ 7612.095725] [<c16820fe>] ? call_netdevice_notifiers_info+0x2e/0x60
> [ 7612.095730] [<c17f60bd>] ieee80211_open+0x4d/0x50
> [ 7612.095736] [<c16891b3>] __dev_open+0xa3/0x130
> [ 7612.095742] [<c183fa53>] ? _raw_spin_unlock_bh+0x13/0x20
> [ 7612.095748] [<c1689499>] __dev_change_flags+0x89/0x140
> [ 7612.095753] [<c127c70d>] ? selinux_capable+0xd/0x10
> [ 7612.095759] [<c1689589>] dev_change_flags+0x29/0x60
> [ 7612.095765] [<c1700b93>] devinet_ioctl+0x553/0x670
> [ 7612.095772] [<c12db758>] ? _copy_to_user+0x28/0x40
> [ 7612.095777] [<c17018b5>] inet_ioctl+0x85/0xb0
> [ 7612.095783] [<c166e647>] sock_ioctl+0x67/0x260
> [ 7612.095788] [<c166e5e0>] ? sock_fasync+0x80/0x80
> [ 7612.095795] [<c115c99b>] do_vfs_ioctl+0x6b/0x550
> [ 7612.095800] [<c127c812>] ? selinux_file_ioctl+0x102/0x1e0
> [ 7612.095807] [<c10a8914>] ? timekeeping_suspend+0x294/0x320
> [ 7612.095813] [<c10a256a>] ? __hrtimer_run_queues+0x14a/0x210
> [ 7612.095820] [<c1276e24>] ? security_file_ioctl+0x34/0x50
> [ 7612.095827] [<c115cef0>] SyS_ioctl+0x70/0x80
> [ 7612.095832] [<c1001804>] do_fast_syscall_32+0x84/0x120
> [ 7612.095839] [<c183ff91>] sysenter_past_esp+0x36/0x55
> [ 7612.095844] ---[ end trace 97e9c637a20e8348 ]---
>
> Signed-off-by: Wang YanQing <udknight@gmail.com>
> ---
> drivers/net/wireless/realtek/rtlwifi/pci.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c
> index 1ac41b8..99a3a03 100644
> --- a/drivers/net/wireless/realtek/rtlwifi/pci.c
> +++ b/drivers/net/wireless/realtek/rtlwifi/pci.c
> @@ -1572,7 +1572,7 @@ int rtl_pci_reset_trx_ring(struct ieee80211_hw *hw)
> true,
> HW_DESC_TXBUFF_ADDR),
> skb->len, PCI_DMA_TODEVICE);
> - kfree_skb(skb);
> + dev_kfree_skb_irq(skb);
> ring->idx = (ring->idx + 1) % ring->entries;
> }
> ring->idx = 0;
After testing, this patch is OK other than needing a Cc to stable. Please fix
that and resubmit V2.
Larry
^ permalink raw reply
* [PATCH] Documentation/networking: more accurate LCO explanation
From: Shmulik Ladkani @ 2016-05-06 15:57 UTC (permalink / raw)
To: David S . Miller, netdev; +Cc: Edward Cree, shmulik.ladkani
In few places the term "ones-complement sum" was used but the actual
meaning is "the complement of the ones-complement sum".
Signed-off-by: Shmulik Ladkani <shmulik.ladkani@gmail.com>
---
I assume readers interpret the term "ones-complement sum" as the sum
using one's complement arithmentic, without the final bitwise
complement of sum's result.
Hence I added "the complement of" where applicable.
Documentation/networking/checksum-offloads.txt | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/Documentation/networking/checksum-offloads.txt b/Documentation/networking/checksum-offloads.txt
index de2a327766..9567200e1f 100644
--- a/Documentation/networking/checksum-offloads.txt
+++ b/Documentation/networking/checksum-offloads.txt
@@ -69,17 +69,17 @@ LCO: Local Checksum Offload
LCO is a technique for efficiently computing the outer checksum of an
encapsulated datagram when the inner checksum is due to be offloaded.
The ones-complement sum of a correctly checksummed TCP or UDP packet is
- equal to the sum of the pseudo header, because everything else gets
- 'cancelled out' by the checksum field. This is because the sum was
+ equal to the complement of the sum of the pseudo header, because everything
+ else gets 'cancelled out' by the checksum field. This is because the sum was
complemented before being written to the checksum field.
More generally, this holds in any case where the 'IP-style' ones complement
checksum is used, and thus any checksum that TX Checksum Offload supports.
That is, if we have set up TX Checksum Offload with a start/offset pair, we
know that _after the device has filled in that checksum_, the ones
complement sum from csum_start to the end of the packet will be equal to
- _whatever value we put in the checksum field beforehand_. This allows us
- to compute the outer checksum without looking at the payload: we simply
- stop summing when we get to csum_start, then add the 16-bit word at
+ the complement of _whatever value we put in the checksum field beforehand_.
+ This allows us to compute the outer checksum without looking at the payload:
+ we simply stop summing when we get to csum_start, then add the 16-bit word at
(csum_start + csum_offset).
Then, when the true inner checksum is filled in (either by hardware or by
skb_checksum_help()), the outer checksum will become correct by virtue of
--
2.7.4
^ permalink raw reply related
* [PATCH net-next] fq_codel: add memory limitation per queue
From: Eric Dumazet @ 2016-05-06 15:55 UTC (permalink / raw)
To: David Miller; +Cc: Jesper Dangaard Brouer, Dave Täht, netdev, moeller0
In-Reply-To: <1462541156.13075.34.camel@edumazet-glaptop3.roam.corp.google.com>
From: Eric Dumazet <edumazet@google.com>
On small embedded routers, one wants to control maximal amount of
memory used by fq_codel, instead of controlling number of packets or
bytes, since GRO/TSO make these not practical.
Assuming skb->truesize is accurate, we have to keep track of
skb->truesize sum for skbs in queue.
This patch adds a new TCA_FQ_CODEL_MEMORY_LIMIT attribute.
I chose a default value of 32 MBytes, which looks reasonable even
for heavy duty usages. (Prior fq_codel users should not be hurt
when they upgrade their kernels)
Two fields are added to tc_fq_codel_qd_stats to report :
- Current memory usage
- Number of drops caused by memory limits
# tc qd replace dev eth1 root est 1sec 4sec fq_codel memory_limit 4M
..
# tc -s -d qd sh dev eth1
qdisc fq_codel 8008: root refcnt 257 limit 10240p flows 1024
quantum 1514 target 5.0ms interval 100.0ms memory_limit 4Mb ecn
Sent 2083566791363 bytes 1376214889 pkt (dropped 4994406, overlimits 0
requeues 21705223)
rate 9841Mbit 812549pps backlog 3906120b 376p requeues 21705223
maxpacket 68130 drop_overlimit 4994406 new_flow_count 28855414
ecn_mark 0 memory_used 4190048 drop_overmemory 4994406
new_flows_len 1 old_flows_len 177
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Jesper Dangaard Brouer <brouer@redhat.com>
Cc: Dave Täht <dave.taht@gmail.com>
Cc: Sebastian Möller <moeller0@gmx.de>
---
include/uapi/linux/pkt_sched.h | 3 +++
net/sched/sch_fq_codel.c | 27 ++++++++++++++++++++++++---
2 files changed, 27 insertions(+), 3 deletions(-)
diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h
index a11afecd4482..2382eed50278 100644
--- a/include/uapi/linux/pkt_sched.h
+++ b/include/uapi/linux/pkt_sched.h
@@ -719,6 +719,7 @@ enum {
TCA_FQ_CODEL_QUANTUM,
TCA_FQ_CODEL_CE_THRESHOLD,
TCA_FQ_CODEL_DROP_BATCH_SIZE,
+ TCA_FQ_CODEL_MEMORY_LIMIT,
__TCA_FQ_CODEL_MAX
};
@@ -743,6 +744,8 @@ struct tc_fq_codel_qd_stats {
__u32 new_flows_len; /* count of flows in new list */
__u32 old_flows_len; /* count of flows in old list */
__u32 ce_mark; /* packets above ce_threshold */
+ __u32 memory_usage; /* in bytes */
+ __u32 drop_overmemory;
};
struct tc_fq_codel_cl_stats {
diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c
index e7b42b0d5145..bb8bd9314629 100644
--- a/net/sched/sch_fq_codel.c
+++ b/net/sched/sch_fq_codel.c
@@ -60,8 +60,11 @@ struct fq_codel_sched_data {
u32 perturbation; /* hash perturbation */
u32 quantum; /* psched_mtu(qdisc_dev(sch)); */
u32 drop_batch_size;
+ u32 memory_limit;
struct codel_params cparams;
struct codel_stats cstats;
+ u32 memory_usage;
+ u32 drop_overmemory;
u32 drop_overlimit;
u32 new_flow_count;
@@ -143,6 +146,7 @@ static unsigned int fq_codel_drop(struct Qdisc *sch, unsigned int max_packets)
unsigned int maxbacklog = 0, idx = 0, i, len;
struct fq_codel_flow *flow;
unsigned int threshold;
+ unsigned int mem = 0;
/* Queue is full! Find the fat flow and drop packet(s) from it.
* This might sound expensive, but with 1024 flows, we scan
@@ -167,11 +171,13 @@ static unsigned int fq_codel_drop(struct Qdisc *sch, unsigned int max_packets)
do {
skb = dequeue_head(flow);
len += qdisc_pkt_len(skb);
+ mem += skb->truesize;
kfree_skb(skb);
} while (++i < max_packets && len < threshold);
flow->dropped += i;
q->backlogs[idx] -= len;
+ q->memory_usage -= mem;
sch->qstats.drops += i;
sch->qstats.backlog -= len;
sch->q.qlen -= i;
@@ -193,6 +199,7 @@ static int fq_codel_enqueue(struct sk_buff *skb, struct Qdisc *sch)
unsigned int idx, prev_backlog, prev_qlen;
struct fq_codel_flow *flow;
int uninitialized_var(ret);
+ bool memory_limited;
idx = fq_codel_classify(skb, sch, &ret);
if (idx == 0) {
@@ -215,7 +222,9 @@ static int fq_codel_enqueue(struct sk_buff *skb, struct Qdisc *sch)
flow->deficit = q->quantum;
flow->dropped = 0;
}
- if (++sch->q.qlen <= sch->limit)
+ q->memory_usage += skb->truesize;
+ memory_limited = q->memory_usage > q->memory_limit;
+ if (++sch->q.qlen <= sch->limit && !memory_limited)
return NET_XMIT_SUCCESS;
prev_backlog = sch->qstats.backlog;
@@ -229,7 +238,8 @@ static int fq_codel_enqueue(struct sk_buff *skb, struct Qdisc *sch)
ret = fq_codel_drop(sch, q->drop_batch_size);
q->drop_overlimit += prev_qlen - sch->q.qlen;
-
+ if (memory_limited)
+ q->drop_overmemory += prev_qlen - sch->q.qlen;
/* As we dropped packet(s), better let upper stack know this */
qdisc_tree_reduce_backlog(sch, prev_qlen - sch->q.qlen,
prev_backlog - sch->qstats.backlog);
@@ -308,6 +318,7 @@ begin:
list_del_init(&flow->flowchain);
goto begin;
}
+ q->memory_usage -= skb->truesize;
qdisc_bstats_update(sch, skb);
flow->deficit -= qdisc_pkt_len(skb);
/* We cant call qdisc_tree_reduce_backlog() if our qlen is 0,
@@ -355,6 +366,7 @@ static const struct nla_policy fq_codel_policy[TCA_FQ_CODEL_MAX + 1] = {
[TCA_FQ_CODEL_QUANTUM] = { .type = NLA_U32 },
[TCA_FQ_CODEL_CE_THRESHOLD] = { .type = NLA_U32 },
[TCA_FQ_CODEL_DROP_BATCH_SIZE] = { .type = NLA_U32 },
+ [TCA_FQ_CODEL_MEMORY_LIMIT] = { .type = NLA_U32 },
};
static int fq_codel_change(struct Qdisc *sch, struct nlattr *opt)
@@ -409,7 +421,11 @@ static int fq_codel_change(struct Qdisc *sch, struct nlattr *opt)
if (tb[TCA_FQ_CODEL_DROP_BATCH_SIZE])
q->drop_batch_size = min(1U, nla_get_u32(tb[TCA_FQ_CODEL_DROP_BATCH_SIZE]));
- while (sch->q.qlen > sch->limit) {
+ if (tb[TCA_FQ_CODEL_MEMORY_LIMIT])
+ q->memory_limit = min(1U << 31, nla_get_u32(tb[TCA_FQ_CODEL_MEMORY_LIMIT]));
+
+ while (sch->q.qlen > sch->limit ||
+ q->memory_usage > q->memory_limit) {
struct sk_buff *skb = fq_codel_dequeue(sch);
q->cstats.drop_len += qdisc_pkt_len(skb);
@@ -454,6 +470,7 @@ static int fq_codel_init(struct Qdisc *sch, struct nlattr *opt)
sch->limit = 10*1024;
q->flows_cnt = 1024;
+ q->memory_limit = 32 << 20; /* 32 MBytes */
q->drop_batch_size = 64;
q->quantum = psched_mtu(qdisc_dev(sch));
q->perturbation = prandom_u32();
@@ -515,6 +532,8 @@ static int fq_codel_dump(struct Qdisc *sch, struct sk_buff *skb)
q->quantum) ||
nla_put_u32(skb, TCA_FQ_CODEL_DROP_BATCH_SIZE,
q->drop_batch_size) ||
+ nla_put_u32(skb, TCA_FQ_CODEL_MEMORY_LIMIT,
+ q->memory_limit) ||
nla_put_u32(skb, TCA_FQ_CODEL_FLOWS,
q->flows_cnt))
goto nla_put_failure;
@@ -543,6 +562,8 @@ static int fq_codel_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
st.qdisc_stats.ecn_mark = q->cstats.ecn_mark;
st.qdisc_stats.new_flow_count = q->new_flow_count;
st.qdisc_stats.ce_mark = q->cstats.ce_mark;
+ st.qdisc_stats.memory_usage = q->memory_usage;
+ st.qdisc_stats.drop_overmemory = q->drop_overmemory;
list_for_each(pos, &q->new_flows)
st.qdisc_stats.new_flows_len++;
^ permalink raw reply related
* [PATCH] Add support for configuring Infiniband GUIDs
From: Eli Cohen @ 2016-05-06 15:43 UTC (permalink / raw)
To: shemminger; +Cc: netdev, Eli Cohen
Add two NLA's that allow configuration of Infiniband node or port GUIDs
by referencing the IPoIB net device set over then physical function. The
format to be used is as follows:
ip link set dev ib0 vf 0 node_guid 00:02:c9:03:00:21:6e:70
ip link set dev ib0 vf 0 port_guid 00:02:c9:03:00:21:6e:78
Issue: 702759
Change-Id: I5ffb54d6de7bfa8650bf5818f484279914991d6e
Signed-off-by: Eli Cohen <eli@mellanox.com>
---
ip/iplink.c | 40 ++++++++++++++++++++++++++++++++++++++++
man/man8/ip-link.8.in | 12 +++++++++++-
2 files changed, 51 insertions(+), 1 deletion(-)
diff --git a/ip/iplink.c b/ip/iplink.c
index d2e586b6d133..3f885defdfeb 100644
--- a/ip/iplink.c
+++ b/ip/iplink.c
@@ -237,6 +237,30 @@ struct iplink_req {
char buf[1024];
};
+static int extract_guid(__u64 *guid, char *arg)
+{
+ __u64 ret;
+ int g[8];
+ int err;
+
+ err = sscanf(arg, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+ g, g + 1, g + 2, g + 3, g + 4, g + 5, g + 6, g + 7);
+ if (err != 8)
+ return -1;
+
+ ret = ((__u64)(g[0]) << 56) |
+ ((__u64)(g[1]) << 48) |
+ ((__u64)(g[2]) << 40) |
+ ((__u64)(g[3]) << 32) |
+ ((__u64)(g[4]) << 24) |
+ ((__u64)(g[5]) << 16) |
+ ((__u64)(g[6]) << 8) |
+ ((__u64)(g[7]));
+ *guid = ret;
+
+ return 0;
+}
+
static int iplink_parse_vf(int vf, int *argcp, char ***argvp,
struct iplink_req *req, int dev_index)
{
@@ -383,6 +407,22 @@ static int iplink_parse_vf(int vf, int *argcp, char ***argvp,
invarg("Invalid \"state\" value\n", *argv);
ivl.vf = vf;
addattr_l(&req->n, sizeof(*req), IFLA_VF_LINK_STATE, &ivl, sizeof(ivl));
+ } else if (matches(*argv, "node_guid") == 0) {
+ struct ifla_vf_guid ivg;
+
+ NEXT_ARG();
+ ivg.vf = vf;
+ if (extract_guid(&ivg.guid, *argv))
+ return -1;
+ addattr_l(&req->n, sizeof(*req), IFLA_VF_IB_NODE_GUID, &ivg, sizeof(ivg));
+ } else if (matches(*argv, "port_guid") == 0) {
+ struct ifla_vf_guid ivg;
+
+ NEXT_ARG();
+ ivg.vf = vf;
+ if (extract_guid(&ivg.guid, *argv))
+ return -1;
+ addattr_l(&req->n, sizeof(*req), IFLA_VF_IB_PORT_GUID, &ivg, sizeof(ivg));
} else {
/* rewind arg */
PREV_ARG();
diff --git a/man/man8/ip-link.8.in b/man/man8/ip-link.8.in
index 805511423ef2..e143a5ec8a9a 100644
--- a/man/man8/ip-link.8.in
+++ b/man/man8/ip-link.8.in
@@ -143,7 +143,11 @@ ip-link \- network device configuration
.br
.RB "[ " state " { " auto " | " enable " | " disable " } ]"
.br
-.RB "[ " trust " { " on " | " off " } ] ]"
+.RB "[ " trust " { " on " | " off " } ]"
+.br
+.RB "[ " node_guid " eui64 ]"
+.br
+.RB "[ " port_guid " eui64 ] ]"
.br
.in -9
.RB "[ " master
@@ -1033,6 +1037,12 @@ sent by the VF.
.BI trust " on|off"
- trust the specified VF user. This enables that VF user can set a specific feature
which may impact security and/or performance. (e.g. VF multicast promiscuous mode)
+.sp
+.BI node_guid " eui64"
+- configure node GUID for the VF.
+.sp
+.BI port_guid " eui64"
+- configure port GUID for the VF.
.in -8
.TP
--
2.8.1
^ permalink raw reply related
* Re: [PATCH iproute2] geneve: fix IPv6 remote address reporting
From: Phil Sutter @ 2016-05-06 15:28 UTC (permalink / raw)
To: Edward Cree; +Cc: Stephen Hemminger, netdev, John W. Linville
In-Reply-To: <572CB4C3.3040708@solarflare.com>
On Fri, May 06, 2016 at 04:14:11PM +0100, Edward Cree wrote:
> On 06/05/16 15:43, Phil Sutter wrote:
> > On Fri, May 06, 2016 at 03:28:25PM +0100, Edward Cree wrote:
> >> Since we can only configure unicast, we probably want to be able to
> >> display unicast, rather than multicast.
> > Furthermore, the kernel even rejects multicast peer addresses.
> Yes, but a future kernel might not, and iproute2 is meant to be forward-
> compatible.
Sorry, but I fail to see how this might break forward compatibility.
Quite the contrary, suppose geneve in future supported multicast peers,
current iproute2 would fail to recognize it's existence. What am I
missing here?
> > Why do you then propose a dubious fix to a dubious check instead of
> > getting rid of it in the first place?
> Because John Linville clearly had some reason for putting a check there,
> and he probably knows better than me. Chesterton's fence.
A valid point, indeed. In my opinion the same applies to your patch as
well, as instead of removing the fence you're moving it to the other
lane. :)
Cheers, Phil
^ permalink raw reply
* Re: [REGRESSION] asix: Lots of asix_rx_fixup() errors and slow transmissions
From: Andrew Lunn @ 2016-05-06 15:27 UTC (permalink / raw)
To: Dean Jenkins
Cc: Guodong Xu, John Stultz, lkml, Mark Craske, David S. Miller,
YongQin Liu, linux-usb, netdev, Ivan Vecera, David B. Robins
In-Reply-To: <572CB188.7050203@mentor.com>
> In other words, the full-speed hub is restricting the USB to
> Ethernet Adaptor to a 12Mbps (half-duplex) bandwidth to support
> Ethernet 100Mbps (full-duplex) traffic. That is not going to work
> very well because Ethernet frames (perhaps partial Ethernet frames)
> need to be discarded within the USB link.
If that really is true, the design is broken. I would expect the
adaptor to reliably transfer whole frames over USB, and drop whole
frames from its receive queue when the USB is congested. TCP is also
going to see the USB bottleneck as just like any bottleneck in the
network and back off. So TCP streams should not cause major congestion
on the USB link. Going over a 12Mbps USB link should be no different
to hitting an old Ethernet hub which can only do 10/Half.
> Therefore please retest with a working high-speed USB hub or remove
> the full-speed USB hub from the test environment and directly
> connect the USB to Ethernet Adaptor to the root hub of the USB port.
> Then repeat the tests to see whether anything improved.
>
> In other words, you need to eliminate the dmesg messages saying "not
> running at top speed; connect to a high speed hub".
I would also suggest testing with the Ethernet at 10/half. You should
be able to use Ethtool to set that up. Your USB and Ethernet bandwidth
become more equal. If you still see errors, it suggests a protocol
implementation error somewhere.
Andrew
^ permalink raw reply
* Re: [PATCH iproute2] geneve: fix IPv6 remote address reporting
From: Edward Cree @ 2016-05-06 15:14 UTC (permalink / raw)
To: Phil Sutter, Stephen Hemminger, netdev, John W. Linville
In-Reply-To: <20160506144345.GC11058@orbyte.nwl.cc>
On 06/05/16 15:43, Phil Sutter wrote:
> On Fri, May 06, 2016 at 03:28:25PM +0100, Edward Cree wrote:
>> Since we can only configure unicast, we probably want to be able to
>> display unicast, rather than multicast.
> Furthermore, the kernel even rejects multicast peer addresses.
Yes, but a future kernel might not, and iproute2 is meant to be forward-
compatible.
> Why do you then propose a dubious fix to a dubious check instead of
> getting rid of it in the first place?
Because John Linville clearly had some reason for putting a check there,
and he probably knows better than me. Chesterton's fence.
-Ed
^ permalink raw reply
* Re: [RFC PATCH net-next 14/20] net: dsa: mv88e6xxx: factorize VLAN Ethertype
From: Vivien Didelot @ 2016-05-06 15:12 UTC (permalink / raw)
To: Andrew Lunn
Cc: netdev, linux-kernel, kernel, David S. Miller, Florian Fainelli
In-Reply-To: <20160506005759.GL7972@lunn.ch>
Hi Andrew,
Andrew Lunn <andrew@lunn.ch> writes:
>> @@ -55,6 +58,7 @@ static const struct mv88e6xxx_info mv88e6131_table[] = {
>> .num_databases = 256,
>> .num_ports = 10,
>> .flags = MV88E6XXX_FLAG_ATU |
>> + MV88E6XXX_FLAG_CORE_TAG_TYPE |
>> MV88E6XXX_FLAG_PPU |
>> MV88E6XXX_FLAG_VLANTABLE |
>> MV88E6XXX_FLAG_VTU,
>
> Rather than repeating these flags again and again, could you add one
> #define containing the flags, and then use that to initialise .flags.
Hum OK, I wasn't sure, but looking at the final mv88e6xxx_info table, I
can see that models from the same family all have the same set of flags,
even if they don't have the same number of ports or databases.
I'll add one MV88E6XXX_FLAGS_xxxx per family.
Thanks,
Vivien
^ permalink raw reply
* Re: [REGRESSION] asix: Lots of asix_rx_fixup() errors and slow transmissions
From: Dean Jenkins @ 2016-05-06 15:00 UTC (permalink / raw)
To: Guodong Xu, Dean Jenkins
Cc: John Stultz, lkml, Mark Craske, David S. Miller, YongQin Liu,
linux-usb, netdev, Ivan Vecera, David B. Robins
In-Reply-To: <CAFGCpxyeXkbQXW6Dm9ZpUXpVDWgaQziPnsC9GcZPqeg5jnbJUA@mail.gmail.com>
On 05/05/16 13:19, Guodong Xu wrote:
> Hi, Dean
>
> I am not sure why do you insist 'not full speed'. Actually, the tests
> I run on ARM-64bit is at USB full speed mode. I pasted my log here:
> http://paste.ubuntu.com/16236442/
> , which includes the information you requested above, ifconfig, dmesg.
> The interval between two consecutive errors varies from 10 to 40ms.
>
Your log from http://paste.ubuntu.com/16236442/ shows high speed for
device 3 is not being used:
[ 3.586968] usb 1-1: new full-speed USB device number 2 using dwc2
[ 3.792091] usb 1-1: not running at top speed; connect to a high
speed hub
[ 3.800477] hub 1-1:1.0: USB hub found
[ 3.803658] hub 1-1:1.0: 3 ports detected
[ 4.086636] usb 1-1.2: new full-speed USB device number 3 using dwc2
[ 4.202209] usb 1-1.2: not running at top speed; connect to a high
speed hub
[ 8.851236] asix 1-1.2:1.0 eth0: register 'asix' at
usb-f72c0000.usb-1.2, ASIX AX88772B USB 2.0 Ethernet, 00:0e:c6:fa:bf:fd
Hopefully, you know USB 2.0 high speed (480Mbps) is faster than full
speed (12Mbps) mode.
Therefore, your USB to Ethernet Adaptor is not running in its optimal
"normal" high speed operation and there is a USB hub in the way that is
not running at USB high speed mode. This is an abnormal configuration
and potentially explains some of your failure observations.
Running at full-speed (12Mbps) mode would explain why the timestamps has
gaps of ms rather than us gaps (for 480Mbps).
In other words, the full-speed hub is restricting the USB to Ethernet
Adaptor to a 12Mbps (half-duplex) bandwidth to support Ethernet 100Mbps
(full-duplex) traffic. That is not going to work very well because
Ethernet frames (perhaps partial Ethernet frames) need to be discarded
within the USB link.
Your ifconfig output from http://paste.ubuntu.com/16236442/ shows 249 errors
eth0 Link encap:Ethernet HWaddr 00:0e:c6:fa:bf:fd
inet addr:192.168.1.11 Bcast:192.168.1.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:865 errors:249 dropped:0 overruns:0 frame:0
TX packets:880 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:1228273 (1.1 MiB) TX bytes:68955 (67.3 KiB)
Before the test
RX packets:28 errors:0 dropped:0 overruns:0 frame:0
After the test
RX packets:865 errors:249 dropped:0 overruns:0 frame:0
Good test packets = 865 - 28 = 837
Detected bad Ethernet frames = 249
Bad to good ratio is 249:837 = 1:3.36 so 1 detected bad Ethernet frame
per 3.36 good Ethernet frames
Your ifconfig output from http://paste.ubuntu.com/16236764/ shows 1282
errors
eth0 Link encap:Ethernet HWaddr 00:0e:c6:fa:bf:fd
inet addr:192.168.1.11 Bcast:192.168.1.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:55 errors:1282 dropped:0 overruns:0 frame:0
TX packets:64 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:14287 (13.9 KiB) TX bytes:7639 (7.4 KiB)
Before the test
RX packets:19 errors:0 dropped:0 overruns:0 frame:0
After the test
RX packets:55 errors:1282 dropped:0 overruns:0 frame:0
Good test packets = 55 - 19 = 36
Detected bad Ethernet frames = 1282
Bad to good ratio is 1282:36 = 1:0.28 so 1 detected bad Ethernet frame
per 0.028 good Ethernet frames
This suggests a very high error rate.
>> It is interesting that the reported "remaining" value is 988. Is 988 always
>> shown ? I mean that do you see any other "remaining" values for the "Data
>> Header synchronisation was lost" error message ?
> Yes and No. When doing iperf test in TCP mode, always 988. I have
> never seen other "remaining" value.
>
> But,
> 1. I tried "ping -f -s 1400 [my.arm.64bit.board.ip]", but this cannot
> trigger the error.
> 2. Tried iperf in UDP mode, I saw "Data Header synchronisation was
> lost" remaining value is 984 (again, seemingly always in several
> tries). Log is pasted here. http://paste.ubuntu.com/16236764/
>
In http://paste.ubuntu.com/16236764/ you see very many
[ 41.938370] asix 1-1.2:1.0 eth0: asix_rx_fixup() Bad Header Length
0x11400040, offset 4
but only a few
[ 42.214607] asix 1-1.2:1.0 eth0: asix_rx_fixup() Data Header
synchronisation was lost, remaining 984
This suggests that the "Bad Header Length" and "Data Header
synchronisation was lost" error messages are not related to consecutive
URBs. The expectation is that a "Data Header synchronisation was lost"
error message is immediately followed by a "Bad Header Length" message
with a timestamp much less than 1ms (for high speed USB). This is
because an Ethernet frame that spans URBs needs low latency so should be
sent quickly in consecutive URBs.
The Bad Header Length error messages with offset 4 indicates that 32-bit
header word was not found in the expected location at the start of the
URB buffer.
[ 41.938370] asix 1-1.2:1.0 eth0: asix_rx_fixup() Bad Header Length
0x11400040, offset 4
This evidence is suggesting either the data stream is garbled such as by
many dropped URBs or lost partial Ethernet frames, or the ASIX protocol
for the AX88772B chipset differs from the AX88772 chipset so the ASIX
driver is looking in the wrong place for the 32-bit header word. I
suspect data is lost due to the restricted 12Mbps bandwidth.
My conclusion is that your USB to Ethernet Adaptor is not running at
high speed (480Mbps) mode which is causing a partial loss (corruption)
of Ethernet frames across the USB link. A USB Protocol Analyser or
software tool usbmon could be used to confirm this scenario.
Therefore please retest with a working high-speed USB hub or remove the
full-speed USB hub from the test environment and directly connect the
USB to Ethernet Adaptor to the root hub of the USB port. Then repeat the
tests to see whether anything improved.
In other words, you need to eliminate the dmesg messages saying "not
running at top speed; connect to a high speed hub".
Best regards,
Dean
> -Guodong Xu
>
>
--
Dean Jenkins
Embedded Software Engineer
Linux Transportation Solutions
Mentor Embedded Software Division
Mentor Graphics (UK) Ltd.
^ permalink raw reply
* Re: [PATCH iproute2] geneve: fix IPv6 remote address reporting
From: Phil Sutter @ 2016-05-06 14:43 UTC (permalink / raw)
To: Edward Cree; +Cc: Stephen Hemminger, netdev, John W. Linville
In-Reply-To: <572CAA09.302@solarflare.com>
On Fri, May 06, 2016 at 03:28:25PM +0100, Edward Cree wrote:
> Since we can only configure unicast, we probably want to be able to
> display unicast, rather than multicast.
Furthermore, the kernel even rejects multicast peer addresses.
> I'm assuming this is what was intended, but tbh I don't know why we
> need to check for multicast on the display side at all, rather than
> just displaying whatever the kernel gives us.
Why do you then propose a dubious fix to a dubious check instead of
getting rid of it in the first place? Reminds me a bit of this here (no
offense intended):
http://geekandpoke.typepad.com/geekandpoke/2011/07/good-coders.html
Cheers, Phil
^ permalink raw reply
* [PATCH iproute2] geneve: fix IPv6 remote address reporting
From: Edward Cree @ 2016-05-06 14:28 UTC (permalink / raw)
To: Stephen Hemminger, netdev; +Cc: John W. Linville
Since we can only configure unicast, we probably want to be able to
display unicast, rather than multicast.
Fixes: 906ac5437ab8 ("geneve: add support for IPv6 link partners")
Signed-off-by: Edward Cree <ecree@solarflare.com>
---
I'm assuming this is what was intended, but tbh I don't know why we
need to check for multicast on the display side at all, rather than
just displaying whatever the kernel gives us.
ip/iplink_geneve.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ip/iplink_geneve.c b/ip/iplink_geneve.c
index 84d948f..65af6b3 100644
--- a/ip/iplink_geneve.c
+++ b/ip/iplink_geneve.c
@@ -204,7 +204,7 @@ static void geneve_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
memcpy(&addr, RTA_DATA(tb[IFLA_GENEVE_REMOTE6]), sizeof(struct in6_addr));
if (memcmp(&addr, &in6addr_any, sizeof(addr)) != 0) {
- if (IN6_IS_ADDR_MULTICAST(&addr))
+ if (!IN6_IS_ADDR_MULTICAST(&addr))
fprintf(f, "remote %s ",
format_host(AF_INET6, sizeof(struct in6_addr), &addr));
}
--
2.4.3
^ permalink raw reply related
* [PATCH v4 2/2] net: Add Qualcomm IPC router
From: Bjorn Andersson @ 2016-05-06 14:09 UTC (permalink / raw)
To: David S. Miller, Andy Gross
Cc: linux-kernel, netdev, linux-arm-msm, Courtney Cavin,
Courtney Cavin, Bjorn Andersson
In-Reply-To: <1462543748-29187-1-git-send-email-bjorn.andersson@linaro.org>
From: Courtney Cavin <courtney.cavin@sonymobile.com>
Add an implementation of Qualcomm's IPC router protocol, used to
communicate with service providing remote processors.
Signed-off-by: Courtney Cavin <courtney.cavin@sonymobile.com>
Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
[bjorn: Cope with 0 being a valid node id and implement RTM_NEWADDR]
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
---
Changes since v3:
- Made it possible to compile qrtr as module
Changes since v2:
- Altered Kconfig dependency for QRTR_SMD to be compile testable
Changes since v1:
- Made node 0 (normally the Qualcomm modem) a valid node
- Implemented RTM_NEWADDR for specifying the local node id
include/linux/socket.h | 4 +-
include/uapi/linux/qrtr.h | 12 +
net/Kconfig | 1 +
net/Makefile | 1 +
net/qrtr/Kconfig | 24 ++
net/qrtr/Makefile | 2 +
net/qrtr/qrtr.c | 1007 +++++++++++++++++++++++++++++++++++++++++++++
net/qrtr/qrtr.h | 31 ++
net/qrtr/smd.c | 117 ++++++
9 files changed, 1198 insertions(+), 1 deletion(-)
create mode 100644 include/uapi/linux/qrtr.h
create mode 100644 net/qrtr/Kconfig
create mode 100644 net/qrtr/Makefile
create mode 100644 net/qrtr/qrtr.c
create mode 100644 net/qrtr/qrtr.h
create mode 100644 net/qrtr/smd.c
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 73bf6c6a833b..b5cc5a6d7011 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -201,8 +201,9 @@ struct ucred {
#define AF_NFC 39 /* NFC sockets */
#define AF_VSOCK 40 /* vSockets */
#define AF_KCM 41 /* Kernel Connection Multiplexor*/
+#define AF_QIPCRTR 42 /* Qualcomm IPC Router */
-#define AF_MAX 42 /* For now.. */
+#define AF_MAX 43 /* For now.. */
/* Protocol families, same as address families. */
#define PF_UNSPEC AF_UNSPEC
@@ -249,6 +250,7 @@ struct ucred {
#define PF_NFC AF_NFC
#define PF_VSOCK AF_VSOCK
#define PF_KCM AF_KCM
+#define PF_QIPCRTR AF_QIPCRTR
#define PF_MAX AF_MAX
/* Maximum queue length specifiable by listen. */
diff --git a/include/uapi/linux/qrtr.h b/include/uapi/linux/qrtr.h
new file mode 100644
index 000000000000..66c0748d26e2
--- /dev/null
+++ b/include/uapi/linux/qrtr.h
@@ -0,0 +1,12 @@
+#ifndef _LINUX_QRTR_H
+#define _LINUX_QRTR_H
+
+#include <linux/socket.h>
+
+struct sockaddr_qrtr {
+ __kernel_sa_family_t sq_family;
+ __u32 sq_node;
+ __u32 sq_port;
+};
+
+#endif /* _LINUX_QRTR_H */
diff --git a/net/Kconfig b/net/Kconfig
index a8934d8c8fda..b841c42e5c9b 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -236,6 +236,7 @@ source "net/mpls/Kconfig"
source "net/hsr/Kconfig"
source "net/switchdev/Kconfig"
source "net/l3mdev/Kconfig"
+source "net/qrtr/Kconfig"
config RPS
bool
diff --git a/net/Makefile b/net/Makefile
index 81d14119eab5..bdd14553a774 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -78,3 +78,4 @@ endif
ifneq ($(CONFIG_NET_L3_MASTER_DEV),)
obj-y += l3mdev/
endif
+obj-$(CONFIG_QRTR) += qrtr/
diff --git a/net/qrtr/Kconfig b/net/qrtr/Kconfig
new file mode 100644
index 000000000000..673fd1f86ebe
--- /dev/null
+++ b/net/qrtr/Kconfig
@@ -0,0 +1,24 @@
+# Qualcomm IPC Router configuration
+#
+
+config QRTR
+ tristate "Qualcomm IPC Router support"
+ depends on ARCH_QCOM || COMPILE_TEST
+ ---help---
+ Say Y if you intend to use Qualcomm IPC router protocol. The
+ protocol is used to communicate with services provided by other
+ hardware blocks in the system.
+
+ In order to do service lookups, a userspace daemon is required to
+ maintain a service listing.
+
+if QRTR
+
+config QRTR_SMD
+ tristate "SMD IPC Router channels"
+ depends on QCOM_SMD || COMPILE_TEST
+ ---help---
+ Say Y here to support SMD based ipcrouter channels. SMD is the
+ most common transport for IPC Router.
+
+endif # QRTR
diff --git a/net/qrtr/Makefile b/net/qrtr/Makefile
new file mode 100644
index 000000000000..6c00dc623b7e
--- /dev/null
+++ b/net/qrtr/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_QRTR) := qrtr.o
+obj-$(CONFIG_QRTR_SMD) += smd.o
diff --git a/net/qrtr/qrtr.c b/net/qrtr/qrtr.c
new file mode 100644
index 000000000000..c985ecbe9bd6
--- /dev/null
+++ b/net/qrtr/qrtr.c
@@ -0,0 +1,1007 @@
+/*
+ * Copyright (c) 2015, Sony Mobile Communications Inc.
+ * Copyright (c) 2013, The Linux Foundation. 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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/qrtr.h>
+#include <linux/termios.h> /* For TIOCINQ/OUTQ */
+
+#include <net/sock.h>
+
+#include "qrtr.h"
+
+#define QRTR_PROTO_VER 1
+
+/* auto-bind range */
+#define QRTR_MIN_EPH_SOCKET 0x4000
+#define QRTR_MAX_EPH_SOCKET 0x7fff
+
+enum qrtr_pkt_type {
+ QRTR_TYPE_DATA = 1,
+ QRTR_TYPE_HELLO = 2,
+ QRTR_TYPE_BYE = 3,
+ QRTR_TYPE_NEW_SERVER = 4,
+ QRTR_TYPE_DEL_SERVER = 5,
+ QRTR_TYPE_DEL_CLIENT = 6,
+ QRTR_TYPE_RESUME_TX = 7,
+ QRTR_TYPE_EXIT = 8,
+ QRTR_TYPE_PING = 9,
+};
+
+/**
+ * struct qrtr_hdr - (I|R)PCrouter packet header
+ * @version: protocol version
+ * @type: packet type; one of QRTR_TYPE_*
+ * @src_node_id: source node
+ * @src_port_id: source port
+ * @confirm_rx: boolean; whether a resume-tx packet should be send in reply
+ * @size: length of packet, excluding this header
+ * @dst_node_id: destination node
+ * @dst_port_id: destination port
+ */
+struct qrtr_hdr {
+ __le32 version;
+ __le32 type;
+ __le32 src_node_id;
+ __le32 src_port_id;
+ __le32 confirm_rx;
+ __le32 size;
+ __le32 dst_node_id;
+ __le32 dst_port_id;
+} __packed;
+
+#define QRTR_HDR_SIZE sizeof(struct qrtr_hdr)
+#define QRTR_NODE_BCAST ((unsigned int)-1)
+#define QRTR_PORT_CTRL ((unsigned int)-2)
+
+struct qrtr_sock {
+ /* WARNING: sk must be the first member */
+ struct sock sk;
+ struct sockaddr_qrtr us;
+ struct sockaddr_qrtr peer;
+};
+
+static inline struct qrtr_sock *qrtr_sk(struct sock *sk)
+{
+ BUILD_BUG_ON(offsetof(struct qrtr_sock, sk) != 0);
+ return container_of(sk, struct qrtr_sock, sk);
+}
+
+static unsigned int qrtr_local_nid = -1;
+
+/* for node ids */
+static RADIX_TREE(qrtr_nodes, GFP_KERNEL);
+/* broadcast list */
+static LIST_HEAD(qrtr_all_nodes);
+/* lock for qrtr_nodes, qrtr_all_nodes and node reference */
+static DEFINE_MUTEX(qrtr_node_lock);
+
+/* local port allocation management */
+static DEFINE_IDR(qrtr_ports);
+static DEFINE_MUTEX(qrtr_port_lock);
+
+/**
+ * struct qrtr_node - endpoint node
+ * @ep_lock: lock for endpoint management and callbacks
+ * @ep: endpoint
+ * @ref: reference count for node
+ * @nid: node id
+ * @rx_queue: receive queue
+ * @work: scheduled work struct for recv work
+ * @item: list item for broadcast list
+ */
+struct qrtr_node {
+ struct mutex ep_lock;
+ struct qrtr_endpoint *ep;
+ struct kref ref;
+ unsigned int nid;
+
+ struct sk_buff_head rx_queue;
+ struct work_struct work;
+ struct list_head item;
+};
+
+/* Release node resources and free the node.
+ *
+ * Do not call directly, use qrtr_node_release. To be used with
+ * kref_put_mutex. As such, the node mutex is expected to be locked on call.
+ */
+static void __qrtr_node_release(struct kref *kref)
+{
+ struct qrtr_node *node = container_of(kref, struct qrtr_node, ref);
+
+ if (node->nid != QRTR_EP_NID_AUTO)
+ radix_tree_delete(&qrtr_nodes, node->nid);
+
+ list_del(&node->item);
+ mutex_unlock(&qrtr_node_lock);
+
+ skb_queue_purge(&node->rx_queue);
+ kfree(node);
+}
+
+/* Increment reference to node. */
+static struct qrtr_node *qrtr_node_acquire(struct qrtr_node *node)
+{
+ if (node)
+ kref_get(&node->ref);
+ return node;
+}
+
+/* Decrement reference to node and release as necessary. */
+static void qrtr_node_release(struct qrtr_node *node)
+{
+ if (!node)
+ return;
+ kref_put_mutex(&node->ref, __qrtr_node_release, &qrtr_node_lock);
+}
+
+/* Pass an outgoing packet socket buffer to the endpoint driver. */
+static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb)
+{
+ int rc = -ENODEV;
+
+ mutex_lock(&node->ep_lock);
+ if (node->ep)
+ rc = node->ep->xmit(node->ep, skb);
+ else
+ kfree_skb(skb);
+ mutex_unlock(&node->ep_lock);
+
+ return rc;
+}
+
+/* Lookup node by id.
+ *
+ * callers must release with qrtr_node_release()
+ */
+static struct qrtr_node *qrtr_node_lookup(unsigned int nid)
+{
+ struct qrtr_node *node;
+
+ mutex_lock(&qrtr_node_lock);
+ node = radix_tree_lookup(&qrtr_nodes, nid);
+ node = qrtr_node_acquire(node);
+ mutex_unlock(&qrtr_node_lock);
+
+ return node;
+}
+
+/* Assign node id to node.
+ *
+ * This is mostly useful for automatic node id assignment, based on
+ * the source id in the incoming packet.
+ */
+static void qrtr_node_assign(struct qrtr_node *node, unsigned int nid)
+{
+ if (node->nid != QRTR_EP_NID_AUTO || nid == QRTR_EP_NID_AUTO)
+ return;
+
+ mutex_lock(&qrtr_node_lock);
+ radix_tree_insert(&qrtr_nodes, nid, node);
+ node->nid = nid;
+ mutex_unlock(&qrtr_node_lock);
+}
+
+/**
+ * qrtr_endpoint_post() - post incoming data
+ * @ep: endpoint handle
+ * @data: data pointer
+ * @len: size of data in bytes
+ *
+ * Return: 0 on success; negative error code on failure
+ */
+int qrtr_endpoint_post(struct qrtr_endpoint *ep, const void *data, size_t len)
+{
+ struct qrtr_node *node = ep->node;
+ const struct qrtr_hdr *phdr = data;
+ struct sk_buff *skb;
+ unsigned int psize;
+ unsigned int size;
+ unsigned int type;
+ unsigned int ver;
+ unsigned int dst;
+
+ if (len < QRTR_HDR_SIZE || len & 3)
+ return -EINVAL;
+
+ ver = le32_to_cpu(phdr->version);
+ size = le32_to_cpu(phdr->size);
+ type = le32_to_cpu(phdr->type);
+ dst = le32_to_cpu(phdr->dst_port_id);
+
+ psize = (size + 3) & ~3;
+
+ if (ver != QRTR_PROTO_VER)
+ return -EINVAL;
+
+ if (len != psize + QRTR_HDR_SIZE)
+ return -EINVAL;
+
+ if (dst != QRTR_PORT_CTRL && type != QRTR_TYPE_DATA)
+ return -EINVAL;
+
+ skb = netdev_alloc_skb(NULL, len);
+ if (!skb)
+ return -ENOMEM;
+
+ skb_reset_transport_header(skb);
+ memcpy(skb_put(skb, len), data, len);
+
+ skb_queue_tail(&node->rx_queue, skb);
+ schedule_work(&node->work);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qrtr_endpoint_post);
+
+/* Allocate and construct a resume-tx packet. */
+static struct sk_buff *qrtr_alloc_resume_tx(u32 src_node,
+ u32 dst_node, u32 port)
+{
+ const int pkt_len = 20;
+ struct qrtr_hdr *hdr;
+ struct sk_buff *skb;
+ u32 *buf;
+
+ skb = alloc_skb(QRTR_HDR_SIZE + pkt_len, GFP_KERNEL);
+ if (!skb)
+ return NULL;
+ skb_reset_transport_header(skb);
+
+ hdr = (struct qrtr_hdr *)skb_put(skb, QRTR_HDR_SIZE);
+ hdr->version = cpu_to_le32(QRTR_PROTO_VER);
+ hdr->type = cpu_to_le32(QRTR_TYPE_RESUME_TX);
+ hdr->src_node_id = cpu_to_le32(src_node);
+ hdr->src_port_id = cpu_to_le32(QRTR_PORT_CTRL);
+ hdr->confirm_rx = cpu_to_le32(0);
+ hdr->size = cpu_to_le32(pkt_len);
+ hdr->dst_node_id = cpu_to_le32(dst_node);
+ hdr->dst_port_id = cpu_to_le32(QRTR_PORT_CTRL);
+
+ buf = (u32 *)skb_put(skb, pkt_len);
+ memset(buf, 0, pkt_len);
+ buf[0] = cpu_to_le32(QRTR_TYPE_RESUME_TX);
+ buf[1] = cpu_to_le32(src_node);
+ buf[2] = cpu_to_le32(port);
+
+ return skb;
+}
+
+static struct qrtr_sock *qrtr_port_lookup(int port);
+static void qrtr_port_put(struct qrtr_sock *ipc);
+
+/* Handle and route a received packet.
+ *
+ * This will auto-reply with resume-tx packet as necessary.
+ */
+static void qrtr_node_rx_work(struct work_struct *work)
+{
+ struct qrtr_node *node = container_of(work, struct qrtr_node, work);
+ struct sk_buff *skb;
+
+ while ((skb = skb_dequeue(&node->rx_queue)) != NULL) {
+ const struct qrtr_hdr *phdr;
+ u32 dst_node, dst_port;
+ struct qrtr_sock *ipc;
+ u32 src_node;
+ int confirm;
+
+ phdr = (const struct qrtr_hdr *)skb_transport_header(skb);
+ src_node = le32_to_cpu(phdr->src_node_id);
+ dst_node = le32_to_cpu(phdr->dst_node_id);
+ dst_port = le32_to_cpu(phdr->dst_port_id);
+ confirm = !!phdr->confirm_rx;
+
+ qrtr_node_assign(node, src_node);
+
+ ipc = qrtr_port_lookup(dst_port);
+ if (!ipc) {
+ kfree_skb(skb);
+ } else {
+ if (sock_queue_rcv_skb(&ipc->sk, skb))
+ kfree_skb(skb);
+
+ qrtr_port_put(ipc);
+ }
+
+ if (confirm) {
+ skb = qrtr_alloc_resume_tx(dst_node, node->nid, dst_port);
+ if (!skb)
+ break;
+ if (qrtr_node_enqueue(node, skb))
+ break;
+ }
+ }
+}
+
+/**
+ * qrtr_endpoint_register() - register a new endpoint
+ * @ep: endpoint to register
+ * @nid: desired node id; may be QRTR_EP_NID_AUTO for auto-assignment
+ * Return: 0 on success; negative error code on failure
+ *
+ * The specified endpoint must have the xmit function pointer set on call.
+ */
+int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int nid)
+{
+ struct qrtr_node *node;
+
+ if (!ep || !ep->xmit)
+ return -EINVAL;
+
+ node = kzalloc(sizeof(*node), GFP_KERNEL);
+ if (!node)
+ return -ENOMEM;
+
+ INIT_WORK(&node->work, qrtr_node_rx_work);
+ kref_init(&node->ref);
+ mutex_init(&node->ep_lock);
+ skb_queue_head_init(&node->rx_queue);
+ node->nid = QRTR_EP_NID_AUTO;
+ node->ep = ep;
+
+ qrtr_node_assign(node, nid);
+
+ mutex_lock(&qrtr_node_lock);
+ list_add(&node->item, &qrtr_all_nodes);
+ mutex_unlock(&qrtr_node_lock);
+ ep->node = node;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qrtr_endpoint_register);
+
+/**
+ * qrtr_endpoint_unregister - unregister endpoint
+ * @ep: endpoint to unregister
+ */
+void qrtr_endpoint_unregister(struct qrtr_endpoint *ep)
+{
+ struct qrtr_node *node = ep->node;
+
+ mutex_lock(&node->ep_lock);
+ node->ep = NULL;
+ mutex_unlock(&node->ep_lock);
+
+ qrtr_node_release(node);
+ ep->node = NULL;
+}
+EXPORT_SYMBOL_GPL(qrtr_endpoint_unregister);
+
+/* Lookup socket by port.
+ *
+ * Callers must release with qrtr_port_put()
+ */
+static struct qrtr_sock *qrtr_port_lookup(int port)
+{
+ struct qrtr_sock *ipc;
+
+ if (port == QRTR_PORT_CTRL)
+ port = 0;
+
+ mutex_lock(&qrtr_port_lock);
+ ipc = idr_find(&qrtr_ports, port);
+ if (ipc)
+ sock_hold(&ipc->sk);
+ mutex_unlock(&qrtr_port_lock);
+
+ return ipc;
+}
+
+/* Release acquired socket. */
+static void qrtr_port_put(struct qrtr_sock *ipc)
+{
+ sock_put(&ipc->sk);
+}
+
+/* Remove port assignment. */
+static void qrtr_port_remove(struct qrtr_sock *ipc)
+{
+ int port = ipc->us.sq_port;
+
+ if (port == QRTR_PORT_CTRL)
+ port = 0;
+
+ __sock_put(&ipc->sk);
+
+ mutex_lock(&qrtr_port_lock);
+ idr_remove(&qrtr_ports, port);
+ mutex_unlock(&qrtr_port_lock);
+}
+
+/* Assign port number to socket.
+ *
+ * Specify port in the integer pointed to by port, and it will be adjusted
+ * on return as necesssary.
+ *
+ * Port may be:
+ * 0: Assign ephemeral port in [QRTR_MIN_EPH_SOCKET, QRTR_MAX_EPH_SOCKET]
+ * <QRTR_MIN_EPH_SOCKET: Specified; requires CAP_NET_ADMIN
+ * >QRTR_MIN_EPH_SOCKET: Specified; available to all
+ */
+static int qrtr_port_assign(struct qrtr_sock *ipc, int *port)
+{
+ int rc;
+
+ mutex_lock(&qrtr_port_lock);
+ if (!*port) {
+ rc = idr_alloc(&qrtr_ports, ipc,
+ QRTR_MIN_EPH_SOCKET, QRTR_MAX_EPH_SOCKET + 1,
+ GFP_ATOMIC);
+ if (rc >= 0)
+ *port = rc;
+ } else if (*port < QRTR_MIN_EPH_SOCKET && !capable(CAP_NET_ADMIN)) {
+ rc = -EACCES;
+ } else if (*port == QRTR_PORT_CTRL) {
+ rc = idr_alloc(&qrtr_ports, ipc, 0, 1, GFP_ATOMIC);
+ } else {
+ rc = idr_alloc(&qrtr_ports, ipc, *port, *port + 1, GFP_ATOMIC);
+ if (rc >= 0)
+ *port = rc;
+ }
+ mutex_unlock(&qrtr_port_lock);
+
+ if (rc == -ENOSPC)
+ return -EADDRINUSE;
+ else if (rc < 0)
+ return rc;
+
+ sock_hold(&ipc->sk);
+
+ return 0;
+}
+
+/* Bind socket to address.
+ *
+ * Socket should be locked upon call.
+ */
+static int __qrtr_bind(struct socket *sock,
+ const struct sockaddr_qrtr *addr, int zapped)
+{
+ struct qrtr_sock *ipc = qrtr_sk(sock->sk);
+ struct sock *sk = sock->sk;
+ int port;
+ int rc;
+
+ /* rebinding ok */
+ if (!zapped && addr->sq_port == ipc->us.sq_port)
+ return 0;
+
+ port = addr->sq_port;
+ rc = qrtr_port_assign(ipc, &port);
+ if (rc)
+ return rc;
+
+ /* unbind previous, if any */
+ if (!zapped)
+ qrtr_port_remove(ipc);
+ ipc->us.sq_port = port;
+
+ sock_reset_flag(sk, SOCK_ZAPPED);
+
+ return 0;
+}
+
+/* Auto bind to an ephemeral port. */
+static int qrtr_autobind(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+ struct sockaddr_qrtr addr;
+
+ if (!sock_flag(sk, SOCK_ZAPPED))
+ return 0;
+
+ addr.sq_family = AF_QIPCRTR;
+ addr.sq_node = qrtr_local_nid;
+ addr.sq_port = 0;
+
+ return __qrtr_bind(sock, &addr, 1);
+}
+
+/* Bind socket to specified sockaddr. */
+static int qrtr_bind(struct socket *sock, struct sockaddr *saddr, int len)
+{
+ DECLARE_SOCKADDR(struct sockaddr_qrtr *, addr, saddr);
+ struct qrtr_sock *ipc = qrtr_sk(sock->sk);
+ struct sock *sk = sock->sk;
+ int rc;
+
+ if (len < sizeof(*addr) || addr->sq_family != AF_QIPCRTR)
+ return -EINVAL;
+
+ if (addr->sq_node != ipc->us.sq_node)
+ return -EINVAL;
+
+ lock_sock(sk);
+ rc = __qrtr_bind(sock, addr, sock_flag(sk, SOCK_ZAPPED));
+ release_sock(sk);
+
+ return rc;
+}
+
+/* Queue packet to local peer socket. */
+static int qrtr_local_enqueue(struct qrtr_node *node, struct sk_buff *skb)
+{
+ const struct qrtr_hdr *phdr;
+ struct qrtr_sock *ipc;
+
+ phdr = (const struct qrtr_hdr *)skb_transport_header(skb);
+
+ ipc = qrtr_port_lookup(le32_to_cpu(phdr->dst_port_id));
+ if (!ipc || &ipc->sk == skb->sk) { /* do not send to self */
+ kfree_skb(skb);
+ return -ENODEV;
+ }
+
+ if (sock_queue_rcv_skb(&ipc->sk, skb)) {
+ qrtr_port_put(ipc);
+ kfree_skb(skb);
+ return -ENOSPC;
+ }
+
+ qrtr_port_put(ipc);
+
+ return 0;
+}
+
+/* Queue packet for broadcast. */
+static int qrtr_bcast_enqueue(struct qrtr_node *node, struct sk_buff *skb)
+{
+ struct sk_buff *skbn;
+
+ mutex_lock(&qrtr_node_lock);
+ list_for_each_entry(node, &qrtr_all_nodes, item) {
+ skbn = skb_clone(skb, GFP_KERNEL);
+ if (!skbn)
+ break;
+ skb_set_owner_w(skbn, skb->sk);
+ qrtr_node_enqueue(node, skbn);
+ }
+ mutex_unlock(&qrtr_node_lock);
+
+ qrtr_local_enqueue(node, skb);
+
+ return 0;
+}
+
+static int qrtr_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
+{
+ DECLARE_SOCKADDR(struct sockaddr_qrtr *, addr, msg->msg_name);
+ int (*enqueue_fn)(struct qrtr_node *, struct sk_buff *);
+ struct qrtr_sock *ipc = qrtr_sk(sock->sk);
+ struct sock *sk = sock->sk;
+ struct qrtr_node *node;
+ struct qrtr_hdr *hdr;
+ struct sk_buff *skb;
+ size_t plen;
+ int rc;
+
+ if (msg->msg_flags & ~(MSG_DONTWAIT))
+ return -EINVAL;
+
+ if (len > 65535)
+ return -EMSGSIZE;
+
+ lock_sock(sk);
+
+ if (addr) {
+ if (msg->msg_namelen < sizeof(*addr)) {
+ release_sock(sk);
+ return -EINVAL;
+ }
+
+ if (addr->sq_family != AF_QIPCRTR) {
+ release_sock(sk);
+ return -EINVAL;
+ }
+
+ rc = qrtr_autobind(sock);
+ if (rc) {
+ release_sock(sk);
+ return rc;
+ }
+ } else if (sk->sk_state == TCP_ESTABLISHED) {
+ addr = &ipc->peer;
+ } else {
+ release_sock(sk);
+ return -ENOTCONN;
+ }
+
+ node = NULL;
+ if (addr->sq_node == QRTR_NODE_BCAST) {
+ enqueue_fn = qrtr_bcast_enqueue;
+ } else if (addr->sq_node == ipc->us.sq_node) {
+ enqueue_fn = qrtr_local_enqueue;
+ } else {
+ enqueue_fn = qrtr_node_enqueue;
+ node = qrtr_node_lookup(addr->sq_node);
+ if (!node) {
+ release_sock(sk);
+ return -ECONNRESET;
+ }
+ }
+
+ plen = (len + 3) & ~3;
+ skb = sock_alloc_send_skb(sk, plen + QRTR_HDR_SIZE,
+ msg->msg_flags & MSG_DONTWAIT, &rc);
+ if (!skb)
+ goto out_node;
+
+ skb_reset_transport_header(skb);
+ skb_put(skb, len + QRTR_HDR_SIZE);
+
+ hdr = (struct qrtr_hdr *)skb_transport_header(skb);
+ hdr->version = cpu_to_le32(QRTR_PROTO_VER);
+ hdr->src_node_id = cpu_to_le32(ipc->us.sq_node);
+ hdr->src_port_id = cpu_to_le32(ipc->us.sq_port);
+ hdr->confirm_rx = cpu_to_le32(0);
+ hdr->size = cpu_to_le32(len);
+ hdr->dst_node_id = cpu_to_le32(addr->sq_node);
+ hdr->dst_port_id = cpu_to_le32(addr->sq_port);
+
+ rc = skb_copy_datagram_from_iter(skb, QRTR_HDR_SIZE,
+ &msg->msg_iter, len);
+ if (rc) {
+ kfree_skb(skb);
+ goto out_node;
+ }
+
+ if (plen != len) {
+ skb_pad(skb, plen - len);
+ skb_put(skb, plen - len);
+ }
+
+ if (ipc->us.sq_port == QRTR_PORT_CTRL) {
+ if (len < 4) {
+ rc = -EINVAL;
+ kfree_skb(skb);
+ goto out_node;
+ }
+
+ /* control messages already require the type as 'command' */
+ skb_copy_bits(skb, QRTR_HDR_SIZE, &hdr->type, 4);
+ } else {
+ hdr->type = cpu_to_le32(QRTR_TYPE_DATA);
+ }
+
+ rc = enqueue_fn(node, skb);
+ if (rc >= 0)
+ rc = len;
+
+out_node:
+ qrtr_node_release(node);
+ release_sock(sk);
+
+ return rc;
+}
+
+static int qrtr_recvmsg(struct socket *sock, struct msghdr *msg,
+ size_t size, int flags)
+{
+ DECLARE_SOCKADDR(struct sockaddr_qrtr *, addr, msg->msg_name);
+ const struct qrtr_hdr *phdr;
+ struct sock *sk = sock->sk;
+ struct sk_buff *skb;
+ int copied, rc;
+
+ lock_sock(sk);
+
+ if (sock_flag(sk, SOCK_ZAPPED)) {
+ release_sock(sk);
+ return -EADDRNOTAVAIL;
+ }
+
+ skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
+ flags & MSG_DONTWAIT, &rc);
+ if (!skb) {
+ release_sock(sk);
+ return rc;
+ }
+
+ phdr = (const struct qrtr_hdr *)skb_transport_header(skb);
+ copied = le32_to_cpu(phdr->size);
+ if (copied > size) {
+ copied = size;
+ msg->msg_flags |= MSG_TRUNC;
+ }
+
+ rc = skb_copy_datagram_msg(skb, QRTR_HDR_SIZE, msg, copied);
+ if (rc < 0)
+ goto out;
+ rc = copied;
+
+ if (addr) {
+ addr->sq_family = AF_QIPCRTR;
+ addr->sq_node = le32_to_cpu(phdr->src_node_id);
+ addr->sq_port = le32_to_cpu(phdr->src_port_id);
+ msg->msg_namelen = sizeof(*addr);
+ }
+
+out:
+ skb_free_datagram(sk, skb);
+ release_sock(sk);
+
+ return rc;
+}
+
+static int qrtr_connect(struct socket *sock, struct sockaddr *saddr,
+ int len, int flags)
+{
+ DECLARE_SOCKADDR(struct sockaddr_qrtr *, addr, saddr);
+ struct qrtr_sock *ipc = qrtr_sk(sock->sk);
+ struct sock *sk = sock->sk;
+ int rc;
+
+ if (len < sizeof(*addr) || addr->sq_family != AF_QIPCRTR)
+ return -EINVAL;
+
+ lock_sock(sk);
+
+ sk->sk_state = TCP_CLOSE;
+ sock->state = SS_UNCONNECTED;
+
+ rc = qrtr_autobind(sock);
+ if (rc) {
+ release_sock(sk);
+ return rc;
+ }
+
+ ipc->peer = *addr;
+ sock->state = SS_CONNECTED;
+ sk->sk_state = TCP_ESTABLISHED;
+
+ release_sock(sk);
+
+ return 0;
+}
+
+static int qrtr_getname(struct socket *sock, struct sockaddr *saddr,
+ int *len, int peer)
+{
+ struct qrtr_sock *ipc = qrtr_sk(sock->sk);
+ struct sockaddr_qrtr qaddr;
+ struct sock *sk = sock->sk;
+
+ lock_sock(sk);
+ if (peer) {
+ if (sk->sk_state != TCP_ESTABLISHED) {
+ release_sock(sk);
+ return -ENOTCONN;
+ }
+
+ qaddr = ipc->peer;
+ } else {
+ qaddr = ipc->us;
+ }
+ release_sock(sk);
+
+ *len = sizeof(qaddr);
+ qaddr.sq_family = AF_QIPCRTR;
+
+ memcpy(saddr, &qaddr, sizeof(qaddr));
+
+ return 0;
+}
+
+static int qrtr_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ struct qrtr_sock *ipc = qrtr_sk(sock->sk);
+ struct sock *sk = sock->sk;
+ struct sockaddr_qrtr *sq;
+ struct sk_buff *skb;
+ struct ifreq ifr;
+ long len = 0;
+ int rc = 0;
+
+ lock_sock(sk);
+
+ switch (cmd) {
+ case TIOCOUTQ:
+ len = sk->sk_sndbuf - sk_wmem_alloc_get(sk);
+ if (len < 0)
+ len = 0;
+ rc = put_user(len, (int __user *)argp);
+ break;
+ case TIOCINQ:
+ skb = skb_peek(&sk->sk_receive_queue);
+ if (skb)
+ len = skb->len - QRTR_HDR_SIZE;
+ rc = put_user(len, (int __user *)argp);
+ break;
+ case SIOCGIFADDR:
+ if (copy_from_user(&ifr, argp, sizeof(ifr))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ sq = (struct sockaddr_qrtr *)&ifr.ifr_addr;
+ *sq = ipc->us;
+ if (copy_to_user(argp, &ifr, sizeof(ifr))) {
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ case SIOCGSTAMP:
+ rc = sock_get_timestamp(sk, argp);
+ break;
+ case SIOCADDRT:
+ case SIOCDELRT:
+ case SIOCSIFADDR:
+ case SIOCGIFDSTADDR:
+ case SIOCSIFDSTADDR:
+ case SIOCGIFBRDADDR:
+ case SIOCSIFBRDADDR:
+ case SIOCGIFNETMASK:
+ case SIOCSIFNETMASK:
+ rc = -EINVAL;
+ break;
+ default:
+ rc = -ENOIOCTLCMD;
+ break;
+ }
+
+ release_sock(sk);
+
+ return rc;
+}
+
+static int qrtr_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+ struct qrtr_sock *ipc;
+
+ if (!sk)
+ return 0;
+
+ lock_sock(sk);
+
+ ipc = qrtr_sk(sk);
+ sk->sk_shutdown = SHUTDOWN_MASK;
+ if (!sock_flag(sk, SOCK_DEAD))
+ sk->sk_state_change(sk);
+
+ sock_set_flag(sk, SOCK_DEAD);
+ sock->sk = NULL;
+
+ if (!sock_flag(sk, SOCK_ZAPPED))
+ qrtr_port_remove(ipc);
+
+ skb_queue_purge(&sk->sk_receive_queue);
+
+ release_sock(sk);
+ sock_put(sk);
+
+ return 0;
+}
+
+static const struct proto_ops qrtr_proto_ops = {
+ .owner = THIS_MODULE,
+ .family = AF_QIPCRTR,
+ .bind = qrtr_bind,
+ .connect = qrtr_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .listen = sock_no_listen,
+ .sendmsg = qrtr_sendmsg,
+ .recvmsg = qrtr_recvmsg,
+ .getname = qrtr_getname,
+ .ioctl = qrtr_ioctl,
+ .poll = datagram_poll,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_no_setsockopt,
+ .getsockopt = sock_no_getsockopt,
+ .release = qrtr_release,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+};
+
+static struct proto qrtr_proto = {
+ .name = "QIPCRTR",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct qrtr_sock),
+};
+
+static int qrtr_create(struct net *net, struct socket *sock,
+ int protocol, int kern)
+{
+ struct qrtr_sock *ipc;
+ struct sock *sk;
+
+ if (sock->type != SOCK_DGRAM)
+ return -EPROTOTYPE;
+
+ sk = sk_alloc(net, AF_QIPCRTR, GFP_KERNEL, &qrtr_proto, kern);
+ if (!sk)
+ return -ENOMEM;
+
+ sock_set_flag(sk, SOCK_ZAPPED);
+
+ sock_init_data(sock, sk);
+ sock->ops = &qrtr_proto_ops;
+
+ ipc = qrtr_sk(sk);
+ ipc->us.sq_family = AF_QIPCRTR;
+ ipc->us.sq_node = qrtr_local_nid;
+ ipc->us.sq_port = 0;
+
+ return 0;
+}
+
+static const struct nla_policy qrtr_policy[IFA_MAX + 1] = {
+ [IFA_LOCAL] = { .type = NLA_U32 },
+};
+
+static int qrtr_addr_doit(struct sk_buff *skb, struct nlmsghdr *nlh)
+{
+ struct nlattr *tb[IFA_MAX + 1];
+ struct ifaddrmsg *ifm;
+ int rc;
+
+ if (!netlink_capable(skb, CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (!netlink_capable(skb, CAP_SYS_ADMIN))
+ return -EPERM;
+
+ ASSERT_RTNL();
+
+ rc = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, qrtr_policy);
+ if (rc < 0)
+ return rc;
+
+ ifm = nlmsg_data(nlh);
+ if (!tb[IFA_LOCAL])
+ return -EINVAL;
+
+ qrtr_local_nid = nla_get_u32(tb[IFA_LOCAL]);
+ return 0;
+}
+
+static const struct net_proto_family qrtr_family = {
+ .owner = THIS_MODULE,
+ .family = AF_QIPCRTR,
+ .create = qrtr_create,
+};
+
+static int __init qrtr_proto_init(void)
+{
+ int rc;
+
+ rc = proto_register(&qrtr_proto, 1);
+ if (rc)
+ return rc;
+
+ rc = sock_register(&qrtr_family);
+ if (rc) {
+ proto_unregister(&qrtr_proto);
+ return rc;
+ }
+
+ rtnl_register(PF_QIPCRTR, RTM_NEWADDR, qrtr_addr_doit, NULL, NULL);
+
+ return 0;
+}
+module_init(qrtr_proto_init);
+
+static void __exit qrtr_proto_fini(void)
+{
+ rtnl_unregister(PF_QIPCRTR, RTM_NEWADDR);
+ sock_unregister(qrtr_family.family);
+ proto_unregister(&qrtr_proto);
+}
+module_exit(qrtr_proto_fini);
+
+MODULE_DESCRIPTION("Qualcomm IPC-router driver");
+MODULE_LICENSE("GPL v2");
diff --git a/net/qrtr/qrtr.h b/net/qrtr/qrtr.h
new file mode 100644
index 000000000000..2b848718f8fe
--- /dev/null
+++ b/net/qrtr/qrtr.h
@@ -0,0 +1,31 @@
+#ifndef __QRTR_H_
+#define __QRTR_H_
+
+#include <linux/types.h>
+
+struct sk_buff;
+
+/* endpoint node id auto assignment */
+#define QRTR_EP_NID_AUTO (-1)
+
+/**
+ * struct qrtr_endpoint - endpoint handle
+ * @xmit: Callback for outgoing packets
+ *
+ * The socket buffer passed to the xmit function becomes owned by the endpoint
+ * driver. As such, when the driver is done with the buffer, it should
+ * call kfree_skb() on failure, or consume_skb() on success.
+ */
+struct qrtr_endpoint {
+ int (*xmit)(struct qrtr_endpoint *ep, struct sk_buff *skb);
+ /* private: not for endpoint use */
+ struct qrtr_node *node;
+};
+
+int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int nid);
+
+void qrtr_endpoint_unregister(struct qrtr_endpoint *ep);
+
+int qrtr_endpoint_post(struct qrtr_endpoint *ep, const void *data, size_t len);
+
+#endif
diff --git a/net/qrtr/smd.c b/net/qrtr/smd.c
new file mode 100644
index 000000000000..84ebce73aa23
--- /dev/null
+++ b/net/qrtr/smd.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2015, Sony Mobile Communications Inc.
+ * Copyright (c) 2013, The Linux Foundation. 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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/soc/qcom/smd.h>
+
+#include "qrtr.h"
+
+struct qrtr_smd_dev {
+ struct qrtr_endpoint ep;
+ struct qcom_smd_channel *channel;
+};
+
+/* from smd to qrtr */
+static int qcom_smd_qrtr_callback(struct qcom_smd_device *sdev,
+ const void *data, size_t len)
+{
+ struct qrtr_smd_dev *qdev = dev_get_drvdata(&sdev->dev);
+ int rc;
+
+ if (!qdev)
+ return -EAGAIN;
+
+ rc = qrtr_endpoint_post(&qdev->ep, data, len);
+ if (rc == -EINVAL) {
+ dev_err(&sdev->dev, "invalid ipcrouter packet\n");
+ /* return 0 to let smd drop the packet */
+ rc = 0;
+ }
+
+ return rc;
+}
+
+/* from qrtr to smd */
+static int qcom_smd_qrtr_send(struct qrtr_endpoint *ep, struct sk_buff *skb)
+{
+ struct qrtr_smd_dev *qdev = container_of(ep, struct qrtr_smd_dev, ep);
+ int rc;
+
+ rc = skb_linearize(skb);
+ if (rc)
+ goto out;
+
+ rc = qcom_smd_send(qdev->channel, skb->data, skb->len);
+
+out:
+ if (rc)
+ kfree_skb(skb);
+ else
+ consume_skb(skb);
+ return rc;
+}
+
+static int qcom_smd_qrtr_probe(struct qcom_smd_device *sdev)
+{
+ struct qrtr_smd_dev *qdev;
+ int rc;
+
+ qdev = devm_kzalloc(&sdev->dev, sizeof(*qdev), GFP_KERNEL);
+ if (!qdev)
+ return -ENOMEM;
+
+ qdev->channel = sdev->channel;
+ qdev->ep.xmit = qcom_smd_qrtr_send;
+
+ rc = qrtr_endpoint_register(&qdev->ep, QRTR_EP_NID_AUTO);
+ if (rc)
+ return rc;
+
+ dev_set_drvdata(&sdev->dev, qdev);
+
+ dev_dbg(&sdev->dev, "Qualcomm SMD QRTR driver probed\n");
+
+ return 0;
+}
+
+static void qcom_smd_qrtr_remove(struct qcom_smd_device *sdev)
+{
+ struct qrtr_smd_dev *qdev = dev_get_drvdata(&sdev->dev);
+
+ qrtr_endpoint_unregister(&qdev->ep);
+
+ dev_set_drvdata(&sdev->dev, NULL);
+}
+
+static const struct qcom_smd_id qcom_smd_qrtr_smd_match[] = {
+ { "IPCRTR" },
+ {}
+};
+
+static struct qcom_smd_driver qcom_smd_qrtr_driver = {
+ .probe = qcom_smd_qrtr_probe,
+ .remove = qcom_smd_qrtr_remove,
+ .callback = qcom_smd_qrtr_callback,
+ .smd_match_table = qcom_smd_qrtr_smd_match,
+ .driver = {
+ .name = "qcom_smd_qrtr",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_qcom_smd_driver(qcom_smd_qrtr_driver);
+
+MODULE_DESCRIPTION("Qualcomm IPC-Router SMD interface driver");
+MODULE_LICENSE("GPL v2");
--
2.5.0
^ permalink raw reply related
* [PATCH v4 1/2] soc: qcom: smd: Introduce compile stubs
From: Bjorn Andersson @ 2016-05-06 14:09 UTC (permalink / raw)
To: David S. Miller, Andy Gross
Cc: linux-kernel, netdev, linux-arm-msm, Courtney Cavin
Introduce compile stubs for the SMD API, allowing consumers to be
compile tested.
Acked-by: Andy Gross <andy.gross@linaro.org>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
---
Changes since v3:
- None
Changes since v2:
- Introduce this patch, to allow compile testing of QRTR_SMD
include/linux/soc/qcom/smd.h | 28 +++++++++++++++++++++++++++-
1 file changed, 27 insertions(+), 1 deletion(-)
diff --git a/include/linux/soc/qcom/smd.h b/include/linux/soc/qcom/smd.h
index d0cb6d189a0a..46a984f5e3a3 100644
--- a/include/linux/soc/qcom/smd.h
+++ b/include/linux/soc/qcom/smd.h
@@ -45,13 +45,39 @@ struct qcom_smd_driver {
int (*callback)(struct qcom_smd_device *, const void *, size_t);
};
+#if IS_ENABLED(CONFIG_QCOM_SMD)
+
int qcom_smd_driver_register(struct qcom_smd_driver *drv);
void qcom_smd_driver_unregister(struct qcom_smd_driver *drv);
+int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len);
+
+#else
+
+static inline int qcom_smd_driver_register(struct qcom_smd_driver *drv)
+{
+ return -ENXIO;
+}
+
+static inline void qcom_smd_driver_unregister(struct qcom_smd_driver *drv)
+{
+ /* This shouldn't be possible */
+ WARN_ON(1);
+}
+
+static inline int qcom_smd_send(struct qcom_smd_channel *channel,
+ const void *data, int len)
+{
+ /* This shouldn't be possible */
+ WARN_ON(1);
+ return -ENXIO;
+}
+
+#endif
+
#define module_qcom_smd_driver(__smd_driver) \
module_driver(__smd_driver, qcom_smd_driver_register, \
qcom_smd_driver_unregister)
-int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len);
#endif
--
2.5.0
^ permalink raw reply related
* [PATCH net v3] vlan: Propagate MAC address to VLANs
From: Mike Manning @ 2016-05-06 13:26 UTC (permalink / raw)
To: netdev
In-Reply-To: <572C99FB.1060905@brocade.com>
The MAC address of the physical interface is only copied to the VLAN
when it is first created, resulting in an inconsistency after MAC
address changes of only newly created VLANs having an up-to-date MAC.
The VLANs should continue inheriting the MAC address of the physical
interface, unless explicitly changed to be different from this.
This allows IPv6 EUI64 addresses for the VLAN to reflect any changes
to the MAC of the physical interface and thus for DAD to behave as
expected.
Signed-off-by: Mike Manning <mmanning@brocade.com>
---
include/linux/if_vlan.h | 2 ++
net/8021q/vlan.c | 17 +++++++++++------
net/8021q/vlan_dev.c | 13 ++++++++++---
3 files changed, 23 insertions(+), 9 deletions(-)
--- a/include/linux/if_vlan.h
+++ b/include/linux/if_vlan.h
@@ -138,6 +138,7 @@ struct netpoll;
* @flags: device flags
* @real_dev: underlying netdevice
* @real_dev_addr: address of underlying netdevice
+ * @addr_assign_type: address assignment type
* @dent: proc dir entry
* @vlan_pcpu_stats: ptr to percpu rx stats
*/
@@ -153,6 +154,7 @@ struct vlan_dev_priv {
struct net_device *real_dev;
unsigned char real_dev_addr[ETH_ALEN];
+ unsigned char addr_assign_type;
struct proc_dir_entry *dent;
struct vlan_pcpu_stats __percpu *vlan_pcpu_stats;
--- a/net/8021q/vlan.c
+++ b/net/8021q/vlan.c
@@ -291,6 +291,15 @@ static void vlan_sync_address(struct net
if (ether_addr_equal(vlan->real_dev_addr, dev->dev_addr))
return;
+ /* vlan continues to inherit address of parent interface */
+ if (vlan->addr_assign_type == NET_ADDR_STOLEN) {
+ ether_addr_copy(vlandev->dev_addr, dev->dev_addr);
+ goto out;
+ }
+
+ if (!(vlandev->flags & IFF_UP))
+ goto out;
+
/* vlan address was different from the old address and is equal to
* the new address */
if (!ether_addr_equal(vlandev->dev_addr, vlan->real_dev_addr) &&
@@ -303,6 +312,7 @@ static void vlan_sync_address(struct net
!ether_addr_equal(vlandev->dev_addr, dev->dev_addr))
dev_uc_add(dev, vlandev->dev_addr);
+out:
ether_addr_copy(vlan->real_dev_addr, dev->dev_addr);
}
@@ -389,13 +399,8 @@ static int vlan_device_event(struct noti
case NETDEV_CHANGEADDR:
/* Adjust unicast filters on underlying device */
- vlan_group_for_each_dev(grp, i, vlandev) {
- flgs = vlandev->flags;
- if (!(flgs & IFF_UP))
- continue;
-
+ vlan_group_for_each_dev(grp, i, vlandev)
vlan_sync_address(dev, vlandev);
- }
break;
case NETDEV_CHANGEMTU:
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -315,17 +315,21 @@ static int vlan_dev_stop(struct net_devi
static int vlan_dev_set_mac_address(struct net_device *dev, void *p)
{
- struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;
+ struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
+ struct net_device *real_dev = vlan->real_dev;
struct sockaddr *addr = p;
+ bool is_real_addr;
int err;
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
+ is_real_addr = ether_addr_equal(addr->sa_data, real_dev->dev_addr);
+
if (!(dev->flags & IFF_UP))
goto out;
- if (!ether_addr_equal(addr->sa_data, real_dev->dev_addr)) {
+ if (!is_real_addr) {
err = dev_uc_add(real_dev, addr->sa_data);
if (err < 0)
return err;
@@ -336,6 +340,7 @@ static int vlan_dev_set_mac_address(stru
out:
ether_addr_copy(dev->dev_addr, addr->sa_data);
+ vlan->addr_assign_type = is_real_addr ? NET_ADDR_STOLEN : NET_ADDR_SET;
return 0;
}
@@ -558,8 +563,10 @@ static int vlan_dev_init(struct net_devi
/* ipv6 shared card related stuff */
dev->dev_id = real_dev->dev_id;
- if (is_zero_ether_addr(dev->dev_addr))
+ if (is_zero_ether_addr(dev->dev_addr)) {
eth_hw_addr_inherit(dev, real_dev);
+ vlan_dev_priv(dev)->addr_assign_type = NET_ADDR_STOLEN;
+ }
if (is_zero_ether_addr(dev->broadcast))
memcpy(dev->broadcast, real_dev->broadcast, dev->addr_len);
--
1.7.10.4
^ permalink raw reply
* [PATCH net] macvtap: segmented packet is consumed
From: Eric Dumazet @ 2016-05-06 12:58 UTC (permalink / raw)
To: David Miller; +Cc: netdev, Vlad Yasevich
From: Eric Dumazet <edumazet@google.com>
If GSO packet is segmented and its segments are properly queued,
we call consume_skb() instead of kfree_skb() to be drop monitor
friendly.
Fixes: 3e4f8b7873709 ("macvtap: Perform GSO on forwarding path.")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Vlad Yasevich <vyasevic@redhat.com>
---
drivers/net/macvtap.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c
index 95394edd1ed5..9a35aa462314 100644
--- a/drivers/net/macvtap.c
+++ b/drivers/net/macvtap.c
@@ -373,7 +373,7 @@ static rx_handler_result_t macvtap_handle_frame(struct sk_buff **pskb)
goto wake_up;
}
- kfree_skb(skb);
+ consume_skb(skb);
while (segs) {
struct sk_buff *nskb = segs->next;
^ permalink raw reply related
* Re: OpenWRT wrong adjustment of fq_codel defaults (Was: fq_codel_drop vs a udp flood)
From: Jesper Dangaard Brouer @ 2016-05-06 12:47 UTC (permalink / raw)
To: Felix Fietkau, Dave Taht
Cc: make-wifi-fast, zajec5, ath10k, codel@lists.bufferbloat.net,
netdev@vger.kernel.org, Jonathan Morton, Roman Yeryomin,
openwrt-devel
In-Reply-To: <20160506114243.4eb4f95e@redhat.com>
I've created a OpenWRT ticket[1] on this issue, as it seems that someone[2]
closed Felix'es OpenWRT email account (bad choice! emails bouncing).
Sounds like OpenWRT and the LEDE https://www.lede-project.org/ project
is in some kind of conflict.
OpenWRT ticket [1] https://dev.openwrt.org/ticket/22349
[2] http://thread.gmane.org/gmane.comp.embedded.openwrt.devel/40298/focus=40335
On Fri, 6 May 2016 11:42:43 +0200
Jesper Dangaard Brouer <brouer@redhat.com> wrote:
> Hi Felix,
>
> This is an important fix for OpenWRT, please read!
>
> OpenWRT changed the default fq_codel sch->limit from 10240 to 1024,
> without also adjusting q->flows_cnt. Eric explains below that you must
> also adjust the buckets (q->flows_cnt) for this not to break. (Just
> adjust it to 128)
>
> Problematic OpenWRT commit in question:
> http://git.openwrt.org/?p=openwrt.git;a=patch;h=12cd6578084e
> 12cd6578084e ("kernel: revert fq_codel quantum override to prevent it from causing too much cpu load with higher speed (#21326)")
>
>
> I also highly recommend you cherry-pick this very recent commit:
> net-next: 9d18562a2278 ("fq_codel: add batch ability to fq_codel_drop()")
> https://git.kernel.org/davem/net-next/c/9d18562a227
>
> This should fix very high CPU usage in-case fq_codel goes into drop mode.
> The problem is that drop mode was considered rare, and implementation
> wise it was chosen to be more expensive (to save cycles on normal mode).
> Unfortunately is it easy to trigger with an UDP flood. Drop mode is
> especially expensive for smaller devices, as it scans a 4K big array,
> thus 64 cache misses for small devices!
>
> The fix is to allow drop-mode to bulk-drop more packets when entering
> drop-mode (default 64 bulk drop). That way we don't suddenly
> experience a significantly higher processing cost per packet, but
> instead can amortize this.
>
> To Eric, should we recommend OpenWRT to adjust default (max) 64 bulk
> drop, given we also recommend bucket size to be 128 ? (thus the amount
> of memory to scan is less, but their CPU is also much smaller).
>
> --Jesper
>
>
> On Thu, 05 May 2016 12:23:27 -0700 Eric Dumazet <eric.dumazet@gmail.com> wrote:
>
> > On Thu, 2016-05-05 at 19:25 +0300, Roman Yeryomin wrote:
> > > On 5 May 2016 at 19:12, Eric Dumazet <eric.dumazet@gmail.com> wrote:
> > > > On Thu, 2016-05-05 at 17:53 +0300, Roman Yeryomin wrote:
> > > >
> > > >>
> > > >> qdisc fq_codel 0: dev eth0 root refcnt 2 limit 1024p flows 1024
> > > >> quantum 1514 target 5.0ms interval 100.0ms ecn
> > > >> Sent 12306 bytes 128 pkt (dropped 0, overlimits 0 requeues 0)
> > > >> backlog 0b 0p requeues 0
> > > >> maxpacket 0 drop_overlimit 0 new_flow_count 0 ecn_mark 0
> > > >> new_flows_len 0 old_flows_len 0
> > > >
> > > >
> > > > Limit of 1024 packets and 1024 flows is not wise I think.
> > > >
> > > > (If all buckets are in use, each bucket has a virtual queue of 1 packet,
> > > > which is almost the same than having no queue at all)
> > > >
> > > > I suggest to have at least 8 packets per bucket, to let Codel have a
> > > > chance to trigger.
> > > >
> > > > So you could either reduce number of buckets to 128 (if memory is
> > > > tight), or increase limit to 8192.
> > >
> > > Will try, but what I've posted is default, I didn't change/configure that.
> >
> > fq_codel has a default of 10240 packets and 1024 buckets.
> >
> > http://lxr.free-electrons.com/source/net/sched/sch_fq_codel.c#L413
> >
> > If someone changed that in the linux variant you use, he probably should
> > explain the rationale.
--
Best regards,
Jesper Dangaard Brouer
MSc.CS, Principal Kernel Engineer at Red Hat
Author of http://www.iptv-analyzer.org
LinkedIn: http://www.linkedin.com/in/brouer
_______________________________________________
Codel mailing list
Codel@lists.bufferbloat.net
https://lists.bufferbloat.net/listinfo/codel
^ permalink raw reply
* [PATCH v2] net: arc/emac: Move arc_emac_tx_clean() into arc_emac_tx() and disable tx interrut
From: Caesar Wang @ 2016-05-06 12:19 UTC (permalink / raw)
To: davem
Cc: heiko, linux-rockchip, Shuyu Wei, Alexander Kochetkov, netdev,
Caesar Wang, linux-kernel
From: Shuyu Wei <sy.w@outlook.com>
Doing tx_clean() inside poll() may scramble the tx ring buffer if
tx() is running. This will cause tx to stop working, which can be
reproduced by simultaneously downloading two large files at high speed.
Moving tx_clean() into tx() will prevent this. And tx interrupt is no
longer needed now.
Picked the Shuyu's patch up, the patch is sent on
https://patchwork.kernel.org/patch/8356821/, since that make sense for
rockchip platform.
Note: Many people feedback the cransh problems with rk3036/rk3188 emac when
download the heavy loading and this patch is indeed can fix the crash.
The crash log as the followings:
...
[ 2191.996127 ] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.4.0-rc6 #114
[ 2192.002475 ] Hardware name: Rockchip (Device Tree)
[ 2192.007174 ] Backtrace:
[ 2192.009658 ] [<c00134d4>] (dump_backtrace) from [<c0013680>]
(show_stack+0x18/0x1c)
[ 2192.017220 ] r7:c051c4f8 r6:ef463180 r5:c05b7000 r4:00000000
[ 2192.022948 ] [<c0013668>] (show_stack) from [<c0219d90>]
(dump_stack+0x90/0xa0)
[ 2192.030176 ] [<c0219d00>] (dump_stack) from [<c00b2cd4>]
(bad_page+0xdc/0x12c)
[ 2192.037302 ] r5:c059a100 r4:c05f430c
[ 2192.040913 ] [<c00b2bf8>] (bad_page) from [<c00b606c>]
(get_page_from_freelist+0x388/0x95c)
[ 2192.049166 ] r9:00000008 r8:ef463180 r7:c051c4d0 r6:00000000
r5:00000000 r4:c051c4e4
[ 2192.056982 ] [<c00b5ce4>] (get_page_from_freelist) from
[<c00b6880>] (__alloc_pages_nodemask+0xd8/0x8e8)
[ 2192.066362 ] r10:c001b068 r9:00000000 r8:ee0b02b0 r7:60000113
r6:00000003 r5:02095220
[ 2192.074254 ] r4:c05ca1c0
[ 2192.076809 ] [<c00b67a8>] (__alloc_pages_nodemask) from
[<c00b7140>] (__alloc_page_frag+0xb0/0x160)
[ 2192.085757 ] r10:c001b068 r9:00000000 r8:ee0b02b0 r7:60000113
r6:02080020 r5:00000740
[ 2192.093650 ] r4:eedbc884
[ 2192.096207 ] [<c00b7090>] (__alloc_page_frag) from [<c03273b4>]
(__netdev_alloc_skb+0xa0/0x104)
[ 2192.104806 ] r7:60000113 r6:eedbc884 r5:ee0b0000 r4:00000740
[ 2192.110525 ] [<c0327314>] (__netdev_alloc_skb) from [<c02aac00>]
(arc_emac_poll+0x318/0x57c)
[ 2192.118865 ] r9:00000000 r8:ee0b02b0 r7:0000019c r6:ee163780
r5:00000670 r4:ee0b0000
[ 2192.126683 ] [<c02aa8e8>] (arc_emac_poll) from [<c0339ed8>]
(net_rx_action+0x1f0/0x2ec)
[ 2192.134590 ] r10:c0599df8 r9:c059a100 r8:00073760 r7:0000012c
r6:00000028 r5:c02aa8e8
[ 2192.142483 ] r4:ee0b04e0
[ 2192.145040 ] [<c0339ce8>] (net_rx_action) from [<c0026f5c>]
(__do_softirq+0x134/0x258)
[ 2192.152860 ] r10:c059a080 r9:40000003 r8:00000003 r7:00000100
r6:c0598000 r5:c059a08c
[ 2192.160751 ] r4:00000000
...
Signed-off-by: Shuyu Wei <sy.w@outlook.com>
Tested-by: Michael Niewoehner <linux@mniewoehner.de>
Tested-by: Xing Zheng <zhengxing@rock-chips.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Alexander Kochetkov <al.kochet@gmail.com>
Cc: netdev@vger.kernel.org
Signed-off-by: Caesar Wang <wxt@rock-chips.com>
---
drivers/net/ethernet/arc/emac_main.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/arc/emac_main.c b/drivers/net/ethernet/arc/emac_main.c
index a3a9392..4f4e25e 100644
--- a/drivers/net/ethernet/arc/emac_main.c
+++ b/drivers/net/ethernet/arc/emac_main.c
@@ -311,12 +311,10 @@ static int arc_emac_poll(struct napi_struct *napi, int budget)
struct arc_emac_priv *priv = netdev_priv(ndev);
unsigned int work_done;
- arc_emac_tx_clean(ndev);
-
work_done = arc_emac_rx(ndev, budget);
if (work_done < budget) {
napi_complete(napi);
- arc_reg_or(priv, R_ENABLE, RXINT_MASK | TXINT_MASK);
+ arc_reg_or(priv, R_ENABLE, RXINT_MASK);
}
return work_done;
@@ -345,9 +343,9 @@ static irqreturn_t arc_emac_intr(int irq, void *dev_instance)
/* Reset all flags except "MDIO complete" */
arc_reg_set(priv, R_STATUS, status);
- if (status & (RXINT_MASK | TXINT_MASK)) {
+ if (status & RXINT_MASK) {
if (likely(napi_schedule_prep(&priv->napi))) {
- arc_reg_clr(priv, R_ENABLE, RXINT_MASK | TXINT_MASK);
+ arc_reg_clr(priv, R_ENABLE, RXINT_MASK);
__napi_schedule(&priv->napi);
}
}
@@ -461,7 +459,7 @@ static int arc_emac_open(struct net_device *ndev)
arc_reg_set(priv, R_TX_RING, (unsigned int)priv->txbd_dma);
/* Enable interrupts */
- arc_reg_set(priv, R_ENABLE, RXINT_MASK | TXINT_MASK | ERR_MASK);
+ arc_reg_set(priv, R_ENABLE, RXINT_MASK | ERR_MASK);
/* Set CONTROL */
arc_reg_set(priv, R_CTRL,
@@ -594,7 +592,7 @@ static int arc_emac_stop(struct net_device *ndev)
netif_stop_queue(ndev);
/* Disable interrupts */
- arc_reg_clr(priv, R_ENABLE, RXINT_MASK | TXINT_MASK | ERR_MASK);
+ arc_reg_clr(priv, R_ENABLE, RXINT_MASK | ERR_MASK);
/* Disable EMAC */
arc_reg_clr(priv, R_CTRL, EN_MASK);
@@ -656,6 +654,8 @@ static int arc_emac_tx(struct sk_buff *skb, struct net_device *ndev)
__le32 *info = &priv->txbd[*txbd_curr].info;
dma_addr_t addr;
+ arc_emac_tx_clean(ndev);
+
if (skb_padto(skb, ETH_ZLEN))
return NETDEV_TX_OK;
--
1.9.1
^ permalink raw reply related
* Re: [PATCH net] netfilter: nf_conntrack: Use net_mutex for helper unregistration.
From: Pablo Neira Ayuso @ 2016-05-06 11:03 UTC (permalink / raw)
To: Joe Stringer; +Cc: netdev, netfilter-devel, fw
In-Reply-To: <1462488637-46877-1-git-send-email-joe@ovn.org>
Hi Joe,
On Thu, May 05, 2016 at 03:50:37PM -0700, Joe Stringer wrote:
> diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c
> index 3b40ec575cd5..6860b19be406 100644
> --- a/net/netfilter/nf_conntrack_helper.c
> +++ b/net/netfilter/nf_conntrack_helper.c
> @@ -449,10 +449,10 @@ void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me)
> */
> synchronize_rcu();
>
> - rtnl_lock();
> + mutex_lock(&net_mutex);
> for_each_net(net)
> __nf_conntrack_helper_unregister(me, net);
> - rtnl_unlock();
> + mutex_unlock(&net_mutex);
This simple solution works because we have no .exit callbacks in any
of our helpers. Otherwise, the helper code may be already gone by when
the worker has a chance to run to release the netns.
If so, probably I can append this as comment to this function so we
don't forget. If we ever have .exit callbacks (I don't expect so), we
would need to wait for worker completion.
^ permalink raw reply
* Re: [PATCHv2 bluetooth-next 07/10] ipv6: introduce neighbour discovery ops
From: Alexander Aring @ 2016-05-06 10:19 UTC (permalink / raw)
To: Stefan Schmidt, linux-wpan
Cc: kernel, marcel, jukka.rissanen, hannes, mcr, werner,
linux-bluetooth, netdev, David S . Miller, Alexey Kuznetsov,
James Morris, Hideaki YOSHIFUJI, Patrick McHardy
In-Reply-To: <741404ec-e23e-30a9-c954-83b26ea71d7e@pengutronix.de>
Hi,
On 05/06/2016 11:47 AM, Alexander Aring wrote:
>
> Hi,
>
> On 05/04/2016 02:23 PM, Stefan Schmidt wrote:
>> Hello.
>>
>> On 20/04/16 10:19, Alexander Aring wrote:
>>> This patch introduces neighbour discovery ops callback structure. The
>>> structure contains at first receive and transmit handling for NS/NA and
>>> userspace option field functionality.
>>>
>>> These callback offers 6lowpan different handling, such as 802.15.4 short
>>> address handling or RFC6775 (Neighbor Discovery Optimization for IPv6 over
>>> 6LoWPANs).
>>>
>>> Cc: David S. Miller<davem@davemloft.net>
>>> Cc: Alexey Kuznetsov<kuznet@ms2.inr.ac.ru>
>>> Cc: James Morris<jmorris@namei.org>
>>> Cc: Hideaki YOSHIFUJI<yoshfuji@linux-ipv6.org>
>>> Cc: Patrick McHardy<kaber@trash.net>
>>> Signed-off-by: Alexander Aring<aar@pengutronix.de>
>>> ---
>>> include/linux/netdevice.h | 3 ++
>>> include/net/ndisc.h | 96 +++++++++++++++++++++++++++++++++++++++++++----
>>> net/ipv6/addrconf.c | 1 +
>>> net/ipv6/ndisc.c | 71 ++++++++++++++++++++++++-----------
>>> net/ipv6/route.c | 2 +-
>>> 5 files changed, 144 insertions(+), 29 deletions(-)
>>>
>>> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
>>> index 0052c42..bc60033 100644
>>> --- a/include/linux/netdevice.h
>>> +++ b/include/linux/netdevice.h
>>> @@ -1677,6 +1677,9 @@ struct net_device {
>>> #ifdef CONFIG_NET_L3_MASTER_DEV
>>> const struct l3mdev_ops *l3mdev_ops;
>>> #endif
>>> +#if IS_ENABLED(CONFIG_IPV6)
>>> + const struct ndisc_ops *ndisc_ops;
>>> +#endif
>>> const struct header_ops *header_ops;
>>> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
>>> index aac868e..14ed016 100644
>>> --- a/include/net/ndisc.h
>>> +++ b/include/net/ndisc.h
>>> @@ -110,7 +110,8 @@ struct ndisc_options {
>>> #define NDISC_OPT_SPACE(len) (((len)+2+7)&~7)
>>> -struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
>>> +struct ndisc_options *ndisc_parse_options(const struct net_device *dev,
>>> + u8 *opt, int opt_len,
>>> struct ndisc_options *ndopts);
>>> /*
>>> @@ -173,6 +174,93 @@ static inline struct neighbour *__ipv6_neigh_lookup(struct net_device *dev, cons
>>> return n;
>>> }
>>> +static inline int __ip6_ndisc_is_useropt(struct nd_opt_hdr *opt)
>>
>> Name it __ipv6... instead of __ip6...?
>
> I had ipv6 before, but then I saw ip6... prefixes functionality in
> ndisc.c and changed it to ip6...., but both seems to be used.
>
> See "ip6_nd_hdr".
>
>>> +{
>>> + return opt->nd_opt_type == ND_OPT_RDNSS ||
>>> + opt->nd_opt_type == ND_OPT_DNSSL;
>>> +}
>>> +
>>> +#if IS_ENABLED(CONFIG_IPV6)
>>> +struct ndisc_ops {
>>> + int (*is_useropt)(struct nd_opt_hdr *opt);
>>> + void (*send_na)(struct net_device *dev,
>>> + const struct in6_addr *daddr,
>>> + const struct in6_addr *solicited_addr,
>>> + bool router, bool solicited,
>>> + bool override, bool inc_opt);
>>> + void (*recv_na)(struct sk_buff *skb);
>>> + void (*send_ns)(struct net_device *dev,
>>> + const struct in6_addr *solicit,
>>> + const struct in6_addr *daddr,
>>> + const struct in6_addr *saddr);
>>> + void (*recv_ns)(struct sk_buff *skb);
>>> +};
>>> +
>>> +static inline int ndisc_is_useropt(const struct net_device *dev,
>>> + struct nd_opt_hdr *opt)
>>> +{
>>> + if (likely(dev->ndisc_ops->is_useropt))
>>> + return dev->ndisc_ops->is_useropt(opt);
>>> + else
>>> + return 0;
>>> +}
>>> +
>>> +static inline void ndisc_send_na(struct net_device *dev,
>>> + const struct in6_addr *daddr,
>>> + const struct in6_addr *solicited_addr,
>>> + bool router, bool solicited, bool override,
>>> + bool inc_opt)
>>> +{
>>> + if (likely(dev->ndisc_ops->send_na))
>>> + dev->ndisc_ops->send_na(dev, daddr, solicited_addr, router,
>>> + solicited, override, inc_opt);
>>> +}
>>> +
>>> +static inline void ndisc_recv_na(struct sk_buff *skb)
>>> +{
>>> + if (likely(skb->dev->ndisc_ops->recv_na))
>>> + skb->dev->ndisc_ops->recv_na(skb);
>>> +}
>>> +
>>> +static inline void ndisc_send_ns(struct net_device *dev,
>>> + const struct in6_addr *solicit,
>>> + const struct in6_addr *daddr,
>>> + const struct in6_addr *saddr)
>>> +{
>>> + if (likely(dev->ndisc_ops->send_ns))
>>> + dev->ndisc_ops->send_ns(dev, solicit, daddr, saddr);
>>> +}
>>> +
>>> +static inline void ndisc_recv_ns(struct sk_buff *skb)
>>> +{
>>> + if (likely(skb->dev->ndisc_ops->recv_ns))
>>> + skb->dev->ndisc_ops->recv_ns(skb);
>>> +}
>>> +#else
>>> +static inline int ndisc_is_useropt(const struct net_device *dev,
>>> + struct nd_opt_hdr *opt)
>>> +{
>>> + return 0;
>>> +}
>>> +
>>> +static inline void ndisc_send_na(struct net_device *dev,
>>> + const struct in6_addr *daddr,
>>> + const struct in6_addr *solicited_addr,
>>> + bool router, bool solicited, bool override,
>>> + bool inc_opt) { }
>>> +
>>> +static inline void ndisc_recv_na(struct sk_buff *skb) { }
>>> +
>>> +static inline void ndisc_send_ns(struct net_device *dev,
>>> + const struct in6_addr *solicit,
>>> + const struct in6_addr *daddr,
>>> + const struct in6_addr *saddr) { }
>>> +
>>> +static inline void ndisc_recv_ns(struct sk_buff *skb) { }
>>> +#endif
>>> +
>>> +void ip6_register_ndisc_ops(struct net_device *dev);
>>> +
>>> int ndisc_init(void);
>>> int ndisc_late_init(void);
>>> @@ -181,14 +269,8 @@ void ndisc_cleanup(void);
>>> int ndisc_rcv(struct sk_buff *skb);
>>> -void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
>>> - const struct in6_addr *daddr, const struct in6_addr *saddr);
>>> -
>>> void ndisc_send_rs(struct net_device *dev,
>>> const struct in6_addr *saddr, const struct in6_addr *daddr);
>>> -void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
>>> - const struct in6_addr *solicited_addr,
>>> - bool router, bool solicited, bool override, bool inc_opt);
>>> void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target);
>>> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
>>> index 54e18c2..a2ef04b 100644
>>> --- a/net/ipv6/addrconf.c
>>> +++ b/net/ipv6/addrconf.c
>>> @@ -3266,6 +3266,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
>>> idev = ipv6_add_dev(dev);
>>> if (IS_ERR(idev))
>>> return notifier_from_errno(PTR_ERR(idev));
>>> + ip6_register_ndisc_ops(dev);
>>> }
>>> break;
>>> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
>>> index 176c7c4..297080a 100644
>>> --- a/net/ipv6/ndisc.c
>>> +++ b/net/ipv6/ndisc.c
>>> @@ -185,24 +185,25 @@ static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
>>> return cur <= end && cur->nd_opt_type == type ? cur : NULL;
>>> }
>>> -static inline int ndisc_is_useropt(struct nd_opt_hdr *opt)
>>> +static inline int ip6_ndisc_is_useropt(struct nd_opt_hdr *opt)
>>> {
>>> - return opt->nd_opt_type == ND_OPT_RDNSS ||
>>> - opt->nd_opt_type == ND_OPT_DNSSL;
>>> + return __ip6_ndisc_is_useropt(opt);
>>
>> Why putting this check into a different function? It looks like a not needed redirection.
>
> because these IPv6 userspace options are valid in 6LoWPAN-ND and IPv6-ND.
>
> In 6LoWPAN we do:
>
> static inline int lowpan_ndisc_is_useropt(struct nd_opt_hdr *opt)
> {
> return __ip6_ndisc_is_useropt(opt) || opt->nd_opt_type == ND_OPT_6CO;
> }
>
> I don't believe that _any_ IPv6 implementation in userspace can do something
> with 6CO option fields. For this reason ARPHRD_6LOWPAN should make them
> valid for userspace only. (btw: the radvd maintainer wants to add some
> patches for 6CO processing support).
>
Okay, I remove the different function and add an EXPORT_SYMBOL. Then the
6LoWPAN-ND is_useropt callback can also use this function instead using
the __ip6_ndisc_is_useropt inline function.
- Alex
^ permalink raw reply
* Re: ixgbe: cannot enable LRO
From: Otto Sabart @ 2016-05-06 10:18 UTC (permalink / raw)
To: Alexander Duyck; +Cc: Netdev, Jirka Hladky, Adam Okuliar
In-Reply-To: <CAKgT0UceTxomp+Zf5ks_B0bFomdx2jyhWLwSXh4716voJ5YFAg@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1958 bytes --]
> On Wed, Apr 27, 2016 at 2:36 AM, Otto Sabart <osabart@redhat.com> wrote:
> >
> > Hello everyone,
> > does anybody have a problem with LRO on ixge (on latest 4.6-rc5)?
> > I cannot find a way to enable it.
> >
> > On stable RHEL7.2 kernel everything works fine.
> >
> > I opened a bug report [0].
> >
> > [0] https://bugzilla.kernel.org/show_bug.cgi?id=117291
> >
> >
> > Thanks!
> >
> > Ota
>
Hello Alex,
> So I am able to turn on LRO without any issues.
Yes, I badly desciribed the problem. The LRO was not possible to turn on
immediately _after the boot_ (I was enabling it in /etc/rc.local).
When I reloaded the ixgbe driver, the LRO was possible to turn on
without problem.
I found out that the problem was caused by network manager. When I
disabled NM, the LRO started to work.
>
> Do you know if you have done anything that might disable LRO such as
> modified the rx-usecs to a value less than 10 or enabled routing or
> bridging on the device? Also I think a stacked device might be able
> to block you from enabling LRO unless all the devices stacked on the
> interface can support it.
I did not modify rx-usecs (at least not intentionally). Its value is
always (with disabled or enabled LRO) equal to 1 (with disabled NM).
$ ethtool -c ixgbe | grep rx-usecs
rx-usecs: 1
rx-usecs-irq: 0
rx-usecs-low: 0
rx-usecs-high: 0
There is no linux bridge or routing enabled on this device. There is
only a VLAN configured. Does it matter?
$ ip l show ixgbe
9: ixgbe: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT qlen 1000
link/ether 00:1b:21:90:c3:86 brd ff:ff:ff:ff:ff:ff
$ ip l show ixgbe.40
19: ixgbe.40@ixgbe: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT qlen 1000
link/ether 00:1b:21:90:c3:86 brd ff:ff:ff:ff:ff:ff
I updated the bugzilla [0] and I think we can close this as a NOTABUG.
Thanks!
Ota
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 520 bytes --]
^ permalink raw reply
* Re: [PATCHv2 bluetooth-next 07/10] ipv6: introduce neighbour discovery ops
From: Alexander Aring @ 2016-05-06 9:47 UTC (permalink / raw)
To: Stefan Schmidt, linux-wpan-u79uwXL29TY76Z2rM5mHXA
Cc: kernel-bIcnvbaLZ9MEGnE8C9+IrQ, marcel-kz+m5ild9QBg9hUCZPvPmw,
jukka.rissanen-VuQAYsv1563Yd54FQh9/CA,
hannes-tFNcAqjVMyqKXQKiL6tip0B+6BGkLq7r,
mcr-SWp7JaYWvAQV+D8aMU/kSg, werner-SEdMjqphH88wryQfseakQg,
linux-bluetooth-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA, David S . Miller, Alexey Kuznetsov,
James Morris, Hideaki YOSHIFUJI, Patrick McHardy
In-Reply-To: <5729E9AB.5000301-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org>
Hi,
On 05/04/2016 02:23 PM, Stefan Schmidt wrote:
> Hello.
>
> On 20/04/16 10:19, Alexander Aring wrote:
>> This patch introduces neighbour discovery ops callback structure. The
>> structure contains at first receive and transmit handling for NS/NA and
>> userspace option field functionality.
>>
>> These callback offers 6lowpan different handling, such as 802.15.4 short
>> address handling or RFC6775 (Neighbor Discovery Optimization for IPv6 over
>> 6LoWPANs).
>>
>> Cc: David S. Miller<davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
>> Cc: Alexey Kuznetsov<kuznet-v/Mj1YrvjDBInbfyfbPRSQ@public.gmane.org>
>> Cc: James Morris<jmorris-gx6/JNMH7DfYtjvyW6yDsg@public.gmane.org>
>> Cc: Hideaki YOSHIFUJI<yoshfuji-VfPWfsRibaP+Ru+s062T9g@public.gmane.org>
>> Cc: Patrick McHardy<kaber-dcUjhNyLwpNeoWH0uzbU5w@public.gmane.org>
>> Signed-off-by: Alexander Aring<aar-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
>> ---
>> include/linux/netdevice.h | 3 ++
>> include/net/ndisc.h | 96 +++++++++++++++++++++++++++++++++++++++++++----
>> net/ipv6/addrconf.c | 1 +
>> net/ipv6/ndisc.c | 71 ++++++++++++++++++++++++-----------
>> net/ipv6/route.c | 2 +-
>> 5 files changed, 144 insertions(+), 29 deletions(-)
>>
>> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
>> index 0052c42..bc60033 100644
>> --- a/include/linux/netdevice.h
>> +++ b/include/linux/netdevice.h
>> @@ -1677,6 +1677,9 @@ struct net_device {
>> #ifdef CONFIG_NET_L3_MASTER_DEV
>> const struct l3mdev_ops *l3mdev_ops;
>> #endif
>> +#if IS_ENABLED(CONFIG_IPV6)
>> + const struct ndisc_ops *ndisc_ops;
>> +#endif
>> const struct header_ops *header_ops;
>> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
>> index aac868e..14ed016 100644
>> --- a/include/net/ndisc.h
>> +++ b/include/net/ndisc.h
>> @@ -110,7 +110,8 @@ struct ndisc_options {
>> #define NDISC_OPT_SPACE(len) (((len)+2+7)&~7)
>> -struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
>> +struct ndisc_options *ndisc_parse_options(const struct net_device *dev,
>> + u8 *opt, int opt_len,
>> struct ndisc_options *ndopts);
>> /*
>> @@ -173,6 +174,93 @@ static inline struct neighbour *__ipv6_neigh_lookup(struct net_device *dev, cons
>> return n;
>> }
>> +static inline int __ip6_ndisc_is_useropt(struct nd_opt_hdr *opt)
>
> Name it __ipv6... instead of __ip6...?
I had ipv6 before, but then I saw ip6... prefixes functionality in
ndisc.c and changed it to ip6...., but both seems to be used.
See "ip6_nd_hdr".
>> +{
>> + return opt->nd_opt_type == ND_OPT_RDNSS ||
>> + opt->nd_opt_type == ND_OPT_DNSSL;
>> +}
>> +
>> +#if IS_ENABLED(CONFIG_IPV6)
>> +struct ndisc_ops {
>> + int (*is_useropt)(struct nd_opt_hdr *opt);
>> + void (*send_na)(struct net_device *dev,
>> + const struct in6_addr *daddr,
>> + const struct in6_addr *solicited_addr,
>> + bool router, bool solicited,
>> + bool override, bool inc_opt);
>> + void (*recv_na)(struct sk_buff *skb);
>> + void (*send_ns)(struct net_device *dev,
>> + const struct in6_addr *solicit,
>> + const struct in6_addr *daddr,
>> + const struct in6_addr *saddr);
>> + void (*recv_ns)(struct sk_buff *skb);
>> +};
>> +
>> +static inline int ndisc_is_useropt(const struct net_device *dev,
>> + struct nd_opt_hdr *opt)
>> +{
>> + if (likely(dev->ndisc_ops->is_useropt))
>> + return dev->ndisc_ops->is_useropt(opt);
>> + else
>> + return 0;
>> +}
>> +
>> +static inline void ndisc_send_na(struct net_device *dev,
>> + const struct in6_addr *daddr,
>> + const struct in6_addr *solicited_addr,
>> + bool router, bool solicited, bool override,
>> + bool inc_opt)
>> +{
>> + if (likely(dev->ndisc_ops->send_na))
>> + dev->ndisc_ops->send_na(dev, daddr, solicited_addr, router,
>> + solicited, override, inc_opt);
>> +}
>> +
>> +static inline void ndisc_recv_na(struct sk_buff *skb)
>> +{
>> + if (likely(skb->dev->ndisc_ops->recv_na))
>> + skb->dev->ndisc_ops->recv_na(skb);
>> +}
>> +
>> +static inline void ndisc_send_ns(struct net_device *dev,
>> + const struct in6_addr *solicit,
>> + const struct in6_addr *daddr,
>> + const struct in6_addr *saddr)
>> +{
>> + if (likely(dev->ndisc_ops->send_ns))
>> + dev->ndisc_ops->send_ns(dev, solicit, daddr, saddr);
>> +}
>> +
>> +static inline void ndisc_recv_ns(struct sk_buff *skb)
>> +{
>> + if (likely(skb->dev->ndisc_ops->recv_ns))
>> + skb->dev->ndisc_ops->recv_ns(skb);
>> +}
>> +#else
>> +static inline int ndisc_is_useropt(const struct net_device *dev,
>> + struct nd_opt_hdr *opt)
>> +{
>> + return 0;
>> +}
>> +
>> +static inline void ndisc_send_na(struct net_device *dev,
>> + const struct in6_addr *daddr,
>> + const struct in6_addr *solicited_addr,
>> + bool router, bool solicited, bool override,
>> + bool inc_opt) { }
>> +
>> +static inline void ndisc_recv_na(struct sk_buff *skb) { }
>> +
>> +static inline void ndisc_send_ns(struct net_device *dev,
>> + const struct in6_addr *solicit,
>> + const struct in6_addr *daddr,
>> + const struct in6_addr *saddr) { }
>> +
>> +static inline void ndisc_recv_ns(struct sk_buff *skb) { }
>> +#endif
>> +
>> +void ip6_register_ndisc_ops(struct net_device *dev);
>> +
>> int ndisc_init(void);
>> int ndisc_late_init(void);
>> @@ -181,14 +269,8 @@ void ndisc_cleanup(void);
>> int ndisc_rcv(struct sk_buff *skb);
>> -void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
>> - const struct in6_addr *daddr, const struct in6_addr *saddr);
>> -
>> void ndisc_send_rs(struct net_device *dev,
>> const struct in6_addr *saddr, const struct in6_addr *daddr);
>> -void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
>> - const struct in6_addr *solicited_addr,
>> - bool router, bool solicited, bool override, bool inc_opt);
>> void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target);
>> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
>> index 54e18c2..a2ef04b 100644
>> --- a/net/ipv6/addrconf.c
>> +++ b/net/ipv6/addrconf.c
>> @@ -3266,6 +3266,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
>> idev = ipv6_add_dev(dev);
>> if (IS_ERR(idev))
>> return notifier_from_errno(PTR_ERR(idev));
>> + ip6_register_ndisc_ops(dev);
>> }
>> break;
>> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
>> index 176c7c4..297080a 100644
>> --- a/net/ipv6/ndisc.c
>> +++ b/net/ipv6/ndisc.c
>> @@ -185,24 +185,25 @@ static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
>> return cur <= end && cur->nd_opt_type == type ? cur : NULL;
>> }
>> -static inline int ndisc_is_useropt(struct nd_opt_hdr *opt)
>> +static inline int ip6_ndisc_is_useropt(struct nd_opt_hdr *opt)
>> {
>> - return opt->nd_opt_type == ND_OPT_RDNSS ||
>> - opt->nd_opt_type == ND_OPT_DNSSL;
>> + return __ip6_ndisc_is_useropt(opt);
>
> Why putting this check into a different function? It looks like a not needed redirection.
because these IPv6 userspace options are valid in 6LoWPAN-ND and IPv6-ND.
In 6LoWPAN we do:
static inline int lowpan_ndisc_is_useropt(struct nd_opt_hdr *opt)
{
return __ip6_ndisc_is_useropt(opt) || opt->nd_opt_type == ND_OPT_6CO;
}
I don't believe that _any_ IPv6 implementation in userspace can do something
with 6CO option fields. For this reason ARPHRD_6LOWPAN should make them
valid for userspace only. (btw: the radvd maintainer wants to add some
patches for 6CO processing support).
- Alex
^ 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