}
}
+
+static void do_probe(struct backend_info *be,
+ struct netback_accelerator *accelerator,
+ struct xenbus_device *xendev)
+{
+ be->accelerator = accelerator;
+ atomic_inc(&be->accelerator->use_count);
+ if (be->accelerator->hooks->probe(xendev) != 0) {
+ atomic_dec(&be->accelerator->use_count);
+ module_put(be->accelerator->hooks->owner);
+ be->accelerator = NULL;
+ }
+}
+
+
/*
- * Notify all suitable backends that a new accelerator is available
- * and connected. This will also notify the accelerator plugin module
+ * Notify suitable backends that a new accelerator is available and
+ * connected. This will also notify the accelerator plugin module
* that it is being used for a device through the probe hook.
*/
-static int netback_accelerator_tell_backend(struct device *dev, void *arg)
+static int netback_accelerator_probe_backend(struct device *dev, void *arg)
{
struct netback_accelerator *accelerator =
(struct netback_accelerator *)arg;
if (!strcmp("vif", xendev->devicetype)) {
struct backend_info *be = xendev->dev.driver_data;
- if (match_accelerator(xendev, be, accelerator)) {
- be->accelerator = accelerator;
- atomic_inc(&be->accelerator->use_count);
- be->accelerator->hooks->probe(xendev);
+ if (match_accelerator(xendev, be, accelerator) &&
+ try_module_get(accelerator->hooks->owner)) {
+ do_probe(be, accelerator, xendev);
}
}
return 0;
}
+/*
+ * Notify suitable backends that an accelerator is unavailable.
+ */
+static int netback_accelerator_remove_backend(struct device *dev, void *arg)
+{
+ struct xenbus_device *xendev = to_xenbus_device(dev);
+ struct netback_accelerator *accelerator =
+ (struct netback_accelerator *)arg;
+
+ if (!strcmp("vif", xendev->devicetype)) {
+ struct backend_info *be = xendev->dev.driver_data;
+
+ if (be->accelerator == accelerator) {
+ be->accelerator->hooks->remove(xendev);
+ atomic_dec(&be->accelerator->use_count);
+ module_put(be->accelerator->hooks->owner);
+ be->accelerator = NULL;
+ }
+ }
+ return 0;
+}
+
+
+
/*
* Entry point for an netback accelerator plugin module. Called to
* advertise its presence, and connect to any suitable backends.
return -ENOMEM;
}
-
new_accelerator->id = id;
eth_name_len = strlen(eth_name)+1;
mutex_lock(&accelerators_mutex);
list_add(&new_accelerator->link, &accelerators_list);
- mutex_unlock(&accelerators_mutex);
/* tell existing backends about new plugin */
xenbus_for_each_backend(new_accelerator,
- netback_accelerator_tell_backend);
+ netback_accelerator_probe_backend);
+
+ mutex_unlock(&accelerators_mutex);
return 0;
EXPORT_SYMBOL_GPL(netback_connect_accelerator);
-/*
- * Remove the link from backend state to a particular accelerator
- */
-static int netback_accelerator_cleanup_backend(struct device *dev, void *arg)
-{
- struct netback_accelerator *accelerator =
- (struct netback_accelerator *)arg;
- struct xenbus_device *xendev = to_xenbus_device(dev);
-
- if (!strcmp("vif", xendev->devicetype)) {
- struct backend_info *be = xendev->dev.driver_data;
- if (be->accelerator == accelerator)
- be->accelerator = NULL;
- }
- return 0;
-}
-
-
/*
* Disconnect an accelerator plugin module that has previously been
* connected.
- *
- * This should only be allowed when there are no remaining users -
- * i.e. it is not necessary to go through and clear all the hooks, as
- * they should have already been removed. This is enforced by taking
- * a module reference to the plugin while the interfaces are in use
*/
void netback_disconnect_accelerator(int id, const char *eth_name)
{
mutex_lock(&accelerators_mutex);
list_for_each_entry_safe(accelerator, next, &accelerators_list, link) {
- if (strcmp(eth_name, accelerator->eth_name)) {
+ if (!strcmp(eth_name, accelerator->eth_name)) {
+ xenbus_for_each_backend
+ (accelerator, netback_accelerator_remove_backend);
BUG_ON(atomic_read(&accelerator->use_count) != 0);
- list_del(&accelerator->link);
- mutex_unlock(&accelerators_mutex);
-
- xenbus_for_each_backend(accelerator,
- netback_accelerator_cleanup_backend);
-
+ list_del(&accelerator->link);
kfree(accelerator->eth_name);
kfree(accelerator);
- return;
+ break;
}
}
mutex_unlock(&accelerators_mutex);
list_for_each_entry(accelerator, &accelerators_list, link) {
if (match_accelerator(dev, be, accelerator) &&
try_module_get(accelerator->hooks->owner)) {
- be->accelerator = accelerator;
- atomic_inc(&be->accelerator->use_count);
- be->accelerator->hooks->probe(dev);
+ do_probe(be, accelerator, dev);
break;
}
}
void netback_remove_accelerators(struct backend_info *be,
struct xenbus_device *dev)
{
+ mutex_lock(&accelerators_mutex);
/* Notify the accelerator (if any) of this device's removal */
- if (be->accelerator) {
+ if (be->accelerator != NULL) {
be->accelerator->hooks->remove(dev);
atomic_dec(&be->accelerator->use_count);
module_put(be->accelerator->hooks->owner);
+ be->accelerator = NULL;
}
- be->accelerator = NULL;
+ mutex_unlock(&accelerators_mutex);
}
#define WPRINTK(fmt, args...) \
printk(KERN_WARNING "netfront/accel: " fmt, ##args)
+static int netfront_remove_accelerator(struct netfront_info *np,
+ struct xenbus_device *dev);
+static int netfront_load_accelerator(struct netfront_info *np,
+ struct xenbus_device *dev,
+ const char *frontend);
+
/*
* List of all netfront accelerator plugin modules available. Each
* list entry is of type struct netfront_accelerator.
}
+/*
+ * Watch the configured accelerator and change plugin if it's modified
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+static void accel_watch_work(struct work_struct *context)
+#else
+static void accel_watch_work(void *context)
+#endif
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+ struct netfront_accel_vif_state *vif_state =
+ container_of(context, struct netfront_accel_vif_state,
+ accel_work);
+#else
+ struct netfront_accel_vif_state *vif_state =
+ (struct netfront_accel_vif_state *)context;
+#endif
+ struct netfront_info *np = vif_state->np;
+ char *accel_frontend;
+ int accel_len, rc = -1;
+
+ mutex_lock(&accelerator_mutex);
+
+ accel_frontend = xenbus_read(XBT_NIL, np->xbdev->otherend,
+ "accel-frontend", &accel_len);
+ if (IS_ERR(accel_frontend)) {
+ accel_frontend = NULL;
+ netfront_remove_accelerator(np, np->xbdev);
+ } else {
+ /* If this is the first time, request the accelerator,
+ otherwise only request one if it has changed */
+ if (vif_state->accel_frontend == NULL) {
+ rc = netfront_load_accelerator(np, np->xbdev,
+ accel_frontend);
+ } else {
+ if (strncmp(vif_state->accel_frontend, accel_frontend,
+ accel_len)) {
+ netfront_remove_accelerator(np, np->xbdev);
+ rc = netfront_load_accelerator(np, np->xbdev,
+ accel_frontend);
+ }
+ }
+ }
+
+ /* Get rid of previous state and replace with the new name */
+ if (vif_state->accel_frontend != NULL)
+ kfree(vif_state->accel_frontend);
+ vif_state->accel_frontend = accel_frontend;
+
+ mutex_unlock(&accelerator_mutex);
+
+ if (rc == 0) {
+ DPRINTK("requesting module %s\n", accel_frontend);
+ request_module("%s", accel_frontend);
+ /*
+ * Module should now call netfront_accelerator_loaded() once
+ * it's up and running, and we can continue from there
+ */
+ }
+}
+
+
+static void accel_watch_changed(struct xenbus_watch *watch,
+ const char **vec, unsigned int len)
+{
+ struct netfront_accel_vif_state *vif_state =
+ container_of(watch, struct netfront_accel_vif_state,
+ accel_watch);
+ schedule_work(&vif_state->accel_work);
+}
+
+
+void netfront_accelerator_add_watch(struct netfront_info *np)
+{
+ int err;
+
+ /* Check we're not trying to overwrite an existing watch */
+ BUG_ON(np->accel_vif_state.accel_watch.node != NULL);
+
+ /* Get a watch on the accelerator plugin */
+ err = xenbus_watch_path2(np->xbdev, np->xbdev->otherend,
+ "accel-frontend",
+ &np->accel_vif_state.accel_watch,
+ accel_watch_changed);
+ if (err) {
+ DPRINTK("%s: Failed to register accel watch: %d\n",
+ __FUNCTION__, err);
+ np->accel_vif_state.accel_watch.node = NULL;
+ }
+}
+
+
+static
+void netfront_accelerator_remove_watch(struct netfront_info *np)
+{
+ struct netfront_accel_vif_state *vif_state = &np->accel_vif_state;
+
+ /* Get rid of watch on accelerator plugin */
+ if (vif_state->accel_watch.node != NULL) {
+ unregister_xenbus_watch(&vif_state->accel_watch);
+ kfree(vif_state->accel_watch.node);
+ vif_state->accel_watch.node = NULL;
+
+ flush_scheduled_work();
+
+ /* Clean up any state left from watch */
+ if (vif_state->accel_frontend != NULL) {
+ kfree(vif_state->accel_frontend);
+ vif_state->accel_frontend = NULL;
+ }
+ }
+}
+
+
/*
* Initialise the accel_vif_state field in the netfront state
*/
/* It's assumed that these things don't change */
np->accel_vif_state.np = np;
np->accel_vif_state.dev = dev;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+ INIT_WORK(&np->accel_vif_state.accel_work, accel_watch_work);
+#else
+ INIT_WORK(&np->accel_vif_state.accel_work, accel_watch_work,
+ &np->accel_vif_state);
+#endif
}
struct netfront_accelerator *accelerator)
{
struct netfront_accel_hooks *hooks;
+ unsigned long flags;
DPRINTK("\n");
hooks = accelerator->hooks;
if (hooks) {
- hooks->new_device(np->netdev, dev);
- /*
- * Hooks will get linked into vif_state by a future
- * call by the accelerator to netfront_accelerator_ready()
- */
+ if (hooks->new_device(np->netdev, dev) == 0) {
+ spin_lock_irqsave
+ (&accelerator->vif_states_lock, flags);
+
+ accelerator_set_vif_state_hooks(&np->accel_vif_state);
+
+ spin_unlock_irqrestore
+ (&accelerator->vif_states_lock, flags);
+ }
}
return;
}
+
/*
* Request that a particular netfront accelerator plugin is loaded.
* Usually called as a result of the vif configuration specifying
- * which one to use.
+ * which one to use. Must be called with accelerator_mutex held
*/
-int netfront_load_accelerator(struct netfront_info *np,
- struct xenbus_device *dev,
- const char *frontend)
+static int netfront_load_accelerator(struct netfront_info *np,
+ struct xenbus_device *dev,
+ const char *frontend)
{
struct netfront_accelerator *accelerator;
int rc = 0;
DPRINTK(" %s\n", frontend);
- mutex_lock(&accelerator_mutex);
-
spin_lock_irqsave(&accelerators_lock, flags);
/*
spin_unlock_irqrestore(&accelerators_lock, flags);
accelerator_probe_new_vif(np, dev, accelerator);
-
- mutex_unlock(&accelerator_mutex);
return 0;
}
}
/* Couldn't find it, so create a new one and load the module */
if ((rc = init_accelerator(frontend, &accelerator, NULL)) < 0) {
spin_unlock_irqrestore(&accelerators_lock, flags);
- mutex_unlock(&accelerator_mutex);
return rc;
}
/* Include this frontend device on the accelerator's list */
add_accelerator_vif(accelerator, np);
- mutex_unlock(&accelerator_mutex);
-
- DPRINTK("requesting module %s\n", frontend);
-
- /* load module */
- request_module("%s", frontend);
-
- /*
- * Module should now call netfront_accelerator_loaded() once
- * it's up and running, and we can continue from there
- */
-
return rc;
}
struct netfront_accel_hooks *hooks)
{
struct netfront_accel_vif_state *vif_state, *tmp;
+ unsigned long flags;
DPRINTK("%p\n", accelerator);
link) {
struct netfront_info *np = vif_state->np;
- hooks->new_device(np->netdev, vif_state->dev);
-
- /*
- * Hooks will get linked into vif_state by a call to
- * netfront_accelerator_ready() once accelerator
- * plugin is ready for action
- */
+ if (hooks->new_device(np->netdev, vif_state->dev) == 0) {
+ spin_lock_irqsave
+ (&accelerator->vif_states_lock, flags);
+
+ accelerator_set_vif_state_hooks(vif_state);
+
+ spin_unlock_irqrestore
+ (&accelerator->vif_states_lock, flags);
+ }
}
}
EXPORT_SYMBOL_GPL(netfront_accelerator_loaded);
-/*
- * Called by the accelerator module after it has been probed with a
- * network device to say that it is ready to start accelerating
- * traffic on that device
- */
-void netfront_accelerator_ready(const char *frontend,
- struct xenbus_device *dev)
-{
- struct netfront_accelerator *accelerator;
- struct netfront_accel_vif_state *accel_vif_state;
- unsigned long flags, flags1;
-
- DPRINTK("%s %p\n", frontend, dev);
-
- spin_lock_irqsave(&accelerators_lock, flags);
-
- list_for_each_entry(accelerator, &accelerators_list, link) {
- if (match_accelerator(frontend, accelerator)) {
- /*
- * Mutex not held so need vif_states_lock for
- * list
- */
- spin_lock_irqsave
- (&accelerator->vif_states_lock, flags1);
-
- list_for_each_entry(accel_vif_state,
- &accelerator->vif_states, link) {
- if (accel_vif_state->dev == dev)
- accelerator_set_vif_state_hooks
- (accel_vif_state);
- }
-
- spin_unlock_irqrestore
- (&accelerator->vif_states_lock, flags1);
- break;
- }
- }
- spin_unlock_irqrestore(&accelerators_lock, flags);
-}
-EXPORT_SYMBOL_GPL(netfront_accelerator_ready);
-
-
/*
* Remove the hooks from a single vif state.
*/
if(vif_state->hooks) {
hooks = vif_state->hooks;
- accelerator_remove_single_hook(accelerator, vif_state);
/* Last chance to get statistics from the accelerator */
hooks->get_stats(vif_state->np->netdev,
&vif_state->np->stats);
- }
- spin_unlock_irqrestore(&accelerator->vif_states_lock, flags);
+ spin_unlock_irqrestore(&accelerator->vif_states_lock,
+ flags);
+
+ accelerator_remove_single_hook(accelerator, vif_state);
- accelerator->hooks->remove(vif_state->dev);
+ accelerator->hooks->remove(vif_state->dev);
+ } else {
+ spin_unlock_irqrestore(&accelerator->vif_states_lock,
+ flags);
+ }
}
accelerator->hooks = NULL;
if (np->accel_vif_state.hooks) {
hooks = np->accel_vif_state.hooks;
+ /* Last chance to get statistics from the accelerator */
+ hooks->get_stats(np->netdev, &np->stats);
+
+ spin_unlock_irqrestore(&accelerator->vif_states_lock,
+ *lock_flags);
+
/*
* Try and do the opposite of accelerator_probe_new_vif
* to ensure there's no state pointing back at the
accelerator_remove_single_hook(accelerator,
&np->accel_vif_state);
- /* Last chance to get statistics from the accelerator */
- hooks->get_stats(np->netdev, &np->stats);
- }
-
- if (accelerator->hooks) {
- spin_unlock_irqrestore(&accelerator->vif_states_lock,
- *lock_flags);
-
rc = accelerator->hooks->remove(dev);
spin_lock_irqsave(&accelerator->vif_states_lock, *lock_flags);
return rc;
}
-
-
-int netfront_accelerator_call_remove(struct netfront_info *np,
- struct xenbus_device *dev)
+
+
+static int netfront_remove_accelerator(struct netfront_info *np,
+ struct xenbus_device *dev)
{
struct netfront_accelerator *accelerator;
struct netfront_accel_vif_state *tmp_vif_state;
unsigned long flags;
int rc = 0;
- mutex_lock(&accelerator_mutex);
-
/* Check that we've got a device that was accelerated */
if (np->accelerator == NULL)
- goto out;
+ return rc;
accelerator = np->accelerator;
np->accelerator = NULL;
spin_unlock_irqrestore(&accelerator->vif_states_lock, flags);
- out:
+
+ return rc;
+}
+
+
+int netfront_accelerator_call_remove(struct netfront_info *np,
+ struct xenbus_device *dev)
+{
+ int rc;
+ netfront_accelerator_remove_watch(np);
+ mutex_lock(&accelerator_mutex);
+ rc = netfront_remove_accelerator(np, dev);
mutex_unlock(&accelerator_mutex);
return rc;
}
-
+
int netfront_accelerator_suspend(struct netfront_info *np,
struct xenbus_device *dev)
{
unsigned long flags;
int rc = 0;
+
+ netfront_accelerator_remove_watch(np);
mutex_lock(&accelerator_mutex);
out:
mutex_unlock(&accelerator_mutex);
+ netfront_accelerator_add_watch(np);
return 0;
}