/***************************************************************************/ /*! \file dumm_accel_frontend.c Dummy accelerated plugin module Copyright 2006 Solarflare Communications Inc, 9501 Jeronimo Road, Suite 250, Irvine, CA 92618, USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation, incorporated herein by reference. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /***************************************************************************/ #include #include #include #include #include #include #include /* drivers/xen/netfront/netfront.h */ #include "netfront.h" static const char *frontend_name = "dummynetaccel"; static struct netfront_accel_hooks accel_hooks = { .new_device = &netfront_accel_probe, .suspend = &netfront_accel_xenbus_suspend, .resume = &netfront_accel_xenbus_resume, .remove = &netfront_accel_xenbus_remove, .backend_changed = &netfront_accel_backend_changed, .netdev_poll = &netfront_accel_netdev_poll, .start_xmit = &netfront_accel_netdev_start_xmit, .start_napi_interrupts = &netfront_accel_start_napi_interrupts, .stop_napi_interrupts = &netfront_accel_stop_napi_interrupts }; static int netfront_accel_init(void) { /* Initialise internal state to module... */ /* Hook into normal netfront */ netfront_accelerator_loaded(frontend_name, &accel_hooks); /* Should now get a probe if netfront has any vifs interested in our acceleration */ return 0; } module_init(netfront_accel_init); static void netfront_accel_exit(void) { /* Unhook from normal netfront */ netfront_accelerator_unloaded(frontend_name); /* ...and rest of module cleanup */ } module_exit(netfront_accel_exit); /* The frontend calls this function to ask this plugin to support a new network interface */ int netfront_accel_probe(struct net_device *net_dev, struct xenbus_device *dev) { unsigned flags; /* Setup per-device internal state */ /* Store internal state handle for access from the xenbus_device */ ((struct netfront_info *)dev->dev.driver_data)->accel_priv = my_internal_state; /* Create shared pages, grants, irqs etc for communication with accelerated backend plugin module */ /* Kick off contact with the backend */ netfront_accel_update_state(dev, XenbusStateConnected); /* Enable interrupts */ local_irq_save(flags); if(netfront_accel_enable_interrupts()) { /* Something to do already */ netif_rx_schedule(net_dev); } local_irq_restore(flags); return 0; } /* Called on xenbus_suspend callback, allows plugin to remove accelerated path */ int netfront_accel_xenbus_suspend(struct xenbus_device *dev) { /* Disconnect the accelerated plugin */ netfront_accelerator_unloaded(frontend_name); /* Tell the accelerated backend that we're going */ net_accel_update_state(dev, XenbusStateClosing); return 0; } /* Called on xenbus_resume callback, allows plugin to restore accelerated path */ int netfront_accel_xenbus_resume(struct xenbus_device *dev) { /* Reconnect to the frontend module */ netfront_accelerator_loaded(frontend_name, &accel_hooks); /* Should now get a probe if netfront has any vifs interested in our acceleration */ return 0; } /* Called on xenbus_remove callback, allows plugin to remove internal state */ int netfront_accel_xenbus_remove(struct xenbus_device *dev) { /* Remove the link to accelerated private state */ ((struct netfront_info *)dev->dev.driver_data)->accel_priv = NULL; return 0; } /* Function to deal with Xenbus state change in backend */ void netfront_accel_backend_changed(struct xenbus_device *dev, XenbusState frontend_state) { /* Not interested in changes of the normal netfront/netback state machine at the moment. Others may be */ } /* Function to deal with Xenbus accelstate change in backend */ void netfront_accel_backend_accel_changed(struct xenbus_device *dev, XenbusState backend_state) { /* This is called off a watch set up by the accelerated plugin */ /* Take appropriate internal action based on accelerated state changing - allows the accelerated plugins to go through a seperate but similar state machine cycle to the normal netfront/netback drivers */ switch(backend_state) { case XenbusStateUnknown: /* ...etc */ case XenbusStateConnected: /* We're now ready for action, notify frontend to start using us for this device */ netfront_accelerator_ready(frontend_name, dev) } } /* The net_device is being polled, check the accelerated hardware for any pending packets */ int netfront_accel_netdev_poll(struct net_device *net_dev, int *budget) { int rx_done, rx_allowed = *budget; struct netfront_info *np = netdev_priv(net_dev); my_internal_state = np->accel_priv; /* Probe the fast path accelerated hardware to see if there have been any packets delivered direct to this frontend plugin. This will call netif_receive_skb() on any received packets to pass to stack */ rx_done = probe_my_event_queue(my_internal_state, rx_allowed); *budget -= rx_done; /* Done all we want to ? */ if(rx_done < rx_allowed) return 0; /* More to do */ return 1; } /* start_xmit: Used to give the accelerated plugin the option of sending a packet. Returns non-zero if has done so, or zero to decline and force the packet onto normal send path */ int netfront_accel_netdev_start_xmit(struct sk_buff *skb, struct net_device *net_dev) { int handled; struct netfront_info *np = netdev_priv(net_dev); my_internal_state = np->accel_priv; /* Have a look at this packet and see if it is one to send on the fast path */ handled = netfront_accel_xmit(my_internal_state, skb); if(handled == BUSY) { /* We'd like to take it, but busy at the moment */ netif_stop_queue(net_dev); /* netif_wake_queue() will be called when no longer busy and this packet has been sent */ } if(handled == CANT) /* We can't send this on the fast path, force onto normal slow path by returning zero */ return 0; else return 1; } /* Process request from netfront to start napi interrupt mode. (i.e. enable interrupts as it's finished polling) */ int netfront_accel_start_napi_interrupts(struct net_device *dev) { struct netfront_info *np = netdev_priv(dev); my_internal_state = np->accel_priv; if(!netfront_accel_enable_interrupts(my_internal_state)) { /* There was something there already, tell caller we had something to do. */ return 1; } return 0; } /* Process request from netfront to stop napi interrupt mode. (i.e. disable interrupts as it's starting to poll */ void netfront_accel_stop_napi_interrupts(struct net_device *dev) { struct netfront_info *np = netdev_priv(dev); my_internal_state = np->accel_priv; netfront_accel_disable_interrupts(my_internal_state); } void netfront_accel_update_state(struct xenbus_device *dev, int state) { struct xenbus_transaction tr; int err; if(xenbus_exists(XBT_NIL, dev->nodename, "")) { again: err = xenbus_transaction_start(&tr); if (err == 0) err = xenbus_printf(tr, dev->nodename, "accelstate", "%d", state); if (err != 0) xenbus_transaction_end(tr, 1); else { err = xenbus_transaction_end(tr, 0); if(err == -EAGAIN) goto again; } } }