case NETDEV_UNREGISTER:
/* after calling list_del_rcu(&wdev->list) */
- wl_dealloc_netinfo(wl, ndev);
+ wl_remove_netinfo(wl, ndev);
break;
case NETDEV_GOING_DOWN:
/* At NETDEV_DOWN state, wdev_cleanup_work work will be called.
return err;
}
+static void wl_dealloc_netinfo(struct work_struct *work)
+{
+ struct net_info *_net_info, *next;
+ struct wl_priv *wl = container_of(work, struct wl_priv, dealloc_work);
+
+ list_for_each_entry_safe(_net_info, next, &wl->dealloc_list, list) {
+ list_del(&_net_info->list);
+ if (_net_info->wdev) {
+ flush_work(&_net_info->wdev->cleanup_work);
+ WARN_ON(work_pending(&_net_info->wdev->cleanup_work));
+ kfree(_net_info->wdev);
+ }
+ kfree(_net_info);
+ }
+
+}
+
static s32 wl_init_priv(struct wl_priv *wl)
{
struct wiphy *wiphy = wl_to_wiphy(wl);
wl_init_prof(wl, ndev);
wl_link_down(wl);
DNGL_FUNC(dhd_cfg80211_init, (wl));
+ INIT_LIST_HEAD(&wl->dealloc_list);
+ INIT_WORK(&wl->dealloc_work, wl_dealloc_netinfo);
return err;
}
#include <proto/ethernet.h>
#include <wlioctl.h>
#include <linux/wireless.h>
+#include <linux/workqueue.h>
#include <net/cfg80211.h>
#include <linux/rfkill.h>
EVENT_HANDLER evt_handler[WLC_E_LAST];
struct list_head eq_list; /* used for event queue */
struct list_head net_list; /* used for struct net_info */
+ struct list_head dealloc_list; /* used for struct net_info which can be freed */
spinlock_t eq_lock; /* for event queue synchronization */
spinlock_t cfgdrv_lock; /* to protect scan status (and others if needed) */
struct completion act_frm_scan;
bool scan_suppressed;
struct timer_list scan_supp_timer;
struct work_struct wlan_work;
+ struct work_struct dealloc_work;
struct mutex event_sync; /* maily for up/down synchronization */
bool pm_enable_work_on;
struct delayed_work pm_enable_work;
return err;
}
static inline void
-wl_dealloc_netinfo(struct wl_priv *wl, struct net_device *ndev)
+wl_remove_netinfo(struct wl_priv *wl, struct net_device *ndev)
{
struct net_info *_net_info, *next;
+ bool dealloc_needed = false;
list_for_each_entry_safe(_net_info, next, &wl->net_list, list) {
if (ndev && (_net_info->ndev == ndev)) {
list_del(&_net_info->list);
wl->iface_cnt--;
if (_net_info->wdev) {
- kfree(_net_info->wdev);
ndev->ieee80211_ptr = NULL;
}
- kfree(_net_info);
+ INIT_LIST_HEAD(&_net_info->list);
+ list_add(&_net_info->list, &wl->dealloc_list);
+ dealloc_needed = true;
}
}
-
+ if (dealloc_needed)
+ schedule_work(&wl->dealloc_work);
}
+
static inline void
wl_delete_all_netinfo(struct wl_priv *wl)
{