* @ibss_dfs_possible: (private) IBSS may change to a DFS channel
  * @event_list: (private) list for internal event processing
  * @event_lock: (private) lock for event list
+ * @owner_nlportid: (private) owner socket port ID
  */
 struct wireless_dev {
        struct wiphy *wiphy;
        unsigned long cac_start_time;
        unsigned int cac_time_ms;
 
+       u32 owner_nlportid;
+
 #ifdef CONFIG_CFG80211_WEXT
        /* wext data */
        struct {
 
  * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32.
  *     As specified in the &enum nl80211_tdls_peer_capability.
  *
+ * @NL80211_ATTR_IFACE_SOCKET_OWNER: flag attribute, if set during interface
+ *     creation then the new interface will be owned by the netlink socket
+ *     that created it and will be destroyed when the socket is closed
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
 
        NL80211_ATTR_TDLS_PEER_CAPABILITY,
 
+       NL80211_ATTR_IFACE_SOCKET_OWNER,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
 
        rtnl_unlock();
 }
 
+void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev)
+{
+       struct cfg80211_iface_destroy *item;
+
+       ASSERT_RTNL();
+
+       spin_lock_irq(&rdev->destroy_list_lock);
+       while ((item = list_first_entry_or_null(&rdev->destroy_list,
+                                               struct cfg80211_iface_destroy,
+                                               list))) {
+               struct wireless_dev *wdev, *tmp;
+               u32 nlportid = item->nlportid;
+
+               list_del(&item->list);
+               kfree(item);
+               spin_unlock_irq(&rdev->destroy_list_lock);
+
+               list_for_each_entry_safe(wdev, tmp, &rdev->wdev_list, list) {
+                       if (nlportid == wdev->owner_nlportid)
+                               rdev_del_virtual_intf(rdev, wdev);
+               }
+
+               spin_lock_irq(&rdev->destroy_list_lock);
+       }
+       spin_unlock_irq(&rdev->destroy_list_lock);
+}
+
+static void cfg80211_destroy_iface_wk(struct work_struct *work)
+{
+       struct cfg80211_registered_device *rdev;
+
+       rdev = container_of(work, struct cfg80211_registered_device,
+                           destroy_work);
+
+       rtnl_lock();
+       cfg80211_destroy_ifaces(rdev);
+       rtnl_unlock();
+}
+
 /* exported functions */
 
 struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
        rdev->wiphy.dev.class = &ieee80211_class;
        rdev->wiphy.dev.platform_data = rdev;
 
+       INIT_LIST_HEAD(&rdev->destroy_list);
+       spin_lock_init(&rdev->destroy_list_lock);
+       INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk);
+
 #ifdef CONFIG_CFG80211_DEFAULT_PS
        rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
 #endif
        cancel_work_sync(&rdev->conn_work);
        flush_work(&rdev->event_work);
        cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
+       flush_work(&rdev->destroy_work);
 
 #ifdef CONFIG_PM
        if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup)
 
 
        struct cfg80211_coalesce *coalesce;
 
+       spinlock_t destroy_list_lock;
+       struct list_head destroy_list;
+       struct work_struct destroy_work;
+
        /* must be last because of the way we do wiphy_priv(),
         * and it should at least be aligned to NETDEV_ALIGN */
        struct wiphy wiphy __aligned(NETDEV_ALIGN);
        u32 nlportid;
 };
 
+struct cfg80211_iface_destroy {
+       struct list_head list;
+       u32 nlportid;
+};
+
+void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev);
+
 /* free object */
 void cfg80211_dev_free(struct cfg80211_registered_device *rdev);
 
 
        [NL80211_ATTR_MAC_HINT] = { .len = ETH_ALEN },
        [NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 },
        [NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 },
+       [NL80211_ATTR_IFACE_SOCKET_OWNER] = { .type = NLA_FLAG },
 };
 
 /* policy for the key attributes */
        enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
        u32 flags;
 
+       /* to avoid failing a new interface creation due to pending removal */
+       cfg80211_destroy_ifaces(rdev);
+
        memset(¶ms, 0, sizeof(params));
 
        if (!info->attrs[NL80211_ATTR_IFNAME])
                return PTR_ERR(wdev);
        }
 
+       if (info->attrs[NL80211_ATTR_IFACE_SOCKET_OWNER])
+               wdev->owner_nlportid = info->snd_portid;
+
        switch (type) {
        case NL80211_IFTYPE_MESH_POINT:
                if (!info->attrs[NL80211_ATTR_MESH_ID])
        rcu_read_lock();
 
        list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
-               list_for_each_entry_rcu(wdev, &rdev->wdev_list, list)
+               bool schedule_destroy_work = false;
+
+               list_for_each_entry_rcu(wdev, &rdev->wdev_list, list) {
                        cfg80211_mlme_unregister_socket(wdev, notify->portid);
 
+                       if (wdev->owner_nlportid == notify->portid)
+                               schedule_destroy_work = true;
+               }
+
                spin_lock_bh(&rdev->beacon_registrations_lock);
                list_for_each_entry_safe(reg, tmp, &rdev->beacon_registrations,
                                         list) {
                        }
                }
                spin_unlock_bh(&rdev->beacon_registrations_lock);
+
+               if (schedule_destroy_work) {
+                       struct cfg80211_iface_destroy *destroy;
+
+                       destroy = kzalloc(sizeof(*destroy), GFP_ATOMIC);
+                       if (destroy) {
+                               destroy->nlportid = notify->portid;
+                               spin_lock(&rdev->destroy_list_lock);
+                               list_add(&destroy->list, &rdev->destroy_list);
+                               spin_unlock(&rdev->destroy_list_lock);
+                               schedule_work(&rdev->destroy_work);
+                       }
+               }
        }
 
        rcu_read_unlock();