|
@@ -1,6 +1,6 @@
|
|
/*
|
|
/*
|
|
*
|
|
*
|
|
- * linux/drivers/s390/net/qeth_main.c ($Revision: 1.210 $)
|
|
|
|
|
|
+ * linux/drivers/s390/net/qeth_main.c ($Revision: 1.214 $)
|
|
*
|
|
*
|
|
* Linux on zSeries OSA Express and HiperSockets support
|
|
* Linux on zSeries OSA Express and HiperSockets support
|
|
*
|
|
*
|
|
@@ -12,7 +12,7 @@
|
|
* Frank Pavlic (pavlic@de.ibm.com) and
|
|
* Frank Pavlic (pavlic@de.ibm.com) and
|
|
* Thomas Spatzier <tspat@de.ibm.com>
|
|
* Thomas Spatzier <tspat@de.ibm.com>
|
|
*
|
|
*
|
|
- * $Revision: 1.210 $ $Date: 2005/04/18 17:27:39 $
|
|
|
|
|
|
+ * $Revision: 1.214 $ $Date: 2005/05/04 20:19:18 $
|
|
*
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* it under the terms of the GNU General Public License as published by
|
|
@@ -80,7 +80,7 @@ qeth_eyecatcher(void)
|
|
#include "qeth_eddp.h"
|
|
#include "qeth_eddp.h"
|
|
#include "qeth_tso.h"
|
|
#include "qeth_tso.h"
|
|
|
|
|
|
-#define VERSION_QETH_C "$Revision: 1.210 $"
|
|
|
|
|
|
+#define VERSION_QETH_C "$Revision: 1.214 $"
|
|
static const char *version = "qeth S/390 OSA-Express driver";
|
|
static const char *version = "qeth S/390 OSA-Express driver";
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -158,6 +158,9 @@ qeth_irq_tasklet(unsigned long);
|
|
static int
|
|
static int
|
|
qeth_set_online(struct ccwgroup_device *);
|
|
qeth_set_online(struct ccwgroup_device *);
|
|
|
|
|
|
|
|
+static int
|
|
|
|
+__qeth_set_online(struct ccwgroup_device *gdev, int recovery_mode);
|
|
|
|
+
|
|
static struct qeth_ipaddr *
|
|
static struct qeth_ipaddr *
|
|
qeth_get_addr_buffer(enum qeth_prot_versions);
|
|
qeth_get_addr_buffer(enum qeth_prot_versions);
|
|
|
|
|
|
@@ -510,10 +513,10 @@ qeth_irq_tasklet(unsigned long data)
|
|
wake_up(&card->wait_q);
|
|
wake_up(&card->wait_q);
|
|
}
|
|
}
|
|
|
|
|
|
-static int qeth_stop_card(struct qeth_card *);
|
|
|
|
|
|
+static int qeth_stop_card(struct qeth_card *, int);
|
|
|
|
|
|
static int
|
|
static int
|
|
-qeth_set_offline(struct ccwgroup_device *cgdev)
|
|
|
|
|
|
+__qeth_set_offline(struct ccwgroup_device *cgdev, int recovery_mode)
|
|
{
|
|
{
|
|
struct qeth_card *card = (struct qeth_card *) cgdev->dev.driver_data;
|
|
struct qeth_card *card = (struct qeth_card *) cgdev->dev.driver_data;
|
|
int rc = 0;
|
|
int rc = 0;
|
|
@@ -523,7 +526,7 @@ qeth_set_offline(struct ccwgroup_device *cgdev)
|
|
QETH_DBF_HEX(setup, 3, &card, sizeof(void *));
|
|
QETH_DBF_HEX(setup, 3, &card, sizeof(void *));
|
|
|
|
|
|
recover_flag = card->state;
|
|
recover_flag = card->state;
|
|
- if (qeth_stop_card(card) == -ERESTARTSYS){
|
|
|
|
|
|
+ if (qeth_stop_card(card, recovery_mode) == -ERESTARTSYS){
|
|
PRINT_WARN("Stopping card %s interrupted by user!\n",
|
|
PRINT_WARN("Stopping card %s interrupted by user!\n",
|
|
CARD_BUS_ID(card));
|
|
CARD_BUS_ID(card));
|
|
return -ERESTARTSYS;
|
|
return -ERESTARTSYS;
|
|
@@ -539,6 +542,12 @@ qeth_set_offline(struct ccwgroup_device *cgdev)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int
|
|
|
|
+qeth_set_offline(struct ccwgroup_device *cgdev)
|
|
|
|
+{
|
|
|
|
+ return __qeth_set_offline(cgdev, 0);
|
|
|
|
+}
|
|
|
|
+
|
|
static int
|
|
static int
|
|
qeth_wait_for_threads(struct qeth_card *card, unsigned long threads);
|
|
qeth_wait_for_threads(struct qeth_card *card, unsigned long threads);
|
|
|
|
|
|
@@ -953,8 +962,8 @@ qeth_recover(void *ptr)
|
|
PRINT_WARN("Recovery of device %s started ...\n",
|
|
PRINT_WARN("Recovery of device %s started ...\n",
|
|
CARD_BUS_ID(card));
|
|
CARD_BUS_ID(card));
|
|
card->use_hard_stop = 1;
|
|
card->use_hard_stop = 1;
|
|
- qeth_set_offline(card->gdev);
|
|
|
|
- rc = qeth_set_online(card->gdev);
|
|
|
|
|
|
+ __qeth_set_offline(card->gdev,1);
|
|
|
|
+ rc = __qeth_set_online(card->gdev,1);
|
|
if (!rc)
|
|
if (!rc)
|
|
PRINT_INFO("Device %s successfully recovered!\n",
|
|
PRINT_INFO("Device %s successfully recovered!\n",
|
|
CARD_BUS_ID(card));
|
|
CARD_BUS_ID(card));
|
|
@@ -3786,16 +3795,12 @@ static inline int
|
|
qeth_prepare_skb(struct qeth_card *card, struct sk_buff **skb,
|
|
qeth_prepare_skb(struct qeth_card *card, struct sk_buff **skb,
|
|
struct qeth_hdr **hdr, int ipv)
|
|
struct qeth_hdr **hdr, int ipv)
|
|
{
|
|
{
|
|
- int rc = 0;
|
|
|
|
#ifdef CONFIG_QETH_VLAN
|
|
#ifdef CONFIG_QETH_VLAN
|
|
u16 *tag;
|
|
u16 *tag;
|
|
#endif
|
|
#endif
|
|
|
|
|
|
QETH_DBF_TEXT(trace, 6, "prepskb");
|
|
QETH_DBF_TEXT(trace, 6, "prepskb");
|
|
|
|
|
|
- rc = qeth_realloc_headroom(card, skb, sizeof(struct qeth_hdr));
|
|
|
|
- if (rc)
|
|
|
|
- return rc;
|
|
|
|
#ifdef CONFIG_QETH_VLAN
|
|
#ifdef CONFIG_QETH_VLAN
|
|
if (card->vlangrp && vlan_tx_tag_present(*skb) &&
|
|
if (card->vlangrp && vlan_tx_tag_present(*skb) &&
|
|
((ipv == 6) || card->options.layer2) ) {
|
|
((ipv == 6) || card->options.layer2) ) {
|
|
@@ -3977,25 +3982,28 @@ qeth_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
|
|
|
|
|
|
static inline void
|
|
static inline void
|
|
__qeth_fill_buffer(struct sk_buff *skb, struct qdio_buffer *buffer,
|
|
__qeth_fill_buffer(struct sk_buff *skb, struct qdio_buffer *buffer,
|
|
- int *next_element_to_fill)
|
|
|
|
|
|
+ int is_tso, int *next_element_to_fill)
|
|
{
|
|
{
|
|
int length = skb->len;
|
|
int length = skb->len;
|
|
int length_here;
|
|
int length_here;
|
|
int element;
|
|
int element;
|
|
char *data;
|
|
char *data;
|
|
- int first_lap = 1;
|
|
|
|
|
|
+ int first_lap ;
|
|
|
|
|
|
element = *next_element_to_fill;
|
|
element = *next_element_to_fill;
|
|
data = skb->data;
|
|
data = skb->data;
|
|
|
|
+ first_lap = (is_tso == 0 ? 1 : 0);
|
|
|
|
+
|
|
while (length > 0) {
|
|
while (length > 0) {
|
|
/* length_here is the remaining amount of data in this page */
|
|
/* length_here is the remaining amount of data in this page */
|
|
length_here = PAGE_SIZE - ((unsigned long) data % PAGE_SIZE);
|
|
length_here = PAGE_SIZE - ((unsigned long) data % PAGE_SIZE);
|
|
if (length < length_here)
|
|
if (length < length_here)
|
|
length_here = length;
|
|
length_here = length;
|
|
|
|
+
|
|
buffer->element[element].addr = data;
|
|
buffer->element[element].addr = data;
|
|
buffer->element[element].length = length_here;
|
|
buffer->element[element].length = length_here;
|
|
length -= length_here;
|
|
length -= length_here;
|
|
- if (!length){
|
|
|
|
|
|
+ if (!length) {
|
|
if (first_lap)
|
|
if (first_lap)
|
|
buffer->element[element].flags = 0;
|
|
buffer->element[element].flags = 0;
|
|
else
|
|
else
|
|
@@ -4022,17 +4030,35 @@ qeth_fill_buffer(struct qeth_qdio_out_q *queue,
|
|
struct sk_buff *skb)
|
|
struct sk_buff *skb)
|
|
{
|
|
{
|
|
struct qdio_buffer *buffer;
|
|
struct qdio_buffer *buffer;
|
|
- int flush_cnt = 0;
|
|
|
|
|
|
+ struct qeth_hdr_tso *hdr;
|
|
|
|
+ int flush_cnt = 0, hdr_len, large_send = 0;
|
|
|
|
|
|
QETH_DBF_TEXT(trace, 6, "qdfillbf");
|
|
QETH_DBF_TEXT(trace, 6, "qdfillbf");
|
|
|
|
+
|
|
buffer = buf->buffer;
|
|
buffer = buf->buffer;
|
|
atomic_inc(&skb->users);
|
|
atomic_inc(&skb->users);
|
|
skb_queue_tail(&buf->skb_list, skb);
|
|
skb_queue_tail(&buf->skb_list, skb);
|
|
|
|
+
|
|
|
|
+ hdr = (struct qeth_hdr_tso *) skb->data;
|
|
|
|
+ /*check first on TSO ....*/
|
|
|
|
+ if (hdr->hdr.hdr.l3.id == QETH_HEADER_TYPE_TSO) {
|
|
|
|
+ int element = buf->next_element_to_fill;
|
|
|
|
+
|
|
|
|
+ hdr_len = sizeof(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].flags = SBAL_FLAGS_FIRST_FRAG;
|
|
|
|
+ buf->next_element_to_fill++;
|
|
|
|
+ skb->data += hdr_len;
|
|
|
|
+ skb->len -= hdr_len;
|
|
|
|
+ large_send = 1;
|
|
|
|
+ }
|
|
if (skb_shinfo(skb)->nr_frags == 0)
|
|
if (skb_shinfo(skb)->nr_frags == 0)
|
|
- __qeth_fill_buffer(skb, buffer,
|
|
|
|
|
|
+ __qeth_fill_buffer(skb, buffer, large_send,
|
|
(int *)&buf->next_element_to_fill);
|
|
(int *)&buf->next_element_to_fill);
|
|
else
|
|
else
|
|
- __qeth_fill_buffer_frag(skb, buffer, 0,
|
|
|
|
|
|
+ __qeth_fill_buffer_frag(skb, buffer, large_send,
|
|
(int *)&buf->next_element_to_fill);
|
|
(int *)&buf->next_element_to_fill);
|
|
|
|
|
|
if (!queue->do_pack) {
|
|
if (!queue->do_pack) {
|
|
@@ -4224,6 +4250,25 @@ out:
|
|
return rc;
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static inline int
|
|
|
|
+qeth_get_elements_no(struct qeth_card *card, void *hdr, struct sk_buff *skb)
|
|
|
|
+{
|
|
|
|
+ int elements_needed = 0;
|
|
|
|
+
|
|
|
|
+ if (skb_shinfo(skb)->nr_frags > 0) {
|
|
|
|
+ elements_needed = (skb_shinfo(skb)->nr_frags + 1);
|
|
|
|
+ }
|
|
|
|
+ if (elements_needed == 0 )
|
|
|
|
+ elements_needed = 1 + (((((unsigned long) hdr) % PAGE_SIZE)
|
|
|
|
+ + skb->len) >> PAGE_SHIFT);
|
|
|
|
+ if (elements_needed > QETH_MAX_BUFFER_ELEMENTS(card)){
|
|
|
|
+ PRINT_ERR("qeth_do_send_packet: invalid size of "
|
|
|
|
+ "IP packet. Discarded.");
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ return elements_needed;
|
|
|
|
+}
|
|
|
|
+
|
|
static inline int
|
|
static inline int
|
|
qeth_send_packet(struct qeth_card *card, struct sk_buff *skb)
|
|
qeth_send_packet(struct qeth_card *card, struct sk_buff *skb)
|
|
{
|
|
{
|
|
@@ -4266,19 +4311,25 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb)
|
|
if (skb_shinfo(skb)->tso_size)
|
|
if (skb_shinfo(skb)->tso_size)
|
|
large_send = card->options.large_send;
|
|
large_send = card->options.large_send;
|
|
|
|
|
|
- if ((rc = qeth_prepare_skb(card, &skb, &hdr, ipv))){
|
|
|
|
- QETH_DBF_TEXT_(trace, 4, "pskbe%d", rc);
|
|
|
|
- return rc;
|
|
|
|
- }
|
|
|
|
/*are we able to do TSO ? If so ,prepare and send it from here */
|
|
/*are we able to do TSO ? If so ,prepare and send it from here */
|
|
if ((large_send == QETH_LARGE_SEND_TSO) &&
|
|
if ((large_send == QETH_LARGE_SEND_TSO) &&
|
|
(cast_type == RTN_UNSPEC)) {
|
|
(cast_type == RTN_UNSPEC)) {
|
|
- rc = qeth_tso_send_packet(card, skb, queue,
|
|
|
|
- ipv, cast_type);
|
|
|
|
- goto do_statistics;
|
|
|
|
|
|
+ rc = qeth_tso_prepare_packet(card, skb, ipv, cast_type);
|
|
|
|
+ if (rc) {
|
|
|
|
+ card->stats.tx_dropped++;
|
|
|
|
+ card->stats.tx_errors++;
|
|
|
|
+ dev_kfree_skb_any(skb);
|
|
|
|
+ return NETDEV_TX_OK;
|
|
|
|
+ }
|
|
|
|
+ elements_needed++;
|
|
|
|
+ } else {
|
|
|
|
+ if ((rc = qeth_prepare_skb(card, &skb, &hdr, ipv))) {
|
|
|
|
+ QETH_DBF_TEXT_(trace, 4, "pskbe%d", rc);
|
|
|
|
+ return rc;
|
|
|
|
+ }
|
|
|
|
+ qeth_fill_header(card, hdr, skb, ipv, cast_type);
|
|
}
|
|
}
|
|
|
|
|
|
- qeth_fill_header(card, hdr, skb, ipv, cast_type);
|
|
|
|
if (large_send == QETH_LARGE_SEND_EDDP) {
|
|
if (large_send == QETH_LARGE_SEND_EDDP) {
|
|
ctx = qeth_eddp_create_context(card, skb, hdr);
|
|
ctx = qeth_eddp_create_context(card, skb, hdr);
|
|
if (ctx == NULL) {
|
|
if (ctx == NULL) {
|
|
@@ -4286,7 +4337,7 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
- elements_needed = qeth_get_elements_no(card,(void*) hdr, skb);
|
|
|
|
|
|
+ elements_needed += qeth_get_elements_no(card,(void*) hdr, skb);
|
|
if (!elements_needed)
|
|
if (!elements_needed)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
@@ -4297,12 +4348,12 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb)
|
|
else
|
|
else
|
|
rc = qeth_do_send_packet_fast(card, queue, skb, hdr,
|
|
rc = qeth_do_send_packet_fast(card, queue, skb, hdr,
|
|
elements_needed, ctx);
|
|
elements_needed, ctx);
|
|
-do_statistics:
|
|
|
|
if (!rc){
|
|
if (!rc){
|
|
card->stats.tx_packets++;
|
|
card->stats.tx_packets++;
|
|
card->stats.tx_bytes += skb->len;
|
|
card->stats.tx_bytes += skb->len;
|
|
#ifdef CONFIG_QETH_PERF_STATS
|
|
#ifdef CONFIG_QETH_PERF_STATS
|
|
- if (skb_shinfo(skb)->tso_size) {
|
|
|
|
|
|
+ if (skb_shinfo(skb)->tso_size &&
|
|
|
|
+ !(large_send == QETH_LARGE_SEND_NO)) {
|
|
card->perf_stats.large_send_bytes += skb->len;
|
|
card->perf_stats.large_send_bytes += skb->len;
|
|
card->perf_stats.large_send_cnt++;
|
|
card->perf_stats.large_send_cnt++;
|
|
}
|
|
}
|
|
@@ -7199,7 +7250,7 @@ qeth_wait_for_threads(struct qeth_card *card, unsigned long threads)
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
static int
|
|
-qeth_stop_card(struct qeth_card *card)
|
|
|
|
|
|
+qeth_stop_card(struct qeth_card *card, int recovery_mode)
|
|
{
|
|
{
|
|
int rc = 0;
|
|
int rc = 0;
|
|
|
|
|
|
@@ -7212,9 +7263,13 @@ qeth_stop_card(struct qeth_card *card)
|
|
if (card->read.state == CH_STATE_UP &&
|
|
if (card->read.state == CH_STATE_UP &&
|
|
card->write.state == CH_STATE_UP &&
|
|
card->write.state == CH_STATE_UP &&
|
|
(card->state == CARD_STATE_UP)) {
|
|
(card->state == CARD_STATE_UP)) {
|
|
- rtnl_lock();
|
|
|
|
- dev_close(card->dev);
|
|
|
|
- rtnl_unlock();
|
|
|
|
|
|
+ if(recovery_mode) {
|
|
|
|
+ qeth_stop(card->dev);
|
|
|
|
+ } else {
|
|
|
|
+ rtnl_lock();
|
|
|
|
+ dev_close(card->dev);
|
|
|
|
+ rtnl_unlock();
|
|
|
|
+ }
|
|
if (!card->use_hard_stop) {
|
|
if (!card->use_hard_stop) {
|
|
__u8 *mac = &card->dev->dev_addr[0];
|
|
__u8 *mac = &card->dev->dev_addr[0];
|
|
rc = qeth_layer2_send_delmac(card, mac);
|
|
rc = qeth_layer2_send_delmac(card, mac);
|
|
@@ -7386,13 +7441,17 @@ qeth_register_netdev(struct qeth_card *card)
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
static void
|
|
-qeth_start_again(struct qeth_card *card)
|
|
|
|
|
|
+qeth_start_again(struct qeth_card *card, int recovery_mode)
|
|
{
|
|
{
|
|
QETH_DBF_TEXT(setup ,2, "startag");
|
|
QETH_DBF_TEXT(setup ,2, "startag");
|
|
|
|
|
|
- rtnl_lock();
|
|
|
|
- dev_open(card->dev);
|
|
|
|
- rtnl_unlock();
|
|
|
|
|
|
+ if(recovery_mode) {
|
|
|
|
+ qeth_open(card->dev);
|
|
|
|
+ } else {
|
|
|
|
+ rtnl_lock();
|
|
|
|
+ dev_open(card->dev);
|
|
|
|
+ rtnl_unlock();
|
|
|
|
+ }
|
|
/* this also sets saved unicast addresses */
|
|
/* this also sets saved unicast addresses */
|
|
qeth_set_multicast_list(card->dev);
|
|
qeth_set_multicast_list(card->dev);
|
|
}
|
|
}
|
|
@@ -7449,7 +7508,7 @@ static void qeth_make_parameters_consistent(struct qeth_card *card)
|
|
|
|
|
|
|
|
|
|
static int
|
|
static int
|
|
-qeth_set_online(struct ccwgroup_device *gdev)
|
|
|
|
|
|
+__qeth_set_online(struct ccwgroup_device *gdev, int recovery_mode)
|
|
{
|
|
{
|
|
struct qeth_card *card = gdev->dev.driver_data;
|
|
struct qeth_card *card = gdev->dev.driver_data;
|
|
int rc = 0;
|
|
int rc = 0;
|
|
@@ -7509,12 +7568,12 @@ qeth_set_online(struct ccwgroup_device *gdev)
|
|
* we can also use this state for recovery purposes*/
|
|
* we can also use this state for recovery purposes*/
|
|
qeth_set_allowed_threads(card, 0xffffffff, 0);
|
|
qeth_set_allowed_threads(card, 0xffffffff, 0);
|
|
if (recover_flag == CARD_STATE_RECOVER)
|
|
if (recover_flag == CARD_STATE_RECOVER)
|
|
- qeth_start_again(card);
|
|
|
|
|
|
+ qeth_start_again(card, recovery_mode);
|
|
qeth_notify_processes();
|
|
qeth_notify_processes();
|
|
return 0;
|
|
return 0;
|
|
out_remove:
|
|
out_remove:
|
|
card->use_hard_stop = 1;
|
|
card->use_hard_stop = 1;
|
|
- qeth_stop_card(card);
|
|
|
|
|
|
+ qeth_stop_card(card, 0);
|
|
ccw_device_set_offline(CARD_DDEV(card));
|
|
ccw_device_set_offline(CARD_DDEV(card));
|
|
ccw_device_set_offline(CARD_WDEV(card));
|
|
ccw_device_set_offline(CARD_WDEV(card));
|
|
ccw_device_set_offline(CARD_RDEV(card));
|
|
ccw_device_set_offline(CARD_RDEV(card));
|
|
@@ -7525,6 +7584,12 @@ out_remove:
|
|
return -ENODEV;
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int
|
|
|
|
+qeth_set_online(struct ccwgroup_device *gdev)
|
|
|
|
+{
|
|
|
|
+ return __qeth_set_online(gdev, 0);
|
|
|
|
+}
|
|
|
|
+
|
|
static struct ccw_device_id qeth_ids[] = {
|
|
static struct ccw_device_id qeth_ids[] = {
|
|
{CCW_DEVICE(0x1731, 0x01), driver_info:QETH_CARD_TYPE_OSAE},
|
|
{CCW_DEVICE(0x1731, 0x01), driver_info:QETH_CARD_TYPE_OSAE},
|
|
{CCW_DEVICE(0x1731, 0x05), driver_info:QETH_CARD_TYPE_IQD},
|
|
{CCW_DEVICE(0x1731, 0x05), driver_info:QETH_CARD_TYPE_IQD},
|