- supported.conf: Added sparse_keymap (eeepc_laptop depends on it)
[linux-flexiantxendom0-3.2.10.git] / drivers / xen / netback / accel.c
1 /******************************************************************************
2  * drivers/xen/netback/accel.c
3  *
4  * Interface between backend virtual network device and accelerated plugin. 
5  * 
6  * Copyright (C) 2007 Solarflare Communications, Inc
7  * 
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:
13  * 
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:
20  * 
21  * The above copyright notice and this permission notice shall be included in
22  * all copies or substantial portions of the Software.
23  * 
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
30  * IN THE SOFTWARE.
31  */
32
33 #include <linux/list.h>
34 #include <asm/atomic.h>
35 #include <xen/xenbus.h>
36 #include <linux/mutex.h>
37
38 #include "common.h"
39
40 #if 0
41 #undef DPRINTK
42 #define DPRINTK(fmt, args...)                                           \
43         printk("netback/accel (%s:%d) " fmt ".\n", __FUNCTION__, __LINE__, ##args)
44 #endif
45
46 /* 
47  * A list of available netback accelerator plugin modules (each list
48  * entry is of type struct netback_accelerator) 
49  */ 
50 static struct list_head accelerators_list;
51 /* Lock used to protect access to accelerators_list */
52 DEFINE_MUTEX(accelerators_mutex);
53
54 /* 
55  * Compare a backend to an accelerator, and decide if they are
56  * compatible (i.e. if the accelerator should be used by the
57  * backend) 
58  */
59 static int match_accelerator(struct xenbus_device *xendev,
60                              struct backend_info *be, 
61                              struct netback_accelerator *accelerator)
62 {
63         int rc = 0;
64         char *eth_name = xenbus_read(XBT_NIL, xendev->nodename, "accel", NULL);
65         
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));
70                 return 0;
71         } else {
72                 if (!strcmp(eth_name, accelerator->eth_name))
73                         rc = 1;
74                 kfree(eth_name);
75                 return rc;
76         }
77 }
78
79
80 static void do_probe(struct backend_info *be, 
81                      struct netback_accelerator *accelerator,
82                      struct xenbus_device *xendev) 
83 {
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;
90         }
91 }
92
93
94 /*
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.
98  */
99 static int netback_accelerator_probe_backend(struct device *dev, void *arg)
100 {
101         struct netback_accelerator *accelerator = 
102                 (struct netback_accelerator *)arg;
103         struct xenbus_device *xendev = to_xenbus_device(dev);
104
105         if (!strcmp("vif", xendev->devicetype)) {
106                 struct backend_info *be = dev_get_drvdata(&xendev->dev);
107
108                 if (match_accelerator(xendev, be, accelerator) &&
109                     try_module_get(accelerator->hooks->owner)) {
110                         do_probe(be, accelerator, xendev);
111                 }
112         }
113         return 0;
114 }
115
116
117 /*
118  * Notify suitable backends that an accelerator is unavailable.
119  */
120 static int netback_accelerator_remove_backend(struct device *dev, void *arg)
121 {
122         struct xenbus_device *xendev = to_xenbus_device(dev);
123         struct netback_accelerator *accelerator = 
124                 (struct netback_accelerator *)arg;
125         
126         if (!strcmp("vif", xendev->devicetype)) {
127                 struct backend_info *be = dev_get_drvdata(&xendev->dev);
128
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;
134                 }
135         }
136         return 0;
137 }
138
139
140
141 /*
142  * Entry point for an netback accelerator plugin module.  Called to
143  * advertise its presence, and connect to any suitable backends.
144  */
145 int netback_connect_accelerator(unsigned version, int id, const char *eth_name, 
146                                 struct netback_accel_hooks *hooks)
147 {
148         struct netback_accelerator *new_accelerator;
149         unsigned eth_name_len;
150
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;
158                 } else {
159                         /* We have a more recent version than caller.
160                            Currently reject, but may in future be able
161                            to be backwardly compatible */
162                         return -EPROTO;
163                 }
164         }
165
166         new_accelerator = 
167                 kmalloc(sizeof(struct netback_accelerator), GFP_KERNEL);
168         if (!new_accelerator) {
169                 DPRINTK("%s: failed to allocate memory for accelerator\n",
170                         __FUNCTION__);
171                 return -ENOMEM;
172         }
173
174         new_accelerator->id = id;
175         
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",
180                         __FUNCTION__);
181                 kfree(new_accelerator);
182                 return -ENOMEM;
183         }
184         strlcpy(new_accelerator->eth_name, eth_name, eth_name_len);
185         
186         new_accelerator->hooks = hooks;
187
188         atomic_set(&new_accelerator->use_count, 0);
189         
190         mutex_lock(&accelerators_mutex);
191         list_add(&new_accelerator->link, &accelerators_list);
192         
193         /* tell existing backends about new plugin */
194         xenbus_for_each_backend(new_accelerator, 
195                                 netback_accelerator_probe_backend);
196
197         mutex_unlock(&accelerators_mutex);
198
199         return 0;
200
201 }
202 EXPORT_SYMBOL_GPL(netback_connect_accelerator);
203
204
205 /* 
206  * Disconnect an accelerator plugin module that has previously been
207  * connected.
208  */
209 void netback_disconnect_accelerator(int id, const char *eth_name)
210 {
211         struct netback_accelerator *accelerator, *next;
212
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);
221                         kfree(accelerator);
222                         break;
223                 }
224         }
225         mutex_unlock(&accelerators_mutex);
226 }
227 EXPORT_SYMBOL_GPL(netback_disconnect_accelerator);
228
229
230 void netback_probe_accelerators(struct backend_info *be,
231                                 struct xenbus_device *dev)
232 {
233         struct netback_accelerator *accelerator;
234
235         /* 
236          * Check list of accelerators to see if any is suitable, and
237          * use it if it is.
238          */
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);
244                         break;
245                 }
246         }
247         mutex_unlock(&accelerators_mutex);
248 }
249
250
251 void netback_remove_accelerators(struct backend_info *be,
252                                  struct xenbus_device *dev)
253 {
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;
261         }
262         mutex_unlock(&accelerators_mutex);
263 }
264
265
266 void netif_accel_init(void)
267 {
268         INIT_LIST_HEAD(&accelerators_list);
269 }