]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] s390: network driver
authorMartin Schwidefsky <schwidefsky@de.ibm.com>
Thu, 2 Dec 2004 23:45:14 +0000 (15:45 -0800)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Thu, 2 Dec 2004 23:45:14 +0000 (15:45 -0800)
From: Thomas Spatzier <tspat@de.ibm.com>
From: Peter Tiedemann <ptiedem@de.ibm.com>

network driver changes:
 - qeth: return -EINVAL if an skb is too large.
 - qeth: don't call netif_stop_queue after cable pull. Drop the
   packets instead.
 - qeth: fix race between SET_IP and SET_MC kernel thread by removing
   SET_MC thread and let the SET_IP thread do multicast requests as well.
 - qeth: make it compile without CONFIG_VLAN.
 - ctc: avoid compiler warnings.
 - lcs: write package sequence number to skb->cb.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
drivers/s390/net/ctcmain.c
drivers/s390/net/lcs.c
drivers/s390/net/lcs.h
drivers/s390/net/qeth.h
drivers/s390/net/qeth_main.c

index ba0348a5fe06a7013f2c72c2560cb0045d1f86e6..fff33b1b8ad95524c68f11fa997b2898d96a87c2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: ctcmain.c,v 1.63 2004/07/28 12:27:54 ptiedem Exp $
+ * $Id: ctcmain.c,v 1.65 2004/10/27 09:12:48 mschwide Exp $
  *
  * CTC / ESCON network driver
  *
@@ -36,7 +36,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.63 $
+ * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.65 $
  *
  */
 \f
@@ -320,7 +320,7 @@ static void
 print_banner(void)
 {
        static int printed = 0;
-       char vbuf[] = "$Revision: 1.63 $";
+       char vbuf[] = "$Revision: 1.65 $";
        char *version = vbuf;
 
        if (printed)
@@ -1224,7 +1224,9 @@ ch_action_setmode(fsm_instance * fi, int event, void *arg)
        fsm_deltimer(&ch->timer);
        fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
        fsm_newstate(fi, CH_STATE_SETUPWAIT);
-       if (event == CH_EVENT_TIMER)
+       saveflags = 0;  /* avoids compiler warning with
+                          spin_unlock_irqrestore */
+       if (event == CH_EVENT_TIMER)    // only for timer not yet locked
                spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
        rc = ccw_device_start(ch->cdev, &ch->ccw[6], (unsigned long) ch, 0xff, 0);
        if (event == CH_EVENT_TIMER)
@@ -1335,7 +1337,9 @@ ch_action_haltio(fsm_instance * fi, int event, void *arg)
        DBF_TEXT(trace, 3, __FUNCTION__);
        fsm_deltimer(&ch->timer);
        fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
-       if (event == CH_EVENT_STOP)
+       saveflags = 0;  /* avoids comp warning with
+                          spin_unlock_irqrestore */
+       if (event == CH_EVENT_STOP)     // only for STOP not yet locked
                spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
        oldstate = fsm_getstate(fi);
        fsm_newstate(fi, CH_STATE_TERM);
@@ -1508,7 +1512,9 @@ ch_action_restart(fsm_instance * fi, int event, void *arg)
        fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
        oldstate = fsm_getstate(fi);
        fsm_newstate(fi, CH_STATE_STARTWAIT);
-       if (event == CH_EVENT_TIMER)
+       saveflags = 0;  /* avoids compiler warning with
+                          spin_unlock_irqrestore */
+       if (event == CH_EVENT_TIMER)    // only for timer not yet locked
                spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
        rc = ccw_device_halt(ch->cdev, (unsigned long) ch);
        if (event == CH_EVENT_TIMER)
@@ -1674,7 +1680,9 @@ ch_action_txretry(fsm_instance * fi, int event, void *arg)
                                return;
                        }
                        fsm_addtimer(&ch->timer, 1000, CH_EVENT_TIMER, ch);
-                       if (event == CH_EVENT_TIMER)
+                       saveflags = 0;  /* avoids compiler warning with
+                                          spin_unlock_irqrestore */
+                       if (event == CH_EVENT_TIMER) // only for TIMER not yet locked
                                spin_lock_irqsave(get_ccwdev_lock(ch->cdev),
                                                  saveflags);
                        rc = ccw_device_start(ch->cdev, &ch->ccw[3],
index 5bedf5692dc4acef97bbbb1e78ac81c11704f717..0f76e945b9841dadd2ac93b4c627fca18d0e9c84 100644 (file)
@@ -11,7 +11,7 @@
  *                       Frank Pavlic (pavlic@de.ibm.com) and
  *                       Martin Schwidefsky <schwidefsky@de.ibm.com>
  *
- *    $Revision: 1.94 $         $Date: 2004/10/19 09:30:54 $
+ *    $Revision: 1.96 $         $Date: 2004/11/11 13:42:33 $
  *
  * 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
@@ -59,7 +59,7 @@
 /**
  * initialization string for output
  */
-#define VERSION_LCS_C  "$Revision: 1.94 $"
+#define VERSION_LCS_C  "$Revision: 1.96 $"
 
 static char version[] __initdata = "LCS driver ("VERSION_LCS_C "/" VERSION_LCS_H ")";
 static char debug_buffer[255];
@@ -191,6 +191,7 @@ lcs_alloc_card(void)
                return NULL;
        memset(card, 0, sizeof(struct lcs_card));
        card->lan_type = LCS_FRAME_TYPE_AUTO;
+       card->pkt_seq = 0;
        card->lancmd_timeout = LCS_LANCMD_TIMEOUT_DEFAULT;
        /* Allocate io buffers for the read channel. */
        rc = lcs_alloc_channel(&card->read);
@@ -1874,6 +1875,7 @@ lcs_get_skb(struct lcs_card *card, char *skb_data, unsigned int skb_len)
        skb->protocol = card->lan_type_trans(skb, card->dev);
        card->stats.rx_bytes += skb_len;
        card->stats.rx_packets++;
+       *((__u32 *)skb->cb) = ++card->pkt_seq;
        netif_rx(skb);
 }
 
index bd3fee02725394010d75ac92c318c53132b8b414..a7f348ef1b0891c556a8d8da58ff2cfba650f176 100644 (file)
@@ -6,7 +6,7 @@
 #include <linux/workqueue.h>
 #include <asm/ccwdev.h>
 
-#define VERSION_LCS_H "$Revision: 1.18 $"
+#define VERSION_LCS_H "$Revision: 1.19 $"
 
 #define LCS_DBF_TEXT(level, name, text) \
        do { \
@@ -309,6 +309,7 @@ struct lcs_card {
        __u16 ip_assists_supported;
        __u16 ip_assists_enabled;
        __s8 lan_type;
+       __u32 pkt_seq;
        __u16 sequence_no;
        __s16 portno;
        /* Some info copied from probeinfo */
index f076b629b188c901e8b3452f9e3a48f7f99dc1c6..96b54af09103dea886776e050fe028b5aaa593bf 100644 (file)
@@ -24,7 +24,7 @@
 
 #include "qeth_mpc.h"
 
-#define VERSION_QETH_H                 "$Revision: 1.116 $"
+#define VERSION_QETH_H                 "$Revision: 1.123 $"
 
 #ifdef CONFIG_QETH_IPV6
 #define QETH_VERSION_IPV6      ":IPv6"
@@ -557,6 +557,7 @@ enum qeth_ip_types {
        QETH_IP_TYPE_NORMAL,
        QETH_IP_TYPE_VIPA,
        QETH_IP_TYPE_RXIP,
+       QETH_IP_TYPE_DEL_ALL_MC,
 };
 
 enum qeth_cmd_buffer_state {
@@ -713,8 +714,7 @@ struct qeth_card_options {
  */
 enum qeth_threads {
        QETH_SET_IP_THREAD  = 1,
-       QETH_SET_MC_THREAD  = 2,
-       QETH_RECOVER_THREAD = 4,
+       QETH_RECOVER_THREAD = 2,
 };
 
 struct qeth_card {
@@ -748,7 +748,7 @@ struct qeth_card {
        volatile unsigned long thread_running_mask;
        spinlock_t ip_lock;
        struct list_head ip_list;
-       struct list_head ip_tbd_list;
+       struct list_head *ip_tbd_list;
        struct qeth_ipato ipato;
        struct list_head cmd_waiter_list;
        /* QDIO buffer handling */
index f7d8229fb64ad4f017784a36330d342928461d77..00711fc8522285c1aa52248ea0d31ed064d9f85b 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *
- * linux/drivers/s390/net/qeth_main.c ($Revision: 1.155 $)
+ * linux/drivers/s390/net/qeth_main.c ($Revision: 1.168 $)
  *
  * Linux on zSeries OSA Express and HiperSockets support
  *
@@ -12,7 +12,7 @@
  *                       Frank Pavlic (pavlic@de.ibm.com) and
  *                       Thomas Spatzier <tspat@de.ibm.com>
  *
- *    $Revision: 1.155 $        $Date: 2004/10/21 13:27:46 $
+ *    $Revision: 1.168 $        $Date: 2004/11/08 15:55:12 $
  *
  * 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
@@ -78,7 +78,7 @@ qeth_eyecatcher(void)
 #include "qeth_mpc.h"
 #include "qeth_fs.h"
 
-#define VERSION_QETH_C "$Revision: 1.155 $"
+#define VERSION_QETH_C "$Revision: 1.168 $"
 static const char *version = "qeth S/390 OSA-Express driver";
 
 /**
@@ -159,6 +159,9 @@ qeth_set_online(struct ccwgroup_device *);
 static struct qeth_ipaddr *
 qeth_get_addr_buffer(enum qeth_prot_versions);
 
+static void
+qeth_set_multicast_list(struct net_device *);
+
 static void
 qeth_notify_processes(void)
 {
@@ -249,6 +252,7 @@ qeth_free_card(struct qeth_card *card)
                free_netdev(card->dev);
        qeth_clear_ip_list(card, 0, 0);
        qeth_clear_ipato_list(card);
+       kfree(card->ip_tbd_list);
        qeth_free_qdio_buffers(card);
        kfree(card);
 }
@@ -660,7 +664,10 @@ __qeth_insert_ip_todo(struct qeth_card *card, struct qeth_ipaddr *addr, int add)
        struct qeth_ipaddr *tmp, *t;
        int found = 0;
 
-       list_for_each_entry_safe(tmp, t, &card->ip_tbd_list, entry) {
+       list_for_each_entry_safe(tmp, t, card->ip_tbd_list, entry) {
+               if ((addr->type == QETH_IP_TYPE_DEL_ALL_MC) &&
+                   (tmp->type == QETH_IP_TYPE_DEL_ALL_MC))
+                       return 0;
                if ((tmp->proto        == QETH_PROT_IPV4)     &&
                    (addr->proto       == QETH_PROT_IPV4)     &&
                    (tmp->type         == addr->type)         &&
@@ -692,14 +699,18 @@ __qeth_insert_ip_todo(struct qeth_card *card, struct qeth_ipaddr *addr, int add)
                }
                return 0;
        } else {
-               if (addr->users == 0)
-                       addr->users += add? 1:-1;
-               if (add && (addr->type == QETH_IP_TYPE_NORMAL) &&
-                   qeth_is_addr_covered_by_ipato(card, addr)){
-                       QETH_DBF_TEXT(trace, 2, "tkovaddr");
-                       addr->set_flags |= QETH_IPA_SETIP_TAKEOVER_FLAG;
+               if (addr->type == QETH_IP_TYPE_DEL_ALL_MC)
+                       list_add(&addr->entry, card->ip_tbd_list);
+               else {
+                       if (addr->users == 0)
+                               addr->users += add? 1:-1;
+                       if (add && (addr->type == QETH_IP_TYPE_NORMAL) &&
+                           qeth_is_addr_covered_by_ipato(card, addr)){
+                               QETH_DBF_TEXT(trace, 2, "tkovaddr");
+                               addr->set_flags |= QETH_IPA_SETIP_TAKEOVER_FLAG;
+                       }
+                       list_add_tail(&addr->entry, card->ip_tbd_list);
                }
-               list_add_tail(&addr->entry, &card->ip_tbd_list);
                return 1;
        }
 }
@@ -717,8 +728,8 @@ qeth_delete_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
        if (addr->proto == QETH_PROT_IPV4)
                QETH_DBF_HEX(trace,4,&addr->u.a4.addr,4);
        else {
-               QETH_DBF_HEX(trace,4,&addr->u.a6.addr,4);
-               QETH_DBF_HEX(trace,4,((char *)&addr->u.a6.addr)+4,4);
+               QETH_DBF_HEX(trace,4,&addr->u.a6.addr,8);
+               QETH_DBF_HEX(trace,4,((char *)&addr->u.a6.addr)+8,8);
        }
        spin_lock_irqsave(&card->ip_lock, flags);
        rc = __qeth_insert_ip_todo(card, addr, 0);
@@ -736,8 +747,8 @@ qeth_add_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
        if (addr->proto == QETH_PROT_IPV4)
                QETH_DBF_HEX(trace,4,&addr->u.a4.addr,4);
        else {
-               QETH_DBF_HEX(trace,4,&addr->u.a6.addr,4);
-               QETH_DBF_HEX(trace,4,((char *)&addr->u.a6.addr)+4,4);
+               QETH_DBF_HEX(trace,4,&addr->u.a6.addr,8);
+               QETH_DBF_HEX(trace,4,((char *)&addr->u.a6.addr)+8,8);
        }
        spin_lock_irqsave(&card->ip_lock, flags);
        rc = __qeth_insert_ip_todo(card, addr, 1);
@@ -745,19 +756,21 @@ qeth_add_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
        return rc;
 }
 
-static void
-qeth_reinsert_todos(struct qeth_card *card, struct list_head *todos)
+static inline void
+__qeth_delete_all_mc(struct qeth_card *card, unsigned long *flags)
 {
-       struct qeth_ipaddr *todo, *tmp;
+       struct qeth_ipaddr *addr, *tmp;
+       int rc;
 
-       list_for_each_entry_safe(todo, tmp, todos, entry){
-               list_del_init(&todo->entry);
-               if (todo->users < 0) {
-                       if (!qeth_delete_ip(card, todo))
-                               kfree(todo);
-               } else {
-                       if (!qeth_add_ip(card, todo))
-                               kfree(todo);
+       list_for_each_entry_safe(addr, tmp, &card->ip_list, entry) {
+               if (addr->is_multicast) {
+                       spin_unlock_irqrestore(&card->ip_lock, *flags);
+                       rc = qeth_deregister_addr_entry(card, addr);
+                       spin_lock_irqsave(&card->ip_lock, *flags);
+                       if (!rc) {
+                               list_del(&addr->entry);
+                               kfree(addr);
+                       }
                }
        }
 }
@@ -765,7 +778,7 @@ qeth_reinsert_todos(struct qeth_card *card, struct list_head *todos)
 static void
 qeth_set_ip_addr_list(struct qeth_card *card)
 {
-       struct list_head failed_todos;
+       struct list_head *tbd_list;
        struct qeth_ipaddr *todo, *addr;
        unsigned long flags;
        int rc;
@@ -773,13 +786,25 @@ qeth_set_ip_addr_list(struct qeth_card *card)
        QETH_DBF_TEXT(trace, 2, "sdiplist");
        QETH_DBF_HEX(trace, 2, &card, sizeof(void *));
 
-       INIT_LIST_HEAD(&failed_todos);
-
        spin_lock_irqsave(&card->ip_lock, flags);
-       while (!list_empty(&card->ip_tbd_list)) {
-               todo = list_entry(card->ip_tbd_list.next,
-                                 struct qeth_ipaddr, entry);
-               list_del_init(&todo->entry);
+       tbd_list = card->ip_tbd_list;
+       card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_ATOMIC);
+       if (!card->ip_tbd_list) {
+               QETH_DBF_TEXT(trace, 0, "silnomem");
+               card->ip_tbd_list = tbd_list;
+               spin_unlock_irqrestore(&card->ip_lock, flags);
+               return;
+       } else
+               INIT_LIST_HEAD(card->ip_tbd_list);
+
+       while (!list_empty(tbd_list)){
+               todo = list_entry(tbd_list->next, struct qeth_ipaddr, entry);
+               list_del(&todo->entry);
+               if (todo->type == QETH_IP_TYPE_DEL_ALL_MC){
+                       __qeth_delete_all_mc(card, &flags);
+                       kfree(todo);
+                       continue;
+               }
                rc = __qeth_ref_ip_on_card(card, todo, &addr);
                if (rc == 0) {
                        /* nothing to be done; only adjusted refcount */
@@ -792,24 +817,22 @@ qeth_set_ip_addr_list(struct qeth_card *card)
                        if (!rc)
                                list_add_tail(&todo->entry, &card->ip_list);
                        else
-                               list_add_tail(&todo->entry, &failed_todos);
+                               kfree(todo);
                } else if (rc == -1) {
                        /* on-card entry to be removed */
                        list_del_init(&addr->entry);
                        spin_unlock_irqrestore(&card->ip_lock, flags);
                        rc = qeth_deregister_addr_entry(card, addr);
                        spin_lock_irqsave(&card->ip_lock, flags);
-                       if (!rc) {
+                       if (!rc)
                                kfree(addr);
-                               kfree(todo);
-                       } else {
+                       else
                                list_add_tail(&addr->entry, &card->ip_list);
-                               list_add_tail(&todo->entry, &failed_todos);
-                       }
+                       kfree(todo);
                }
        }
        spin_unlock_irqrestore(&card->ip_lock, flags);
-       qeth_reinsert_todos(card, &failed_todos);
+       kfree(tbd_list);
 }
 
 static void qeth_delete_mc_addresses(struct qeth_card *);
@@ -887,28 +910,7 @@ qeth_do_run_thread(struct qeth_card *card, unsigned long thread)
 }
 
 static int
-qeth_register_mc_addresses(void *ptr)
-{
-       struct qeth_card *card;
-
-       card = (struct qeth_card *) ptr;
-       daemonize("qeth_reg_mcaddrs");
-       QETH_DBF_TEXT(trace,4,"regmcth1");
-       if (!qeth_do_run_thread(card, QETH_SET_MC_THREAD))
-               return 0;
-       QETH_DBF_TEXT(trace,4,"regmcth2");
-       qeth_delete_mc_addresses(card);
-       qeth_add_multicast_ipv4(card);
-#ifdef CONFIG_QETH_IPV6
-       qeth_add_multicast_ipv6(card);
-#endif
-       qeth_set_ip_addr_list(card);
-       qeth_clear_thread_running_bit(card, QETH_SET_MC_THREAD);
-       return 0;
-}
-
-static int
-qeth_register_ip_address(void *ptr)
+qeth_register_ip_addresses(void *ptr)
 {
        struct qeth_card *card;
 
@@ -988,9 +990,7 @@ qeth_start_kernel_thread(struct qeth_card *card)
                return;
 
        if (qeth_do_start_thread(card, QETH_SET_IP_THREAD))
-               kernel_thread(qeth_register_ip_address, (void *) card, SIGCHLD);
-       if (qeth_do_start_thread(card, QETH_SET_MC_THREAD))
-               kernel_thread(qeth_register_mc_addresses, (void *)card,SIGCHLD);
+               kernel_thread(qeth_register_ip_addresses, (void *)card,SIGCHLD);
        if (qeth_do_start_thread(card, QETH_RECOVER_THREAD))
                kernel_thread(qeth_recover, (void *) card, SIGCHLD);
 }
@@ -1041,7 +1041,12 @@ qeth_setup_card(struct qeth_card *card)
        INIT_WORK(&card->kernel_thread_starter,
                  (void *)qeth_start_kernel_thread,card);
        INIT_LIST_HEAD(&card->ip_list);
-       INIT_LIST_HEAD(&card->ip_tbd_list);
+       card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_KERNEL);
+       if (!card->ip_tbd_list) {
+               QETH_DBF_TEXT(setup, 0, "iptbdnom");
+               return -ENOMEM;
+       }
+       INIT_LIST_HEAD(card->ip_tbd_list);
        INIT_LIST_HEAD(&card->cmd_waiter_list);
        init_waitqueue_head(&card->wait_q);
        /* intial options */
@@ -1575,9 +1580,8 @@ qeth_reset_ip_addresses(struct qeth_card *card)
        QETH_DBF_TEXT(trace, 2, "rstipadd");
 
        qeth_clear_ip_list(card, 0, 1);
-       if ( (qeth_set_thread_start_bit(card, QETH_SET_IP_THREAD) == 0) ||
-            (qeth_set_thread_start_bit(card, QETH_SET_MC_THREAD) == 0) )
-               schedule_work(&card->kernel_thread_starter);
+       /* this function will also schedule the SET_IP_THREAD */
+       qeth_set_multicast_list(card->dev);
 }
 
 static struct qeth_ipa_cmd *
@@ -1600,10 +1604,7 @@ qeth_check_ipa_data(struct qeth_card *card, struct qeth_cmd_buffer *iob)
                                           card->info.if_name,
                                           card->info.chpid);
                                card->lan_online = 0;
-                               if (netif_carrier_ok(card->dev)) {
-                                       netif_carrier_off(card->dev);
-                                       netif_stop_queue(card->dev);
-                               }
+                               netif_carrier_off(card->dev);
                                return NULL;
                        case IPA_CMD_STARTLAN:
                                PRINT_INFO("Link reestablished on %s "
@@ -1612,10 +1613,7 @@ qeth_check_ipa_data(struct qeth_card *card, struct qeth_cmd_buffer *iob)
                                           card->info.if_name,
                                           card->info.chpid);
                                card->lan_online = 1;
-                               if (!netif_carrier_ok(card->dev)) {
-                                       netif_carrier_on(card->dev);
-                                       netif_wake_queue(card->dev);
-                               }
+                               netif_carrier_on(card->dev);
                                qeth_reset_ip_addresses(card);
                                return NULL;
                        case IPA_CMD_REGISTER_LOCAL_ADDR:
@@ -2807,7 +2805,8 @@ qeth_qdio_output_handler(struct ccw_device * ccwdev, unsigned int status,
        }
        atomic_sub(count, &queue->used_buffers);
        /* check if we need to do something on this outbound queue */
-       qeth_check_outbound_queue(queue);
+       if (card->info.type != QETH_CARD_TYPE_IQD)
+               qeth_check_outbound_queue(queue);
 
        netif_wake_queue(card->dev);
 #ifdef CONFIG_QETH_PERF_STATS
@@ -3381,13 +3380,16 @@ qeth_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        if (skb==NULL) {
                card->stats.tx_dropped++;
                card->stats.tx_errors++;
-               return -EIO;
+               /* return OK; otherwise ksoftirqd goes to 100% */
+               return NETDEV_TX_OK;
        }
-       if ((card->state != CARD_STATE_UP) || !netif_carrier_ok(dev)) {
+       if ((card->state != CARD_STATE_UP) || !card->lan_online) {
                card->stats.tx_dropped++;
                card->stats.tx_errors++;
                card->stats.tx_carrier_errors++;
-               return -EIO;
+               dev_kfree_skb_any(skb);
+               /* return OK; otherwise ksoftirqd goes to 100% */
+               return NETDEV_TX_OK;
        }
 #ifdef CONFIG_QETH_PERF_STATS
        card->perf_stats.outbound_cnt++;
@@ -3398,8 +3400,18 @@ qeth_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
         * got our own synchronization on queues we can keep the stack's
         * queue running.
         */
-       if ((rc = qeth_send_packet(card, skb)))
-               netif_stop_queue(dev);
+       if ((rc = qeth_send_packet(card, skb))){
+               if (rc == -EBUSY) {
+                       netif_stop_queue(dev);
+                       rc = NETDEV_TX_BUSY;
+               } else {
+                       card->stats.tx_errors++;
+                       card->stats.tx_dropped++;
+                       dev_kfree_skb_any(skb);
+                       /* set to OK; otherwise ksoftirqd goes to 100% */
+                       rc = NETDEV_TX_OK;
+               }
+       }
 
 #ifdef CONFIG_QETH_PERF_STATS
        card->perf_stats.outbound_time += qeth_get_micros() -
@@ -3503,7 +3515,6 @@ qeth_open(struct net_device *dev)
        if (!card->lan_online){
                if (netif_carrier_ok(dev))
                        netif_carrier_off(dev);
-               netif_stop_queue(dev);
        }
        return 0;
 }
@@ -4988,12 +4999,28 @@ qeth_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
        spin_unlock_irqrestore(&card->vlanlock, flags);
        if (card->options.layer2)
                qeth_layer2_send_setdelvlan(card, vid, IPA_CMD_DELVLAN);
-       if ( (qeth_set_thread_start_bit(card, QETH_SET_IP_THREAD) == 0) ||
-            (qeth_set_thread_start_bit(card, QETH_SET_MC_THREAD) == 0) )
-               schedule_work(&card->kernel_thread_starter);
+       qeth_set_multicast_list(card->dev);
 }
 #endif
 
+/**
+ * set multicast address on card
+ */
+static void
+qeth_set_multicast_list(struct net_device *dev)
+{
+       struct qeth_card *card = (struct qeth_card *) dev->priv;
+
+       QETH_DBF_TEXT(trace,3,"setmulti");
+       qeth_delete_mc_addresses(card);
+       qeth_add_multicast_ipv4(card);
+#ifdef CONFIG_QETH_IPV6
+       qeth_add_multicast_ipv6(card);
+#endif
+       if (qeth_set_thread_start_bit(card, QETH_SET_IP_THREAD) == 0)
+               schedule_work(&card->kernel_thread_starter);
+}
+
 static int
 qeth_neigh_setup(struct net_device *dev, struct neigh_parms *np)
 {
@@ -5049,24 +5076,19 @@ qeth_get_addr_buffer(enum qeth_prot_versions prot)
 static void
 qeth_delete_mc_addresses(struct qeth_card *card)
 {
-       struct qeth_ipaddr *ipm, *iptodo;
+       struct qeth_ipaddr *iptodo;
        unsigned long flags;
 
        QETH_DBF_TEXT(trace,4,"delmc");
-       spin_lock_irqsave(&card->ip_lock, flags);
-       list_for_each_entry(ipm, &card->ip_list, entry){
-               if (!ipm->is_multicast)
-                       continue;
-               iptodo = qeth_get_addr_buffer(ipm->proto);
-               if (!iptodo) {
-                       QETH_DBF_TEXT(trace, 2, "dmcnomem");
-                       continue;
-               }
-               memcpy(iptodo, ipm, sizeof(struct qeth_ipaddr));
-               iptodo->users = iptodo->users * -1;
-               if (!__qeth_insert_ip_todo(card, iptodo, 0))
-                       kfree(iptodo);
+       iptodo = qeth_get_addr_buffer(QETH_PROT_IPV4);
+       if (!iptodo) {
+               QETH_DBF_TEXT(trace, 2, "dmcnomem");
+               return;
        }
+       iptodo->type = QETH_IP_TYPE_DEL_ALL_MC;
+       spin_lock_irqsave(&card->ip_lock, flags);
+       if (!__qeth_insert_ip_todo(card, iptodo, 0))
+               kfree(iptodo);
        spin_unlock_irqrestore(&card->ip_lock, flags);
 }
 
@@ -5277,20 +5299,6 @@ qeth_layer2_set_mac_address(struct net_device *dev, void *p)
 
        return rc;
 }
-/**
- * set multicast address on card
- */
-static void
-qeth_set_multicast_list(struct net_device *dev)
-{
-       struct qeth_card *card;
-
-       QETH_DBF_TEXT(trace,3,"setmulti");
-       card = (struct qeth_card *) dev->priv;
-
-       if (qeth_set_thread_start_bit(card, QETH_SET_MC_THREAD) == 0)
-               schedule_work(&card->kernel_thread_starter);
-}
 
 static void
 qeth_fill_ipacmd_header(struct qeth_card *card, struct qeth_ipa_cmd *cmd,
@@ -6635,7 +6643,7 @@ qeth_clear_ip_list(struct qeth_card *card, int clean, int recover)
        QETH_DBF_TEXT(trace,4,"clearip");
        spin_lock_irqsave(&card->ip_lock, flags);
        /* clear todo list */
-       list_for_each_entry_safe(addr, tmp, &card->ip_tbd_list, entry){
+       list_for_each_entry_safe(addr, tmp, card->ip_tbd_list, entry){
                list_del(&addr->entry);
                kfree(addr);
        }
@@ -6653,7 +6661,7 @@ qeth_clear_ip_list(struct qeth_card *card, int clean, int recover)
                        kfree(addr);
                        continue;
                }
-               list_add_tail(&addr->entry, &card->ip_tbd_list);
+               list_add_tail(&addr->entry, card->ip_tbd_list);
        }
        spin_unlock_irqrestore(&card->ip_lock, flags);
 }
@@ -6893,8 +6901,7 @@ qeth_start_again(struct qeth_card *card)
        rtnl_lock();
        dev_open(card->dev);
        rtnl_unlock();
-       if (qeth_set_thread_start_bit(card, QETH_SET_MC_THREAD) == 0)
-               schedule_work(&card->kernel_thread_starter);
+       qeth_set_multicast_list(card->dev);
 }
 
 
@@ -7008,9 +7015,7 @@ qeth_set_online(struct ccwgroup_device *gdev)
 /*maybe it was set offline without ifconfig down
  * we can also use this state for recovery purposes*/
        if (card->options.layer2)
-               qeth_set_allowed_threads(card,
-                                        QETH_RECOVER_THREAD |
-                                        QETH_SET_MC_THREAD,0);
+               qeth_set_allowed_threads(card, QETH_RECOVER_THREAD, 0);
        else
                qeth_set_allowed_threads(card, 0xffffffff, 0);
        if (recover_flag == CARD_STATE_RECOVER)
@@ -7339,7 +7344,7 @@ qeth_add_vipa(struct qeth_card *card, enum qeth_prot_versions proto,
                return -ENOMEM;
        spin_lock_irqsave(&card->ip_lock, flags);
        if (__qeth_address_exists_in_list(&card->ip_list, ipaddr, 0) ||
-           __qeth_address_exists_in_list(&card->ip_tbd_list, ipaddr, 0))
+           __qeth_address_exists_in_list(card->ip_tbd_list, ipaddr, 0))
                rc = -EEXIST;
        spin_unlock_irqrestore(&card->ip_lock, flags);
        if (rc){
@@ -7412,7 +7417,7 @@ qeth_add_rxip(struct qeth_card *card, enum qeth_prot_versions proto,
                return -ENOMEM;
        spin_lock_irqsave(&card->ip_lock, flags);
        if (__qeth_address_exists_in_list(&card->ip_list, ipaddr, 0) ||
-           __qeth_address_exists_in_list(&card->ip_tbd_list, ipaddr, 0))
+           __qeth_address_exists_in_list(card->ip_tbd_list, ipaddr, 0))
                rc = -EEXIST;
        spin_unlock_irqrestore(&card->ip_lock, flags);
        if (rc){