Commit 2863c613 authored by Eugene Crosser's avatar Eugene Crosser Committed by David S. Miller

qeth: refactor calculation of SBALE count

Rewrite the functions that calculate the required number of buffer
elements needed to represent SKB data, to make them hopefully more
comprehensible. Plus a few cleanups.
Signed-off-by: default avatarEugene Crosser <Eugene.Crosser@ru.ibm.com>
Signed-off-by: default avatarUrsula Braun <ubraun@linux.vnet.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 1b05cf62
...@@ -844,6 +844,19 @@ struct qeth_trap_id { ...@@ -844,6 +844,19 @@ struct qeth_trap_id {
/*some helper functions*/ /*some helper functions*/
#define QETH_CARD_IFNAME(card) (((card)->dev)? (card)->dev->name : "") #define QETH_CARD_IFNAME(card) (((card)->dev)? (card)->dev->name : "")
/**
* qeth_get_elements_for_range() - find number of SBALEs to cover range.
* @start: Start of the address range.
* @end: Address after the end of the range.
*
* Returns the number of pages, and thus QDIO buffer elements, needed to cover
* the specified address range.
*/
static inline int qeth_get_elements_for_range(addr_t start, addr_t end)
{
return PFN_UP(end - 1) - PFN_DOWN(start);
}
static inline int qeth_get_micros(void) static inline int qeth_get_micros(void)
{ {
return (int) (get_tod_clock() >> 12); return (int) (get_tod_clock() >> 12);
......
...@@ -3810,41 +3810,54 @@ int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb, ...@@ -3810,41 +3810,54 @@ int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb,
} }
EXPORT_SYMBOL_GPL(qeth_get_priority_queue); EXPORT_SYMBOL_GPL(qeth_get_priority_queue);
/**
* qeth_get_elements_for_frags() - find number of SBALEs for skb frags.
* @skb: SKB address
*
* Returns the number of pages, and thus QDIO buffer elements, needed to cover
* fragmented part of the SKB. Returns zero for linear SKB.
*/
int qeth_get_elements_for_frags(struct sk_buff *skb) int qeth_get_elements_for_frags(struct sk_buff *skb)
{ {
int cnt, length, e, elements = 0; int cnt, elements = 0;
struct skb_frag_struct *frag;
char *data;
for (cnt = 0; cnt < skb_shinfo(skb)->nr_frags; cnt++) { for (cnt = 0; cnt < skb_shinfo(skb)->nr_frags; cnt++) {
frag = &skb_shinfo(skb)->frags[cnt]; struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[cnt];
data = (char *)page_to_phys(skb_frag_page(frag)) +
frag->page_offset; elements += qeth_get_elements_for_range(
length = frag->size; (addr_t)skb_frag_address(frag),
e = PFN_UP((unsigned long)data + length - 1) - (addr_t)skb_frag_address(frag) + skb_frag_size(frag));
PFN_DOWN((unsigned long)data);
elements += e;
} }
return elements; return elements;
} }
EXPORT_SYMBOL_GPL(qeth_get_elements_for_frags); EXPORT_SYMBOL_GPL(qeth_get_elements_for_frags);
/**
* qeth_get_elements_no() - find number of SBALEs for skb data, inc. frags.
* @card: qeth card structure, to check max. elems.
* @skb: SKB address
* @extra_elems: extra elems needed, to check against max.
*
* 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
* extra_elems fits under the limit for the card. Returns 0 if it does not.
* 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 elems) struct sk_buff *skb, int extra_elems)
{ {
int dlen = skb->len - skb->data_len; int elements = qeth_get_elements_for_range(
int elements_needed = PFN_UP((unsigned long)skb->data + dlen - 1) - (addr_t)skb->data,
PFN_DOWN((unsigned long)skb->data); (addr_t)skb->data + skb_headlen(skb)) +
qeth_get_elements_for_frags(skb);
elements_needed += qeth_get_elements_for_frags(skb);
if ((elements_needed + elems) > QETH_MAX_BUFFER_ELEMENTS(card)) { if ((elements + extra_elems) > QETH_MAX_BUFFER_ELEMENTS(card)) {
QETH_DBF_MESSAGE(2, "Invalid size of IP packet " QETH_DBF_MESSAGE(2, "Invalid size of IP packet "
"(Number=%d / Length=%d). Discarded.\n", "(Number=%d / Length=%d). Discarded.\n",
(elements_needed+elems), skb->len); elements + extra_elems, skb->len);
return 0; return 0;
} }
return elements_needed; return elements;
} }
EXPORT_SYMBOL_GPL(qeth_get_elements_no); EXPORT_SYMBOL_GPL(qeth_get_elements_no);
...@@ -3859,7 +3872,7 @@ int qeth_hdr_chk_and_bounce(struct sk_buff *skb, struct qeth_hdr **hdr, int len) ...@@ -3859,7 +3872,7 @@ int qeth_hdr_chk_and_bounce(struct sk_buff *skb, struct qeth_hdr **hdr, int len)
rest = len - inpage; rest = len - inpage;
if (rest > hroom) if (rest > hroom)
return 1; return 1;
memmove(skb->data - rest, skb->data, skb->len - skb->data_len); memmove(skb->data - rest, skb->data, skb_headlen(skb));
skb->data -= rest; skb->data -= rest;
skb->tail -= rest; skb->tail -= rest;
*hdr = (struct qeth_hdr *)skb->data; *hdr = (struct qeth_hdr *)skb->data;
...@@ -3873,7 +3886,7 @@ static inline void __qeth_fill_buffer(struct sk_buff *skb, ...@@ -3873,7 +3886,7 @@ static inline void __qeth_fill_buffer(struct sk_buff *skb,
struct qdio_buffer *buffer, int is_tso, int *next_element_to_fill, struct qdio_buffer *buffer, int is_tso, int *next_element_to_fill,
int offset) int offset)
{ {
int length = skb->len - skb->data_len; int length = skb_headlen(skb);
int length_here; int length_here;
int element; int element;
char *data; char *data;
......
...@@ -2793,15 +2793,34 @@ static void qeth_tso_fill_header(struct qeth_card *card, ...@@ -2793,15 +2793,34 @@ static void qeth_tso_fill_header(struct qeth_card *card,
} }
} }
static inline int qeth_l3_tso_elements(struct sk_buff *skb) /**
* qeth_get_elements_no_tso() - find number of SBALEs for skb data, inc. frags.
* @card: qeth card structure, to check max. elems.
* @skb: SKB address
* @extra_elems: extra elems needed, to check against max.
*
* Returns the number of pages, and thus QDIO buffer elements, needed to cover
* skb data, including linear part and fragments, but excluding TCP header.
* (Exclusion of TCP header distinguishes it from qeth_get_elements_no().)
* Checks if the result plus extra_elems fits under the limit for the card.
* Returns 0 if it does not.
* Note: extra_elems is not included in the returned result.
*/
static int qeth_get_elements_no_tso(struct qeth_card *card,
struct sk_buff *skb, int extra_elems)
{ {
unsigned long tcpd = (unsigned long)tcp_hdr(skb) + addr_t tcpdptr = (addr_t)tcp_hdr(skb) + tcp_hdrlen(skb);
tcp_hdr(skb)->doff * 4; int elements = qeth_get_elements_for_range(
int tcpd_len = skb_headlen(skb) - (tcpd - (unsigned long)skb->data); tcpdptr,
int elements = PFN_UP(tcpd + tcpd_len - 1) - PFN_DOWN(tcpd); (addr_t)skb->data + skb_headlen(skb)) +
qeth_get_elements_for_frags(skb);
elements += qeth_get_elements_for_frags(skb);
if ((elements + extra_elems) > QETH_MAX_BUFFER_ELEMENTS(card)) {
QETH_DBF_MESSAGE(2,
"Invalid size of TSO IP packet (Number=%d / Length=%d). Discarded.\n",
elements + extra_elems, skb->len);
return 0;
}
return elements; return elements;
} }
...@@ -2810,8 +2829,8 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -2810,8 +2829,8 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
int rc; int rc;
u16 *tag; u16 *tag;
struct qeth_hdr *hdr = NULL; struct qeth_hdr *hdr = NULL;
int elements_needed = 0; int hdr_elements = 0;
int elems; int elements;
struct qeth_card *card = dev->ml_priv; struct qeth_card *card = dev->ml_priv;
struct sk_buff *new_skb = NULL; struct sk_buff *new_skb = NULL;
int ipv = qeth_get_ip_version(skb); int ipv = qeth_get_ip_version(skb);
...@@ -2859,7 +2878,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -2859,7 +2878,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
hdr = kmem_cache_alloc(qeth_core_header_cache, GFP_ATOMIC); hdr = kmem_cache_alloc(qeth_core_header_cache, GFP_ATOMIC);
if (!hdr) if (!hdr)
goto tx_drop; goto tx_drop;
elements_needed++; hdr_elements++;
} else { } else {
/* create a clone with writeable headroom */ /* create a clone with writeable headroom */
new_skb = skb_realloc_headroom(skb, sizeof(struct qeth_hdr_tso) new_skb = skb_realloc_headroom(skb, sizeof(struct qeth_hdr_tso)
...@@ -2895,7 +2914,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -2895,7 +2914,7 @@ static int qeth_l3_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 (large_send) { if (large_send) {
if (qeth_l3_tso_elements(new_skb) + 1 > 16) { if (!qeth_get_elements_no_tso(card, new_skb, 1)) {
if (skb_linearize(new_skb)) if (skb_linearize(new_skb))
goto tx_drop; goto tx_drop;
if (card->options.performance_stats) if (card->options.performance_stats)
...@@ -2909,7 +2928,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -2909,7 +2928,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
memset(hdr, 0, sizeof(struct qeth_hdr_tso)); memset(hdr, 0, sizeof(struct qeth_hdr_tso));
qeth_l3_fill_header(card, hdr, new_skb, ipv, cast_type); qeth_l3_fill_header(card, hdr, new_skb, ipv, cast_type);
qeth_tso_fill_header(card, hdr, new_skb); qeth_tso_fill_header(card, hdr, new_skb);
elements_needed++; hdr_elements++;
} else { } else {
if (data_offset < 0) { if (data_offset < 0) {
hdr = (struct qeth_hdr *)skb_push(new_skb, hdr = (struct qeth_hdr *)skb_push(new_skb,
...@@ -2930,31 +2949,29 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -2930,31 +2949,29 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
qeth_l3_hdr_csum(card, hdr, new_skb); qeth_l3_hdr_csum(card, hdr, new_skb);
} }
elems = qeth_get_elements_no(card, new_skb, elements_needed); elements = qeth_get_elements_no(card, new_skb, hdr_elements);
if (!elems) { 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);
goto tx_drop; goto tx_drop;
} }
elements_needed += elems; elements += hdr_elements;
nr_frags = skb_shinfo(new_skb)->nr_frags;
if (card->info.type != QETH_CARD_TYPE_IQD) { if (card->info.type != QETH_CARD_TYPE_IQD) {
int len; int len;
if (large_send) if (large_send)
len = ((unsigned long)tcp_hdr(new_skb) + len = ((unsigned long)tcp_hdr(new_skb) +
tcp_hdr(new_skb)->doff * 4) - tcp_hdrlen(new_skb)) -
(unsigned long)new_skb->data; (unsigned long)new_skb->data;
else else
len = sizeof(struct qeth_hdr_layer3); len = sizeof(struct qeth_hdr_layer3);
if (qeth_hdr_chk_and_bounce(new_skb, &hdr, len)) if (qeth_hdr_chk_and_bounce(new_skb, &hdr, len))
goto tx_drop; goto tx_drop;
rc = qeth_do_send_packet(card, queue, new_skb, hdr, rc = qeth_do_send_packet(card, queue, new_skb, hdr, elements);
elements_needed);
} else } else
rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr, rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr,
elements_needed, data_offset, 0); elements, data_offset, 0);
if (!rc) { if (!rc) {
card->stats.tx_packets++; card->stats.tx_packets++;
...@@ -2962,6 +2979,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -2962,6 +2979,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (new_skb != skb) if (new_skb != skb)
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
if (card->options.performance_stats) { if (card->options.performance_stats) {
nr_frags = skb_shinfo(new_skb)->nr_frags;
if (large_send) { if (large_send) {
card->perf_stats.large_send_bytes += tx_bytes; card->perf_stats.large_send_bytes += tx_bytes;
card->perf_stats.large_send_cnt++; card->perf_stats.large_send_cnt++;
......
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