Commit f7b65d70 authored by Frank Pavlic's avatar Frank Pavlic Committed by Jeff Garzik

[PATCH] s390: qeth driver fixes [3/6]

[PATCH 6/9] s390: qeth driver fixes [3/6]

From: Frank Pavlic <fpavlic@de.ibm.com>
       	fixed kernel panic caused by qeth driver:
        Using a bonding device qeth driver will realloc
        headroom for every skb coming from the bond device.
        Once this happens qeth frees the original skb and
        set the skb pointer to the new realloced skb.
        Under heavy transmit workload (e.g.UDP streams) through bond
        network device the qdio output queue might get full.
        In this case we return with EBUSY from qeth_send_packet.
        Returning to qeth_hard_start_xmit routine
        the skb address on the stack still points to the old address,
        which has been freed before.
        Returning from qeth_hard_start_xmit with EBUSY results in
        requeuing the skb. In this case it corrupts the qdisc queue
        and results in kernel panic.
Signed-off-by: default avatarFrank Pavlic <fpavlic@de.ibm.com>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent 1fda1a12
...@@ -859,23 +859,18 @@ qeth_get_ipa_adp_type(enum qeth_link_types link_type) ...@@ -859,23 +859,18 @@ qeth_get_ipa_adp_type(enum qeth_link_types link_type)
} }
} }
static inline int static inline struct sk_buff *
qeth_realloc_headroom(struct qeth_card *card, struct sk_buff **skb, int size) qeth_realloc_headroom(struct qeth_card *card, struct sk_buff *skb, int size)
{ {
struct sk_buff *new_skb = NULL; struct sk_buff *new_skb = skb;
if (skb_headroom(*skb) < size){ if (skb_headroom(skb) >= size)
new_skb = skb_realloc_headroom(*skb, size); return skb;
if (!new_skb) { new_skb = skb_realloc_headroom(skb, size);
PRINT_ERR("qeth_prepare_skb: could " if (!new_skb)
"not realloc headroom for qeth_hdr " PRINT_ERR("Could not realloc headroom for qeth_hdr "
"on interface %s", QETH_CARD_IFNAME(card)); "on interface %s", QETH_CARD_IFNAME(card));
return -ENOMEM; return new_skb;
}
kfree_skb(*skb);
*skb = new_skb;
}
return 0;
} }
static inline struct sk_buff * static inline struct sk_buff *
...@@ -885,16 +880,15 @@ qeth_pskb_unshare(struct sk_buff *skb, int pri) ...@@ -885,16 +880,15 @@ qeth_pskb_unshare(struct sk_buff *skb, int pri)
if (!skb_cloned(skb)) if (!skb_cloned(skb))
return skb; return skb;
nskb = skb_copy(skb, pri); nskb = skb_copy(skb, pri);
kfree_skb(skb); /* free our shared copy */
return nskb; return nskb;
} }
static inline void * static inline void *
qeth_push_skb(struct qeth_card *card, struct sk_buff **skb, int size) qeth_push_skb(struct qeth_card *card, struct sk_buff *skb, int size)
{ {
void *hdr; void *hdr;
hdr = (void *) skb_push(*skb, size); hdr = (void *) skb_push(skb, size);
/* /*
* sanity check, the Linux memory allocation scheme should * sanity check, the Linux memory allocation scheme should
* never present us cases like this one (the qdio header size plus * never present us cases like this one (the qdio header size plus
...@@ -903,8 +897,7 @@ qeth_push_skb(struct qeth_card *card, struct sk_buff **skb, int size) ...@@ -903,8 +897,7 @@ qeth_push_skb(struct qeth_card *card, struct sk_buff **skb, int size)
if ((((unsigned long) hdr) & (~(PAGE_SIZE - 1))) != if ((((unsigned long) hdr) & (~(PAGE_SIZE - 1))) !=
(((unsigned long) hdr + size + (((unsigned long) hdr + size +
QETH_IP_HEADER_SIZE) & (~(PAGE_SIZE - 1)))) { QETH_IP_HEADER_SIZE) & (~(PAGE_SIZE - 1)))) {
PRINT_ERR("qeth_prepare_skb: misaligned " PRINT_ERR("Misaligned packet on interface %s. Discarded.",
"packet on interface %s. Discarded.",
QETH_CARD_IFNAME(card)); QETH_CARD_IFNAME(card));
return NULL; return NULL;
} }
......
This diff is collapsed.
...@@ -24,7 +24,7 @@ static inline struct qeth_hdr_tso * ...@@ -24,7 +24,7 @@ static inline struct qeth_hdr_tso *
qeth_tso_prepare_skb(struct qeth_card *card, struct sk_buff **skb) qeth_tso_prepare_skb(struct qeth_card *card, struct sk_buff **skb)
{ {
QETH_DBF_TEXT(trace, 5, "tsoprsk"); QETH_DBF_TEXT(trace, 5, "tsoprsk");
return qeth_push_skb(card, skb, sizeof(struct qeth_hdr_tso)); return qeth_push_skb(card, *skb, sizeof(struct qeth_hdr_tso));
} }
/** /**
......
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