Commit 7d969d2e authored by Julian Wiedmann's avatar Julian Wiedmann Committed by David S. Miller

s390/qeth: size calculation outbound buffers

Depending on the device type, hard_start_xmit() builds different output
buffer formats. For instance with HiperSockets, on both L2 and L3 we
strip the ETH header from the skb - L3 doesn't need it, and L2 carries
it in the buffer's header element.
For this, we pass data_offset = ETH_HLEN all the way down to
__qeth_fill_buffer(), where skb->data is then adjusted accordingly.
But the initial size calculation still considers the *full* skb length
(including the ETH header). So qeth_get_elements_no() can erroneously
reject a skb as too big, even though it would actually fit into an
output buffer once the ETH header has been trimmed off later.

Fix this by passing an additional offset to qeth_get_elements_no(),
that indicates where in the skb the on-wire data actually begins.
Since the current code uses data_offset=-1 for some special handling
on OSA, we need to clamp data_offset to 0...

On HiperSockets this helps when sending ~MTU-size skbs with weird page
alignment. No change for OSA or AF_IUCV.
Signed-off-by: default avatarJulian Wiedmann <jwi@linux.vnet.ibm.com>
Signed-off-by: default avatarUrsula Braun <ubraun@linux.vnet.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 031b8c6d
...@@ -961,7 +961,8 @@ int qeth_bridgeport_query_ports(struct qeth_card *card, ...@@ -961,7 +961,8 @@ int qeth_bridgeport_query_ports(struct qeth_card *card,
int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role); int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role);
int qeth_bridgeport_an_set(struct qeth_card *card, int enable); int qeth_bridgeport_an_set(struct qeth_card *card, int enable);
int qeth_get_priority_queue(struct qeth_card *, struct sk_buff *, int, int); int qeth_get_priority_queue(struct qeth_card *, struct sk_buff *, int, int);
int qeth_get_elements_no(struct qeth_card *, struct sk_buff *, int); int qeth_get_elements_no(struct qeth_card *card, struct sk_buff *skb,
int extra_elems, int data_offset);
int qeth_get_elements_for_frags(struct sk_buff *); int qeth_get_elements_for_frags(struct sk_buff *);
int qeth_do_send_packet_fast(struct qeth_card *, struct qeth_qdio_out_q *, int qeth_do_send_packet_fast(struct qeth_card *, struct qeth_qdio_out_q *,
struct sk_buff *, struct qeth_hdr *, int, int, int); struct sk_buff *, struct qeth_hdr *, int, int, int);
......
...@@ -3837,6 +3837,7 @@ EXPORT_SYMBOL_GPL(qeth_get_elements_for_frags); ...@@ -3837,6 +3837,7 @@ EXPORT_SYMBOL_GPL(qeth_get_elements_for_frags);
* @card: qeth card structure, to check max. elems. * @card: qeth card structure, to check max. elems.
* @skb: SKB address * @skb: SKB address
* @extra_elems: extra elems needed, to check against max. * @extra_elems: extra elems needed, to check against max.
* @data_offset: range starts at skb->data + data_offset
* *
* Returns the number of pages, and thus QDIO buffer elements, needed to cover * Returns the number of pages, and thus QDIO buffer elements, needed to cover
* skb data, including linear part and fragments. Checks if the result plus * skb data, including linear part and fragments. Checks if the result plus
...@@ -3844,10 +3845,10 @@ EXPORT_SYMBOL_GPL(qeth_get_elements_for_frags); ...@@ -3844,10 +3845,10 @@ EXPORT_SYMBOL_GPL(qeth_get_elements_for_frags);
* Note: extra_elems is not included in the returned result. * Note: extra_elems is not included in the returned result.
*/ */
int qeth_get_elements_no(struct qeth_card *card, int qeth_get_elements_no(struct qeth_card *card,
struct sk_buff *skb, int extra_elems) struct sk_buff *skb, int extra_elems, int data_offset)
{ {
int elements = qeth_get_elements_for_range( int elements = qeth_get_elements_for_range(
(addr_t)skb->data, (addr_t)skb->data + data_offset,
(addr_t)skb->data + skb_headlen(skb)) + (addr_t)skb->data + skb_headlen(skb)) +
qeth_get_elements_for_frags(skb); qeth_get_elements_for_frags(skb);
......
...@@ -849,7 +849,7 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -849,7 +849,7 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
* chaining we can not send long frag lists * chaining we can not send long frag lists
*/ */
if ((card->info.type != QETH_CARD_TYPE_IQD) && if ((card->info.type != QETH_CARD_TYPE_IQD) &&
!qeth_get_elements_no(card, new_skb, 0)) { !qeth_get_elements_no(card, new_skb, 0, 0)) {
int lin_rc = skb_linearize(new_skb); int lin_rc = skb_linearize(new_skb);
if (card->options.performance_stats) { if (card->options.performance_stats) {
...@@ -894,7 +894,8 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -894,7 +894,8 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
} }
} }
elements = qeth_get_elements_no(card, new_skb, elements_needed); elements = qeth_get_elements_no(card, new_skb, elements_needed,
(data_offset > 0) ? data_offset : 0);
if (!elements) { if (!elements) {
if (data_offset >= 0) if (data_offset >= 0)
kmem_cache_free(qeth_core_header_cache, hdr); kmem_cache_free(qeth_core_header_cache, hdr);
......
...@@ -2867,7 +2867,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -2867,7 +2867,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
*/ */
if ((card->info.type != QETH_CARD_TYPE_IQD) && if ((card->info.type != QETH_CARD_TYPE_IQD) &&
((use_tso && !qeth_l3_get_elements_no_tso(card, new_skb, 1)) || ((use_tso && !qeth_l3_get_elements_no_tso(card, new_skb, 1)) ||
(!use_tso && !qeth_get_elements_no(card, new_skb, 0)))) { (!use_tso && !qeth_get_elements_no(card, new_skb, 0, 0)))) {
int lin_rc = skb_linearize(new_skb); int lin_rc = skb_linearize(new_skb);
if (card->options.performance_stats) { if (card->options.performance_stats) {
...@@ -2909,7 +2909,8 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -2909,7 +2909,8 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
elements = use_tso ? elements = use_tso ?
qeth_l3_get_elements_no_tso(card, new_skb, hdr_elements) : qeth_l3_get_elements_no_tso(card, new_skb, hdr_elements) :
qeth_get_elements_no(card, new_skb, hdr_elements); qeth_get_elements_no(card, new_skb, hdr_elements,
(data_offset > 0) ? data_offset : 0);
if (!elements) { if (!elements) {
if (data_offset >= 0) if (data_offset >= 0)
kmem_cache_free(qeth_core_header_cache, hdr); kmem_cache_free(qeth_core_header_cache, hdr);
......
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