summaryrefslogtreecommitdiffstats
path: root/drivers/s390/net/qeth_core_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/net/qeth_core_main.c')
-rw-r--r--drivers/s390/net/qeth_core_main.c231
1 files changed, 154 insertions, 77 deletions
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index b7b74776e2ff..7dba6c8537a1 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -1464,8 +1464,6 @@ static int qeth_setup_card(struct qeth_card *card)
card->thread_allowed_mask = 0;
card->thread_running_mask = 0;
INIT_WORK(&card->kernel_thread_starter, qeth_start_kernel_thread);
- INIT_LIST_HEAD(&card->ip_list);
- INIT_LIST_HEAD(card->ip_tbd_list);
INIT_LIST_HEAD(&card->cmd_waiter_list);
init_waitqueue_head(&card->wait_q);
/* initial options */
@@ -1500,11 +1498,6 @@ static struct qeth_card *qeth_alloc_card(void)
if (!card)
goto out;
QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *));
- card->ip_tbd_list = kzalloc(sizeof(struct list_head), GFP_KERNEL);
- if (!card->ip_tbd_list) {
- QETH_DBF_TEXT(SETUP, 0, "iptbdnom");
- goto out_card;
- }
if (qeth_setup_channel(&card->read))
goto out_ip;
if (qeth_setup_channel(&card->write))
@@ -1517,8 +1510,6 @@ static struct qeth_card *qeth_alloc_card(void)
out_channel:
qeth_clean_channel(&card->read);
out_ip:
- kfree(card->ip_tbd_list);
-out_card:
kfree(card);
out:
return NULL;
@@ -3757,6 +3748,14 @@ void qeth_qdio_output_handler(struct ccw_device *ccwdev,
}
EXPORT_SYMBOL_GPL(qeth_qdio_output_handler);
+/* We cannot use outbound queue 3 for unicast packets on HiperSockets */
+static inline int qeth_cut_iqd_prio(struct qeth_card *card, int queue_num)
+{
+ if ((card->info.type == QETH_CARD_TYPE_IQD) && (queue_num == 3))
+ return 2;
+ return queue_num;
+}
+
/**
* Note: Function assumes that we have 4 outbound queues.
*/
@@ -3784,9 +3783,9 @@ int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb,
return card->qdio.default_out_queue;
}
if (card->qdio.do_prio_queueing == QETH_PRIO_Q_ING_PREC)
- return ~tos >> 6 & 3;
+ return qeth_cut_iqd_prio(card, ~tos >> 6 & 3);
if (tos & IPTOS_MINCOST)
- return 3;
+ return qeth_cut_iqd_prio(card, 3);
if (tos & IPTOS_RELIABILITY)
return 2;
if (tos & IPTOS_THROUGHPUT)
@@ -3797,11 +3796,12 @@ int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb,
case QETH_PRIO_Q_ING_SKB:
if (skb->priority > 5)
return 0;
- return ~skb->priority >> 1 & 3;
+ return qeth_cut_iqd_prio(card, ~skb->priority >> 1 & 3);
case QETH_PRIO_Q_ING_VLAN:
tci = &((struct ethhdr *)skb->data)->h_proto;
if (*tci == ETH_P_8021Q)
- return ~*(tci + 1) >> (VLAN_PRIO_SHIFT + 1) & 3;
+ return qeth_cut_iqd_prio(card, ~*(tci + 1) >>
+ (VLAN_PRIO_SHIFT + 1) & 3);
break;
default:
break;
@@ -3810,41 +3810,54 @@ int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb,
}
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 cnt, length, e, elements = 0;
- struct skb_frag_struct *frag;
- char *data;
+ int cnt, elements = 0;
for (cnt = 0; cnt < skb_shinfo(skb)->nr_frags; cnt++) {
- frag = &skb_shinfo(skb)->frags[cnt];
- data = (char *)page_to_phys(skb_frag_page(frag)) +
- frag->page_offset;
- length = frag->size;
- e = PFN_UP((unsigned long)data + length - 1) -
- PFN_DOWN((unsigned long)data);
- elements += e;
+ struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[cnt];
+
+ elements += qeth_get_elements_for_range(
+ (addr_t)skb_frag_address(frag),
+ (addr_t)skb_frag_address(frag) + skb_frag_size(frag));
}
return elements;
}
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,
- struct sk_buff *skb, int elems)
+ struct sk_buff *skb, int extra_elems)
{
- int dlen = skb->len - skb->data_len;
- int elements_needed = PFN_UP((unsigned long)skb->data + dlen - 1) -
- PFN_DOWN((unsigned long)skb->data);
-
- elements_needed += qeth_get_elements_for_frags(skb);
+ int elements = qeth_get_elements_for_range(
+ (addr_t)skb->data,
+ (addr_t)skb->data + skb_headlen(skb)) +
+ 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 "
"(Number=%d / Length=%d). Discarded.\n",
- (elements_needed+elems), skb->len);
+ elements + extra_elems, skb->len);
return 0;
}
- return elements_needed;
+ return elements;
}
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)
rest = len - inpage;
if (rest > hroom)
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->tail -= rest;
*hdr = (struct qeth_hdr *)skb->data;
@@ -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,
int offset)
{
- int length = skb->len - skb->data_len;
+ int length = skb_headlen(skb);
int length_here;
int element;
char *data;
@@ -4967,7 +4980,6 @@ static void qeth_core_free_card(struct qeth_card *card)
qeth_clean_channel(&card->write);
if (card->dev)
free_netdev(card->dev);
- kfree(card->ip_tbd_list);
qeth_free_qdio_buffers(card);
unregister_service_level(&card->qeth_service_level);
kfree(card);
@@ -5265,8 +5277,8 @@ no_mem:
}
EXPORT_SYMBOL_GPL(qeth_core_get_next_skb);
-static int qeth_setassparms_cb(struct qeth_card *card,
- struct qeth_reply *reply, unsigned long data)
+int qeth_setassparms_cb(struct qeth_card *card,
+ struct qeth_reply *reply, unsigned long data)
{
struct qeth_ipa_cmd *cmd;
@@ -5294,6 +5306,7 @@ static int qeth_setassparms_cb(struct qeth_card *card,
return 0;
}
+EXPORT_SYMBOL_GPL(qeth_setassparms_cb);
struct qeth_cmd_buffer *qeth_get_setassparms_cmd(struct qeth_card *card,
enum qeth_ipa_funcs ipa_func,
@@ -5788,6 +5801,7 @@ static struct {
{"tx do_QDIO count"},
{"tx csum"},
{"tx lin"},
+ {"tx linfail"},
{"cq handler count"},
{"cq handler time"}
};
@@ -5848,8 +5862,9 @@ void qeth_core_get_ethtool_stats(struct net_device *dev,
data[32] = card->perf_stats.outbound_do_qdio_cnt;
data[33] = card->perf_stats.tx_csum;
data[34] = card->perf_stats.tx_lin;
- data[35] = card->perf_stats.cq_cnt;
- data[36] = card->perf_stats.cq_time;
+ data[35] = card->perf_stats.tx_linfail;
+ data[36] = card->perf_stats.cq_cnt;
+ data[37] = card->perf_stats.cq_time;
}
EXPORT_SYMBOL_GPL(qeth_core_get_ethtool_stats);
@@ -6048,74 +6063,136 @@ int qeth_core_ethtool_get_settings(struct net_device *netdev,
}
EXPORT_SYMBOL_GPL(qeth_core_ethtool_get_settings);
-static int qeth_send_checksum_command(struct qeth_card *card)
+static int qeth_send_checksum_on(struct qeth_card *card, int cstype)
{
+ long rxtx_arg;
int rc;
- rc = qeth_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM,
- IPA_CMD_ASS_START, 0);
+ rc = qeth_send_simple_setassparms(card, cstype, IPA_CMD_ASS_START, 0);
if (rc) {
- dev_warn(&card->gdev->dev, "Starting HW checksumming for %s "
- "failed, using SW checksumming\n",
- QETH_CARD_IFNAME(card));
+ dev_warn(&card->gdev->dev,
+ "Starting HW checksumming for %s failed, using SW checksumming\n",
+ QETH_CARD_IFNAME(card));
return rc;
}
- rc = qeth_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM,
- IPA_CMD_ASS_ENABLE,
- card->info.csum_mask);
+ rxtx_arg = (cstype == IPA_OUTBOUND_CHECKSUM) ? card->info.tx_csum_mask
+ : card->info.csum_mask;
+ rc = qeth_send_simple_setassparms(card, cstype, IPA_CMD_ASS_ENABLE,
+ rxtx_arg);
if (rc) {
- dev_warn(&card->gdev->dev, "Enabling HW checksumming for %s "
- "failed, using SW checksumming\n",
- QETH_CARD_IFNAME(card));
+ dev_warn(&card->gdev->dev,
+ "Enabling HW checksumming for %s failed, using SW checksumming\n",
+ QETH_CARD_IFNAME(card));
return rc;
}
+
+ dev_info(&card->gdev->dev, "HW Checksumming (%sbound) enabled\n",
+ cstype == IPA_INBOUND_CHECKSUM ? "in" : "out");
return 0;
}
-int qeth_set_rx_csum(struct qeth_card *card, int on)
+static int qeth_set_ipa_csum(struct qeth_card *card, int on, int cstype)
{
int rc;
if (on) {
- rc = qeth_send_checksum_command(card);
+ rc = qeth_send_checksum_on(card, cstype);
if (rc)
return -EIO;
- dev_info(&card->gdev->dev,
- "HW Checksumming (inbound) enabled\n");
} else {
- rc = qeth_send_simple_setassparms(card,
- IPA_INBOUND_CHECKSUM, IPA_CMD_ASS_STOP, 0);
+ rc = qeth_send_simple_setassparms(card, cstype,
+ IPA_CMD_ASS_STOP, 0);
if (rc)
return -EIO;
}
return 0;
}
-EXPORT_SYMBOL_GPL(qeth_set_rx_csum);
-int qeth_start_ipa_tx_checksum(struct qeth_card *card)
+static int qeth_set_ipa_tso(struct qeth_card *card, int on)
{
- int rc = 0;
+ int rc;
- if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM))
- return rc;
- rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM,
- IPA_CMD_ASS_START, 0);
- if (rc)
- goto err_out;
- rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM,
- IPA_CMD_ASS_ENABLE,
- card->info.tx_csum_mask);
- if (rc)
- goto err_out;
+ QETH_CARD_TEXT(card, 3, "sttso");
- dev_info(&card->gdev->dev, "HW TX Checksumming enabled\n");
- return rc;
-err_out:
- dev_warn(&card->gdev->dev, "Enabling HW TX checksumming for %s "
- "failed, using SW TX checksumming\n", QETH_CARD_IFNAME(card));
+ if (on) {
+ rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_TSO,
+ IPA_CMD_ASS_START, 0);
+ if (rc) {
+ dev_warn(&card->gdev->dev,
+ "Starting outbound TCP segmentation offload for %s failed\n",
+ QETH_CARD_IFNAME(card));
+ return -EIO;
+ }
+ dev_info(&card->gdev->dev, "Outbound TSO enabled\n");
+ } else {
+ rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_TSO,
+ IPA_CMD_ASS_STOP, 0);
+ }
return rc;
}
-EXPORT_SYMBOL_GPL(qeth_start_ipa_tx_checksum);
+
+int qeth_set_features(struct net_device *dev, netdev_features_t features)
+{
+ struct qeth_card *card = dev->ml_priv;
+ netdev_features_t changed = dev->features ^ features;
+ int rc = 0;
+
+ QETH_DBF_TEXT(SETUP, 2, "setfeat");
+ QETH_DBF_HEX(SETUP, 2, &features, sizeof(features));
+
+ if ((changed & NETIF_F_IP_CSUM)) {
+ rc = qeth_set_ipa_csum(card,
+ features & NETIF_F_IP_CSUM ? 1 : 0,
+ IPA_OUTBOUND_CHECKSUM);
+ if (rc)
+ changed ^= NETIF_F_IP_CSUM;
+ }
+ if ((changed & NETIF_F_RXCSUM)) {
+ rc = qeth_set_ipa_csum(card,
+ features & NETIF_F_RXCSUM ? 1 : 0,
+ IPA_INBOUND_CHECKSUM);
+ if (rc)
+ changed ^= NETIF_F_RXCSUM;
+ }
+ if ((changed & NETIF_F_TSO)) {
+ rc = qeth_set_ipa_tso(card, features & NETIF_F_TSO ? 1 : 0);
+ if (rc)
+ changed ^= NETIF_F_TSO;
+ }
+
+ /* everything changed successfully? */
+ if ((dev->features ^ features) == changed)
+ return 0;
+ /* something went wrong. save changed features and return error */
+ dev->features ^= changed;
+ return -EIO;
+}
+EXPORT_SYMBOL_GPL(qeth_set_features);
+
+netdev_features_t qeth_fix_features(struct net_device *dev,
+ netdev_features_t features)
+{
+ struct qeth_card *card = dev->ml_priv;
+
+ QETH_DBF_TEXT(SETUP, 2, "fixfeat");
+ if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM))
+ features &= ~NETIF_F_IP_CSUM;
+ if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM))
+ features &= ~NETIF_F_RXCSUM;
+ if (!qeth_is_supported(card, IPA_OUTBOUND_TSO)) {
+ features &= ~NETIF_F_TSO;
+ dev_info(&card->gdev->dev, "Outbound TSO not supported on %s\n",
+ QETH_CARD_IFNAME(card));
+ }
+ /* if the card isn't up, remove features that require hw changes */
+ if (card->state == CARD_STATE_DOWN ||
+ card->state == CARD_STATE_RECOVER)
+ features = features & ~(NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
+ NETIF_F_TSO);
+ QETH_DBF_HEX(SETUP, 2, &features, sizeof(features));
+ return features;
+}
+EXPORT_SYMBOL_GPL(qeth_fix_features);
static int __init qeth_core_init(void)
{