]> git.hungrycats.org Git - linux/commitdiff
[PATCH] s390: lcs multicast deadlock
authorMartin Schwidefsky <schwidefsky@de.ibm.com>
Wed, 8 Sep 2004 00:54:21 +0000 (17:54 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Wed, 8 Sep 2004 00:54:21 +0000 (17:54 -0700)
From: Thomas Spatzier <tspat@de.ibm.com>

lcs network driver changes:
 - Fix deadlock on card->ipm_lock.

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/lcs.c

index 31eaf711742532499e197b1036811d795a944389..f78636132c9980849dde41eb5b0e605bf9706e8b 100644 (file)
@@ -11,7 +11,7 @@
  *                       Frank Pavlic (pavlic@de.ibm.com) and
  *                       Martin Schwidefsky <schwidefsky@de.ibm.com>
  *
- *    $Revision: 1.89 $         $Date: 2004/08/24 10:49:27 $
+ *    $Revision: 1.92 $         $Date: 2004/09/03 08:06:11 $
  *
  * 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.89 $"
+#define VERSION_LCS_C  "$Revision: 1.92 $"
 
 static char version[] __initdata = "LCS driver ("VERSION_LCS_C "/" VERSION_LCS_H ")";
 static char debug_buffer[255];
@@ -454,17 +454,21 @@ static inline void
 lcs_clear_multicast_list(struct lcs_card *card)
 {
 #ifdef CONFIG_IP_MULTICAST
-       struct list_head *l, *n;
        struct lcs_ipm_list *ipm;
        unsigned long flags;
+
        /* Free multicast list. */
        LCS_DBF_TEXT(3, setup, "clmclist");
        spin_lock_irqsave(&card->ipm_lock, flags);
-       list_for_each_safe(l, n, &card->ipm_list) {
-               ipm = list_entry(l, struct lcs_ipm_list, list);
+       while (!list_empty(&card->ipm_list)){
+               ipm = list_entry(card->ipm_list.next,
+                                struct lcs_ipm_list, list);
                list_del(&ipm->list);
-               if (ipm->ipm_state != LCS_IPM_STATE_SET_REQUIRED)
+               if (ipm->ipm_state != LCS_IPM_STATE_SET_REQUIRED){
+                       spin_unlock_irqrestore(&card->ipm_lock, flags);
                        lcs_send_delipm(card, ipm);
+                       spin_lock_irqsave(&card->ipm_lock, flags);
+               }
                kfree(ipm);
        }
        spin_unlock_irqrestore(&card->ipm_lock, flags);
@@ -1111,31 +1115,53 @@ lcs_check_multicast_support(struct lcs_card *card)
 static void
 lcs_fix_multicast_list(struct lcs_card *card)
 {
-       struct list_head *l, *n;
-       struct lcs_ipm_list *ipm;
+       struct list_head failed_list;
+       struct lcs_ipm_list *ipm, *tmp;
        unsigned long flags;
+       int rc;
 
        LCS_DBF_TEXT(4,trace, "fixipm");
+       INIT_LIST_HEAD(&failed_list);
        spin_lock_irqsave(&card->ipm_lock, flags);
-       list_for_each_safe(l, n, &card->ipm_list) {
-               ipm = list_entry(l, struct lcs_ipm_list, list);
+list_modified:
+       list_for_each_entry_safe(ipm, tmp, &card->ipm_list, list){
                switch (ipm->ipm_state) {
                case LCS_IPM_STATE_SET_REQUIRED:
-                       if (lcs_send_setipm(card, ipm))
+                       /* del from ipm_list so noone else can tamper with
+                        * this entry */
+                       list_del_init(&ipm->list);
+                       spin_unlock_irqrestore(&card->ipm_lock, flags);
+                       rc = lcs_send_setipm(card, ipm);
+                       spin_lock_irqsave(&card->ipm_lock, flags);
+                       if (rc) {
                                PRINT_INFO("Adding multicast address failed."
                                           "Table possibly full!\n");
-                       else
+                               /* store ipm in failed list -> will be added
+                                * to ipm_list again, so a retry will be done
+                                * during the next call of this function */
+                               list_add_tail(&ipm->list, &failed_list);
+                       } else {
                                ipm->ipm_state = LCS_IPM_STATE_ON_CARD;
-                       break;
+                               /* re-insert into ipm_list */
+                               list_add_tail(&ipm->list, &card->ipm_list);
+                       }
+                       goto list_modified;
                case LCS_IPM_STATE_DEL_REQUIRED:
-                       lcs_send_delipm(card, ipm);
                        list_del(&ipm->list);
+                       spin_unlock_irqrestore(&card->ipm_lock, flags);
+                       lcs_send_delipm(card, ipm);
+                       spin_lock_irqsave(&card->ipm_lock, flags);
                        kfree(ipm);
-                       break;
+                       goto list_modified;
                case LCS_IPM_STATE_ON_CARD:
                        break;
                }
        }
+       /* re-insert all entries from the failed_list into ipm_list */
+       list_for_each_entry(ipm, &failed_list, list) {
+               list_del_init(&ipm->list);
+               list_add_tail(&ipm->list, &card->ipm_list);
+       }
        spin_unlock_irqrestore(&card->ipm_lock, flags);
        if (card->state == DEV_STATE_UP)
                netif_wake_queue(card->dev);