diff options
Diffstat (limited to 'drivers/s390/net/qeth_main.c')
-rw-r--r-- | drivers/s390/net/qeth_main.c | 145 |
1 files changed, 105 insertions, 40 deletions
diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c index e18bcf9fb588..208127a5033a 100644 --- a/drivers/s390/net/qeth_main.c +++ b/drivers/s390/net/qeth_main.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * | 2 | * |
3 | * linux/drivers/s390/net/qeth_main.c ($Revision: 1.210 $) | 3 | * linux/drivers/s390/net/qeth_main.c ($Revision: 1.214 $) |
4 | * | 4 | * |
5 | * Linux on zSeries OSA Express and HiperSockets support | 5 | * Linux on zSeries OSA Express and HiperSockets support |
6 | * | 6 | * |
@@ -12,7 +12,7 @@ | |||
12 | * Frank Pavlic (pavlic@de.ibm.com) and | 12 | * Frank Pavlic (pavlic@de.ibm.com) and |
13 | * Thomas Spatzier <tspat@de.ibm.com> | 13 | * Thomas Spatzier <tspat@de.ibm.com> |
14 | * | 14 | * |
15 | * $Revision: 1.210 $ $Date: 2005/04/18 17:27:39 $ | 15 | * $Revision: 1.214 $ $Date: 2005/05/04 20:19:18 $ |
16 | * | 16 | * |
17 | * This program is free software; you can redistribute it and/or modify | 17 | * This program is free software; you can redistribute it and/or modify |
18 | * it under the terms of the GNU General Public License as published by | 18 | * it under the terms of the GNU General Public License as published by |
@@ -80,7 +80,7 @@ qeth_eyecatcher(void) | |||
80 | #include "qeth_eddp.h" | 80 | #include "qeth_eddp.h" |
81 | #include "qeth_tso.h" | 81 | #include "qeth_tso.h" |
82 | 82 | ||
83 | #define VERSION_QETH_C "$Revision: 1.210 $" | 83 | #define VERSION_QETH_C "$Revision: 1.214 $" |
84 | static const char *version = "qeth S/390 OSA-Express driver"; | 84 | static const char *version = "qeth S/390 OSA-Express driver"; |
85 | 85 | ||
86 | /** | 86 | /** |
@@ -158,6 +158,9 @@ qeth_irq_tasklet(unsigned long); | |||
158 | static int | 158 | static int |
159 | qeth_set_online(struct ccwgroup_device *); | 159 | qeth_set_online(struct ccwgroup_device *); |
160 | 160 | ||
161 | static int | ||
162 | __qeth_set_online(struct ccwgroup_device *gdev, int recovery_mode); | ||
163 | |||
161 | static struct qeth_ipaddr * | 164 | static struct qeth_ipaddr * |
162 | qeth_get_addr_buffer(enum qeth_prot_versions); | 165 | qeth_get_addr_buffer(enum qeth_prot_versions); |
163 | 166 | ||
@@ -510,10 +513,10 @@ qeth_irq_tasklet(unsigned long data) | |||
510 | wake_up(&card->wait_q); | 513 | wake_up(&card->wait_q); |
511 | } | 514 | } |
512 | 515 | ||
513 | static int qeth_stop_card(struct qeth_card *); | 516 | static int qeth_stop_card(struct qeth_card *, int); |
514 | 517 | ||
515 | static int | 518 | static int |
516 | qeth_set_offline(struct ccwgroup_device *cgdev) | 519 | __qeth_set_offline(struct ccwgroup_device *cgdev, int recovery_mode) |
517 | { | 520 | { |
518 | struct qeth_card *card = (struct qeth_card *) cgdev->dev.driver_data; | 521 | struct qeth_card *card = (struct qeth_card *) cgdev->dev.driver_data; |
519 | int rc = 0; | 522 | int rc = 0; |
@@ -523,7 +526,7 @@ qeth_set_offline(struct ccwgroup_device *cgdev) | |||
523 | QETH_DBF_HEX(setup, 3, &card, sizeof(void *)); | 526 | QETH_DBF_HEX(setup, 3, &card, sizeof(void *)); |
524 | 527 | ||
525 | recover_flag = card->state; | 528 | recover_flag = card->state; |
526 | if (qeth_stop_card(card) == -ERESTARTSYS){ | 529 | if (qeth_stop_card(card, recovery_mode) == -ERESTARTSYS){ |
527 | PRINT_WARN("Stopping card %s interrupted by user!\n", | 530 | PRINT_WARN("Stopping card %s interrupted by user!\n", |
528 | CARD_BUS_ID(card)); | 531 | CARD_BUS_ID(card)); |
529 | return -ERESTARTSYS; | 532 | return -ERESTARTSYS; |
@@ -540,6 +543,12 @@ qeth_set_offline(struct ccwgroup_device *cgdev) | |||
540 | } | 543 | } |
541 | 544 | ||
542 | static int | 545 | static int |
546 | qeth_set_offline(struct ccwgroup_device *cgdev) | ||
547 | { | ||
548 | return __qeth_set_offline(cgdev, 0); | ||
549 | } | ||
550 | |||
551 | static int | ||
543 | qeth_wait_for_threads(struct qeth_card *card, unsigned long threads); | 552 | qeth_wait_for_threads(struct qeth_card *card, unsigned long threads); |
544 | 553 | ||
545 | 554 | ||
@@ -953,8 +962,8 @@ qeth_recover(void *ptr) | |||
953 | PRINT_WARN("Recovery of device %s started ...\n", | 962 | PRINT_WARN("Recovery of device %s started ...\n", |
954 | CARD_BUS_ID(card)); | 963 | CARD_BUS_ID(card)); |
955 | card->use_hard_stop = 1; | 964 | card->use_hard_stop = 1; |
956 | qeth_set_offline(card->gdev); | 965 | __qeth_set_offline(card->gdev,1); |
957 | rc = qeth_set_online(card->gdev); | 966 | rc = __qeth_set_online(card->gdev,1); |
958 | if (!rc) | 967 | if (!rc) |
959 | PRINT_INFO("Device %s successfully recovered!\n", | 968 | PRINT_INFO("Device %s successfully recovered!\n", |
960 | CARD_BUS_ID(card)); | 969 | CARD_BUS_ID(card)); |
@@ -3786,16 +3795,12 @@ static inline int | |||
3786 | qeth_prepare_skb(struct qeth_card *card, struct sk_buff **skb, | 3795 | qeth_prepare_skb(struct qeth_card *card, struct sk_buff **skb, |
3787 | struct qeth_hdr **hdr, int ipv) | 3796 | struct qeth_hdr **hdr, int ipv) |
3788 | { | 3797 | { |
3789 | int rc = 0; | ||
3790 | #ifdef CONFIG_QETH_VLAN | 3798 | #ifdef CONFIG_QETH_VLAN |
3791 | u16 *tag; | 3799 | u16 *tag; |
3792 | #endif | 3800 | #endif |
3793 | 3801 | ||
3794 | QETH_DBF_TEXT(trace, 6, "prepskb"); | 3802 | QETH_DBF_TEXT(trace, 6, "prepskb"); |
3795 | 3803 | ||
3796 | rc = qeth_realloc_headroom(card, skb, sizeof(struct qeth_hdr)); | ||
3797 | if (rc) | ||
3798 | return rc; | ||
3799 | #ifdef CONFIG_QETH_VLAN | 3804 | #ifdef CONFIG_QETH_VLAN |
3800 | if (card->vlangrp && vlan_tx_tag_present(*skb) && | 3805 | if (card->vlangrp && vlan_tx_tag_present(*skb) && |
3801 | ((ipv == 6) || card->options.layer2) ) { | 3806 | ((ipv == 6) || card->options.layer2) ) { |
@@ -3977,25 +3982,28 @@ qeth_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, | |||
3977 | 3982 | ||
3978 | static inline void | 3983 | static inline void |
3979 | __qeth_fill_buffer(struct sk_buff *skb, struct qdio_buffer *buffer, | 3984 | __qeth_fill_buffer(struct sk_buff *skb, struct qdio_buffer *buffer, |
3980 | int *next_element_to_fill) | 3985 | int is_tso, int *next_element_to_fill) |
3981 | { | 3986 | { |
3982 | int length = skb->len; | 3987 | int length = skb->len; |
3983 | int length_here; | 3988 | int length_here; |
3984 | int element; | 3989 | int element; |
3985 | char *data; | 3990 | char *data; |
3986 | int first_lap = 1; | 3991 | int first_lap ; |
3987 | 3992 | ||
3988 | element = *next_element_to_fill; | 3993 | element = *next_element_to_fill; |
3989 | data = skb->data; | 3994 | data = skb->data; |
3995 | first_lap = (is_tso == 0 ? 1 : 0); | ||
3996 | |||
3990 | while (length > 0) { | 3997 | while (length > 0) { |
3991 | /* length_here is the remaining amount of data in this page */ | 3998 | /* length_here is the remaining amount of data in this page */ |
3992 | length_here = PAGE_SIZE - ((unsigned long) data % PAGE_SIZE); | 3999 | length_here = PAGE_SIZE - ((unsigned long) data % PAGE_SIZE); |
3993 | if (length < length_here) | 4000 | if (length < length_here) |
3994 | length_here = length; | 4001 | length_here = length; |
4002 | |||
3995 | buffer->element[element].addr = data; | 4003 | buffer->element[element].addr = data; |
3996 | buffer->element[element].length = length_here; | 4004 | buffer->element[element].length = length_here; |
3997 | length -= length_here; | 4005 | length -= length_here; |
3998 | if (!length){ | 4006 | if (!length) { |
3999 | if (first_lap) | 4007 | if (first_lap) |
4000 | buffer->element[element].flags = 0; | 4008 | buffer->element[element].flags = 0; |
4001 | else | 4009 | else |
@@ -4022,17 +4030,35 @@ qeth_fill_buffer(struct qeth_qdio_out_q *queue, | |||
4022 | struct sk_buff *skb) | 4030 | struct sk_buff *skb) |
4023 | { | 4031 | { |
4024 | struct qdio_buffer *buffer; | 4032 | struct qdio_buffer *buffer; |
4025 | int flush_cnt = 0; | 4033 | struct qeth_hdr_tso *hdr; |
4034 | int flush_cnt = 0, hdr_len, large_send = 0; | ||
4026 | 4035 | ||
4027 | QETH_DBF_TEXT(trace, 6, "qdfillbf"); | 4036 | QETH_DBF_TEXT(trace, 6, "qdfillbf"); |
4037 | |||
4028 | buffer = buf->buffer; | 4038 | buffer = buf->buffer; |
4029 | atomic_inc(&skb->users); | 4039 | atomic_inc(&skb->users); |
4030 | skb_queue_tail(&buf->skb_list, skb); | 4040 | skb_queue_tail(&buf->skb_list, skb); |
4041 | |||
4042 | hdr = (struct qeth_hdr_tso *) skb->data; | ||
4043 | /*check first on TSO ....*/ | ||
4044 | if (hdr->hdr.hdr.l3.id == QETH_HEADER_TYPE_TSO) { | ||
4045 | int element = buf->next_element_to_fill; | ||
4046 | |||
4047 | hdr_len = sizeof(struct qeth_hdr_tso) + hdr->ext.dg_hdr_len; | ||
4048 | /*fill first buffer entry only with header information */ | ||
4049 | buffer->element[element].addr = skb->data; | ||
4050 | buffer->element[element].length = hdr_len; | ||
4051 | buffer->element[element].flags = SBAL_FLAGS_FIRST_FRAG; | ||
4052 | buf->next_element_to_fill++; | ||
4053 | skb->data += hdr_len; | ||
4054 | skb->len -= hdr_len; | ||
4055 | large_send = 1; | ||
4056 | } | ||
4031 | if (skb_shinfo(skb)->nr_frags == 0) | 4057 | if (skb_shinfo(skb)->nr_frags == 0) |
4032 | __qeth_fill_buffer(skb, buffer, | 4058 | __qeth_fill_buffer(skb, buffer, large_send, |
4033 | (int *)&buf->next_element_to_fill); | 4059 | (int *)&buf->next_element_to_fill); |
4034 | else | 4060 | else |
4035 | __qeth_fill_buffer_frag(skb, buffer, 0, | 4061 | __qeth_fill_buffer_frag(skb, buffer, large_send, |
4036 | (int *)&buf->next_element_to_fill); | 4062 | (int *)&buf->next_element_to_fill); |
4037 | 4063 | ||
4038 | if (!queue->do_pack) { | 4064 | if (!queue->do_pack) { |
@@ -4225,6 +4251,25 @@ out: | |||
4225 | } | 4251 | } |
4226 | 4252 | ||
4227 | static inline int | 4253 | static inline int |
4254 | qeth_get_elements_no(struct qeth_card *card, void *hdr, struct sk_buff *skb) | ||
4255 | { | ||
4256 | int elements_needed = 0; | ||
4257 | |||
4258 | if (skb_shinfo(skb)->nr_frags > 0) { | ||
4259 | elements_needed = (skb_shinfo(skb)->nr_frags + 1); | ||
4260 | } | ||
4261 | if (elements_needed == 0 ) | ||
4262 | elements_needed = 1 + (((((unsigned long) hdr) % PAGE_SIZE) | ||
4263 | + skb->len) >> PAGE_SHIFT); | ||
4264 | if (elements_needed > QETH_MAX_BUFFER_ELEMENTS(card)){ | ||
4265 | PRINT_ERR("qeth_do_send_packet: invalid size of " | ||
4266 | "IP packet. Discarded."); | ||
4267 | return 0; | ||
4268 | } | ||
4269 | return elements_needed; | ||
4270 | } | ||
4271 | |||
4272 | static inline int | ||
4228 | qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) | 4273 | qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) |
4229 | { | 4274 | { |
4230 | int ipv = 0; | 4275 | int ipv = 0; |
@@ -4266,19 +4311,25 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) | |||
4266 | if (skb_shinfo(skb)->tso_size) | 4311 | if (skb_shinfo(skb)->tso_size) |
4267 | large_send = card->options.large_send; | 4312 | large_send = card->options.large_send; |
4268 | 4313 | ||
4269 | if ((rc = qeth_prepare_skb(card, &skb, &hdr, ipv))){ | ||
4270 | QETH_DBF_TEXT_(trace, 4, "pskbe%d", rc); | ||
4271 | return rc; | ||
4272 | } | ||
4273 | /*are we able to do TSO ? If so ,prepare and send it from here */ | 4314 | /*are we able to do TSO ? If so ,prepare and send it from here */ |
4274 | if ((large_send == QETH_LARGE_SEND_TSO) && | 4315 | if ((large_send == QETH_LARGE_SEND_TSO) && |
4275 | (cast_type == RTN_UNSPEC)) { | 4316 | (cast_type == RTN_UNSPEC)) { |
4276 | rc = qeth_tso_send_packet(card, skb, queue, | 4317 | rc = qeth_tso_prepare_packet(card, skb, ipv, cast_type); |
4277 | ipv, cast_type); | 4318 | if (rc) { |
4278 | goto do_statistics; | 4319 | card->stats.tx_dropped++; |
4320 | card->stats.tx_errors++; | ||
4321 | dev_kfree_skb_any(skb); | ||
4322 | return NETDEV_TX_OK; | ||
4323 | } | ||
4324 | elements_needed++; | ||
4325 | } else { | ||
4326 | if ((rc = qeth_prepare_skb(card, &skb, &hdr, ipv))) { | ||
4327 | QETH_DBF_TEXT_(trace, 4, "pskbe%d", rc); | ||
4328 | return rc; | ||
4329 | } | ||
4330 | qeth_fill_header(card, hdr, skb, ipv, cast_type); | ||
4279 | } | 4331 | } |
4280 | 4332 | ||
4281 | qeth_fill_header(card, hdr, skb, ipv, cast_type); | ||
4282 | if (large_send == QETH_LARGE_SEND_EDDP) { | 4333 | if (large_send == QETH_LARGE_SEND_EDDP) { |
4283 | ctx = qeth_eddp_create_context(card, skb, hdr); | 4334 | ctx = qeth_eddp_create_context(card, skb, hdr); |
4284 | if (ctx == NULL) { | 4335 | if (ctx == NULL) { |
@@ -4286,7 +4337,7 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) | |||
4286 | return -EINVAL; | 4337 | return -EINVAL; |
4287 | } | 4338 | } |
4288 | } else { | 4339 | } else { |
4289 | elements_needed = qeth_get_elements_no(card,(void*) hdr, skb); | 4340 | elements_needed += qeth_get_elements_no(card,(void*) hdr, skb); |
4290 | if (!elements_needed) | 4341 | if (!elements_needed) |
4291 | return -EINVAL; | 4342 | return -EINVAL; |
4292 | } | 4343 | } |
@@ -4297,12 +4348,12 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) | |||
4297 | else | 4348 | else |
4298 | rc = qeth_do_send_packet_fast(card, queue, skb, hdr, | 4349 | rc = qeth_do_send_packet_fast(card, queue, skb, hdr, |
4299 | elements_needed, ctx); | 4350 | elements_needed, ctx); |
4300 | do_statistics: | ||
4301 | if (!rc){ | 4351 | if (!rc){ |
4302 | card->stats.tx_packets++; | 4352 | card->stats.tx_packets++; |
4303 | card->stats.tx_bytes += skb->len; | 4353 | card->stats.tx_bytes += skb->len; |
4304 | #ifdef CONFIG_QETH_PERF_STATS | 4354 | #ifdef CONFIG_QETH_PERF_STATS |
4305 | if (skb_shinfo(skb)->tso_size) { | 4355 | if (skb_shinfo(skb)->tso_size && |
4356 | !(large_send == QETH_LARGE_SEND_NO)) { | ||
4306 | card->perf_stats.large_send_bytes += skb->len; | 4357 | card->perf_stats.large_send_bytes += skb->len; |
4307 | card->perf_stats.large_send_cnt++; | 4358 | card->perf_stats.large_send_cnt++; |
4308 | } | 4359 | } |
@@ -7199,7 +7250,7 @@ qeth_wait_for_threads(struct qeth_card *card, unsigned long threads) | |||
7199 | } | 7250 | } |
7200 | 7251 | ||
7201 | static int | 7252 | static int |
7202 | qeth_stop_card(struct qeth_card *card) | 7253 | qeth_stop_card(struct qeth_card *card, int recovery_mode) |
7203 | { | 7254 | { |
7204 | int rc = 0; | 7255 | int rc = 0; |
7205 | 7256 | ||
@@ -7212,9 +7263,13 @@ qeth_stop_card(struct qeth_card *card) | |||
7212 | if (card->read.state == CH_STATE_UP && | 7263 | if (card->read.state == CH_STATE_UP && |
7213 | card->write.state == CH_STATE_UP && | 7264 | card->write.state == CH_STATE_UP && |
7214 | (card->state == CARD_STATE_UP)) { | 7265 | (card->state == CARD_STATE_UP)) { |
7215 | rtnl_lock(); | 7266 | if(recovery_mode) { |
7216 | dev_close(card->dev); | 7267 | qeth_stop(card->dev); |
7217 | rtnl_unlock(); | 7268 | } else { |
7269 | rtnl_lock(); | ||
7270 | dev_close(card->dev); | ||
7271 | rtnl_unlock(); | ||
7272 | } | ||
7218 | if (!card->use_hard_stop) { | 7273 | if (!card->use_hard_stop) { |
7219 | __u8 *mac = &card->dev->dev_addr[0]; | 7274 | __u8 *mac = &card->dev->dev_addr[0]; |
7220 | rc = qeth_layer2_send_delmac(card, mac); | 7275 | rc = qeth_layer2_send_delmac(card, mac); |
@@ -7386,13 +7441,17 @@ qeth_register_netdev(struct qeth_card *card) | |||
7386 | } | 7441 | } |
7387 | 7442 | ||
7388 | static void | 7443 | static void |
7389 | qeth_start_again(struct qeth_card *card) | 7444 | qeth_start_again(struct qeth_card *card, int recovery_mode) |
7390 | { | 7445 | { |
7391 | QETH_DBF_TEXT(setup ,2, "startag"); | 7446 | QETH_DBF_TEXT(setup ,2, "startag"); |
7392 | 7447 | ||
7393 | rtnl_lock(); | 7448 | if(recovery_mode) { |
7394 | dev_open(card->dev); | 7449 | qeth_open(card->dev); |
7395 | rtnl_unlock(); | 7450 | } else { |
7451 | rtnl_lock(); | ||
7452 | dev_open(card->dev); | ||
7453 | rtnl_unlock(); | ||
7454 | } | ||
7396 | /* this also sets saved unicast addresses */ | 7455 | /* this also sets saved unicast addresses */ |
7397 | qeth_set_multicast_list(card->dev); | 7456 | qeth_set_multicast_list(card->dev); |
7398 | } | 7457 | } |
@@ -7449,7 +7508,7 @@ static void qeth_make_parameters_consistent(struct qeth_card *card) | |||
7449 | 7508 | ||
7450 | 7509 | ||
7451 | static int | 7510 | static int |
7452 | qeth_set_online(struct ccwgroup_device *gdev) | 7511 | __qeth_set_online(struct ccwgroup_device *gdev, int recovery_mode) |
7453 | { | 7512 | { |
7454 | struct qeth_card *card = gdev->dev.driver_data; | 7513 | struct qeth_card *card = gdev->dev.driver_data; |
7455 | int rc = 0; | 7514 | int rc = 0; |
@@ -7509,12 +7568,12 @@ qeth_set_online(struct ccwgroup_device *gdev) | |||
7509 | * we can also use this state for recovery purposes*/ | 7568 | * we can also use this state for recovery purposes*/ |
7510 | qeth_set_allowed_threads(card, 0xffffffff, 0); | 7569 | qeth_set_allowed_threads(card, 0xffffffff, 0); |
7511 | if (recover_flag == CARD_STATE_RECOVER) | 7570 | if (recover_flag == CARD_STATE_RECOVER) |
7512 | qeth_start_again(card); | 7571 | qeth_start_again(card, recovery_mode); |
7513 | qeth_notify_processes(); | 7572 | qeth_notify_processes(); |
7514 | return 0; | 7573 | return 0; |
7515 | out_remove: | 7574 | out_remove: |
7516 | card->use_hard_stop = 1; | 7575 | card->use_hard_stop = 1; |
7517 | qeth_stop_card(card); | 7576 | qeth_stop_card(card, 0); |
7518 | ccw_device_set_offline(CARD_DDEV(card)); | 7577 | ccw_device_set_offline(CARD_DDEV(card)); |
7519 | ccw_device_set_offline(CARD_WDEV(card)); | 7578 | ccw_device_set_offline(CARD_WDEV(card)); |
7520 | ccw_device_set_offline(CARD_RDEV(card)); | 7579 | ccw_device_set_offline(CARD_RDEV(card)); |
@@ -7525,6 +7584,12 @@ out_remove: | |||
7525 | return -ENODEV; | 7584 | return -ENODEV; |
7526 | } | 7585 | } |
7527 | 7586 | ||
7587 | static int | ||
7588 | qeth_set_online(struct ccwgroup_device *gdev) | ||
7589 | { | ||
7590 | return __qeth_set_online(gdev, 0); | ||
7591 | } | ||
7592 | |||
7528 | static struct ccw_device_id qeth_ids[] = { | 7593 | static struct ccw_device_id qeth_ids[] = { |
7529 | {CCW_DEVICE(0x1731, 0x01), driver_info:QETH_CARD_TYPE_OSAE}, | 7594 | {CCW_DEVICE(0x1731, 0x01), driver_info:QETH_CARD_TYPE_OSAE}, |
7530 | {CCW_DEVICE(0x1731, 0x05), driver_info:QETH_CARD_TYPE_IQD}, | 7595 | {CCW_DEVICE(0x1731, 0x05), driver_info:QETH_CARD_TYPE_IQD}, |