Cleanup vif_state on device removal diff -r f992a517aba5 drivers/xen/netfront/accel.c --- a/drivers/xen/netfront/accel.c Wed Oct 03 13:41:33 2007 +0100 +++ b/drivers/xen/netfront/accel.c Wed Oct 03 13:45:55 2007 +0100 @@ -500,6 +500,30 @@ EXPORT_SYMBOL_GPL(netfront_accelerator_r /* + * Remove the hooks from a single vif state. + */ +static void +accelerator_remove_single_hook(struct netfront_accelerator *accelerator, + struct netfront_accel_vif_state *vif_state) +{ + /* Make sure there are no data path operations going on */ + netif_poll_disable(vif_state->np->netdev); + netif_tx_lock_bh(vif_state->np->netdev); + + /* + * Remove the hooks, but leave the vif_state on the + * accelerator's list as that signifies this vif is + * interested in using that accelerator if it becomes + * available again + */ + vif_state->hooks = NULL; + + netif_tx_unlock_bh(vif_state->np->netdev); + netif_poll_enable(vif_state->np->netdev); +} + + +/* * Safely remove the accelerator function hooks from a netfront state. */ static void accelerator_remove_hooks(struct netfront_accelerator *accelerator, @@ -513,20 +537,7 @@ static void accelerator_remove_hooks(str list_for_each_entry_safe(vif_state, tmp, &accelerator->vif_states, link) { - /* Make sure there are no data path operations going on */ - netif_poll_disable(vif_state->np->netdev); - netif_tx_lock_bh(vif_state->np->netdev); - - /* - * Remove the hooks, but leave the vif_state on the - * accelerator's list as that signifies this vif is - * interested in using that accelerator if it becomes - * available again - */ - vif_state->hooks = NULL; - - netif_tx_unlock_bh(vif_state->np->netdev); - netif_poll_enable(vif_state->np->netdev); + accelerator_remove_single_hook(accelerator, vif_state); /* * Remove the reference taken when the vif_state hooks @@ -623,6 +634,8 @@ int netfront_accelerator_call_remove(str int netfront_accelerator_call_remove(struct netfront_info *np, struct xenbus_device *dev) { + struct netfront_accelerator *accelerator = np->accelerator; + struct netfront_accel_vif_state *tmp_vif_state; struct netfront_accel_hooks *hooks; unsigned flags; int rc = 0; @@ -633,23 +646,53 @@ int netfront_accelerator_call_remove(str * call to prevent the accelerator being able to modify the * hooks in the middle (by, for example, unloading) */ - if (np->accel_vif_state.hooks) { - spin_lock_irqsave(&np->accelerator->vif_states_lock, flags); - hooks = np->accel_vif_state.hooks; - if (hooks) { - kref_get(&np->accel_vif_state.vif_kref); - spin_unlock_irqrestore - (&np->accelerator->vif_states_lock, flags); - - rc = np->accel_vif_state.hooks->remove(dev); - - kref_put(&np->accel_vif_state.vif_kref, - vif_kref_release); - } else { - spin_unlock_irqrestore - (&np->accelerator->vif_states_lock, flags); - } - } + spin_lock_irqsave(&np->accelerator->vif_states_lock, flags); + hooks = np->accel_vif_state.hooks; + + /* + * Remove this vif_state from the accelerator's list + */ + list_for_each_entry(tmp_vif_state, &accelerator->vif_states, link) { + if (tmp_vif_state == &np->accel_vif_state) { + list_del(&np->accel_vif_state.link); + break; + } + } + + if (hooks) { + kref_get(&np->accel_vif_state.vif_kref); + spin_unlock_irqrestore + (&np->accelerator->vif_states_lock, flags); + + rc = np->accel_vif_state.hooks->remove(dev); + + kref_put(&np->accel_vif_state.vif_kref, + vif_kref_release); + + spin_lock_irqsave(&np->accelerator->vif_states_lock, + flags); + + /* + * Try and do the opposite of accelerator_probe_new_vif + * to ensure there's no state pointing back at the + * netdev + */ + accelerator_remove_single_hook(accelerator, + &np->accel_vif_state); + + /* + * Remove the reference taken when the vif_state hooks + * were set, must be called without lock held + */ + spin_unlock_irqrestore(&accelerator->vif_states_lock, + flags); + kref_put(&np->accel_vif_state.vif_kref, + vif_kref_release); + } else { + spin_unlock_irqrestore(&np->accelerator->vif_states_lock, + flags); + } + return rc; }