📄 xen-netfront.c
字号:
printk(KERN_WARNING "%s: add sysfs failed err=%d\n", __func__, err); goto fail; } return 0; fail: free_netdev(netdev); dev->dev.driver_data = NULL; return err;}static void xennet_end_access(int ref, void *page){ /* This frees the page as a side-effect */ if (ref != GRANT_INVALID_REF) gnttab_end_foreign_access(ref, 0, (unsigned long)page);}static void xennet_disconnect_backend(struct netfront_info *info){ /* Stop old i/f to prevent errors whilst we rebuild the state. */ spin_lock_bh(&info->rx_lock); spin_lock_irq(&info->tx_lock); netif_carrier_off(info->netdev); spin_unlock_irq(&info->tx_lock); spin_unlock_bh(&info->rx_lock); if (info->netdev->irq) unbind_from_irqhandler(info->netdev->irq, info->netdev); info->evtchn = info->netdev->irq = 0; /* End access and free the pages */ xennet_end_access(info->tx_ring_ref, info->tx.sring); xennet_end_access(info->rx_ring_ref, info->rx.sring); info->tx_ring_ref = GRANT_INVALID_REF; info->rx_ring_ref = GRANT_INVALID_REF; info->tx.sring = NULL; info->rx.sring = NULL;}/** * We are reconnecting to the backend, due to a suspend/resume, or a backend * driver restart. We tear down our netif structure and recreate it, but * leave the device-layer structures intact so that this is transparent to the * rest of the kernel. */static int netfront_resume(struct xenbus_device *dev){ struct netfront_info *info = dev->dev.driver_data; dev_dbg(&dev->dev, "%s\n", dev->nodename); xennet_disconnect_backend(info); return 0;}static int xen_net_read_mac(struct xenbus_device *dev, u8 mac[]){ char *s, *e, *macstr; int i; macstr = s = xenbus_read(XBT_NIL, dev->nodename, "mac", NULL); if (IS_ERR(macstr)) return PTR_ERR(macstr); for (i = 0; i < ETH_ALEN; i++) { mac[i] = simple_strtoul(s, &e, 16); if ((s == e) || (*e != ((i == ETH_ALEN-1) ? '\0' : ':'))) { kfree(macstr); return -ENOENT; } s = e+1; } kfree(macstr); return 0;}static irqreturn_t xennet_interrupt(int irq, void *dev_id){ struct net_device *dev = dev_id; struct netfront_info *np = netdev_priv(dev); unsigned long flags; spin_lock_irqsave(&np->tx_lock, flags); if (likely(netif_carrier_ok(dev))) { xennet_tx_buf_gc(dev); /* Under tx_lock: protects access to rx shared-ring indexes. */ if (RING_HAS_UNCONSUMED_RESPONSES(&np->rx)) netif_rx_schedule(dev, &np->napi); } spin_unlock_irqrestore(&np->tx_lock, flags); return IRQ_HANDLED;}static int setup_netfront(struct xenbus_device *dev, struct netfront_info *info){ struct xen_netif_tx_sring *txs; struct xen_netif_rx_sring *rxs; int err; struct net_device *netdev = info->netdev; info->tx_ring_ref = GRANT_INVALID_REF; info->rx_ring_ref = GRANT_INVALID_REF; info->rx.sring = NULL; info->tx.sring = NULL; netdev->irq = 0; err = xen_net_read_mac(dev, netdev->dev_addr); if (err) { xenbus_dev_fatal(dev, err, "parsing %s/mac", dev->nodename); goto fail; } txs = (struct xen_netif_tx_sring *)get_zeroed_page(GFP_KERNEL); if (!txs) { err = -ENOMEM; xenbus_dev_fatal(dev, err, "allocating tx ring page"); goto fail; } SHARED_RING_INIT(txs); FRONT_RING_INIT(&info->tx, txs, PAGE_SIZE); err = xenbus_grant_ring(dev, virt_to_mfn(txs)); if (err < 0) { free_page((unsigned long)txs); goto fail; } info->tx_ring_ref = err; rxs = (struct xen_netif_rx_sring *)get_zeroed_page(GFP_KERNEL); if (!rxs) { err = -ENOMEM; xenbus_dev_fatal(dev, err, "allocating rx ring page"); goto fail; } SHARED_RING_INIT(rxs); FRONT_RING_INIT(&info->rx, rxs, PAGE_SIZE); err = xenbus_grant_ring(dev, virt_to_mfn(rxs)); if (err < 0) { free_page((unsigned long)rxs); goto fail; } info->rx_ring_ref = err; err = xenbus_alloc_evtchn(dev, &info->evtchn); if (err) goto fail; err = bind_evtchn_to_irqhandler(info->evtchn, xennet_interrupt, IRQF_SAMPLE_RANDOM, netdev->name, netdev); if (err < 0) goto fail; netdev->irq = err; return 0; fail: return err;}/* Common code used when first setting up, and when resuming. */static int talk_to_backend(struct xenbus_device *dev, struct netfront_info *info){ const char *message; struct xenbus_transaction xbt; int err; /* Create shared ring, alloc event channel. */ err = setup_netfront(dev, info); if (err) goto out;again: err = xenbus_transaction_start(&xbt); if (err) { xenbus_dev_fatal(dev, err, "starting transaction"); goto destroy_ring; } err = xenbus_printf(xbt, dev->nodename, "tx-ring-ref", "%u", info->tx_ring_ref); if (err) { message = "writing tx ring-ref"; goto abort_transaction; } err = xenbus_printf(xbt, dev->nodename, "rx-ring-ref", "%u", info->rx_ring_ref); if (err) { message = "writing rx ring-ref"; goto abort_transaction; } err = xenbus_printf(xbt, dev->nodename, "event-channel", "%u", info->evtchn); if (err) { message = "writing event-channel"; goto abort_transaction; } err = xenbus_printf(xbt, dev->nodename, "request-rx-copy", "%u", 1); if (err) { message = "writing request-rx-copy"; goto abort_transaction; } err = xenbus_printf(xbt, dev->nodename, "feature-rx-notify", "%d", 1); if (err) { message = "writing feature-rx-notify"; goto abort_transaction; } err = xenbus_printf(xbt, dev->nodename, "feature-sg", "%d", 1); if (err) { message = "writing feature-sg"; goto abort_transaction; } err = xenbus_printf(xbt, dev->nodename, "feature-gso-tcpv4", "%d", 1); if (err) { message = "writing feature-gso-tcpv4"; goto abort_transaction; } err = xenbus_transaction_end(xbt, 0); if (err) { if (err == -EAGAIN) goto again; xenbus_dev_fatal(dev, err, "completing transaction"); goto destroy_ring; } return 0; abort_transaction: xenbus_transaction_end(xbt, 1); xenbus_dev_fatal(dev, err, "%s", message); destroy_ring: xennet_disconnect_backend(info); out: return err;}static int xennet_set_sg(struct net_device *dev, u32 data){ if (data) { struct netfront_info *np = netdev_priv(dev); int val; if (xenbus_scanf(XBT_NIL, np->xbdev->otherend, "feature-sg", "%d", &val) < 0) val = 0; if (!val) return -ENOSYS; } else if (dev->mtu > ETH_DATA_LEN) dev->mtu = ETH_DATA_LEN; return ethtool_op_set_sg(dev, data);}static int xennet_set_tso(struct net_device *dev, u32 data){ if (data) { struct netfront_info *np = netdev_priv(dev); int val; if (xenbus_scanf(XBT_NIL, np->xbdev->otherend, "feature-gso-tcpv4", "%d", &val) < 0) val = 0; if (!val) return -ENOSYS; } return ethtool_op_set_tso(dev, data);}static void xennet_set_features(struct net_device *dev){ /* Turn off all GSO bits except ROBUST. */ dev->features &= (1 << NETIF_F_GSO_SHIFT) - 1; dev->features |= NETIF_F_GSO_ROBUST; xennet_set_sg(dev, 0); /* We need checksum offload to enable scatter/gather and TSO. */ if (!(dev->features & NETIF_F_IP_CSUM)) return; if (!xennet_set_sg(dev, 1)) xennet_set_tso(dev, 1);}static int xennet_connect(struct net_device *dev){ struct netfront_info *np = netdev_priv(dev); int i, requeue_idx, err; struct sk_buff *skb; grant_ref_t ref; struct xen_netif_rx_request *req; unsigned int feature_rx_copy; err = xenbus_scanf(XBT_NIL, np->xbdev->otherend, "feature-rx-copy", "%u", &feature_rx_copy); if (err != 1) feature_rx_copy = 0; if (!feature_rx_copy) { dev_info(&dev->dev, "backend does not support copying receive path\n"); return -ENODEV; } err = talk_to_backend(np->xbdev, np); if (err) return err; xennet_set_features(dev); spin_lock_bh(&np->rx_lock); spin_lock_irq(&np->tx_lock); /* Step 1: Discard all pending TX packet fragments. */ xennet_release_tx_bufs(np); /* Step 2: Rebuild the RX buffer freelist and the RX ring itself. */ for (requeue_idx = 0, i = 0; i < NET_RX_RING_SIZE; i++) { if (!np->rx_skbs[i]) continue; skb = np->rx_skbs[requeue_idx] = xennet_get_rx_skb(np, i); ref = np->grant_rx_ref[requeue_idx] = xennet_get_rx_ref(np, i); req = RING_GET_REQUEST(&np->rx, requeue_idx); gnttab_grant_foreign_access_ref( ref, np->xbdev->otherend_id, pfn_to_mfn(page_to_pfn(skb_shinfo(skb)-> frags->page)), 0); req->gref = ref; req->id = requeue_idx; requeue_idx++; } np->rx.req_prod_pvt = requeue_idx; /* * Step 3: All public and private state should now be sane. Get * ready to start sending and receiving packets and give the driver * domain a kick because we've probably just requeued some * packets. */ netif_carrier_on(np->netdev); notify_remote_via_irq(np->netdev->irq); xennet_tx_buf_gc(dev); xennet_alloc_rx_buffers(dev); spin_unlock_irq(&np->tx_lock); spin_unlock_bh(&np->rx_lock); return 0;}/** * Callback received when the backend's state changes. */static void backend_changed(struct xenbus_device *dev, enum xenbus_state backend_state){ struct netfront_info *np = dev->dev.driver_data; struct net_device *netdev = np->netdev; dev_dbg(&dev->dev, "%s\n", xenbus_strstate(backend_state)); switch (backend_state) { case XenbusStateInitialising: case XenbusStateInitialised: case XenbusStateConnected: case XenbusStateUnknown: case XenbusStateClosed: break; case XenbusStateInitWait: if (dev->state != XenbusStateInitialising) break; if (xennet_connect(netdev) != 0) break; xenbus_switch_state(dev, XenbusStateConnected); break; case XenbusStateClosing: xenbus_frontend_closed(dev); break; }}static struct ethtool_ops xennet_ethtool_ops ={ .set_tx_csum = ethtool_op_set_tx_csum, .set_sg = xennet_set_sg, .set_tso = xennet_set_tso, .get_link = ethtool_op_get_link,};#ifdef CONFIG_SYSFSstatic ssize_t show_rxbuf_min(struct device *dev, struct device_attribute *attr, char *buf){ struct net_device *netdev = to_net_dev(dev); struct netfront_info *info = netdev_priv(netdev); return sprintf(buf, "%u\n", info->rx_min_target);}static ssize_t store_rxbuf_min(struct device *dev, struct device_attribute *attr, const char *buf, size_t len){ struct net_device *netdev = to_net_dev(dev); struct netfront_info *np = netdev_priv(netdev); char *endp; unsigned long target; if (!capable(CAP_NET_ADMIN)) return -EPERM; target = simple_strtoul(buf, &endp, 0); if (endp == buf) return -EBADMSG; if (target < RX_MIN_TARGET) target = RX_MIN_TARGET; if (target > RX_MAX_TARGET) target = RX_MAX_TARGET; spin_lock_bh(&np->rx_lock); if (target > np->rx_max_target) np->rx_max_target = target; np->rx_min_target = target; if (target > np->rx_target) np->rx_target = target; xennet_alloc_rx_buffers(netdev); spin_unlock_bh(&np->rx_lock); return len;}static ssize_t show_rxbuf_max(struct device *dev, struct device_attribute *attr, char *buf){ struct net_device *netdev = to_net_dev(dev); struct netfront_info *info = netdev_priv(netdev); return sprintf(buf, "%u\n", info->rx_max_target);}static ssize_t store_rxbuf_max(struct device *dev, struct device_attribute *attr, const char *buf, size_t len){ struct net_device *netdev = to_net_dev(dev); struct netfront_info *np = netdev_priv(netdev); char *endp; unsigned long target; if (!capable(CAP_NET_ADMIN)) return -EPERM; target = simple_strtoul(buf, &endp, 0); if (endp == buf) return -EBADMSG; if (target < RX_MIN_TARGET) target = RX_MIN_TARGET; if (target > RX_MAX_TARGET) target = RX_MAX_TARGET; spin_lock_bh(&np->rx_lock); if (target < np->rx_min_target) np->rx_min_target = target; np->rx_max_target = target; if (target < np->rx_target) np->rx_target = target; xennet_alloc_rx_buffers(netdev); spin_unlock_bh(&np->rx_lock); return len;}static ssize_t show_rxbuf_cur(struct device *dev, struct device_attribute *attr, char *buf){ struct net_device *netdev = to_net_dev(dev); struct netfront_info *info = netdev_priv(netdev); return sprintf(buf, "%u\n", info->rx_target);}static struct device_attribute xennet_attrs[] = { __ATTR(rxbuf_min, S_IRUGO|S_IWUSR, show_rxbuf_min, store_rxbuf_min), __ATTR(rxbuf_max, S_IRUGO|S_IWUSR, show_rxbuf_max, store_rxbuf_max), __ATTR(rxbuf_cur, S_IRUGO, show_rxbuf_cur, NULL),};static int xennet_sysfs_addif(struct net_device *netdev){ int i; int err; for (i = 0; i < ARRAY_SIZE(xennet_attrs); i++) { err = device_create_file(&netdev->dev, &xennet_attrs[i]); if (err) goto fail; } return 0; fail: while (--i >= 0) device_remove_file(&netdev->dev, &xennet_attrs[i]); return err;}static void xennet_sysfs_delif(struct net_device *netdev){ int i; for (i = 0; i < ARRAY_SIZE(xennet_attrs); i++) device_remove_file(&netdev->dev, &xennet_attrs[i]);}#endif /* CONFIG_SYSFS */static struct xenbus_device_id netfront_ids[] = { { "vif" }, { "" }};static int __devexit xennet_remove(struct xenbus_device *dev){ struct netfront_info *info = dev->dev.driver_data; dev_dbg(&dev->dev, "%s\n", dev->nodename); unregister_netdev(info->netdev); xennet_disconnect_backend(info); del_timer_sync(&info->rx_refill_timer); xennet_sysfs_delif(info->netdev); free_netdev(info->netdev); return 0;}static struct xenbus_driver netfront = { .name = "vif", .owner = THIS_MODULE, .ids = netfront_ids, .probe = netfront_probe, .remove = __devexit_p(xennet_remove), .resume = netfront_resume, .otherend_changed = backend_changed,};static int __init netif_init(void){ if (!is_running_on_xen()) return -ENODEV; if (is_initial_xendomain()) return 0; printk(KERN_INFO "Initialising Xen virtual ethernet driver.\n"); return xenbus_register_frontend(&netfront);}module_init(netif_init);static void __exit netif_exit(void){ if (is_initial_xendomain()) return; return xenbus_unregister_driver(&netfront);}module_exit(netif_exit);MODULE_DESCRIPTION("Xen virtual network device frontend");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -