From: Francois Romieu There is no guarantee that the event which gets passed is associated to a via-velocity device, thus preventing to dereference dev->priv as if it always was a struct velocity_info *. The via-velocity devices are kept in a module private list for comparison. Signed-off-by: Francois Romieu Signed-off-by: Andrew Morton --- 25-akpm/drivers/net/via-velocity.c | 72 +++++++++++++++++++++++++++---------- 25-akpm/drivers/net/via-velocity.h | 3 - 2 files changed, 55 insertions(+), 20 deletions(-) diff -puN drivers/net/via-velocity.c~via-velocity-more-inetaddr_notifier-fix drivers/net/via-velocity.c --- 25/drivers/net/via-velocity.c~via-velocity-more-inetaddr_notifier-fix Thu Aug 5 16:53:30 2004 +++ 25-akpm/drivers/net/via-velocity.c Thu Aug 5 16:53:30 2004 @@ -262,6 +262,7 @@ static u32 check_connection_type(struct static int velocity_set_media_mode(struct velocity_info *vptr, u32 mii_status); #ifdef CONFIG_PM + static int velocity_suspend(struct pci_dev *pdev, u32 state); static int velocity_resume(struct pci_dev *pdev); @@ -270,9 +271,26 @@ static int velocity_netdev_event(struct static struct notifier_block velocity_inetaddr_notifier = { .notifier_call = velocity_netdev_event, }; -static int velocity_notifier_registered; -#endif /* CONFIG_PM */ +static spinlock_t velocity_dev_list_lock = SPIN_LOCK_UNLOCKED; +static LIST_HEAD(velocity_dev_list); + +static void velocity_register_notifier(void) +{ + register_inetaddr_notifier(&velocity_inetaddr_notifier); +} + +static void velocity_unregister_notifier(void) +{ + unregister_inetaddr_notifier(&velocity_inetaddr_notifier); +} + +#else /* CONFIG_PM */ + +#define velocity_register_notifier() do {} while (0) +#define velocity_unregister_notifier() do {} while (0) + +#endif /* !CONFIG_PM */ /* * Internal board variants. At the moment we have only one @@ -327,6 +345,14 @@ static void __devexit velocity_remove1(s struct net_device *dev = pci_get_drvdata(pdev); struct velocity_info *vptr = dev->priv; +#ifdef CONFIG_PM + unsigned long flags; + + spin_lock_irqsave(&velocity_dev_list_lock, flags); + if (!list_empty(&velocity_dev_list)) + list_del(&vptr->list); + spin_unlock_irqrestore(&velocity_dev_list_lock, flags); +#endif unregister_netdev(dev); iounmap(vptr->mac_regs); pci_release_regions(pdev); @@ -782,13 +808,16 @@ static int __devinit velocity_found1(str /* and leave the chip powered down */ pci_set_power_state(pdev, 3); -out: #ifdef CONFIG_PM - if (ret == 0 && !velocity_notifier_registered) { - velocity_notifier_registered = 1; - register_inetaddr_notifier(&velocity_inetaddr_notifier); + { + unsigned long flags; + + spin_lock_irqsave(&velocity_dev_list_lock, flags); + list_add(&vptr->list, &velocity_dev_list); + spin_unlock_irqrestore(&velocity_dev_list_lock, flags); } #endif +out: return ret; err_iounmap: @@ -843,6 +872,8 @@ static void __devinit velocity_init_info spin_lock_init(&vptr->lock); spin_lock_init(&vptr->xmit_lock); + + INIT_LIST_HEAD(&vptr->list); } /** @@ -2211,8 +2242,11 @@ static struct pci_driver velocity_driver static int __init velocity_init_module(void) { int ret; - ret = pci_module_init(&velocity_driver); + velocity_register_notifier(); + ret = pci_module_init(&velocity_driver); + if (ret < 0) + velocity_unregister_notifier(); return ret; } @@ -2227,12 +2261,7 @@ static int __init velocity_init_module(v static void __exit velocity_cleanup_module(void) { -#ifdef CONFIG_PM - if (velocity_notifier_registered) { - unregister_inetaddr_notifier(&velocity_inetaddr_notifier); - velocity_notifier_registered = 0; - } -#endif + velocity_unregister_notifier(); pci_unregister_driver(&velocity_driver); } @@ -3252,13 +3281,20 @@ static int velocity_resume(struct pci_de static int velocity_netdev_event(struct notifier_block *nb, unsigned long notification, void *ptr) { struct in_ifaddr *ifa = (struct in_ifaddr *) ptr; - struct net_device *dev; - struct velocity_info *vptr; if (ifa) { - dev = ifa->ifa_dev->dev; - vptr = dev->priv; - velocity_get_ip(vptr); + struct net_device *dev = ifa->ifa_dev->dev; + struct velocity_info *vptr; + unsigned long flags; + + spin_lock_irqsave(&velocity_dev_list_lock, flags); + list_for_each_entry(vptr, &velocity_dev_list, list) { + if (vptr->dev == dev) { + velocity_get_ip(vptr); + break; + } + } + spin_unlock_irqrestore(&velocity_dev_list_lock, flags); } return NOTIFY_DONE; } diff -puN drivers/net/via-velocity.h~via-velocity-more-inetaddr_notifier-fix drivers/net/via-velocity.h --- 25/drivers/net/via-velocity.h~via-velocity-more-inetaddr_notifier-fix Thu Aug 5 16:53:30 2004 +++ 25-akpm/drivers/net/via-velocity.h Thu Aug 5 16:53:30 2004 @@ -1733,8 +1733,7 @@ struct velocity_opt { }; struct velocity_info { - struct velocity_info *next; - struct velocity_info *prev; + struct list_head list; struct pci_dev *pdev; struct net_device *dev; _