Commit 3c84f01e authored by Martin Schwidefsky's avatar Martin Schwidefsky Committed by Linus Torvalds

[PATCH] s390: lcs multicast deadlock

From: Thomas Spatzier <tspat@de.ibm.com>

lcs network driver changes:
 - Fix deadlock on card->ipm_lock.
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 35616b1d
......@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment