1 /****************************************************************************
2 * Solarflare driver for Xen network acceleration
4 * Copyright 2006-2008: Solarflare Communications Inc,
5 * 9501 Jeronimo Road, Suite 250,
6 * Irvine, CA 92618, USA
8 * Maintained by Solarflare Communications <linux-xen-drivers@solarflare.com>
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.
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.
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 ****************************************************************************
25 #include <linux/slab.h>
26 #include <linux/if_ether.h>
27 #include <linux/module.h>
29 #include <asm/pgtable.h>
30 #include <asm/hypercall.h>
31 #include <xen/xenbus.h>
32 #include <xen/gnttab.h>
34 #include "accel_util.h"
39 static int __init net_accel_init(void)
41 gcov_provider_init(THIS_MODULE);
44 module_init(net_accel_init);
46 static void __exit net_accel_exit(void)
48 gcov_provider_fini(THIS_MODULE);
50 module_exit(net_accel_exit);
53 /* Shutdown remote domain that is misbehaving */
54 int net_accel_shutdown_remote(int domain)
56 struct sched_remote_shutdown sched_shutdown = {
58 .reason = SHUTDOWN_crash
61 EPRINTK("Crashing domain %d\n", domain);
63 return HYPERVISOR_sched_op(SCHEDOP_remote_shutdown, &sched_shutdown);
65 EXPORT_SYMBOL(net_accel_shutdown_remote);
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)
73 struct gnttab_map_grant_ref op;
76 gnttab_set_map_op(&op, (unsigned long)vaddr, flags,
77 gnt_ref, dev->otherend_id);
79 gnttab_check_GNTST_eagain_do_while(GNTTABOP_map_grant_ref, &op);
81 if (op.status != GNTST_okay) {
84 "failed mapping in shared page %d from domain %d\n",
85 gnt_ref, dev->otherend_id);
90 *dev_bus_addr = op.dev_bus_addr;
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,
104 struct gnttab_unmap_grant_ref op;
106 gnttab_set_unmap_op(&op, (unsigned long)vaddr, flags, handle);
109 op.dev_bus_addr = dev_bus_addr;
111 BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1));
113 if (op.status != GNTST_okay)
114 xenbus_dev_error(dev, op.status,
115 "failed unmapping page at handle %d error %d\n",
118 return op.status == GNTST_okay ? 0 : -EINVAL;
122 int net_accel_map_device_page(struct xenbus_device *dev,
123 int gnt_ref, grant_handle_t *handle,
126 return net_accel_map_grant(dev, gnt_ref, handle, 0, dev_bus_addr,
129 EXPORT_SYMBOL_GPL(net_accel_map_device_page);
132 int net_accel_unmap_device_page(struct xenbus_device *dev,
133 grant_handle_t handle, u64 dev_bus_addr)
135 return net_accel_unmap_grant(dev, handle, 0, dev_bus_addr,
138 EXPORT_SYMBOL_GPL(net_accel_unmap_device_page);
141 struct net_accel_valloc_grant_mapping {
142 struct vm_struct *vm;
144 grant_handle_t grant_handles[0];
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)
152 struct net_accel_valloc_grant_mapping *map;
153 struct vm_struct *vm;
157 vm = alloc_vm_area(PAGE_SIZE * npages, NULL);
159 EPRINTK("No memory from alloc_vm_area.\n");
163 * Get a structure in which we will record all the info needed
164 * to undo the mapping.
166 map = kzalloc(sizeof(struct net_accel_valloc_grant_mapping) +
167 npages * sizeof(grant_handle_t), GFP_KERNEL);
169 EPRINTK("No memory for net_accel_valloc_grant_mapping\n");
176 /* Do the actual mapping */
179 for (i = 0; i < npages; i++) {
180 rc = net_accel_map_grant(dev, grants[i], map->grant_handles + i,
184 addr = (void*)((unsigned long)addr + PAGE_SIZE);
195 EPRINTK("Aborting contig map due to single map failure %d (%d of %d)\n",
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,
207 /* Undo the result of the mapping */
208 static void net_accel_unmap_grants_vfree(struct xenbus_device *dev,
209 unsigned flags, void *priv)
211 struct net_accel_valloc_grant_mapping *map =
212 (struct net_accel_valloc_grant_mapping *)priv;
214 void *addr = map->vm->addr;
215 int npages = map->pages;
218 for (i = 0; i < npages; i++) {
219 net_accel_unmap_grant(dev, map->grant_handles[i], addr, 0,
221 addr = (void*)((unsigned long)addr + PAGE_SIZE);
223 free_vm_area(map->vm);
228 void *net_accel_map_grants_contig(struct xenbus_device *dev,
229 unsigned *grants, int npages,
232 return net_accel_map_grants_valloc(dev, grants, npages, GNTMAP_host_map, priv);
234 EXPORT_SYMBOL(net_accel_map_grants_contig);
237 void net_accel_unmap_grants_contig(struct xenbus_device *dev,
240 net_accel_unmap_grants_vfree(dev, GNTMAP_host_map, priv);
242 EXPORT_SYMBOL(net_accel_unmap_grants_contig);
245 void *net_accel_map_iomem_page(struct xenbus_device *dev, int gnt_ref,
248 return net_accel_map_grants_valloc(dev, &gnt_ref, 1, GNTMAP_host_map, priv);
250 EXPORT_SYMBOL(net_accel_map_iomem_page);
253 void net_accel_unmap_iomem_page(struct xenbus_device *dev, void *priv)
255 net_accel_unmap_grants_vfree(dev, GNTMAP_host_map, priv);
257 EXPORT_SYMBOL(net_accel_unmap_iomem_page);
260 int net_accel_grant_page(struct xenbus_device *dev, unsigned long mfn,
263 int err = gnttab_grant_foreign_access(dev->otherend_id, mfn,
264 is_iomem ? GTF_PCD : 0);
266 xenbus_dev_error(dev, err, "failed granting access to page\n");
269 EXPORT_SYMBOL_GPL(net_accel_grant_page);
272 int net_accel_ungrant_page(grant_ref_t gntref)
274 if (unlikely(gnttab_query_foreign_access(gntref) != 0)) {
275 EPRINTK("%s: remote domain still using grant %d\n", __FUNCTION__,
280 gnttab_end_foreign_access(gntref, 0);
283 EXPORT_SYMBOL_GPL(net_accel_ungrant_page);
286 int net_accel_xen_net_read_mac(struct xenbus_device *dev, u8 mac[])
288 char *s, *e, *macstr;
291 macstr = s = xenbus_read(XBT_NIL, dev->nodename, "mac", NULL);
293 return PTR_ERR(macstr);
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' : ':'))) {
307 EXPORT_SYMBOL_GPL(net_accel_xen_net_read_mac);
310 void net_accel_update_state(struct xenbus_device *dev, int state)
312 struct xenbus_transaction tr;
315 DPRINTK("%s: setting accelstate to %s\n", __FUNCTION__,
316 xenbus_strstate(state));
318 if (xenbus_exists(XBT_NIL, dev->nodename, "")) {
319 VPRINTK("%s: nodename %s\n", __FUNCTION__, dev->nodename);
321 err = xenbus_transaction_start(&tr);
323 err = xenbus_printf(tr, dev->nodename, "accelstate",
326 xenbus_transaction_end(tr, 1);
328 err = xenbus_transaction_end(tr, 0);
334 EXPORT_SYMBOL_GPL(net_accel_update_state);
336 MODULE_LICENSE("GPL");