Kieran Mansley
2007-Oct-30 17:07 UTC
[Xen-devel] [Patch 2/8] Netfront accelerator bug fixes
Use remove and probe hooks rather than extra suspend and resume hooks for acceleration Signed-off-by <kmansley@solarflare.com> diff -r bf4201f8be4e drivers/xen/netfront/accel.c --- a/drivers/xen/netfront/accel.c Mon Oct 29 16:48:42 2007 +0000 +++ b/drivers/xen/netfront/accel.c Tue Oct 30 13:21:29 2007 +0000 @@ -118,9 +118,9 @@ static void add_accelerator_vif(struct n list_add(&np->accel_vif_state.link, &accelerator->vif_states); } else { /* - * May get here legitimately if reconnecting to the - * same accelerator, eg. after resume, so check that - * is the case + * May get here legitimately if suspend_cancel is + * called, but in that case configuration should not + * have changed */ BUG_ON(np->accelerator != accelerator); } @@ -313,7 +313,7 @@ static void static void accelerator_probe_vifs(struct netfront_accelerator *accelerator, struct netfront_accel_hooks *hooks, - unsigned lock_flags) + unsigned *lock_flags) { struct netfront_accel_vif_state *vif_state, *tmp; @@ -349,14 +349,15 @@ accelerator_probe_vifs(struct netfront_a * protected by the kref */ spin_unlock_irqrestore(&accelerator->vif_states_lock, - lock_flags); + (*lock_flags)); hooks->new_device(np->netdev, vif_state->dev); kref_put(&accelerator->accel_kref, accel_kref_release); /* Retake lock for next go round the loop */ - spin_lock_irqsave(&accelerator->vif_states_lock, lock_flags); + spin_lock_irqsave(&accelerator->vif_states_lock, + (*lock_flags)); /* * Hooks will get linked into vif_state by a call to @@ -391,7 +392,7 @@ accelerator_probe_vifs_on_load(struct ne if (accelerator->ready_for_probe) { accelerator->ready_for_probe = 0; - accelerator_probe_vifs(accelerator, hooks, flags); + accelerator_probe_vifs(accelerator, hooks, &flags); } else { if (accelerator->need_probe) DPRINTK("Probe request on accelerator awaiting probe\n"); @@ -526,8 +527,7 @@ accelerator_remove_single_hook(struct ne /* * Safely remove the accelerator function hooks from a netfront state. */ -static void accelerator_remove_hooks(struct netfront_accelerator *accelerator, - int remove_master) +static void accelerator_remove_hooks(struct netfront_accelerator *accelerator) { struct netfront_accel_hooks *hooks; struct netfront_accel_vif_state *vif_state, *tmp; @@ -538,31 +538,33 @@ static void accelerator_remove_hooks(str list_for_each_entry_safe(vif_state, tmp, &accelerator->vif_states, link) { + BUG_ON(vif_state->hooks == NULL); hooks = vif_state->hooks; accelerator_remove_single_hook(accelerator, vif_state); - /* - * Remove the reference taken when the vif_state hooks - * were set, must be called without lock held - */ + /* + * Tell accelerator that this device is being removed, + * and remove the reference taken when the vif_state + * hooks were set; both must be called without lock + * held + */ spin_unlock_irqrestore(&accelerator->vif_states_lock, flags); /* Last chance to get statistics from the accelerator */ hooks->get_stats(vif_state->np->netdev, &vif_state->np->stats); + hooks->remove(vif_state->dev); kref_put(&vif_state->vif_kref, vif_kref_release); spin_lock_irqsave(&accelerator->vif_states_lock, flags); } - if(remove_master) - accelerator->hooks = NULL; + accelerator->hooks = NULL; spin_unlock_irqrestore(&accelerator->vif_states_lock, flags); - if(remove_master) - /* Remove the reference taken when module loaded */ - kref_put(&accelerator->accel_kref, accel_kref_release); + /* Remove the reference taken when module loaded */ + kref_put(&accelerator->accel_kref, accel_kref_release); } @@ -571,7 +573,7 @@ static void accelerator_remove_hooks(str * removes the hooks into the plugin and blocks until all devices have * finished using it, so on return it is safe to unload. */ -void netfront_accelerator_stop(const char *frontend, int unloading) +void netfront_accelerator_stop(const char *frontend) { struct netfront_accelerator *accelerator; unsigned flags; @@ -588,11 +590,10 @@ void netfront_accelerator_stop(const cha */ sema_init(&accelerator->exit_semaphore, 0); - accelerator_remove_hooks(accelerator, unloading); - - if (unloading) - /* Wait for hooks to be unused, then return */ - down(&accelerator->exit_semaphore); + accelerator_remove_hooks(accelerator); + + /* Wait for hooks to be unused, then return */ + down(&accelerator->exit_semaphore); return; } @@ -606,9 +607,12 @@ int netfront_check_accelerator_queue_bus int netfront_check_accelerator_queue_busy(struct net_device *dev, struct netfront_info *np) { + struct netfront_accelerator *accelerator; struct netfront_accel_hooks *hooks; int rc = 1; unsigned flags; + + accelerator = np->accelerator; /* * Call the check busy accelerator hook. The use count for the @@ -616,6 +620,212 @@ int netfront_check_accelerator_queue_bus * call to prevent the accelerator being able to modify the * hooks in the middle (by, for example, unloading) */ + if (np->accel_vif_state.hooks && accelerator) { + spin_lock_irqsave(&accelerator->vif_states_lock, flags); + hooks = np->accel_vif_state.hooks; + if (hooks && np->accelerator == accelerator) { + kref_get(&np->accel_vif_state.vif_kref); + spin_unlock_irqrestore + (&accelerator->vif_states_lock, flags); + + rc = np->accel_vif_state.hooks->check_busy(dev); + + kref_put(&np->accel_vif_state.vif_kref, + vif_kref_release); + } else { + spin_unlock_irqrestore + (&accelerator->vif_states_lock, flags); + } + } + + return rc; +} + + +/* Helper for call_remove and do_suspend */ +static int do_remove(struct netfront_info *np, struct xenbus_device *dev, + unsigned lock_flags, int clear_accelerator) +{ + struct netfront_accelerator *accelerator = np->accelerator; + struct netfront_accel_hooks *hooks; + unsigned flags; + int rc = 0; + + if(clear_accelerator) + np->accelerator = NULL; + + if (np->accel_vif_state.hooks) { + hooks = np->accel_vif_state.hooks; + + /* + * 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); + + spin_unlock_irqrestore(&accelerator->vif_states_lock, + lock_flags); + + /* + * No need to do kref_get before calling these hooks as we + * can borrow the one we have to drop after this call + */ + + /* Last chance to get statistics from the accelerator */ + hooks->get_stats(np->netdev, &np->stats); + + rc = hooks->remove(dev); + + /* + * Remove the reference taken when the vif_state hooks + * were set, must be called without lock held + */ + kref_put(&np->accel_vif_state.vif_kref, vif_kref_release); + } else { + spin_unlock_irqrestore(&accelerator->vif_states_lock, + lock_flags); + } + + + return rc; +} + + +int netfront_accelerator_call_remove(struct netfront_info *np, + struct xenbus_device *dev) +{ + struct netfront_accel_vif_state *tmp_vif_state; + unsigned flags; + + /* Check that we''ve got a device that was accelerated */ + if (np->accelerator == NULL) + return 0; + + spin_lock_irqsave(&np->accelerator->vif_states_lock, flags); + + list_for_each_entry(tmp_vif_state, &np->accelerator->vif_states, + link) { + if (tmp_vif_state == &np->accel_vif_state) { + list_del(&np->accel_vif_state.link); + break; + } + } + + /* do_remove drops the lock for us */ + return do_remove(np, dev, flags, 1); +} + + +int netfront_accelerator_suspend(struct netfront_info *np, + struct xenbus_device *dev) +{ + unsigned flags; + + + /* Check that we''ve got a device that was accelerated */ + if (np->accelerator == NULL) + return 0; + + /* + * Call the remove accelerator hook, but leave the vif_state + * on the accelerator''s list in case there is a suspend_cancel. + */ + spin_lock_irqsave(&np->accelerator->vif_states_lock, + flags); + + /* do_remove drops the lock for us */ + return do_remove(np, dev, flags, 0); +} + + +int netfront_accelerator_suspend_cancel(struct netfront_info *np, + struct xenbus_device *dev) +{ + struct netfront_accel_vif_state *accel_vif_state = NULL; + unsigned flags1, flags2; + + /* Check that we''ve got a device that was accelerated */ + if (np->accelerator == NULL) + return 0; + + spin_lock_irqsave(&np->accelerator->vif_states_lock, flags); + /* Find the vif_state from the accelerator''s list */ + list_for_each_entry(accel_vif_state, &np->accelerator->vif_states, + link) { + if (accel_vif_state->dev == dev) { + BUG_ON(accel_vif_state != &np->accel_vif_state); + + spin_unlock_irqrestore(&np->accelerator->vif_states_lock, + flags); + + /* + * Kick things off again to restore + * acceleration as it was before suspend + */ + accelerator_probe_new_vif(np, dev, np->accelerator); + + return 0; + } + } + + spin_unlock_irqrestore(&np->accelerator->vif_states_lock, flags); + + return 0; +} + + +void netfront_accelerator_resume(struct netfront_info *np, + struct xenbus_device *dev) +{ + struct netfront_accel_vif_state *accel_vif_state = NULL; + spinlock_t *vif_states_lock; + unsigned flags; + + /* Check that we''ve got a device that was accelerated */ + if(np->accelerator == NULL) + return; + + spin_lock_irqsave(&np->accelerator->vif_states_lock, flags); + + /* Find the vif_state from the accelerator''s list */ + list_for_each_entry(accel_vif_state, &np->accelerator->vif_states, + link) { + if (accel_vif_state->dev == dev) { + BUG_ON(accel_vif_state != &np->accel_vif_state); + + /* + * Remove it from the accelerator''s list so + * state is consistent for probing new vifs + * when they get connected + */ + list_del(&accel_vif_state->link); + vif_states_lock = &np->accelerator->vif_states_lock; + np->accelerator = NULL; + + spin_unlock_irqrestore(vif_states_lock, flags); + + return; + } + } + spin_unlock_irqrestore(&np->accelerator->vif_states_lock, flags); +} + + +void netfront_accelerator_call_backend_changed(struct netfront_info *np, + struct xenbus_device *dev, + enum xenbus_state backend_state) +{ + struct netfront_accel_hooks *hooks; + unsigned flags; + + /* + * Call the backend_changed accelerator hook. The use count + * for the accelerator''s hooks is incremented for the duration + * of the 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; @@ -624,8 +834,9 @@ int netfront_check_accelerator_queue_bus spin_unlock_irqrestore (&np->accelerator->vif_states_lock, flags); - rc = np->accel_vif_state.hooks->check_busy(dev); - + np->accel_vif_state.hooks->backend_changed + (dev, backend_state); + kref_put(&np->accel_vif_state.vif_kref, vif_kref_release); } else { @@ -633,227 +844,17 @@ int netfront_check_accelerator_queue_bus (&np->accelerator->vif_states_lock, flags); } } - - return rc; -} - - -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; - - /* - * Call the remove accelerator hook. The use count for the - * accelerator''s hooks is incremented for the duration of the - * call to prevent the accelerator being able to modify the - * hooks in the middle (by, for example, unloading) - */ - 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); - /* Last chance to get statistics from the accelerator */ - vif_state->hooks->get_stats(np->netdev, &np->stats); - 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; -} - - -int netfront_accelerator_call_suspend(struct netfront_info *np, - struct xenbus_device *dev) -{ - struct netfront_accel_hooks *hooks; - unsigned flags; - int rc = 0; - - IPRINTK("netfront_accelerator_call_suspend\n"); - - /* - * Call the suspend accelerator hook. The use count for the - * accelerator''s hooks is incremented for the duration of - * the 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); - - /* Last chance to get stats from the accelerator */ - np->accel_vif_state.hooks->get_stats(dev, &np->stats); - - rc = np->accel_vif_state.hooks->suspend(dev); - - kref_put(&np->accel_vif_state.vif_kref, - vif_kref_release); - } else { - spin_unlock_irqrestore - (&np->accelerator->vif_states_lock, flags); - } - } - return rc; -} - - -int netfront_accelerator_call_suspend_cancel(struct netfront_info *np, - struct xenbus_device *dev) -{ - struct netfront_accel_hooks *hooks; - unsigned flags; - int rc = 0; - - IPRINTK(" netfront_accelerator_call_suspend_cancel\n"); - - /* - * Call the suspend_cancel accelerator hook. The use count - * for the accelerator''s hooks is incremented for the - * duration of the 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->suspend_cancel(dev); - - kref_put(&np->accel_vif_state.vif_kref, - vif_kref_release); - } else { - spin_unlock_irqrestore - (&np->accelerator->vif_states_lock, flags); - } - } - return rc; -} - - -int netfront_accelerator_call_resume(struct netfront_info *np, - struct xenbus_device *dev) -{ - struct netfront_accel_hooks *hooks; - unsigned flags; - int rc = 0; - - /* - * Call the resume accelerator hook. The use count for the - * accelerator''s hooks is incremented for the duration of - * the 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->resume(dev); - - kref_put(&np->accel_vif_state.vif_kref, - vif_kref_release); - } else { - spin_unlock_irqrestore - (&np->accelerator->vif_states_lock, flags); - } - } - return rc; -} - - -void netfront_accelerator_call_backend_changed(struct netfront_info *np, - struct xenbus_device *dev, - enum xenbus_state backend_state) -{ - struct netfront_accel_hooks *hooks; - unsigned flags; - - /* - * Call the backend_changed accelerator hook. The use count - * for the accelerator''s hooks is incremented for the duration - * of the 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); - - np->accel_vif_state.hooks->backend_changed - (dev, backend_state); - - kref_put(&np->accel_vif_state.vif_kref, - vif_kref_release); - } else { - spin_unlock_irqrestore - (&np->accelerator->vif_states_lock, flags); - } - } } void netfront_accelerator_call_stop_napi_irq(struct netfront_info *np, struct net_device *dev) { + struct netfront_accelerator *accelerator; struct netfront_accel_hooks *hooks; unsigned flags; + + accelerator = np->accelerator; /* * Call the stop_napi_interrupts accelerator hook. The use @@ -863,13 +864,13 @@ void netfront_accelerator_call_stop_napi * unloading) */ - if (np->accel_vif_state.hooks) { - spin_lock_irqsave(&np->accelerator->vif_states_lock, flags); + if (np->accel_vif_state.hooks && accelerator != NULL) { + spin_lock_irqsave(&accelerator->vif_states_lock, flags); hooks = np->accel_vif_state.hooks; - if (hooks) { + if (hooks && np->accelerator == accelerator) { kref_get(&np->accel_vif_state.vif_kref); spin_unlock_irqrestore - (&np->accelerator->vif_states_lock, flags); + (&accelerator->vif_states_lock, flags); np->accel_vif_state.hooks->stop_napi_irq(dev); @@ -877,7 +878,7 @@ void netfront_accelerator_call_stop_napi vif_kref_release); } else { spin_unlock_irqrestore - (&np->accelerator->vif_states_lock, flags); + (&accelerator->vif_states_lock, flags); } } } @@ -886,9 +887,12 @@ int netfront_accelerator_call_get_stats( int netfront_accelerator_call_get_stats(struct netfront_info *np, struct net_device *dev) { + struct netfront_accelerator *accelerator; struct netfront_accel_hooks *hooks; unsigned flags; int rc = 0; + + accelerator = np->accelerator; /* * Call the get_stats accelerator hook. The use count for the @@ -897,13 +901,13 @@ int netfront_accelerator_call_get_stats( * hooks in the middle (by, for example, unloading) */ - if (np->accel_vif_state.hooks) { - spin_lock_irqsave(&np->accelerator->vif_states_lock, flags); + if (np->accel_vif_state.hooks && accelerator != NULL) { + spin_lock_irqsave(accelerator->vif_states_lock, flags); hooks = np->accel_vif_state.hooks; - if (hooks) { + if (hooks && np->accelerator == accelerator) { kref_get(&np->accel_vif_state.vif_kref); spin_unlock_irqrestore - (&np->accelerator->vif_states_lock, flags); + (&accelerator->vif_states_lock, flags); rc = np->accel_vif_state.hooks->get_stats(dev, &np->stats); @@ -912,7 +916,7 @@ int netfront_accelerator_call_get_stats( vif_kref_release); } else { spin_unlock_irqrestore - (&np->accelerator->vif_states_lock, flags); + (&accelerator->vif_states_lock, flags); } } return rc; @@ -939,7 +943,7 @@ static void accel_kref_release(struct kr if (accelerator->need_probe) { hooks = accelerator->need_probe; accelerator->need_probe = NULL; - accelerator_probe_vifs(accelerator, hooks, flags); + accelerator_probe_vifs(accelerator, hooks, &flags); } else accelerator->ready_for_probe = 1; diff -r bf4201f8be4e drivers/xen/netfront/netfront.c --- a/drivers/xen/netfront/netfront.c Mon Oct 29 16:48:42 2007 +0000 +++ b/drivers/xen/netfront/netfront.c Tue Oct 30 13:21:29 2007 +0000 @@ -308,14 +308,14 @@ static int netfront_suspend(struct xenbu static int netfront_suspend(struct xenbus_device *dev) { struct netfront_info *info = dev->dev.driver_data; - return netfront_accelerator_call_suspend(info, dev); + return netfront_accelerator_suspend(info, dev); } static int netfront_suspend_cancel(struct xenbus_device *dev) { struct netfront_info *info = dev->dev.driver_data; - return netfront_accelerator_call_suspend_cancel(info, dev); + return netfront_accelerator_suspend_cancel(info, dev); } @@ -331,7 +331,7 @@ static int netfront_resume(struct xenbus DPRINTK("%s\n", dev->nodename); - netfront_accelerator_call_resume(info, dev); + netfront_accelerator_resume(info, dev); netif_disconnect_backend(info); return 0; @@ -1507,7 +1507,7 @@ err: * fast path is likewise */ accel_more_to_do = - np->accel_vif_state.hooks->start_napi_irq(dev); + np->accel_vif_state.hooks->start_napi_irq(dev); } if (!more_to_do && !accel_more_to_do) diff -r bf4201f8be4e drivers/xen/netfront/netfront.h --- a/drivers/xen/netfront/netfront.h Mon Oct 29 16:48:42 2007 +0000 +++ b/drivers/xen/netfront/netfront.h Tue Oct 30 13:21:29 2007 +0000 @@ -60,12 +60,8 @@ struct netfront_accel_hooks { */ int (*new_device)(struct net_device *net_dev, struct xenbus_device *dev); /* - * suspend, suspend_cancel, resume, remove: Equivalent to the - * normal xenbus_* callbacks - */ - int (*suspend)(struct xenbus_device *dev); - int (*suspend_cancel)(struct xenbus_device *dev); - int (*resume)(struct xenbus_device *dev); + * remove: Opposite of new_device + */ int (*remove)(struct xenbus_device *dev); /* * backend_changed: Callback from watch based on backend''s @@ -256,11 +252,8 @@ extern void netfront_accelerator_ready(c * * frontend: the string describing the accelerator. Must match the * one passed to netfront_accelerator_loaded() - * - * wait: 1 => wait for all users of module to complete before - * returning, thus making it safe to unload on return */ -extern void netfront_accelerator_stop(const char *frontend, int wait); +extern void netfront_accelerator_stop(const char *frontend); /* * Called by an accelerator before waking the net device''s TX queue to @@ -285,14 +278,14 @@ int netfront_accelerator_call_remove(str int netfront_accelerator_call_remove(struct netfront_info *np, struct xenbus_device *dev); extern -int netfront_accelerator_call_suspend(struct netfront_info *np, - struct xenbus_device *dev); -extern -int netfront_accelerator_call_suspend_cancel(struct netfront_info *np, - struct xenbus_device *dev); -extern -int netfront_accelerator_call_resume(struct netfront_info *np, - struct xenbus_device *dev); +int netfront_accelerator_suspend(struct netfront_info *np, + struct xenbus_device *dev); +extern +int netfront_accelerator_suspend_cancel(struct netfront_info *np, + struct xenbus_device *dev); +extern +void netfront_accelerator_resume(struct netfront_info *np, + struct xenbus_device *dev); extern void netfront_accelerator_call_backend_changed(struct netfront_info *np, struct xenbus_device *dev, _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel