* [PATCH net-next 1/7] s390/qeth: split L2 xmit paths
2017-08-18 8:19 [PATCH net-next 0/7] s390/net: more updates for 4.14 Julian Wiedmann
@ 2017-08-18 8:19 ` Julian Wiedmann
2017-08-18 8:19 ` [PATCH net-next 2/7] s390/qeth: pass full data length to l2_fill_header() Julian Wiedmann
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Julian Wiedmann @ 2017-08-18 8:19 UTC (permalink / raw)
To: David Miller
Cc: netdev, linux-s390, Martin Schwidefsky, Heiko Carstens,
Stefan Raspl, Ursula Braun, Julian Wiedmann
l2_hard_start_xmit() actually doesn't contain much shared code,
and having device-specific paths makes isolated changes a lot easier.
So split it into three routines for IQD, OSN and OSD/OSM/OSX.
No functional change.
Signed-off-by: Julian Wiedmann <jwi@linux.vnet.ibm.com>
---
drivers/s390/net/qeth_l2_main.c | 225 ++++++++++++++++++++++------------------
1 file changed, 123 insertions(+), 102 deletions(-)
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index 438a7f29e99f..310bfa225e20 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -676,143 +676,164 @@ static void qeth_l2_set_rx_mode(struct net_device *dev)
qeth_promisc_to_bridge(card);
}
-static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
+static int qeth_l2_xmit_iqd(struct qeth_card *card, struct sk_buff *skb,
+ struct qeth_qdio_out_q *queue, int cast_type)
{
+ unsigned int data_offset = ETH_HLEN;
+ struct qeth_hdr *hdr;
int rc;
- struct qeth_hdr *hdr = NULL;
- int elements = 0;
- struct qeth_card *card = dev->ml_priv;
- struct sk_buff *new_skb = skb;
- int cast_type = qeth_l2_get_cast_type(card, skb);
- struct qeth_qdio_out_q *queue;
- int tx_bytes = skb->len;
- int data_offset = -1;
- int elements_needed = 0;
- int hd_len = 0;
- unsigned int nr_frags;
- if (card->qdio.do_prio_queueing || (cast_type &&
- card->info.is_multicast_different))
- queue = card->qdio.out_qs[qeth_get_priority_queue(card, skb,
- qeth_get_ip_version(skb), cast_type)];
- else
- queue = card->qdio.out_qs[card->qdio.default_out_queue];
+ hdr = kmem_cache_alloc(qeth_core_header_cache, GFP_ATOMIC);
+ if (!hdr)
+ return -ENOMEM;
+ qeth_l2_fill_header(card, hdr, skb, cast_type);
+ hdr->hdr.l2.pkt_length = skb->len;
+ skb_copy_from_linear_data(skb, ((char *)hdr) + sizeof(*hdr),
+ data_offset);
- if ((card->state != CARD_STATE_UP) || !card->lan_online) {
- card->stats.tx_carrier_errors++;
- goto tx_drop;
+ if (!qeth_get_elements_no(card, skb, 1, data_offset)) {
+ rc = -E2BIG;
+ goto out;
}
+ rc = qeth_do_send_packet_fast(card, queue, skb, hdr, data_offset,
+ data_offset);
+out:
+ if (rc)
+ kmem_cache_free(qeth_core_header_cache, hdr);
+ return rc;
+}
- if ((card->info.type == QETH_CARD_TYPE_OSN) &&
- (skb->protocol == htons(ETH_P_IPV6)))
- goto tx_drop;
-
- if (card->options.performance_stats) {
- card->perf_stats.outbound_cnt++;
- card->perf_stats.outbound_start_time = qeth_get_micros();
- }
- netif_stop_queue(dev);
+static int qeth_l2_xmit_osa(struct qeth_card *card, struct sk_buff *skb,
+ struct qeth_qdio_out_q *queue, int cast_type)
+{
+ unsigned int elements, nr_frags;
+ struct sk_buff *skb_copy;
+ struct qeth_hdr *hdr;
+ int rc;
/* fix hardware limitation: as long as we do not have sbal
* chaining we can not send long frag lists
*/
- if ((card->info.type != QETH_CARD_TYPE_IQD) &&
- !qeth_get_elements_no(card, new_skb, 0, 0)) {
- int lin_rc = skb_linearize(new_skb);
+ if (!qeth_get_elements_no(card, skb, 0, 0)) {
+ rc = skb_linearize(skb);
if (card->options.performance_stats) {
- if (lin_rc)
+ if (rc)
card->perf_stats.tx_linfail++;
else
card->perf_stats.tx_lin++;
}
- if (lin_rc)
- goto tx_drop;
+ if (rc)
+ return rc;
}
- nr_frags = skb_shinfo(new_skb)->nr_frags;
+ nr_frags = skb_shinfo(skb)->nr_frags;
- if (card->info.type == QETH_CARD_TYPE_OSN)
- hdr = (struct qeth_hdr *)skb->data;
- else {
- if (card->info.type == QETH_CARD_TYPE_IQD) {
- new_skb = skb;
- data_offset = ETH_HLEN;
- hd_len = ETH_HLEN;
- hdr = kmem_cache_alloc(qeth_core_header_cache,
- GFP_ATOMIC);
- if (!hdr)
- goto tx_drop;
- elements_needed++;
- qeth_l2_fill_header(card, hdr, new_skb, cast_type);
- hdr->hdr.l2.pkt_length = new_skb->len;
- skb_copy_from_linear_data(new_skb,
- ((char *)hdr) + sizeof(*hdr),
- ETH_HLEN);
- } else {
- /* create a clone with writeable headroom */
- new_skb = skb_realloc_headroom(skb,
- sizeof(struct qeth_hdr));
- if (!new_skb)
- goto tx_drop;
- hdr = skb_push(new_skb, sizeof(struct qeth_hdr));
- qeth_l2_fill_header(card, hdr, new_skb, cast_type);
- if (new_skb->ip_summed == CHECKSUM_PARTIAL)
- qeth_l2_hdr_csum(card, hdr, new_skb);
- }
- }
+ /* create a copy with writeable headroom */
+ skb_copy = skb_realloc_headroom(skb, sizeof(struct qeth_hdr));
+ if (!skb_copy)
+ return -ENOMEM;
+ hdr = skb_push(skb_copy, sizeof(struct qeth_hdr));
+ qeth_l2_fill_header(card, hdr, skb_copy, cast_type);
+ if (skb_copy->ip_summed == CHECKSUM_PARTIAL)
+ qeth_l2_hdr_csum(card, hdr, skb_copy);
- elements = qeth_get_elements_no(card, new_skb, elements_needed,
- (data_offset > 0) ? data_offset : 0);
+ elements = qeth_get_elements_no(card, skb_copy, 0, 0);
if (!elements) {
- if (data_offset >= 0)
- kmem_cache_free(qeth_core_header_cache, hdr);
- goto tx_drop;
+ rc = -E2BIG;
+ goto out;
}
-
- if (card->info.type != QETH_CARD_TYPE_IQD) {
- if (qeth_hdr_chk_and_bounce(new_skb, &hdr,
- sizeof(struct qeth_hdr_layer2)))
- goto tx_drop;
- rc = qeth_do_send_packet(card, queue, new_skb, hdr,
- elements);
- } else
- rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr,
- data_offset, hd_len);
+ if (qeth_hdr_chk_and_bounce(skb_copy, &hdr, sizeof(*hdr))) {
+ rc = -EINVAL;
+ goto out;
+ }
+ rc = qeth_do_send_packet(card, queue, skb_copy, hdr, elements);
+out:
if (!rc) {
- card->stats.tx_packets++;
- card->stats.tx_bytes += tx_bytes;
+ /* tx success, free dangling original */
+ dev_kfree_skb_any(skb);
if (card->options.performance_stats && nr_frags) {
card->perf_stats.sg_skbs_sent++;
/* nr_frags + skb->data */
card->perf_stats.sg_frags_sent += nr_frags + 1;
}
- if (new_skb != skb)
- dev_kfree_skb_any(skb);
- rc = NETDEV_TX_OK;
} else {
- if (data_offset >= 0)
- kmem_cache_free(qeth_core_header_cache, hdr);
+ /* tx fail, free copy */
+ dev_kfree_skb_any(skb_copy);
+ }
+ return rc;
+}
- if (rc == -EBUSY) {
- if (new_skb != skb)
- dev_kfree_skb_any(new_skb);
- return NETDEV_TX_BUSY;
- } else
- goto tx_drop;
+static int qeth_l2_xmit_osn(struct qeth_card *card, struct sk_buff *skb,
+ struct qeth_qdio_out_q *queue)
+{
+ unsigned int elements;
+ struct qeth_hdr *hdr;
+
+ if (skb->protocol == htons(ETH_P_IPV6))
+ return -EPROTONOSUPPORT;
+
+ hdr = (struct qeth_hdr *)skb->data;
+ elements = qeth_get_elements_no(card, skb, 0, 0);
+ if (!elements)
+ return -E2BIG;
+ if (qeth_hdr_chk_and_bounce(skb, &hdr, sizeof(*hdr)))
+ return -EINVAL;
+ return qeth_do_send_packet(card, queue, skb, hdr, elements);
+}
+
+static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct qeth_card *card = dev->ml_priv;
+ int cast_type = qeth_l2_get_cast_type(card, skb);
+ struct qeth_qdio_out_q *queue;
+ int tx_bytes = skb->len;
+ int rc;
+
+ if (card->qdio.do_prio_queueing || (cast_type &&
+ card->info.is_multicast_different))
+ queue = card->qdio.out_qs[qeth_get_priority_queue(card, skb,
+ qeth_get_ip_version(skb), cast_type)];
+ else
+ queue = card->qdio.out_qs[card->qdio.default_out_queue];
+
+ if ((card->state != CARD_STATE_UP) || !card->lan_online) {
+ card->stats.tx_carrier_errors++;
+ goto tx_drop;
}
- netif_wake_queue(dev);
- if (card->options.performance_stats)
- card->perf_stats.outbound_time += qeth_get_micros() -
- card->perf_stats.outbound_start_time;
- return rc;
+ if (card->options.performance_stats) {
+ card->perf_stats.outbound_cnt++;
+ card->perf_stats.outbound_start_time = qeth_get_micros();
+ }
+ netif_stop_queue(dev);
+
+ switch (card->info.type) {
+ case QETH_CARD_TYPE_OSN:
+ rc = qeth_l2_xmit_osn(card, skb, queue);
+ break;
+ case QETH_CARD_TYPE_IQD:
+ rc = qeth_l2_xmit_iqd(card, skb, queue, cast_type);
+ break;
+ default:
+ rc = qeth_l2_xmit_osa(card, skb, queue, cast_type);
+ }
+
+ if (!rc) {
+ card->stats.tx_packets++;
+ card->stats.tx_bytes += tx_bytes;
+ if (card->options.performance_stats)
+ card->perf_stats.outbound_time += qeth_get_micros() -
+ card->perf_stats.outbound_start_time;
+ netif_wake_queue(dev);
+ return NETDEV_TX_OK;
+ } else if (rc == -EBUSY) {
+ return NETDEV_TX_BUSY;
+ } /* else fall through */
tx_drop:
card->stats.tx_dropped++;
card->stats.tx_errors++;
- if ((new_skb != skb) && new_skb)
- dev_kfree_skb_any(new_skb);
dev_kfree_skb_any(skb);
netif_wake_queue(dev);
return NETDEV_TX_OK;
--
2.11.2
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH net-next 2/7] s390/qeth: pass full data length to l2_fill_header()
2017-08-18 8:19 [PATCH net-next 0/7] s390/net: more updates for 4.14 Julian Wiedmann
2017-08-18 8:19 ` [PATCH net-next 1/7] s390/qeth: split L2 xmit paths Julian Wiedmann
@ 2017-08-18 8:19 ` Julian Wiedmann
2017-08-18 8:19 ` [PATCH net-next 3/7] s390/qeth: pass TSO header length to fill_buffer() Julian Wiedmann
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Julian Wiedmann @ 2017-08-18 8:19 UTC (permalink / raw)
To: David Miller
Cc: netdev, linux-s390, Martin Schwidefsky, Heiko Carstens,
Stefan Raspl, Ursula Braun, Julian Wiedmann
For IQD we already need to fix up the qeth_hdr's length field, and
future changes will require more flexibility for OSA as well. The
device-specific path knows best what header length it requires, so just
pass it from there.
While at it, remove the unused qeth_card parameter.
No functional change.
Signed-off-by: Julian Wiedmann <jwi@linux.vnet.ibm.com>
---
drivers/s390/net/qeth_l2_main.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index 310bfa225e20..3f5b852408d3 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -259,13 +259,14 @@ static void qeth_l2_hdr_csum(struct qeth_card *card, struct qeth_hdr *hdr,
card->perf_stats.tx_csum++;
}
-static void qeth_l2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
- struct sk_buff *skb, int cast_type)
+static void qeth_l2_fill_header(struct qeth_hdr *hdr, struct sk_buff *skb,
+ int cast_type, unsigned int data_len)
{
struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb_mac_header(skb);
memset(hdr, 0, sizeof(struct qeth_hdr));
hdr->hdr.l2.id = QETH_HEADER_TYPE_LAYER2;
+ hdr->hdr.l2.pkt_length = data_len;
/* set byte byte 3 to casting flags */
if (cast_type == RTN_MULTICAST)
@@ -275,7 +276,6 @@ static void qeth_l2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
else
hdr->hdr.l2.flags[2] |= QETH_LAYER2_FLAG_UNICAST;
- hdr->hdr.l2.pkt_length = skb->len - sizeof(struct qeth_hdr);
/* VSWITCH relies on the VLAN
* information to be present in
* the QDIO header */
@@ -686,8 +686,7 @@ static int qeth_l2_xmit_iqd(struct qeth_card *card, struct sk_buff *skb,
hdr = kmem_cache_alloc(qeth_core_header_cache, GFP_ATOMIC);
if (!hdr)
return -ENOMEM;
- qeth_l2_fill_header(card, hdr, skb, cast_type);
- hdr->hdr.l2.pkt_length = skb->len;
+ qeth_l2_fill_header(hdr, skb, cast_type, skb->len);
skb_copy_from_linear_data(skb, ((char *)hdr) + sizeof(*hdr),
data_offset);
@@ -733,7 +732,8 @@ static int qeth_l2_xmit_osa(struct qeth_card *card, struct sk_buff *skb,
if (!skb_copy)
return -ENOMEM;
hdr = skb_push(skb_copy, sizeof(struct qeth_hdr));
- qeth_l2_fill_header(card, hdr, skb_copy, cast_type);
+ qeth_l2_fill_header(hdr, skb_copy, cast_type,
+ skb_copy->len - sizeof(*hdr));
if (skb_copy->ip_summed == CHECKSUM_PARTIAL)
qeth_l2_hdr_csum(card, hdr, skb_copy);
--
2.11.2
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH net-next 3/7] s390/qeth: pass TSO header length to fill_buffer()
2017-08-18 8:19 [PATCH net-next 0/7] s390/net: more updates for 4.14 Julian Wiedmann
2017-08-18 8:19 ` [PATCH net-next 1/7] s390/qeth: split L2 xmit paths Julian Wiedmann
2017-08-18 8:19 ` [PATCH net-next 2/7] s390/qeth: pass full data length to l2_fill_header() Julian Wiedmann
@ 2017-08-18 8:19 ` Julian Wiedmann
2017-08-18 8:19 ` [PATCH net-next 4/7] s390/qeth: pass TSO data offset " Julian Wiedmann
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Julian Wiedmann @ 2017-08-18 8:19 UTC (permalink / raw)
To: David Miller
Cc: netdev, linux-s390, Martin Schwidefsky, Heiko Carstens,
Stefan Raspl, Ursula Braun, Julian Wiedmann
The TSO code already calculates the length of its header element,
no need to duplicate this in the low-level code again.
Use this opportunity to make hd_len unsigned, and for TSO match
its calculation to what tso_fill_header() does.
No functional change.
Signed-off-by: Julian Wiedmann <jwi@linux.vnet.ibm.com>
---
drivers/s390/net/qeth_core.h | 7 ++++---
drivers/s390/net/qeth_core_main.c | 18 ++++++++----------
drivers/s390/net/qeth_l2_main.c | 4 ++--
drivers/s390/net/qeth_l3_main.c | 15 +++++++++------
4 files changed, 23 insertions(+), 21 deletions(-)
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
index 4a4ca5cb37a0..2f5673812810 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -949,9 +949,10 @@ int qeth_get_elements_for_frags(struct sk_buff *);
int qeth_do_send_packet_fast(struct qeth_card *card,
struct qeth_qdio_out_q *queue, struct sk_buff *skb,
struct qeth_hdr *hdr, unsigned int offset,
- int hd_len);
-int qeth_do_send_packet(struct qeth_card *, struct qeth_qdio_out_q *,
- struct sk_buff *, struct qeth_hdr *, int);
+ unsigned int hd_len);
+int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
+ struct sk_buff *skb, struct qeth_hdr *hdr,
+ unsigned int hd_len, int elements);
int qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
int qeth_core_get_sset_count(struct net_device *, int);
void qeth_core_get_ethtool_stats(struct net_device *,
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index 415424e618ad..6cafeceea3ce 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -3956,11 +3956,11 @@ static void __qeth_fill_buffer(struct sk_buff *skb,
static int qeth_fill_buffer(struct qeth_qdio_out_q *queue,
struct qeth_qdio_out_buffer *buf,
struct sk_buff *skb, struct qeth_hdr *hdr,
- unsigned int offset, int hd_len)
+ unsigned int offset, unsigned int hd_len)
{
struct qdio_buffer *buffer;
- int flush_cnt = 0, hdr_len;
bool is_first_elem = true;
+ int flush_cnt = 0;
buffer = buf->buffer;
refcount_inc(&skb->users);
@@ -3970,14 +3970,12 @@ static int qeth_fill_buffer(struct qeth_qdio_out_q *queue,
int element = buf->next_element_to_fill;
is_first_elem = false;
- hdr_len = sizeof(struct qeth_hdr_tso) +
- ((struct qeth_hdr_tso *)hdr)->ext.dg_hdr_len;
/*fill first buffer entry only with header information */
buffer->element[element].addr = skb->data;
- buffer->element[element].length = hdr_len;
+ buffer->element[element].length = hd_len;
buffer->element[element].eflags = SBAL_EFLAGS_FIRST_FRAG;
buf->next_element_to_fill++;
- skb_pull(skb, hdr_len);
+ skb_pull(skb, hd_len);
}
/* IQD */
@@ -4020,7 +4018,7 @@ static int qeth_fill_buffer(struct qeth_qdio_out_q *queue,
int qeth_do_send_packet_fast(struct qeth_card *card,
struct qeth_qdio_out_q *queue, struct sk_buff *skb,
struct qeth_hdr *hdr, unsigned int offset,
- int hd_len)
+ unsigned int hd_len)
{
struct qeth_qdio_out_buffer *buffer;
int index;
@@ -4050,8 +4048,8 @@ int qeth_do_send_packet_fast(struct qeth_card *card,
EXPORT_SYMBOL_GPL(qeth_do_send_packet_fast);
int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
- struct sk_buff *skb, struct qeth_hdr *hdr,
- int elements_needed)
+ struct sk_buff *skb, struct qeth_hdr *hdr,
+ unsigned int hd_len, int elements_needed)
{
struct qeth_qdio_out_buffer *buffer;
int start_index;
@@ -4100,7 +4098,7 @@ int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
}
}
}
- tmp = qeth_fill_buffer(queue, buffer, skb, hdr, 0, 0);
+ tmp = qeth_fill_buffer(queue, buffer, skb, hdr, 0, hd_len);
queue->next_buf_to_fill = (queue->next_buf_to_fill + tmp) %
QDIO_MAX_BUFFERS_PER_Q;
flush_count += tmp;
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index 3f5b852408d3..c78d9fadb9c8 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -746,7 +746,7 @@ static int qeth_l2_xmit_osa(struct qeth_card *card, struct sk_buff *skb,
rc = -EINVAL;
goto out;
}
- rc = qeth_do_send_packet(card, queue, skb_copy, hdr, elements);
+ rc = qeth_do_send_packet(card, queue, skb_copy, hdr, 0, elements);
out:
if (!rc) {
/* tx success, free dangling original */
@@ -778,7 +778,7 @@ static int qeth_l2_xmit_osn(struct qeth_card *card, struct sk_buff *skb,
return -E2BIG;
if (qeth_hdr_chk_and_bounce(skb, &hdr, sizeof(*hdr)))
return -EINVAL;
- return qeth_do_send_packet(card, queue, skb, hdr, elements);
+ return qeth_do_send_packet(card, queue, skb, hdr, 0, elements);
}
static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb,
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index 0a3dc14a1381..fa8b638e3842 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -2637,6 +2637,7 @@ static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb,
qeth_get_priority_queue(card, skb, ipv, cast_type) :
card->qdio.default_out_queue];
int tx_bytes = skb->len;
+ unsigned int hd_len = 0;
bool use_tso;
int data_offset = -1;
unsigned int nr_frags;
@@ -2756,16 +2757,18 @@ static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb,
if (card->info.type != QETH_CARD_TYPE_IQD) {
int len;
- if (use_tso)
- len = ((unsigned long)tcp_hdr(new_skb) +
- tcp_hdrlen(new_skb)) -
- (unsigned long)new_skb->data;
- else
+ if (use_tso) {
+ hd_len = sizeof(struct qeth_hdr_tso) +
+ ip_hdrlen(new_skb) + tcp_hdrlen(new_skb);
+ len = hd_len;
+ } else {
len = sizeof(struct qeth_hdr_layer3);
+ }
if (qeth_hdr_chk_and_bounce(new_skb, &hdr, len))
goto tx_drop;
- rc = qeth_do_send_packet(card, queue, new_skb, hdr, elements);
+ rc = qeth_do_send_packet(card, queue, new_skb, hdr, hd_len,
+ elements);
} else
rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr,
data_offset, 0);
--
2.11.2
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH net-next 4/7] s390/qeth: pass TSO data offset to fill_buffer()
2017-08-18 8:19 [PATCH net-next 0/7] s390/net: more updates for 4.14 Julian Wiedmann
` (2 preceding siblings ...)
2017-08-18 8:19 ` [PATCH net-next 3/7] s390/qeth: pass TSO header length to fill_buffer() Julian Wiedmann
@ 2017-08-18 8:19 ` Julian Wiedmann
2017-08-18 8:19 ` [PATCH net-next 5/7] s390/qeth: pass full IQD header length " Julian Wiedmann
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Julian Wiedmann @ 2017-08-18 8:19 UTC (permalink / raw)
To: David Miller
Cc: netdev, linux-s390, Martin Schwidefsky, Heiko Carstens,
Stefan Raspl, Ursula Braun, Julian Wiedmann
For TSO we need to skip the skb's qeth/IP/TCP headers when mapping
it into buffer elements. Instead of (mis)using skb_pull(), pass a
corresponding offset to fill_buffer() like we already do for IQDs.
No actual change in the resulting TSO buffers.
Signed-off-by: Julian Wiedmann <jwi@linux.vnet.ibm.com>
---
drivers/s390/net/qeth_core.h | 2 +-
drivers/s390/net/qeth_core_main.c | 10 ++++------
drivers/s390/net/qeth_l2_main.c | 4 ++--
drivers/s390/net/qeth_l3_main.c | 2 +-
4 files changed, 8 insertions(+), 10 deletions(-)
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
index 2f5673812810..5753fbc485d5 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -952,7 +952,7 @@ int qeth_do_send_packet_fast(struct qeth_card *card,
unsigned int hd_len);
int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
struct sk_buff *skb, struct qeth_hdr *hdr,
- unsigned int hd_len, int elements);
+ unsigned int hd_len, unsigned int offset, int elements);
int qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
int qeth_core_get_sset_count(struct net_device *, int);
void qeth_core_get_ethtool_stats(struct net_device *,
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index 6cafeceea3ce..4a5c3028dfb6 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -3975,11 +3975,8 @@ static int qeth_fill_buffer(struct qeth_qdio_out_q *queue,
buffer->element[element].length = hd_len;
buffer->element[element].eflags = SBAL_EFLAGS_FIRST_FRAG;
buf->next_element_to_fill++;
- skb_pull(skb, hd_len);
- }
-
/* IQD */
- if (offset > 0) {
+ } else if (offset) {
int element = buf->next_element_to_fill;
is_first_elem = false;
@@ -4049,7 +4046,8 @@ EXPORT_SYMBOL_GPL(qeth_do_send_packet_fast);
int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
struct sk_buff *skb, struct qeth_hdr *hdr,
- unsigned int hd_len, int elements_needed)
+ unsigned int offset, unsigned int hd_len,
+ int elements_needed)
{
struct qeth_qdio_out_buffer *buffer;
int start_index;
@@ -4098,7 +4096,7 @@ int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
}
}
}
- tmp = qeth_fill_buffer(queue, buffer, skb, hdr, 0, hd_len);
+ tmp = qeth_fill_buffer(queue, buffer, skb, hdr, offset, hd_len);
queue->next_buf_to_fill = (queue->next_buf_to_fill + tmp) %
QDIO_MAX_BUFFERS_PER_Q;
flush_count += tmp;
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index c78d9fadb9c8..a6233ab562f0 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -746,7 +746,7 @@ static int qeth_l2_xmit_osa(struct qeth_card *card, struct sk_buff *skb,
rc = -EINVAL;
goto out;
}
- rc = qeth_do_send_packet(card, queue, skb_copy, hdr, 0, elements);
+ rc = qeth_do_send_packet(card, queue, skb_copy, hdr, 0, 0, elements);
out:
if (!rc) {
/* tx success, free dangling original */
@@ -778,7 +778,7 @@ static int qeth_l2_xmit_osn(struct qeth_card *card, struct sk_buff *skb,
return -E2BIG;
if (qeth_hdr_chk_and_bounce(skb, &hdr, sizeof(*hdr)))
return -EINVAL;
- return qeth_do_send_packet(card, queue, skb, hdr, 0, elements);
+ return qeth_do_send_packet(card, queue, skb, hdr, 0, 0, elements);
}
static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb,
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index fa8b638e3842..02400bbcb610 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -2768,7 +2768,7 @@ static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb,
if (qeth_hdr_chk_and_bounce(new_skb, &hdr, len))
goto tx_drop;
rc = qeth_do_send_packet(card, queue, new_skb, hdr, hd_len,
- elements);
+ hd_len, elements);
} else
rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr,
data_offset, 0);
--
2.11.2
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH net-next 5/7] s390/qeth: pass full IQD header length to fill_buffer()
2017-08-18 8:19 [PATCH net-next 0/7] s390/net: more updates for 4.14 Julian Wiedmann
` (3 preceding siblings ...)
2017-08-18 8:19 ` [PATCH net-next 4/7] s390/qeth: pass TSO data offset " Julian Wiedmann
@ 2017-08-18 8:19 ` Julian Wiedmann
2017-08-18 8:19 ` [PATCH net-next 6/7] s390/qeth: unify code to build header elements Julian Wiedmann
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Julian Wiedmann @ 2017-08-18 8:19 UTC (permalink / raw)
To: David Miller
Cc: netdev, linux-s390, Martin Schwidefsky, Heiko Carstens,
Stefan Raspl, Ursula Braun, Julian Wiedmann
This is a prerequisite for unifying the code to build header elements.
The TSO header has a different size, so we can no longer rely on implicitly
adding the size of a normal qeth_hdr.
No functional change.
Signed-off-by: Julian Wiedmann <jwi@linux.vnet.ibm.com>
---
drivers/s390/net/qeth_core_main.c | 3 +--
drivers/s390/net/qeth_l2_main.c | 2 +-
drivers/s390/net/qeth_l3_main.c | 3 ++-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index 4a5c3028dfb6..cef9f54d0eb9 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -3981,8 +3981,7 @@ static int qeth_fill_buffer(struct qeth_qdio_out_q *queue,
is_first_elem = false;
buffer->element[element].addr = hdr;
- buffer->element[element].length = sizeof(struct qeth_hdr) +
- hd_len;
+ buffer->element[element].length = hd_len;
buffer->element[element].eflags = SBAL_EFLAGS_FIRST_FRAG;
buf->is_header[element] = 1;
buf->next_element_to_fill++;
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index a6233ab562f0..c85fadf21b38 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -695,7 +695,7 @@ static int qeth_l2_xmit_iqd(struct qeth_card *card, struct sk_buff *skb,
goto out;
}
rc = qeth_do_send_packet_fast(card, queue, skb, hdr, data_offset,
- data_offset);
+ sizeof(*hdr) + data_offset);
out:
if (rc)
kmem_cache_free(qeth_core_header_cache, hdr);
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index 02400bbcb610..ab661a431f7c 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -2670,6 +2670,7 @@ static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb,
if (card->info.type == QETH_CARD_TYPE_IQD) {
new_skb = skb;
data_offset = ETH_HLEN;
+ hd_len = sizeof(*hdr);
hdr = kmem_cache_alloc(qeth_core_header_cache, GFP_ATOMIC);
if (!hdr)
goto tx_drop;
@@ -2771,7 +2772,7 @@ static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb,
hd_len, elements);
} else
rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr,
- data_offset, 0);
+ data_offset, hd_len);
if (!rc) {
card->stats.tx_packets++;
--
2.11.2
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH net-next 6/7] s390/qeth: unify code to build header elements
2017-08-18 8:19 [PATCH net-next 0/7] s390/net: more updates for 4.14 Julian Wiedmann
` (4 preceding siblings ...)
2017-08-18 8:19 ` [PATCH net-next 5/7] s390/qeth: pass full IQD header length " Julian Wiedmann
@ 2017-08-18 8:19 ` Julian Wiedmann
2017-08-18 8:19 ` [PATCH net-next 7/7] s390/qeth: use skb_cow_head() for L2 OSA xmit Julian Wiedmann
2017-08-18 17:22 ` [PATCH net-next 0/7] s390/net: more updates for 4.14 David Miller
7 siblings, 0 replies; 9+ messages in thread
From: Julian Wiedmann @ 2017-08-18 8:19 UTC (permalink / raw)
To: David Miller
Cc: netdev, linux-s390, Martin Schwidefsky, Heiko Carstens,
Stefan Raspl, Ursula Braun, Julian Wiedmann
After plenty of refactoring, use hd_len as single indication that
the skb needs a dedicated header element.
This preserves existing behaviour for TSO, as 'hdr' always points
to skb->data.
Signed-off-by: Julian Wiedmann <jwi@linux.vnet.ibm.com>
---
drivers/s390/net/qeth_core_main.c | 29 +++++++++++++++--------------
1 file changed, 15 insertions(+), 14 deletions(-)
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index cef9f54d0eb9..ffefdd97abca 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -3953,37 +3953,38 @@ static void __qeth_fill_buffer(struct sk_buff *skb,
buf->next_element_to_fill = element;
}
+/**
+ * qeth_fill_buffer() - map skb into an output buffer
+ * @queue: QDIO queue to submit the buffer on
+ * @buf: buffer to transport the skb
+ * @skb: skb to map into the buffer
+ * @hdr: qeth_hdr for this skb. Either at skb->data, or allocated
+ * from qeth_core_header_cache.
+ * @offset: when mapping the skb, start at skb->data + offset
+ * @hd_len: if > 0, build a dedicated header element of this size
+ */
static int qeth_fill_buffer(struct qeth_qdio_out_q *queue,
struct qeth_qdio_out_buffer *buf,
struct sk_buff *skb, struct qeth_hdr *hdr,
unsigned int offset, unsigned int hd_len)
{
- struct qdio_buffer *buffer;
+ struct qdio_buffer *buffer = buf->buffer;
bool is_first_elem = true;
int flush_cnt = 0;
- buffer = buf->buffer;
refcount_inc(&skb->users);
skb_queue_tail(&buf->skb_list, skb);
- if (hdr->hdr.l3.id == QETH_HEADER_TYPE_TSO) {
- int element = buf->next_element_to_fill;
- is_first_elem = false;
-
- /*fill first buffer entry only with header information */
- buffer->element[element].addr = skb->data;
- buffer->element[element].length = hd_len;
- buffer->element[element].eflags = SBAL_EFLAGS_FIRST_FRAG;
- buf->next_element_to_fill++;
- /* IQD */
- } else if (offset) {
+ /* build dedicated header element */
+ if (hd_len) {
int element = buf->next_element_to_fill;
is_first_elem = false;
buffer->element[element].addr = hdr;
buffer->element[element].length = hd_len;
buffer->element[element].eflags = SBAL_EFLAGS_FIRST_FRAG;
- buf->is_header[element] = 1;
+ /* remember to free cache-allocated qeth_hdr: */
+ buf->is_header[element] = ((void *)hdr != skb->data);
buf->next_element_to_fill++;
}
--
2.11.2
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH net-next 7/7] s390/qeth: use skb_cow_head() for L2 OSA xmit
2017-08-18 8:19 [PATCH net-next 0/7] s390/net: more updates for 4.14 Julian Wiedmann
` (5 preceding siblings ...)
2017-08-18 8:19 ` [PATCH net-next 6/7] s390/qeth: unify code to build header elements Julian Wiedmann
@ 2017-08-18 8:19 ` Julian Wiedmann
2017-08-18 17:22 ` [PATCH net-next 0/7] s390/net: more updates for 4.14 David Miller
7 siblings, 0 replies; 9+ messages in thread
From: Julian Wiedmann @ 2017-08-18 8:19 UTC (permalink / raw)
To: David Miller
Cc: netdev, linux-s390, Martin Schwidefsky, Heiko Carstens,
Stefan Raspl, Ursula Braun, Julian Wiedmann
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>
---
drivers/s390/net/qeth_core.h | 1 +
drivers/s390/net/qeth_core_main.c | 28 +++++++++++++++++++
drivers/s390/net/qeth_l2_main.c | 58 ++++++++++++++++++++++++---------------
3 files changed, 65 insertions(+), 22 deletions(-)
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
index 5753fbc485d5..59e09854c4f7 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -985,6 +985,7 @@ int qeth_set_features(struct net_device *, netdev_features_t);
int qeth_recover_features(struct net_device *);
netdev_features_t qeth_fix_features(struct net_device *, netdev_features_t);
int qeth_vm_request_mac(struct qeth_card *card);
+int qeth_push_hdr(struct sk_buff *skb, struct qeth_hdr **hdr, unsigned int len);
/* exports for OSN */
int qeth_osn_assist(struct net_device *, void *, int);
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)
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index c85fadf21b38..760b023eae95 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -705,9 +705,11 @@ static int qeth_l2_xmit_iqd(struct qeth_card *card, struct sk_buff *skb,
static int qeth_l2_xmit_osa(struct qeth_card *card, struct sk_buff *skb,
struct qeth_qdio_out_q *queue, int cast_type)
{
+ int push_len = sizeof(struct qeth_hdr);
unsigned int elements, nr_frags;
- struct sk_buff *skb_copy;
- struct qeth_hdr *hdr;
+ unsigned int hdr_elements = 0;
+ struct qeth_hdr *hdr = NULL;
+ unsigned int hd_len = 0;
int rc;
/* fix hardware limitation: as long as we do not have sbal
@@ -727,38 +729,44 @@ static int qeth_l2_xmit_osa(struct qeth_card *card, struct sk_buff *skb,
}
nr_frags = skb_shinfo(skb)->nr_frags;
- /* create a copy with writeable headroom */
- skb_copy = skb_realloc_headroom(skb, sizeof(struct qeth_hdr));
- if (!skb_copy)
- return -ENOMEM;
- hdr = skb_push(skb_copy, sizeof(struct qeth_hdr));
- qeth_l2_fill_header(hdr, skb_copy, cast_type,
- skb_copy->len - sizeof(*hdr));
- if (skb_copy->ip_summed == CHECKSUM_PARTIAL)
- qeth_l2_hdr_csum(card, hdr, skb_copy);
-
- elements = qeth_get_elements_no(card, skb_copy, 0, 0);
+ rc = skb_cow_head(skb, push_len);
+ if (rc)
+ return rc;
+ push_len = qeth_push_hdr(skb, &hdr, push_len);
+ if (push_len < 0)
+ return push_len;
+ if (!push_len) {
+ /* hdr was allocated from cache */
+ hd_len = sizeof(*hdr);
+ hdr_elements = 1;
+ }
+ qeth_l2_fill_header(hdr, skb, cast_type, skb->len - push_len);
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ qeth_l2_hdr_csum(card, hdr, skb);
+
+ elements = qeth_get_elements_no(card, skb, hdr_elements, 0);
if (!elements) {
rc = -E2BIG;
goto out;
}
- if (qeth_hdr_chk_and_bounce(skb_copy, &hdr, sizeof(*hdr))) {
- rc = -EINVAL;
- goto out;
- }
- rc = qeth_do_send_packet(card, queue, skb_copy, hdr, 0, 0, elements);
+ elements += hdr_elements;
+
+ /* TODO: remove the skb_orphan() once TX completion is fast enough */
+ skb_orphan(skb);
+ rc = qeth_do_send_packet(card, queue, skb, hdr, 0, hd_len, elements);
out:
if (!rc) {
- /* tx success, free dangling original */
- dev_kfree_skb_any(skb);
if (card->options.performance_stats && nr_frags) {
card->perf_stats.sg_skbs_sent++;
/* nr_frags + skb->data */
card->perf_stats.sg_frags_sent += nr_frags + 1;
}
} else {
- /* tx fail, free copy */
- dev_kfree_skb_any(skb_copy);
+ if (hd_len)
+ kmem_cache_free(qeth_core_header_cache, hdr);
+ if (rc == -EBUSY)
+ /* roll back to ETH header */
+ skb_pull(skb, push_len);
}
return rc;
}
@@ -1011,6 +1019,12 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
card->dev->vlan_features |= NETIF_F_RXCSUM;
}
}
+ if (card->info.type != QETH_CARD_TYPE_OSN &&
+ card->info.type != QETH_CARD_TYPE_IQD) {
+ card->dev->priv_flags &= ~IFF_TX_SKB_SHARING;
+ card->dev->needed_headroom = sizeof(struct qeth_hdr);
+ }
+
card->info.broadcast_capable = 1;
qeth_l2_request_initial_mac(card);
card->dev->gso_max_size = (QETH_MAX_BUFFER_ELEMENTS(card) - 1) *
--
2.11.2
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH net-next 0/7] s390/net: more updates for 4.14
2017-08-18 8:19 [PATCH net-next 0/7] s390/net: more updates for 4.14 Julian Wiedmann
` (6 preceding siblings ...)
2017-08-18 8:19 ` [PATCH net-next 7/7] s390/qeth: use skb_cow_head() for L2 OSA xmit Julian Wiedmann
@ 2017-08-18 17:22 ` David Miller
7 siblings, 0 replies; 9+ messages in thread
From: David Miller @ 2017-08-18 17:22 UTC (permalink / raw)
To: jwi; +Cc: netdev, linux-s390, schwidefsky, heiko.carstens, raspl, ubraun
From: Julian Wiedmann <jwi@linux.vnet.ibm.com>
Date: Fri, 18 Aug 2017 10:19:03 +0200
> please apply another batch of qeth patches for net-next.
> This reworks the xmit path for L2 OSAs to use skb_cow_head() instead of
> skb_realloc_headroom().
Series applied, thanks Julian.
^ permalink raw reply [flat|nested] 9+ messages in thread