Commit c9e9de3e authored by Stanislaw Gruszka's avatar Stanislaw Gruszka Committed by Luis Henriques

rt2x00: do not align payload on modern H/W

commit cfd9167a upstream.

RT2800 and newer hardware require padding between header and payload if
header length is not multiple of 4.

For historical reasons we also align payload to to 4 bytes boundary, but
such alignment is not needed on modern H/W.

Patch fixes skb_under_panic problems reported from time to time:

https://bugzilla.kernel.org/show_bug.cgi?id=84911
https://bugzilla.kernel.org/show_bug.cgi?id=72471
http://marc.info/?l=linux-wireless&m=139108549530402&w=2
https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1087591

Panic happened because we eat 4 bytes of skb headroom on each
(re)transmission when sending frame without the payload and the header
length not being multiple of 4 (i.e. QoS header has 26 bytes). On such
case because paylad_aling=2 is bigger than header_align=0 we increase
header_align by 4 bytes. To prevent that we could change the check to:

	if (payload_length && payload_align > header_align)
		header_align += 4;

but not aligning payload at all is more effective and alignment is not
really needed by H/W (that has been tested on OpenWrt project for few
years now).
Reported-and-tested-by: default avatarAntti S. Lankila <alankila@bel.fi>
Debugged-by: default avatarAntti S. Lankila <alankila@bel.fi>
Reported-by: default avatarHenrik Asp <solenskiner@gmail.com>
Originally-From: Helmut Schaa <helmut.schaa@googlemail.com>
Signed-off-by: default avatarStanislaw Gruszka <sgruszka@redhat.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
Signed-off-by: default avatarLuis Henriques <luis.henriques@canonical.com>
parent 7988de14
...@@ -158,55 +158,29 @@ void rt2x00queue_align_frame(struct sk_buff *skb) ...@@ -158,55 +158,29 @@ void rt2x00queue_align_frame(struct sk_buff *skb)
skb_trim(skb, frame_length); skb_trim(skb, frame_length);
} }
void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int header_length) /*
* H/W needs L2 padding between the header and the paylod if header size
* is not 4 bytes aligned.
*/
void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int hdr_len)
{ {
unsigned int payload_length = skb->len - header_length; unsigned int l2pad = (skb->len > hdr_len) ? L2PAD_SIZE(hdr_len) : 0;
unsigned int header_align = ALIGN_SIZE(skb, 0);
unsigned int payload_align = ALIGN_SIZE(skb, header_length);
unsigned int l2pad = payload_length ? L2PAD_SIZE(header_length) : 0;
/* if (!l2pad)
* Adjust the header alignment if the payload needs to be moved more
* than the header.
*/
if (payload_align > header_align)
header_align += 4;
/* There is nothing to do if no alignment is needed */
if (!header_align)
return; return;
/* Reserve the amount of space needed in front of the frame */ skb_push(skb, l2pad);
skb_push(skb, header_align); memmove(skb->data, skb->data + l2pad, hdr_len);
/*
* Move the header.
*/
memmove(skb->data, skb->data + header_align, header_length);
/* Move the payload, if present and if required */
if (payload_length && payload_align)
memmove(skb->data + header_length + l2pad,
skb->data + header_length + l2pad + payload_align,
payload_length);
/* Trim the skb to the correct size */
skb_trim(skb, header_length + l2pad + payload_length);
} }
void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int header_length) void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int hdr_len)
{ {
/* unsigned int l2pad = (skb->len > hdr_len) ? L2PAD_SIZE(hdr_len) : 0;
* L2 padding is only present if the skb contains more than just the
* IEEE 802.11 header.
*/
unsigned int l2pad = (skb->len > header_length) ?
L2PAD_SIZE(header_length) : 0;
if (!l2pad) if (!l2pad)
return; return;
memmove(skb->data + l2pad, skb->data, header_length); memmove(skb->data + l2pad, skb->data, hdr_len);
skb_pull(skb, l2pad); skb_pull(skb, l2pad);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment