key->conf.keyidx = idx;
        key->conf.keylen = key_len;
        memcpy(key->conf.key, key_data, key_len);
+       INIT_LIST_HEAD(&key->list);
 
        if (alg == ALG_CCMP) {
                /*
                        ieee80211_set_default_key(sdata, -1);
 
                rcu_assign_pointer(sdata->keys[idx], new);
+               if (new)
+                       list_add(&new->list, &sdata->key_list);
 
                if (defkey && new)
                        ieee80211_set_default_key(sdata, new->conf.keyidx);
 
        if (key) {
                ieee80211_key_mark_hw_accel_off(key);
-               list_del(&key->list);
+               /*
+                * We'll use an empty list to indicate that the key
+                * has already been removed.
+                */
+               list_del_init(&key->list);
        }
 }
 
 
        __ieee80211_key_replace(sdata, sta, old_key, key);
 
-       list_add(&key->list, &sdata->key_list);
-
-       synchronize_rcu();
+       if (old_key) {
+               synchronize_rcu();
+               ieee80211_key_free(old_key);
+       }
 
-       ieee80211_key_free(old_key);
-       ieee80211_key_enable_hw_accel(key);
+       if (netif_running(sdata->dev))
+               ieee80211_key_enable_hw_accel(key);
 }
 
 void ieee80211_key_free(struct ieee80211_key *key)
                 * Because other code may have key reference (RCU protected)
                 * right now, we then wait for a grace period before freeing
                 * it.
+                * An empty list indicates it was never added to the key list
+                * or has been removed already. It may, however, still be in
+                * hardware for acceleration.
                 */
-               __ieee80211_key_replace(key->sdata, key->sta, key, NULL);
+               if (!list_empty(&key->list))
+                       __ieee80211_key_replace(key->sdata, key->sta,
+                                               key, NULL);
 
                synchronize_rcu();