提交 64ef8957 编写于 作者: F Frank Blaschka 提交者: David S. Miller

qeth: remove EDDP

Performance measurements showed EDDP does not lower CPU costs but increase
them. So we dump out EDDP code from qeth driver.
Signed-off-by: NFrank Blaschka <frank.blaschka@de.ibm.com>
Signed-off-by: NDavid S. Miller <davem@davemloft.net>
上级 f61a0d05
...@@ -8,7 +8,7 @@ obj-$(CONFIG_NETIUCV) += netiucv.o fsm.o ...@@ -8,7 +8,7 @@ obj-$(CONFIG_NETIUCV) += netiucv.o fsm.o
obj-$(CONFIG_SMSGIUCV) += smsgiucv.o obj-$(CONFIG_SMSGIUCV) += smsgiucv.o
obj-$(CONFIG_LCS) += lcs.o cu3088.o obj-$(CONFIG_LCS) += lcs.o cu3088.o
obj-$(CONFIG_CLAW) += claw.o cu3088.o obj-$(CONFIG_CLAW) += claw.o cu3088.o
qeth-y += qeth_core_sys.o qeth_core_main.o qeth_core_mpc.o qeth_core_offl.o qeth-y += qeth_core_sys.o qeth_core_main.o qeth_core_mpc.o
obj-$(CONFIG_QETH) += qeth.o obj-$(CONFIG_QETH) += qeth.o
qeth_l2-y += qeth_l2_main.o qeth_l2-y += qeth_l2_main.o
obj-$(CONFIG_QETH_L2) += qeth_l2.o obj-$(CONFIG_QETH_L2) += qeth_l2.o
......
...@@ -404,7 +404,6 @@ struct qeth_qdio_q { ...@@ -404,7 +404,6 @@ struct qeth_qdio_q {
/* possible types of qeth large_send support */ /* possible types of qeth large_send support */
enum qeth_large_send_types { enum qeth_large_send_types {
QETH_LARGE_SEND_NO, QETH_LARGE_SEND_NO,
QETH_LARGE_SEND_EDDP,
QETH_LARGE_SEND_TSO, QETH_LARGE_SEND_TSO,
}; };
...@@ -839,11 +838,9 @@ int qeth_get_cast_type(struct qeth_card *, struct sk_buff *); ...@@ -839,11 +838,9 @@ int qeth_get_cast_type(struct qeth_card *, struct sk_buff *);
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 *, void *, struct sk_buff *, int); int qeth_get_elements_no(struct qeth_card *, void *, struct sk_buff *, int);
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, struct sk_buff *, struct qeth_hdr *, int, int, int);
struct qeth_eddp_context *, int, int);
int qeth_do_send_packet(struct qeth_card *, struct qeth_qdio_out_q *, int qeth_do_send_packet(struct qeth_card *, struct qeth_qdio_out_q *,
struct sk_buff *, struct qeth_hdr *, struct sk_buff *, struct qeth_hdr *, int);
int, struct qeth_eddp_context *);
int qeth_core_get_stats_count(struct net_device *); int qeth_core_get_stats_count(struct net_device *);
void qeth_core_get_ethtool_stats(struct net_device *, void qeth_core_get_ethtool_stats(struct net_device *,
struct ethtool_stats *, u64 *); struct ethtool_stats *, u64 *);
......
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/tcp.h> #include <linux/tcp.h>
#include <linux/mii.h> #include <linux/mii.h>
#include <linux/kthread.h> #include <linux/kthread.h>
...@@ -26,7 +25,6 @@ ...@@ -26,7 +25,6 @@
#include <asm/io.h> #include <asm/io.h>
#include "qeth_core.h" #include "qeth_core.h"
#include "qeth_core_offl.h"
struct qeth_dbf_info qeth_dbf[QETH_DBF_INFOS] = { struct qeth_dbf_info qeth_dbf[QETH_DBF_INFOS] = {
/* define dbf - Name, Pages, Areas, Maxlen, Level, View, Handle */ /* define dbf - Name, Pages, Areas, Maxlen, Level, View, Handle */
...@@ -285,17 +283,6 @@ int qeth_set_large_send(struct qeth_card *card, ...@@ -285,17 +283,6 @@ int qeth_set_large_send(struct qeth_card *card,
netif_tx_disable(card->dev); netif_tx_disable(card->dev);
card->options.large_send = type; card->options.large_send = type;
switch (card->options.large_send) { switch (card->options.large_send) {
case QETH_LARGE_SEND_EDDP:
if (card->info.type != QETH_CARD_TYPE_IQD) {
card->dev->features |= NETIF_F_TSO | NETIF_F_SG |
NETIF_F_HW_CSUM;
} else {
card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG |
NETIF_F_HW_CSUM);
card->options.large_send = QETH_LARGE_SEND_NO;
rc = -EOPNOTSUPP;
}
break;
case QETH_LARGE_SEND_TSO: case QETH_LARGE_SEND_TSO:
if (qeth_is_supported(card, IPA_OUTBOUND_TSO)) { if (qeth_is_supported(card, IPA_OUTBOUND_TSO)) {
card->dev->features |= NETIF_F_TSO | NETIF_F_SG | card->dev->features |= NETIF_F_TSO | NETIF_F_SG |
...@@ -956,7 +943,6 @@ static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue, ...@@ -956,7 +943,6 @@ static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue,
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
skb = skb_dequeue(&buf->skb_list); skb = skb_dequeue(&buf->skb_list);
} }
qeth_eddp_buf_release_contexts(buf);
for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(queue->card); ++i) { for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(queue->card); ++i) {
if (buf->buffer->element[i].addr && buf->is_header[i]) if (buf->buffer->element[i].addr && buf->is_header[i])
kmem_cache_free(qeth_core_header_cache, kmem_cache_free(qeth_core_header_cache,
...@@ -3187,11 +3173,9 @@ static inline int qeth_fill_buffer(struct qeth_qdio_out_q *queue, ...@@ -3187,11 +3173,9 @@ static inline int qeth_fill_buffer(struct qeth_qdio_out_q *queue,
int qeth_do_send_packet_fast(struct qeth_card *card, int qeth_do_send_packet_fast(struct qeth_card *card,
struct qeth_qdio_out_q *queue, struct sk_buff *skb, struct qeth_qdio_out_q *queue, struct sk_buff *skb,
struct qeth_hdr *hdr, int elements_needed, struct qeth_hdr *hdr, int elements_needed,
struct qeth_eddp_context *ctx, int offset, int hd_len) int offset, int hd_len)
{ {
struct qeth_qdio_out_buffer *buffer; struct qeth_qdio_out_buffer *buffer;
int buffers_needed = 0;
int flush_cnt = 0;
int index; int index;
/* spin until we get the queue ... */ /* spin until we get the queue ... */
...@@ -3206,27 +3190,11 @@ int qeth_do_send_packet_fast(struct qeth_card *card, ...@@ -3206,27 +3190,11 @@ int qeth_do_send_packet_fast(struct qeth_card *card,
*/ */
if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY) if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY)
goto out; goto out;
if (ctx == NULL) queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) %
queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) %
QDIO_MAX_BUFFERS_PER_Q; QDIO_MAX_BUFFERS_PER_Q;
else {
buffers_needed = qeth_eddp_check_buffers_for_context(queue,
ctx);
if (buffers_needed < 0)
goto out;
queue->next_buf_to_fill =
(queue->next_buf_to_fill + buffers_needed) %
QDIO_MAX_BUFFERS_PER_Q;
}
atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
if (ctx == NULL) { qeth_fill_buffer(queue, buffer, skb, hdr, offset, hd_len);
qeth_fill_buffer(queue, buffer, skb, hdr, offset, hd_len); qeth_flush_buffers(queue, index, 1);
qeth_flush_buffers(queue, index, 1);
} else {
flush_cnt = qeth_eddp_fill_buffer(queue, ctx, index);
WARN_ON(buffers_needed != flush_cnt);
qeth_flush_buffers(queue, index, flush_cnt);
}
return 0; return 0;
out: out:
atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
...@@ -3236,7 +3204,7 @@ EXPORT_SYMBOL_GPL(qeth_do_send_packet_fast); ...@@ -3236,7 +3204,7 @@ EXPORT_SYMBOL_GPL(qeth_do_send_packet_fast);
int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue, int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
struct sk_buff *skb, struct qeth_hdr *hdr, struct sk_buff *skb, struct qeth_hdr *hdr,
int elements_needed, struct qeth_eddp_context *ctx) int elements_needed)
{ {
struct qeth_qdio_out_buffer *buffer; struct qeth_qdio_out_buffer *buffer;
int start_index; int start_index;
...@@ -3262,53 +3230,32 @@ int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue, ...@@ -3262,53 +3230,32 @@ int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
qeth_switch_to_packing_if_needed(queue); qeth_switch_to_packing_if_needed(queue);
if (queue->do_pack) { if (queue->do_pack) {
do_pack = 1; do_pack = 1;
if (ctx == NULL) { /* does packet fit in current buffer? */
/* does packet fit in current buffer? */ if ((QETH_MAX_BUFFER_ELEMENTS(card) -
if ((QETH_MAX_BUFFER_ELEMENTS(card) - buffer->next_element_to_fill) < elements_needed) {
buffer->next_element_to_fill) < elements_needed) { /* ... no -> set state PRIMED */
/* ... no -> set state PRIMED */ atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED);
atomic_set(&buffer->state, flush_count++;
QETH_QDIO_BUF_PRIMED); queue->next_buf_to_fill =
flush_count++; (queue->next_buf_to_fill + 1) %
queue->next_buf_to_fill = QDIO_MAX_BUFFERS_PER_Q;
(queue->next_buf_to_fill + 1) % buffer = &queue->bufs[queue->next_buf_to_fill];
QDIO_MAX_BUFFERS_PER_Q; /* we did a step forward, so check buffer state
buffer = &queue->bufs[queue->next_buf_to_fill]; * again */
/* we did a step forward, so check buffer state if (atomic_read(&buffer->state) !=
* again */ QETH_QDIO_BUF_EMPTY) {
if (atomic_read(&buffer->state) != qeth_flush_buffers(queue, start_index,
QETH_QDIO_BUF_EMPTY){
qeth_flush_buffers(queue, start_index,
flush_count); flush_count);
atomic_set(&queue->state, atomic_set(&queue->state,
QETH_OUT_Q_UNLOCKED); QETH_OUT_Q_UNLOCKED);
return -EBUSY; return -EBUSY;
}
}
} else {
/* check if we have enough elements (including following
* free buffers) to handle eddp context */
if (qeth_eddp_check_buffers_for_context(queue, ctx)
< 0) {
rc = -EBUSY;
goto out;
} }
} }
} }
if (ctx == NULL) tmp = qeth_fill_buffer(queue, buffer, skb, hdr, -1, 0);
tmp = qeth_fill_buffer(queue, buffer, skb, hdr, -1, 0);
else {
tmp = qeth_eddp_fill_buffer(queue, ctx,
queue->next_buf_to_fill);
if (tmp < 0) {
rc = -EBUSY;
goto out;
}
}
queue->next_buf_to_fill = (queue->next_buf_to_fill + tmp) % queue->next_buf_to_fill = (queue->next_buf_to_fill + tmp) %
QDIO_MAX_BUFFERS_PER_Q; QDIO_MAX_BUFFERS_PER_Q;
flush_count += tmp; flush_count += tmp;
out:
if (flush_count) if (flush_count)
qeth_flush_buffers(queue, start_index, flush_count); qeth_flush_buffers(queue, start_index, flush_count);
else if (!atomic_read(&queue->set_pci_flags_count)) else if (!atomic_read(&queue->set_pci_flags_count))
......
此差异已折叠。
/*
* drivers/s390/net/qeth_core_offl.h
*
* Copyright IBM Corp. 2007
* Author(s): Thomas Spatzier <tspat@de.ibm.com>,
* Frank Blaschka <frank.blaschka@de.ibm.com>
*/
#ifndef __QETH_CORE_OFFL_H__
#define __QETH_CORE_OFFL_H__
struct qeth_eddp_element {
u32 flags;
u32 length;
void *addr;
};
struct qeth_eddp_context {
atomic_t refcnt;
enum qeth_large_send_types type;
int num_pages; /* # of allocated pages */
u8 **pages; /* pointers to pages */
int offset; /* offset in ctx during creation */
int num_elements; /* # of required 'SBALEs' */
struct qeth_eddp_element *elements; /* array of 'SBALEs' */
int elements_per_skb; /* # of 'SBALEs' per skb **/
};
struct qeth_eddp_context_reference {
struct list_head list;
struct qeth_eddp_context *ctx;
};
struct qeth_eddp_data {
struct qeth_hdr qh;
struct ethhdr mac;
__be16 vlan[2];
union {
struct {
struct iphdr h;
u8 options[40];
} ip4;
struct {
struct ipv6hdr h;
} ip6;
} nh;
u8 nhl;
void *nh_in_ctx; /* address of nh within the ctx */
union {
struct {
struct tcphdr h;
u8 options[40];
} tcp;
} th;
u8 thl;
void *th_in_ctx; /* address of th within the ctx */
struct sk_buff *skb;
int skb_offset;
int frag;
int frag_offset;
} __attribute__ ((packed));
extern struct qeth_eddp_context *qeth_eddp_create_context(struct qeth_card *,
struct sk_buff *, struct qeth_hdr *, unsigned char);
extern void qeth_eddp_put_context(struct qeth_eddp_context *);
extern int qeth_eddp_fill_buffer(struct qeth_qdio_out_q *,
struct qeth_eddp_context *, int);
extern void qeth_eddp_buf_release_contexts(struct qeth_qdio_out_buffer *);
extern int qeth_eddp_check_buffers_for_context(struct qeth_qdio_out_q *,
struct qeth_eddp_context *);
void qeth_tso_fill_header(struct qeth_card *, struct qeth_hdr *,
struct sk_buff *);
void qeth_tx_csum(struct sk_buff *skb);
#endif /* __QETH_CORE_EDDP_H__ */
...@@ -427,8 +427,6 @@ static ssize_t qeth_dev_large_send_show(struct device *dev, ...@@ -427,8 +427,6 @@ static ssize_t qeth_dev_large_send_show(struct device *dev,
switch (card->options.large_send) { switch (card->options.large_send) {
case QETH_LARGE_SEND_NO: case QETH_LARGE_SEND_NO:
return sprintf(buf, "%s\n", "no"); return sprintf(buf, "%s\n", "no");
case QETH_LARGE_SEND_EDDP:
return sprintf(buf, "%s\n", "EDDP");
case QETH_LARGE_SEND_TSO: case QETH_LARGE_SEND_TSO:
return sprintf(buf, "%s\n", "TSO"); return sprintf(buf, "%s\n", "TSO");
default: default:
...@@ -449,8 +447,6 @@ static ssize_t qeth_dev_large_send_store(struct device *dev, ...@@ -449,8 +447,6 @@ static ssize_t qeth_dev_large_send_store(struct device *dev,
tmp = strsep((char **) &buf, "\n"); tmp = strsep((char **) &buf, "\n");
if (!strcmp(tmp, "no")) { if (!strcmp(tmp, "no")) {
type = QETH_LARGE_SEND_NO; type = QETH_LARGE_SEND_NO;
} else if (!strcmp(tmp, "EDDP")) {
type = QETH_LARGE_SEND_EDDP;
} else if (!strcmp(tmp, "TSO")) { } else if (!strcmp(tmp, "TSO")) {
type = QETH_LARGE_SEND_TSO; type = QETH_LARGE_SEND_TSO;
} else { } else {
......
...@@ -21,7 +21,6 @@ ...@@ -21,7 +21,6 @@
#include <linux/ip.h> #include <linux/ip.h>
#include "qeth_core.h" #include "qeth_core.h"
#include "qeth_core_offl.h"
static int qeth_l2_set_offline(struct ccwgroup_device *); static int qeth_l2_set_offline(struct ccwgroup_device *);
static int qeth_l2_stop(struct net_device *); static int qeth_l2_stop(struct net_device *);
...@@ -634,8 +633,6 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -634,8 +633,6 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
struct qeth_qdio_out_q *queue = card->qdio.out_qs struct qeth_qdio_out_q *queue = card->qdio.out_qs
[qeth_get_priority_queue(card, skb, ipv, cast_type)]; [qeth_get_priority_queue(card, skb, ipv, cast_type)];
int tx_bytes = skb->len; int tx_bytes = skb->len;
enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO;
struct qeth_eddp_context *ctx = NULL;
int data_offset = -1; int data_offset = -1;
int elements_needed = 0; int elements_needed = 0;
int hd_len = 0; int hd_len = 0;
...@@ -655,14 +652,10 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -655,14 +652,10 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
} }
netif_stop_queue(dev); netif_stop_queue(dev);
if (skb_is_gso(skb))
large_send = QETH_LARGE_SEND_EDDP;
if (card->info.type == QETH_CARD_TYPE_OSN) if (card->info.type == QETH_CARD_TYPE_OSN)
hdr = (struct qeth_hdr *)skb->data; hdr = (struct qeth_hdr *)skb->data;
else { else {
if ((card->info.type == QETH_CARD_TYPE_IQD) && (!large_send) && if (card->info.type == QETH_CARD_TYPE_IQD) {
(skb_shinfo(skb)->nr_frags == 0)) {
new_skb = skb; new_skb = skb;
data_offset = ETH_HLEN; data_offset = ETH_HLEN;
hd_len = ETH_HLEN; hd_len = ETH_HLEN;
...@@ -689,62 +682,26 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -689,62 +682,26 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
} }
} }
if (large_send == QETH_LARGE_SEND_EDDP) { elements = qeth_get_elements_no(card, (void *)hdr, new_skb,
ctx = qeth_eddp_create_context(card, new_skb, hdr,
skb->sk->sk_protocol);
if (ctx == NULL) {
QETH_DBF_MESSAGE(2, "could not create eddp context\n");
goto tx_drop;
}
} else {
elements = qeth_get_elements_no(card, (void *)hdr, new_skb,
elements_needed); elements_needed);
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);
goto tx_drop; goto tx_drop;
}
}
if ((large_send == QETH_LARGE_SEND_NO) &&
(skb->ip_summed == CHECKSUM_PARTIAL)) {
qeth_tx_csum(new_skb);
if (card->options.performance_stats)
card->perf_stats.tx_csum++;
} }
if (card->info.type != QETH_CARD_TYPE_IQD) if (card->info.type != QETH_CARD_TYPE_IQD)
rc = qeth_do_send_packet(card, queue, new_skb, hdr, rc = qeth_do_send_packet(card, queue, new_skb, hdr,
elements, ctx); elements);
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, ctx, data_offset, hd_len); elements, data_offset, hd_len);
if (!rc) { if (!rc) {
card->stats.tx_packets++; card->stats.tx_packets++;
card->stats.tx_bytes += tx_bytes; card->stats.tx_bytes += tx_bytes;
if (new_skb != skb) if (new_skb != skb)
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
if (card->options.performance_stats) {
if (large_send != QETH_LARGE_SEND_NO) {
card->perf_stats.large_send_bytes += tx_bytes;
card->perf_stats.large_send_cnt++;
}
if (skb_shinfo(new_skb)->nr_frags > 0) {
card->perf_stats.sg_skbs_sent++;
/* nr_frags + skb->data */
card->perf_stats.sg_frags_sent +=
skb_shinfo(new_skb)->nr_frags + 1;
}
}
if (ctx != NULL) {
qeth_eddp_put_context(ctx);
dev_kfree_skb_any(new_skb);
}
} else { } else {
if (ctx != NULL)
qeth_eddp_put_context(ctx);
if (data_offset >= 0) if (data_offset >= 0)
kmem_cache_free(qeth_core_header_cache, hdr); kmem_cache_free(qeth_core_header_cache, hdr);
...@@ -881,30 +838,8 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev) ...@@ -881,30 +838,8 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev)
return; return;
} }
static int qeth_l2_ethtool_set_tso(struct net_device *dev, u32 data)
{
struct qeth_card *card = dev->ml_priv;
if (data) {
if (card->options.large_send == QETH_LARGE_SEND_NO) {
card->options.large_send = QETH_LARGE_SEND_EDDP;
dev->features |= NETIF_F_TSO;
}
} else {
dev->features &= ~NETIF_F_TSO;
card->options.large_send = QETH_LARGE_SEND_NO;
}
return 0;
}
static struct ethtool_ops qeth_l2_ethtool_ops = { static struct ethtool_ops qeth_l2_ethtool_ops = {
.get_link = ethtool_op_get_link, .get_link = ethtool_op_get_link,
.get_tx_csum = ethtool_op_get_tx_csum,
.set_tx_csum = ethtool_op_set_tx_hw_csum,
.get_sg = ethtool_op_get_sg,
.set_sg = ethtool_op_set_sg,
.get_tso = ethtool_op_get_tso,
.set_tso = qeth_l2_ethtool_set_tso,
.get_strings = qeth_core_get_strings, .get_strings = qeth_core_get_strings,
.get_ethtool_stats = qeth_core_get_ethtool_stats, .get_ethtool_stats = qeth_core_get_ethtool_stats,
.get_stats_count = qeth_core_get_stats_count, .get_stats_count = qeth_core_get_stats_count,
......
...@@ -19,15 +19,15 @@ ...@@ -19,15 +19,15 @@
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/mii.h> #include <linux/mii.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/reboot.h> #include <linux/ipv6.h>
#include <linux/inetdevice.h> #include <linux/inetdevice.h>
#include <linux/igmp.h> #include <linux/igmp.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/arp.h> #include <net/arp.h>
#include <net/ip6_checksum.h>
#include "qeth_l3.h" #include "qeth_l3.h"
#include "qeth_core_offl.h"
static int qeth_l3_set_offline(struct ccwgroup_device *); static int qeth_l3_set_offline(struct ccwgroup_device *);
static int qeth_l3_recover(void *); static int qeth_l3_recover(void *);
...@@ -2577,12 +2577,63 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, ...@@ -2577,12 +2577,63 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
} }
} }
static void qeth_tso_fill_header(struct qeth_card *card,
struct qeth_hdr *qhdr, struct sk_buff *skb)
{
struct qeth_hdr_tso *hdr = (struct qeth_hdr_tso *)qhdr;
struct tcphdr *tcph = tcp_hdr(skb);
struct iphdr *iph = ip_hdr(skb);
struct ipv6hdr *ip6h = ipv6_hdr(skb);
/*fix header to TSO values ...*/
hdr->hdr.hdr.l3.id = QETH_HEADER_TYPE_TSO;
/*set values which are fix for the first approach ...*/
hdr->ext.hdr_tot_len = (__u16) sizeof(struct qeth_hdr_ext_tso);
hdr->ext.imb_hdr_no = 1;
hdr->ext.hdr_type = 1;
hdr->ext.hdr_version = 1;
hdr->ext.hdr_len = 28;
/*insert non-fix values */
hdr->ext.mss = skb_shinfo(skb)->gso_size;
hdr->ext.dg_hdr_len = (__u16)(iph->ihl*4 + tcph->doff*4);
hdr->ext.payload_len = (__u16)(skb->len - hdr->ext.dg_hdr_len -
sizeof(struct qeth_hdr_tso));
tcph->check = 0;
if (skb->protocol == ETH_P_IPV6) {
ip6h->payload_len = 0;
tcph->check = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
0, IPPROTO_TCP, 0);
} else {
/*OSA want us to set these values ...*/
tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
0, IPPROTO_TCP, 0);
iph->tot_len = 0;
iph->check = 0;
}
}
static void qeth_tx_csum(struct sk_buff *skb)
{
__wsum csum;
int offset;
skb_set_transport_header(skb, skb->csum_start - skb_headroom(skb));
offset = skb->csum_start - skb_headroom(skb);
BUG_ON(offset >= skb_headlen(skb));
csum = skb_checksum(skb, offset, skb->len - offset, 0);
offset += skb->csum_offset;
BUG_ON(offset + sizeof(__sum16) > skb_headlen(skb));
*(__sum16 *)(skb->data + offset) = csum_fold(csum);
}
static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) 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 elements_needed = 0;
int elems;
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);
...@@ -2591,8 +2642,8 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -2591,8 +2642,8 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
[qeth_get_priority_queue(card, skb, ipv, cast_type)]; [qeth_get_priority_queue(card, skb, ipv, cast_type)];
int tx_bytes = skb->len; int tx_bytes = skb->len;
enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO; enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO;
struct qeth_eddp_context *ctx = NULL;
int data_offset = -1; int data_offset = -1;
int nr_frags;
if ((card->info.type == QETH_CARD_TYPE_IQD) && if ((card->info.type == QETH_CARD_TYPE_IQD) &&
(skb->protocol != htons(ETH_P_IPV6)) && (skb->protocol != htons(ETH_P_IPV6)) &&
...@@ -2615,6 +2666,12 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -2615,6 +2666,12 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (skb_is_gso(skb)) if (skb_is_gso(skb))
large_send = card->options.large_send; large_send = card->options.large_send;
else
if (skb->ip_summed == CHECKSUM_PARTIAL) {
qeth_tx_csum(skb);
if (card->options.performance_stats)
card->perf_stats.tx_csum++;
}
if ((card->info.type == QETH_CARD_TYPE_IQD) && (!large_send) && if ((card->info.type == QETH_CARD_TYPE_IQD) && (!large_send) &&
(skb_shinfo(skb)->nr_frags == 0)) { (skb_shinfo(skb)->nr_frags == 0)) {
...@@ -2661,12 +2718,13 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -2661,12 +2718,13 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
netif_stop_queue(dev); netif_stop_queue(dev);
/* fix hardware limitation: as long as we do not have sbal /* fix hardware limitation: as long as we do not have sbal
* chaining we can not send long frag lists so we temporary * chaining we can not send long frag lists
* switch to EDDP
*/ */
if ((large_send == QETH_LARGE_SEND_TSO) && if ((large_send == QETH_LARGE_SEND_TSO) &&
((skb_shinfo(new_skb)->nr_frags + 2) > 16)) ((skb_shinfo(new_skb)->nr_frags + 2) > 16)) {
large_send = QETH_LARGE_SEND_EDDP; if (skb_linearize(new_skb))
goto tx_drop;
}
if ((large_send == QETH_LARGE_SEND_TSO) && if ((large_send == QETH_LARGE_SEND_TSO) &&
(cast_type == RTN_UNSPEC)) { (cast_type == RTN_UNSPEC)) {
...@@ -2689,40 +2747,22 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -2689,40 +2747,22 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
} }
} }
if (large_send == QETH_LARGE_SEND_EDDP) { elems = qeth_get_elements_no(card, (void *)hdr, new_skb,
/* new_skb is not owned by a socket so we use skb to get
* the protocol
*/
ctx = qeth_eddp_create_context(card, new_skb, hdr,
skb->sk->sk_protocol);
if (ctx == NULL) {
QETH_DBF_MESSAGE(2, "could not create eddp context\n");
goto tx_drop;
}
} else {
int elems = qeth_get_elements_no(card, (void *)hdr, new_skb,
elements_needed); elements_needed);
if (!elems) { if (!elems) {
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;
}
if ((large_send == QETH_LARGE_SEND_NO) &&
(new_skb->ip_summed == CHECKSUM_PARTIAL)) {
qeth_tx_csum(new_skb);
if (card->options.performance_stats)
card->perf_stats.tx_csum++;
} }
elements_needed += elems;
nr_frags = skb_shinfo(new_skb)->nr_frags;
if (card->info.type != QETH_CARD_TYPE_IQD) if (card->info.type != QETH_CARD_TYPE_IQD)
rc = qeth_do_send_packet(card, queue, new_skb, hdr, rc = qeth_do_send_packet(card, queue, new_skb, hdr,
elements_needed, ctx); 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, ctx, data_offset, 0); elements_needed, data_offset, 0);
if (!rc) { if (!rc) {
card->stats.tx_packets++; card->stats.tx_packets++;
...@@ -2734,22 +2774,13 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -2734,22 +2774,13 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
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++;
} }
if (skb_shinfo(new_skb)->nr_frags > 0) { if (nr_frags) {
card->perf_stats.sg_skbs_sent++; card->perf_stats.sg_skbs_sent++;
/* nr_frags + skb->data */ /* nr_frags + skb->data */
card->perf_stats.sg_frags_sent += card->perf_stats.sg_frags_sent += nr_frags + 1;
skb_shinfo(new_skb)->nr_frags + 1;
} }
} }
if (ctx != NULL) {
qeth_eddp_put_context(ctx);
dev_kfree_skb_any(new_skb);
}
} else { } else {
if (ctx != NULL)
qeth_eddp_put_context(ctx);
if (data_offset >= 0) if (data_offset >= 0)
kmem_cache_free(qeth_core_header_cache, hdr); kmem_cache_free(qeth_core_header_cache, hdr);
...@@ -2844,7 +2875,7 @@ static int qeth_l3_ethtool_set_tso(struct net_device *dev, u32 data) ...@@ -2844,7 +2875,7 @@ static int qeth_l3_ethtool_set_tso(struct net_device *dev, u32 data)
if (data) { if (data) {
if (card->options.large_send == QETH_LARGE_SEND_NO) { if (card->options.large_send == QETH_LARGE_SEND_NO) {
if (card->info.type == QETH_CARD_TYPE_IQD) if (card->info.type == QETH_CARD_TYPE_IQD)
card->options.large_send = QETH_LARGE_SEND_EDDP; return -EPERM;
else else
card->options.large_send = QETH_LARGE_SEND_TSO; card->options.large_send = QETH_LARGE_SEND_TSO;
dev->features |= NETIF_F_TSO; dev->features |= NETIF_F_TSO;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册