- Update Xen patches to 3.3-rc5 and c/s 1157.
[linux-flexiantxendom0-3.2.10.git] / drivers / xen / sfc_netutil / accel_util.c
1 /****************************************************************************
2  * Solarflare driver for Xen network acceleration
3  *
4  * Copyright 2006-2008: Solarflare Communications Inc,
5  *                      9501 Jeronimo Road, Suite 250,
6  *                      Irvine, CA 92618, USA
7  *
8  * Maintained by Solarflare Communications <linux-xen-drivers@solarflare.com>
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU General Public License version 2 as published
12  * by the Free Software Foundation, incorporated herein by reference.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  ****************************************************************************
23  */
24
25 #include <linux/slab.h>
26 #include <linux/if_ether.h>
27 #include <linux/module.h>
28 #include <asm/io.h>
29 #include <asm/pgtable.h>
30 #include <asm/hypercall.h>
31 #include <xen/xenbus.h>
32 #include <xen/gnttab.h>
33
34 #include "accel_util.h"
35
36 #ifdef EFX_GCOV
37 #include "gcov.h"
38
39 static int __init net_accel_init(void)
40 {
41         gcov_provider_init(THIS_MODULE);
42         return 0;
43 }
44 module_init(net_accel_init);
45
46 static void __exit net_accel_exit(void)
47 {
48         gcov_provider_fini(THIS_MODULE);
49 }
50 module_exit(net_accel_exit);
51 #endif
52
53 /* Shutdown remote domain that is misbehaving */
54 int net_accel_shutdown_remote(int domain)
55 {
56         struct sched_remote_shutdown sched_shutdown = {
57                 .domain_id = domain,
58                 .reason = SHUTDOWN_crash
59         };
60
61         EPRINTK("Crashing domain %d\n", domain);
62
63         return HYPERVISOR_sched_op(SCHEDOP_remote_shutdown, &sched_shutdown);
64 }
65 EXPORT_SYMBOL(net_accel_shutdown_remote);
66
67
68 /* Based on xenbus_backend_client.c:xenbus_map_ring() */
69 static int net_accel_map_grant(struct xenbus_device *dev, int gnt_ref,
70                                grant_handle_t *handle, void *vaddr, 
71                                u64 *dev_bus_addr, unsigned flags)
72 {
73         struct gnttab_map_grant_ref op;
74         int ret;
75         
76         gnttab_set_map_op(&op, (unsigned long)vaddr, flags,
77                           gnt_ref, dev->otherend_id);
78
79         gnttab_check_GNTST_eagain_do_while(GNTTABOP_map_grant_ref, &op);
80
81         if (op.status != GNTST_okay) {
82                 xenbus_dev_error
83                         (dev, op.status,
84                          "failed mapping in shared page %d from domain %d\n",
85                          gnt_ref, dev->otherend_id);
86                 ret = -EINVAL;
87         } else {
88                 *handle = op.handle;
89                 if (dev_bus_addr)
90                         *dev_bus_addr = op.dev_bus_addr;
91                 ret = 0;
92         }
93
94         return ret;
95 }
96
97
98 /* Based on xenbus_backend_client.c:xenbus_unmap_ring() */
99 static int net_accel_unmap_grant(struct xenbus_device *dev, 
100                                  grant_handle_t handle,
101                                  void *vaddr, u64 dev_bus_addr,
102                                  unsigned flags)
103 {
104         struct gnttab_unmap_grant_ref op;
105
106         gnttab_set_unmap_op(&op, (unsigned long)vaddr, flags, handle);
107         
108         if (dev_bus_addr)
109                 op.dev_bus_addr = dev_bus_addr;
110
111         BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1));
112
113         if (op.status != GNTST_okay)
114                 xenbus_dev_error(dev, op.status,
115                                  "failed unmapping page at handle %d error %d\n",
116                                  handle, op.status);
117
118         return op.status == GNTST_okay ? 0 : -EINVAL;
119 }
120
121
122 int net_accel_map_device_page(struct xenbus_device *dev,  
123                               int gnt_ref, grant_handle_t *handle,
124                               u64 *dev_bus_addr)
125 {
126         return net_accel_map_grant(dev, gnt_ref, handle, 0, dev_bus_addr,
127                                    GNTMAP_device_map);
128 }
129 EXPORT_SYMBOL_GPL(net_accel_map_device_page);
130
131  
132 int net_accel_unmap_device_page(struct xenbus_device *dev,
133                                 grant_handle_t handle, u64 dev_bus_addr)
134 {
135         return net_accel_unmap_grant(dev, handle, 0, dev_bus_addr, 
136                                      GNTMAP_device_map);
137 }
138 EXPORT_SYMBOL_GPL(net_accel_unmap_device_page);
139
140
141 struct net_accel_valloc_grant_mapping {
142         struct vm_struct *vm;
143         int pages;
144         grant_handle_t grant_handles[0];
145 };
146
147 /* Map a series of grants into a contiguous virtual area */
148 static void *net_accel_map_grants_valloc(struct xenbus_device *dev, 
149                                          unsigned *grants, int npages, 
150                                          unsigned flags, void **priv)
151 {
152         struct net_accel_valloc_grant_mapping *map;
153         struct vm_struct *vm;
154         void *addr;
155         int i, j, rc;
156
157         vm  = alloc_vm_area(PAGE_SIZE * npages, NULL);
158         if (vm == NULL) {
159                 EPRINTK("No memory from alloc_vm_area.\n");
160                 return NULL;
161         }
162         /* 
163          * Get a structure in which we will record all the info needed
164          * to undo the mapping.
165          */
166         map = kzalloc(sizeof(struct net_accel_valloc_grant_mapping)  + 
167                       npages * sizeof(grant_handle_t), GFP_KERNEL);
168         if (map == NULL) {
169                 EPRINTK("No memory for net_accel_valloc_grant_mapping\n");
170                 free_vm_area(vm);
171                 return NULL;
172         }
173         map->vm = vm;
174         map->pages = npages;
175
176         /* Do the actual mapping */
177         addr = vm->addr;
178
179         for (i = 0; i < npages; i++) {
180                 rc = net_accel_map_grant(dev, grants[i], map->grant_handles + i, 
181                                          addr, NULL, flags);
182                 if (rc < 0)
183                         goto undo;
184                 addr = (void*)((unsigned long)addr + PAGE_SIZE);
185         }
186
187         if (priv)
188                 *priv = (void *)map;
189         else
190                 kfree(map);
191
192         return vm->addr;
193
194  undo:
195         EPRINTK("Aborting contig map due to single map failure %d (%d of %d)\n",
196                 rc, i+1, npages);
197         for (j = 0; j < i; j++) {
198                 addr = (void*)((unsigned long)vm->addr + (j * PAGE_SIZE));
199                 net_accel_unmap_grant(dev, map->grant_handles[j], addr, 0,
200                                       flags);
201         }
202         free_vm_area(vm);
203         kfree(map);
204         return NULL;
205 }
206
207 /* Undo the result of the mapping */
208 static void net_accel_unmap_grants_vfree(struct xenbus_device *dev, 
209                                          unsigned flags, void *priv)
210 {
211         struct net_accel_valloc_grant_mapping *map = 
212                 (struct net_accel_valloc_grant_mapping *)priv;
213
214         void *addr = map->vm->addr;
215         int npages = map->pages;
216         int i;
217
218         for (i = 0; i < npages; i++) {
219                 net_accel_unmap_grant(dev, map->grant_handles[i], addr, 0,
220                                       flags);
221                 addr = (void*)((unsigned long)addr + PAGE_SIZE);
222         }
223         free_vm_area(map->vm);
224         kfree(map);
225 }
226
227
228 void *net_accel_map_grants_contig(struct xenbus_device *dev,
229                                 unsigned *grants, int npages, 
230                                 void **priv)
231 {
232     return net_accel_map_grants_valloc(dev, grants, npages, GNTMAP_host_map, priv);
233 }
234 EXPORT_SYMBOL(net_accel_map_grants_contig);
235
236
237 void net_accel_unmap_grants_contig(struct xenbus_device *dev,
238                                    void *priv)
239 {
240         net_accel_unmap_grants_vfree(dev, GNTMAP_host_map, priv);
241 }
242 EXPORT_SYMBOL(net_accel_unmap_grants_contig);
243
244
245 void *net_accel_map_iomem_page(struct xenbus_device *dev, int gnt_ref,
246                              void **priv)
247 {
248         return net_accel_map_grants_valloc(dev, &gnt_ref, 1, GNTMAP_host_map, priv);
249 }
250 EXPORT_SYMBOL(net_accel_map_iomem_page);
251
252
253 void net_accel_unmap_iomem_page(struct xenbus_device *dev, void *priv)
254 {
255         net_accel_unmap_grants_vfree(dev, GNTMAP_host_map, priv);
256 }
257 EXPORT_SYMBOL(net_accel_unmap_iomem_page);
258
259
260 int net_accel_grant_page(struct xenbus_device *dev, unsigned long mfn, 
261                          int is_iomem)
262 {
263         int err = gnttab_grant_foreign_access(dev->otherend_id, mfn,
264                                               is_iomem ? GTF_PCD : 0);
265         if (err < 0)
266                 xenbus_dev_error(dev, err, "failed granting access to page\n");
267         return err;
268 }
269 EXPORT_SYMBOL_GPL(net_accel_grant_page);
270
271
272 int net_accel_ungrant_page(grant_ref_t gntref)
273 {
274         if (unlikely(gnttab_query_foreign_access(gntref) != 0)) {
275                 EPRINTK("%s: remote domain still using grant %d\n", __FUNCTION__, 
276                         gntref);
277                 return -EBUSY;
278         }
279
280         gnttab_end_foreign_access(gntref, 0);
281         return 0;
282 }
283 EXPORT_SYMBOL_GPL(net_accel_ungrant_page);
284
285
286 int net_accel_xen_net_read_mac(struct xenbus_device *dev, u8 mac[])
287 {
288         char *s, *e, *macstr;
289         int i;
290
291         macstr = s = xenbus_read(XBT_NIL, dev->nodename, "mac", NULL);
292         if (IS_ERR(macstr))
293                 return PTR_ERR(macstr);
294
295         for (i = 0; i < ETH_ALEN; i++) {
296                 mac[i] = simple_strtoul(s, &e, 16);
297                 if ((s == e) || (*e != ((i == ETH_ALEN-1) ? '\0' : ':'))) {
298                         kfree(macstr);
299                         return -ENOENT;
300                 }
301                 s = e+1;
302         }
303
304         kfree(macstr);
305         return 0;
306 }
307 EXPORT_SYMBOL_GPL(net_accel_xen_net_read_mac);
308
309
310 void net_accel_update_state(struct xenbus_device *dev, int state)
311 {
312         struct xenbus_transaction tr;
313         int err;
314
315         DPRINTK("%s: setting accelstate to %s\n", __FUNCTION__,
316                 xenbus_strstate(state));
317
318         if (xenbus_exists(XBT_NIL, dev->nodename, "")) {
319                 VPRINTK("%s: nodename %s\n", __FUNCTION__, dev->nodename);
320         again:
321                 err = xenbus_transaction_start(&tr);
322                 if (err == 0)
323                         err = xenbus_printf(tr, dev->nodename, "accelstate",
324                                             "%d", state);
325                 if (err != 0) {
326                         xenbus_transaction_end(tr, 1);
327                 } else {
328                         err = xenbus_transaction_end(tr, 0);
329                         if (err == -EAGAIN)
330                                 goto again;
331                 }
332         }
333 }
334 EXPORT_SYMBOL_GPL(net_accel_update_state);
335
336 MODULE_LICENSE("GPL");