summaryrefslogtreecommitdiffstats
path: root/drivers/s390/net/qeth_core_main.c
diff options
context:
space:
mode:
authorJulian Wiedmann2017-08-18 10:19:10 +0200
committerDavid S. Miller2017-08-18 19:21:30 +0200
commit0d6f02d37531ff6fad15c211162f3974fadc9ede (patch)
tree3d24d85688c7492f4c6f383a388c8df5db1838db /drivers/s390/net/qeth_core_main.c
parents390/qeth: unify code to build header elements (diff)
downloadkernel-qcow2-linux-0d6f02d37531ff6fad15c211162f3974fadc9ede.tar.gz
kernel-qcow2-linux-0d6f02d37531ff6fad15c211162f3974fadc9ede.tar.xz
kernel-qcow2-linux-0d6f02d37531ff6fad15c211162f3974fadc9ede.zip
s390/qeth: use skb_cow_head() for L2 OSA xmit
Taking a full copy via skb_realloc_headroom() on every xmit is overkill and wastes CPU time; all we actually need is to push on the qeth_hdr. So rework the L2 OSA TX path to avoid the copy. Minor complications arise because struct qeth_hdr must not cross a page boundary. So add a new helper qeth_push_hdr() that catches this, and falls back to the hdr cache that we already use for IQDs. This change uncovered that qeth's TX completion takes rather long. Now that we no longer free the original skb straight away and thus call skb->destructor later than before, throughput regresses significantly. For now, restore old behaviour by adding an explicit skb_orphan(), and a big TODO to improve the TX completion time. Tested-by: Nils Hoppmann <niho@de.ibm.com> Signed-off-by: Julian Wiedmann <jwi@linux.vnet.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/s390/net/qeth_core_main.c')
-rw-r--r--drivers/s390/net/qeth_core_main.c28
1 files changed, 28 insertions, 0 deletions
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index ffefdd97abca..bae7440abc01 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -3890,6 +3890,34 @@ int qeth_hdr_chk_and_bounce(struct sk_buff *skb, struct qeth_hdr **hdr, int len)
}
EXPORT_SYMBOL_GPL(qeth_hdr_chk_and_bounce);
+/**
+ * qeth_push_hdr() - push a qeth_hdr onto an skb.
+ * @skb: skb that the qeth_hdr should be pushed onto.
+ * @hdr: double pointer to a qeth_hdr. When returning with >= 0,
+ * it contains a valid pointer to a qeth_hdr.
+ * @len: length of the hdr that needs to be pushed on.
+ *
+ * Returns the pushed length. If the header can't be pushed on
+ * (eg. because it would cross a page boundary), it is allocated from
+ * the cache instead and 0 is returned.
+ * Error to create the hdr is indicated by returning with < 0.
+ */
+int qeth_push_hdr(struct sk_buff *skb, struct qeth_hdr **hdr, unsigned int len)
+{
+ if (skb_headroom(skb) >= len &&
+ qeth_get_elements_for_range((addr_t)skb->data - len,
+ (addr_t)skb->data) == 1) {
+ *hdr = skb_push(skb, len);
+ return len;
+ }
+ /* fall back */
+ *hdr = kmem_cache_alloc(qeth_core_header_cache, GFP_ATOMIC);
+ if (!*hdr)
+ return -ENOMEM;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qeth_push_hdr);
+
static void __qeth_fill_buffer(struct sk_buff *skb,
struct qeth_qdio_out_buffer *buf,
bool is_first_elem, unsigned int offset)