1 /******************************************************************************
2 * drivers/xen/netback/accel.c
4 * Interface between backend virtual network device and accelerated plugin.
6 * Copyright (C) 2007 Solarflare Communications, Inc
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License version 2
10 * as published by the Free Software Foundation; or, when distributed
11 * separately from the Linux kernel or incorporated into other
12 * software packages, subject to the following license:
14 * Permission is hereby granted, free of charge, to any person obtaining a copy
15 * of this source file (the "Software"), to deal in the Software without
16 * restriction, including without limitation the rights to use, copy, modify,
17 * merge, publish, distribute, sublicense, and/or sell copies of the Software,
18 * and to permit persons to whom the Software is furnished to do so, subject to
19 * the following conditions:
21 * The above copyright notice and this permission notice shall be included in
22 * all copies or substantial portions of the Software.
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
33 #include <linux/list.h>
34 #include <asm/atomic.h>
35 #include <xen/xenbus.h>
36 #include <linux/mutex.h>
42 #define DPRINTK(fmt, args...) \
43 printk("netback/accel (%s:%d) " fmt ".\n", __FUNCTION__, __LINE__, ##args)
47 * A list of available netback accelerator plugin modules (each list
48 * entry is of type struct netback_accelerator)
50 static struct list_head accelerators_list;
51 /* Lock used to protect access to accelerators_list */
52 DEFINE_MUTEX(accelerators_mutex);
55 * Compare a backend to an accelerator, and decide if they are
56 * compatible (i.e. if the accelerator should be used by the
59 static int match_accelerator(struct xenbus_device *xendev,
60 struct backend_info *be,
61 struct netback_accelerator *accelerator)
64 char *eth_name = xenbus_read(XBT_NIL, xendev->nodename, "accel", NULL);
66 if (IS_ERR(eth_name)) {
67 /* Probably means not present */
68 DPRINTK("%s: no match due to xenbus_read accel error %ld\n",
69 __FUNCTION__, PTR_ERR(eth_name));
72 if (!strcmp(eth_name, accelerator->eth_name))
80 static void do_probe(struct backend_info *be,
81 struct netback_accelerator *accelerator,
82 struct xenbus_device *xendev)
84 be->accelerator = accelerator;
85 atomic_inc(&be->accelerator->use_count);
86 if (be->accelerator->hooks->probe(xendev) != 0) {
87 atomic_dec(&be->accelerator->use_count);
88 module_put(be->accelerator->hooks->owner);
89 be->accelerator = NULL;
95 * Notify suitable backends that a new accelerator is available and
96 * connected. This will also notify the accelerator plugin module
97 * that it is being used for a device through the probe hook.
99 static int netback_accelerator_probe_backend(struct device *dev, void *arg)
101 struct netback_accelerator *accelerator =
102 (struct netback_accelerator *)arg;
103 struct xenbus_device *xendev = to_xenbus_device(dev);
105 if (!strcmp("vif", xendev->devicetype)) {
106 struct backend_info *be = dev_get_drvdata(&xendev->dev);
108 if (match_accelerator(xendev, be, accelerator) &&
109 try_module_get(accelerator->hooks->owner)) {
110 do_probe(be, accelerator, xendev);
118 * Notify suitable backends that an accelerator is unavailable.
120 static int netback_accelerator_remove_backend(struct device *dev, void *arg)
122 struct xenbus_device *xendev = to_xenbus_device(dev);
123 struct netback_accelerator *accelerator =
124 (struct netback_accelerator *)arg;
126 if (!strcmp("vif", xendev->devicetype)) {
127 struct backend_info *be = dev_get_drvdata(&xendev->dev);
129 if (be->accelerator == accelerator) {
130 be->accelerator->hooks->remove(xendev);
131 atomic_dec(&be->accelerator->use_count);
132 module_put(be->accelerator->hooks->owner);
133 be->accelerator = NULL;
142 * Entry point for an netback accelerator plugin module. Called to
143 * advertise its presence, and connect to any suitable backends.
145 int netback_connect_accelerator(unsigned version, int id, const char *eth_name,
146 struct netback_accel_hooks *hooks)
148 struct netback_accelerator *new_accelerator;
149 unsigned eth_name_len;
151 if (version != NETBACK_ACCEL_VERSION) {
152 if (version > NETBACK_ACCEL_VERSION) {
153 /* Caller has higher version number, leave it
154 up to them to decide whether to continue.
155 They can recall with a lower number if
156 they're happy to be compatible with us */
157 return NETBACK_ACCEL_VERSION;
159 /* We have a more recent version than caller.
160 Currently reject, but may in future be able
161 to be backwardly compatible */
167 kmalloc(sizeof(struct netback_accelerator), GFP_KERNEL);
168 if (!new_accelerator) {
169 DPRINTK("%s: failed to allocate memory for accelerator\n",
174 new_accelerator->id = id;
176 eth_name_len = strlen(eth_name)+1;
177 new_accelerator->eth_name = kmalloc(eth_name_len, GFP_KERNEL);
178 if (!new_accelerator->eth_name) {
179 DPRINTK("%s: failed to allocate memory for eth_name string\n",
181 kfree(new_accelerator);
184 strlcpy(new_accelerator->eth_name, eth_name, eth_name_len);
186 new_accelerator->hooks = hooks;
188 atomic_set(&new_accelerator->use_count, 0);
190 mutex_lock(&accelerators_mutex);
191 list_add(&new_accelerator->link, &accelerators_list);
193 /* tell existing backends about new plugin */
194 xenbus_for_each_backend(new_accelerator,
195 netback_accelerator_probe_backend);
197 mutex_unlock(&accelerators_mutex);
202 EXPORT_SYMBOL_GPL(netback_connect_accelerator);
206 * Disconnect an accelerator plugin module that has previously been
209 void netback_disconnect_accelerator(int id, const char *eth_name)
211 struct netback_accelerator *accelerator, *next;
213 mutex_lock(&accelerators_mutex);
214 list_for_each_entry_safe(accelerator, next, &accelerators_list, link) {
215 if (!strcmp(eth_name, accelerator->eth_name)) {
216 xenbus_for_each_backend
217 (accelerator, netback_accelerator_remove_backend);
218 BUG_ON(atomic_read(&accelerator->use_count) != 0);
219 list_del(&accelerator->link);
220 kfree(accelerator->eth_name);
225 mutex_unlock(&accelerators_mutex);
227 EXPORT_SYMBOL_GPL(netback_disconnect_accelerator);
230 void netback_probe_accelerators(struct backend_info *be,
231 struct xenbus_device *dev)
233 struct netback_accelerator *accelerator;
236 * Check list of accelerators to see if any is suitable, and
239 mutex_lock(&accelerators_mutex);
240 list_for_each_entry(accelerator, &accelerators_list, link) {
241 if (match_accelerator(dev, be, accelerator) &&
242 try_module_get(accelerator->hooks->owner)) {
243 do_probe(be, accelerator, dev);
247 mutex_unlock(&accelerators_mutex);
251 void netback_remove_accelerators(struct backend_info *be,
252 struct xenbus_device *dev)
254 mutex_lock(&accelerators_mutex);
255 /* Notify the accelerator (if any) of this device's removal */
256 if (be->accelerator != NULL) {
257 be->accelerator->hooks->remove(dev);
258 atomic_dec(&be->accelerator->use_count);
259 module_put(be->accelerator->hooks->owner);
260 be->accelerator = NULL;
262 mutex_unlock(&accelerators_mutex);
266 void netif_accel_init(void)
268 INIT_LIST_HEAD(&accelerators_list);