qeth: fix page breaks in hw headers

Turning on memory debugging showed there could be page breaks in
hardware headers. OSA does not allow this so we had to add code
to bounce the header in case there is a page break. This patch also
fixes a problem in case the skb->data part of a fragmented skb
spreads multiple pages.

Signed-off-by: Frank Blaschka <frank.blaschka@de.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Frank Blaschka
2010-06-21 22:57:10 +00:00
committed by David S. Miller
parent 43a65303fe
commit 51aa165c9f
4 changed files with 65 additions and 73 deletions

View File

@@ -57,48 +57,6 @@ static void qeth_free_buffer_pool(struct qeth_card *);
static int qeth_qdio_establish(struct qeth_card *);
static inline void __qeth_fill_buffer_frag(struct sk_buff *skb,
struct qdio_buffer *buffer, int is_tso,
int *next_element_to_fill)
{
struct skb_frag_struct *frag;
int fragno;
unsigned long addr;
int element, cnt, dlen;
fragno = skb_shinfo(skb)->nr_frags;
element = *next_element_to_fill;
dlen = 0;
if (is_tso)
buffer->element[element].flags =
SBAL_FLAGS_MIDDLE_FRAG;
else
buffer->element[element].flags =
SBAL_FLAGS_FIRST_FRAG;
dlen = skb->len - skb->data_len;
if (dlen) {
buffer->element[element].addr = skb->data;
buffer->element[element].length = dlen;
element++;
}
for (cnt = 0; cnt < fragno; cnt++) {
frag = &skb_shinfo(skb)->frags[cnt];
addr = (page_to_pfn(frag->page) << PAGE_SHIFT) +
frag->page_offset;
buffer->element[element].addr = (char *)addr;
buffer->element[element].length = frag->size;
if (cnt < (fragno - 1))
buffer->element[element].flags =
SBAL_FLAGS_MIDDLE_FRAG;
else
buffer->element[element].flags =
SBAL_FLAGS_LAST_FRAG;
element++;
}
*next_element_to_fill = element;
}
static inline const char *qeth_get_cardname(struct qeth_card *card)
{
if (card->info.guestlan) {
@@ -3020,13 +2978,11 @@ EXPORT_SYMBOL_GPL(qeth_get_priority_queue);
int qeth_get_elements_no(struct qeth_card *card, void *hdr,
struct sk_buff *skb, int elems)
{
int elements_needed = 0;
int dlen = skb->len - skb->data_len;
int elements_needed = PFN_UP((unsigned long)skb->data + dlen - 1) -
PFN_DOWN((unsigned long)skb->data);
if (skb_shinfo(skb)->nr_frags > 0)
elements_needed = (skb_shinfo(skb)->nr_frags + 1);
if (elements_needed == 0)
elements_needed = 1 + (((((unsigned long) skb->data) %
PAGE_SIZE) + skb->len) >> PAGE_SHIFT);
elements_needed += skb_shinfo(skb)->nr_frags;
if ((elements_needed + elems) > QETH_MAX_BUFFER_ELEMENTS(card)) {
QETH_DBF_MESSAGE(2, "Invalid size of IP packet "
"(Number=%d / Length=%d). Discarded.\n",
@@ -3037,15 +2993,35 @@ int qeth_get_elements_no(struct qeth_card *card, void *hdr,
}
EXPORT_SYMBOL_GPL(qeth_get_elements_no);
int qeth_hdr_chk_and_bounce(struct sk_buff *skb, int len)
{
int hroom, inpage, rest;
if (((unsigned long)skb->data & PAGE_MASK) !=
(((unsigned long)skb->data + len - 1) & PAGE_MASK)) {
hroom = skb_headroom(skb);
inpage = PAGE_SIZE - ((unsigned long) skb->data % PAGE_SIZE);
rest = len - inpage;
if (rest > hroom)
return 1;
memmove(skb->data - rest, skb->data, skb->len - skb->data_len);
skb->data -= rest;
QETH_DBF_MESSAGE(2, "skb bounce len: %d rest: %d\n", len, rest);
}
return 0;
}
EXPORT_SYMBOL_GPL(qeth_hdr_chk_and_bounce);
static inline void __qeth_fill_buffer(struct sk_buff *skb,
struct qdio_buffer *buffer, int is_tso, int *next_element_to_fill,
int offset)
{
int length = skb->len;
int length = skb->len - skb->data_len;
int length_here;
int element;
char *data;
int first_lap ;
int first_lap, cnt;
struct skb_frag_struct *frag;
element = *next_element_to_fill;
data = skb->data;
@@ -3068,10 +3044,14 @@ static inline void __qeth_fill_buffer(struct sk_buff *skb,
length -= length_here;
if (!length) {
if (first_lap)
buffer->element[element].flags = 0;
if (skb_shinfo(skb)->nr_frags)
buffer->element[element].flags =
SBAL_FLAGS_FIRST_FRAG;
else
buffer->element[element].flags = 0;
else
buffer->element[element].flags =
SBAL_FLAGS_LAST_FRAG;
SBAL_FLAGS_MIDDLE_FRAG;
} else {
if (first_lap)
buffer->element[element].flags =
@@ -3084,6 +3064,18 @@ static inline void __qeth_fill_buffer(struct sk_buff *skb,
element++;
first_lap = 0;
}
for (cnt = 0; cnt < skb_shinfo(skb)->nr_frags; cnt++) {
frag = &skb_shinfo(skb)->frags[cnt];
buffer->element[element].addr = (char *)page_to_phys(frag->page)
+ frag->page_offset;
buffer->element[element].length = frag->size;
buffer->element[element].flags = SBAL_FLAGS_MIDDLE_FRAG;
element++;
}
if (buffer->element[element - 1].flags)
buffer->element[element - 1].flags = SBAL_FLAGS_LAST_FRAG;
*next_element_to_fill = element;
}
@@ -3124,12 +3116,8 @@ static inline int qeth_fill_buffer(struct qeth_qdio_out_q *queue,
buf->next_element_to_fill++;
}
if (skb_shinfo(skb)->nr_frags == 0)
__qeth_fill_buffer(skb, buffer, large_send,
(int *)&buf->next_element_to_fill, offset);
else
__qeth_fill_buffer_frag(skb, buffer, large_send,
(int *)&buf->next_element_to_fill);
__qeth_fill_buffer(skb, buffer, large_send,
(int *)&buf->next_element_to_fill, offset);
if (!queue->do_pack) {
QETH_CARD_TEXT(queue->card, 6, "fillbfnp");