From: Tom Herbert <hidden> Date: 2012-06-08 05:05:45
In the transmit path of the bonding driver, skb->cb is used to
stash the skb->queue_mapping so that the bonding device can set its
own queue mapping. This value becomes corrupted since the skb->cb is
also used in __dev_xmit_skb.
When transmitting through bonding driver, bond_select_queue is
called from dev_queue_xmit. In bond_select_queue the original
skb->queue_mapping is copied into skb->cb (via bond_queue_mapping)
and skb->queue_mapping is overwritten with the bond driver queue.
Subsequently in dev_queue_xmit, __dev_xmit_skb is called which writes
the packet length into skb->cb, thereby overwriting the stashed
queue mappping. In bond_dev_queue_xmit (called from hard_start_xmit),
the queue mapping for the skb is set to the stashed value which is now
the skb length and hence is an invalid queue for the slave device.
Fix is to set bond_queue_mapping to skb->cb +
sizeof((struct qdisc_skb_cb)
Signed-off-by: Tom Herbert <redacted>
---
drivers/net/bonding/bond_main.c | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
I know it's a little bit more work, but please declare a proper
datastructure which shows explicitly what's going on, like Infiniband
does in drivers/infiniband/ulp/ipoib/ipoib.h
struct bond_skb_cb {
struct qdisc_skb_cb qdisc_cb;
u16 queue_mapping;
};
Actually, this probably means there is also a conflict and thus
queue mapping corruption possible for bonded infiniband. :-/
From: Eric Dumazet <hidden> Date: 2012-06-08 05:57:44
On Thu, 2012-06-07 at 22:05 -0700, Tom Herbert wrote:
quoted hunk
In the transmit path of the bonding driver, skb->cb is used to
stash the skb->queue_mapping so that the bonding device can set its
own queue mapping. This value becomes corrupted since the skb->cb is
also used in __dev_xmit_skb.
When transmitting through bonding driver, bond_select_queue is
called from dev_queue_xmit. In bond_select_queue the original
skb->queue_mapping is copied into skb->cb (via bond_queue_mapping)
and skb->queue_mapping is overwritten with the bond driver queue.
Subsequently in dev_queue_xmit, __dev_xmit_skb is called which writes
the packet length into skb->cb, thereby overwriting the stashed
queue mappping. In bond_dev_queue_xmit (called from hard_start_xmit),
the queue mapping for the skb is set to the stashed value which is now
the skb length and hence is an invalid queue for the slave device.
Fix is to set bond_queue_mapping to skb->cb +
sizeof((struct qdisc_skb_cb)
Signed-off-by: Tom Herbert <redacted>
---
drivers/net/bonding/bond_main.c | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
From: David Miller <davem@davemloft.net> Date: 2012-06-08 06:02:17
From: Eric Dumazet <redacted>
Date: Fri, 08 Jun 2012 07:57:37 +0200
On Thu, 2012-06-07 at 22:05 -0700, Tom Herbert wrote:
quoted
In the transmit path of the bonding driver, skb->cb is used to
stash the skb->queue_mapping so that the bonding device can set its
own queue mapping. This value becomes corrupted since the skb->cb is
also used in __dev_xmit_skb.
When transmitting through bonding driver, bond_select_queue is
called from dev_queue_xmit. In bond_select_queue the original
skb->queue_mapping is copied into skb->cb (via bond_queue_mapping)
and skb->queue_mapping is overwritten with the bond driver queue.
Subsequently in dev_queue_xmit, __dev_xmit_skb is called which writes
the packet length into skb->cb, thereby overwriting the stashed
queue mappping. In bond_dev_queue_xmit (called from hard_start_xmit),
the queue mapping for the skb is set to the stashed value which is now
the skb length and hence is an invalid queue for the slave device.
Fix is to set bond_queue_mapping to skb->cb +
sizeof((struct qdisc_skb_cb)
Signed-off-by: Tom Herbert <redacted>
---
drivers/net/bonding/bond_main.c | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
From: Eric Dumazet <hidden> Date: 2012-06-08 06:11:26
On Thu, 2012-06-07 at 23:02 -0700, David Miller wrote:
From: Eric Dumazet <redacted>
Date: Fri, 08 Jun 2012 07:57:37 +0200
quoted
On Thu, 2012-06-07 at 22:05 -0700, Tom Herbert wrote:
quoted
In the transmit path of the bonding driver, skb->cb is used to
stash the skb->queue_mapping so that the bonding device can set its
own queue mapping. This value becomes corrupted since the skb->cb is
also used in __dev_xmit_skb.
When transmitting through bonding driver, bond_select_queue is
called from dev_queue_xmit. In bond_select_queue the original
skb->queue_mapping is copied into skb->cb (via bond_queue_mapping)
and skb->queue_mapping is overwritten with the bond driver queue.
Subsequently in dev_queue_xmit, __dev_xmit_skb is called which writes
the packet length into skb->cb, thereby overwriting the stashed
queue mappping. In bond_dev_queue_xmit (called from hard_start_xmit),
the queue mapping for the skb is set to the stashed value which is now
the skb length and hence is an invalid queue for the slave device.
Fix is to set bond_queue_mapping to skb->cb +
sizeof((struct qdisc_skb_cb)
Signed-off-by: Tom Herbert <redacted>
---
drivers/net/bonding/bond_main.c | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
From: Eric Dumazet <hidden> Date: 2012-06-08 06:17:21
On Thu, 2012-06-07 at 23:02 -0700, David Miller wrote:
Hmmm, isn't that what qdisc_skb_cb is for? And even private data is
explicitly allocated:
quoted
unsigned char data[24];
there. :-)
By the way, I notice data[] is not aligned on a long on 64bit arches.
This might break net/sched/sch_netem.c on some arches, since
time_to_send is a u64.
From: David Miller <davem@davemloft.net> Date: 2012-06-08 06:22:28
From: Eric Dumazet <redacted>
Date: Fri, 08 Jun 2012 08:17:18 +0200
On Thu, 2012-06-07 at 23:02 -0700, David Miller wrote:
quoted
Hmmm, isn't that what qdisc_skb_cb is for? And even private data is
explicitly allocated:
quoted
unsigned char data[24];
there. :-)
By the way, I notice data[] is not aligned on a long on 64bit arches.
This might break net/sched/sch_netem.c on some arches, since
time_to_send is a u64.
Looks like we'll get the bonding queue mapping and fix this alignment
bug for free then :-)
From: Eric Dumazet <hidden> Date: 2012-06-08 06:47:27
On Thu, 2012-06-07 at 23:15 -0700, David Miller wrote:
From: Eric Dumazet <redacted>
Date: Fri, 08 Jun 2012 08:11:21 +0200
quoted
On Thu, 2012-06-07 at 23:02 -0700, David Miller wrote:
quoted
Hmmm, isn't that what qdisc_skb_cb is for? And even private data is
explicitly allocated:
quoted
unsigned char data[24];
there. :-)
Yes, but some other layers can use the same trick so it might collide.
Inserting the bond field in qdisc_skb_cb (level0) is safer.
Do you suggest that Infiniband does the same thing? :-)
I wonder if another way to solve this is not letting ndo_select_queue()
method the responsibility to call skb_set_queue_mapping() itself ?
(ie removing skb_set_queue_mapping() done in dev_pick_tx())
bonding would not have to save/restore skb queue mapping ?
Partial patch : (we have to audit all ndo_select_queue()
From: Eric Dumazet <hidden> Date: 2012-06-08 07:24:02
On Fri, 2012-06-08 at 08:47 +0200, Eric Dumazet wrote:
quoted hunk
On Thu, 2012-06-07 at 23:15 -0700, David Miller wrote:
quoted
From: Eric Dumazet <redacted>
Date: Fri, 08 Jun 2012 08:11:21 +0200
quoted
On Thu, 2012-06-07 at 23:02 -0700, David Miller wrote:
quoted
Hmmm, isn't that what qdisc_skb_cb is for? And even private data is
explicitly allocated:
quoted
unsigned char data[24];
there. :-)
Yes, but some other layers can use the same trick so it might collide.
Inserting the bond field in qdisc_skb_cb (level0) is safer.
Do you suggest that Infiniband does the same thing? :-)
I wonder if another way to solve this is not letting ndo_select_queue()
method the responsibility to call skb_set_queue_mapping() itself ?
(ie removing skb_set_queue_mapping() done in dev_pick_tx())
bonding would not have to save/restore skb queue mapping ?
Partial patch : (we have to audit all ndo_select_queue()
I must say I dont understand dev_pick_tx() anymore.
It seems to ignore skb->queue_mapping (unless device provides its own
ndo_select_queue() and this functions is aware of skb->queue_mapping, as
correctly done in ixgbe)
So commit fff3269907897ee (tcp: reflect SYN queue_mapping into SYNACK
packets) works on ixgbe, but probably not on other multiqueue devices.
This sounds like a regression to me.
From: John Fastabend <hidden> Date: 2012-06-08 07:42:26
On 6/8/2012 12:23 AM, Eric Dumazet wrote:
On Fri, 2012-06-08 at 08:47 +0200, Eric Dumazet wrote:
quoted
On Thu, 2012-06-07 at 23:15 -0700, David Miller wrote:
quoted
From: Eric Dumazet <redacted>
Date: Fri, 08 Jun 2012 08:11:21 +0200
quoted
On Thu, 2012-06-07 at 23:02 -0700, David Miller wrote:
quoted
Hmmm, isn't that what qdisc_skb_cb is for? And even private data is
explicitly allocated:
quoted
unsigned char data[24];
there. :-)
Yes, but some other layers can use the same trick so it might collide.
Inserting the bond field in qdisc_skb_cb (level0) is safer.
Do you suggest that Infiniband does the same thing? :-)
I wonder if another way to solve this is not letting ndo_select_queue()
method the responsibility to call skb_set_queue_mapping() itself ?
(ie removing skb_set_queue_mapping() done in dev_pick_tx())
bonding would not have to save/restore skb queue mapping ?
Partial patch : (we have to audit all ndo_select_queue()
I must say I dont understand dev_pick_tx() anymore.
It seems to ignore skb->queue_mapping (unless device provides its own
ndo_select_queue() and this functions is aware of skb->queue_mapping, as
correctly done in ixgbe)
So commit fff3269907897ee (tcp: reflect SYN queue_mapping into SYNACK
packets) works on ixgbe, but probably not on other multiqueue devices.
This sounds like a regression to me.
Well it would get picked up via skb_tx_hash(),
else if (ops->ndo_select_queue) {
[...]
} else {
struct sock *sk = skb->sk;
queue_index = sk_tx_queue_get(sk);
if (queue_index < 0 || skb->ooo_okay ||
queue_index >= dev->real_num_tx_queues) {
int old_index = queue_index;
queue_index = get_xps_queue(dev, skb);
if (queue_index < 0)
queue_index = skb_tx_hash(dev, skb);
[...]
So think this might be OK.
From: Eric Dumazet <hidden> Date: 2012-06-08 07:46:42
On Fri, 2012-06-08 at 09:24 +0200, Eric Dumazet wrote:
I must say I dont understand dev_pick_tx() anymore.
It seems to ignore skb->queue_mapping (unless device provides its own
ndo_select_queue() and this functions is aware of skb->queue_mapping, as
correctly done in ixgbe)
So commit fff3269907897ee (tcp: reflect SYN queue_mapping into SYNACK
packets) works on ixgbe, but probably not on other multiqueue devices.
This sounds like a regression to me.
Oh well, its done in skb_tx_hash(), after a few indirections, and if
skb->sk is NULL.
Which happens to be true in net-next for SYNACKS after commit
90ba9b1986b5ac (tcp: tcp_make_synack() can use alloc_skb())
From: Eric Dumazet <hidden> Date: 2012-06-08 07:49:04
On Fri, 2012-06-08 at 00:42 -0700, John Fastabend wrote:
On 6/8/2012 12:23 AM, Eric Dumazet wrote:
quoted
On Fri, 2012-06-08 at 08:47 +0200, Eric Dumazet wrote:
quoted
On Thu, 2012-06-07 at 23:15 -0700, David Miller wrote:
quoted
From: Eric Dumazet <redacted>
Date: Fri, 08 Jun 2012 08:11:21 +0200
quoted
On Thu, 2012-06-07 at 23:02 -0700, David Miller wrote:
quoted
Hmmm, isn't that what qdisc_skb_cb is for? And even private data is
explicitly allocated:
quoted
unsigned char data[24];
there. :-)
Yes, but some other layers can use the same trick so it might collide.
Inserting the bond field in qdisc_skb_cb (level0) is safer.
Do you suggest that Infiniband does the same thing? :-)
I wonder if another way to solve this is not letting ndo_select_queue()
method the responsibility to call skb_set_queue_mapping() itself ?
(ie removing skb_set_queue_mapping() done in dev_pick_tx())
bonding would not have to save/restore skb queue mapping ?
Partial patch : (we have to audit all ndo_select_queue()
I must say I dont understand dev_pick_tx() anymore.
It seems to ignore skb->queue_mapping (unless device provides its own
ndo_select_queue() and this functions is aware of skb->queue_mapping, as
correctly done in ixgbe)
So commit fff3269907897ee (tcp: reflect SYN queue_mapping into SYNACK
packets) works on ixgbe, but probably not on other multiqueue devices.
This sounds like a regression to me.
Well it would get picked up via skb_tx_hash(),
else if (ops->ndo_select_queue) {
[...]
} else {
struct sock *sk = skb->sk;
queue_index = sk_tx_queue_get(sk);
if (queue_index < 0 || skb->ooo_okay ||
queue_index >= dev->real_num_tx_queues) {
int old_index = queue_index;
queue_index = get_xps_queue(dev, skb);
if (queue_index < 0)
queue_index = skb_tx_hash(dev, skb);
[...]
So think this might be OK.
Yes, it sounds like sk setting (sk->sk_tx_queue_mapping) has precedence
over skb->queue_mapping.
Not sure how it works for UDP workload for example.
From: Tom Herbert <hidden> Date: 2012-06-08 15:04:04
I must say I dont understand dev_pick_tx() anymore.
It seems to ignore skb->queue_mapping (unless device provides its own
ndo_select_queue() and this functions is aware of skb->queue_mapping, as
correctly done in ixgbe)
So commit fff3269907897ee (tcp: reflect SYN queue_mapping into SYNACK
packets) works on ixgbe, but probably not on other multiqueue devices.
This sounds like a regression to me.
Maybe the fundamental issue is that the queue mappings only allow for
one level of multi queue device. It might be better if bonding didn't
have one and dev_pick_tx did the right thin (use xps on bonding
maybe).
Tom
From: Eric Dumazet <hidden> Date: 2012-06-08 15:11:16
On Fri, 2012-06-08 at 08:04 -0700, Tom Herbert wrote:
Maybe the fundamental issue is that the queue mappings only allow for
one level of multi queue device. It might be better if bonding didn't
have one and dev_pick_tx did the right thin (use xps on bonding
maybe).
bonding misuses multiqueue infrastructure to divert frames on selected
slaves, or maybe I am wrong.
From: John Fastabend <hidden> Date: 2012-06-08 16:16:16
On 6/8/2012 8:11 AM, Eric Dumazet wrote:
On Fri, 2012-06-08 at 08:04 -0700, Tom Herbert wrote:
quoted
Maybe the fundamental issue is that the queue mappings only allow for
one level of multi queue device. It might be better if bonding didn't
have one and dev_pick_tx did the right thin (use xps on bonding
maybe).
bonding misuses multiqueue infrastructure to divert frames on selected
slaves, or maybe I am wrong.
This is right see bond_slave_override() here the slaves queue_ids
are mapped to skb->queue_mapping via this TX_QUEUE_OVERRIDE param.