/* Must be called under rtnl. */ void netmap_catch_tx(struct netmap_generic_adapter *gna, int enable) { struct netmap_adapter *na = &gna->up.up; struct ifnet *ifp = netmap_generic_getifp(gna); if (enable) { /* * Save the old pointer to the netdev_ops, * create an updated netdev ops replacing the * ndo_select_queue() and ndo_start_xmit() methods * with our custom ones, and make the driver use it. */ na->if_transmit = (void *)ifp->netdev_ops; /* Save a redundant copy of ndo_start_xmit(). */ gna->save_start_xmit = ifp->netdev_ops->ndo_start_xmit; gna->generic_ndo = *ifp->netdev_ops; /* Copy all */ gna->generic_ndo.ndo_start_xmit = &generic_ndo_start_xmit; #ifndef NETMAP_LINUX_SELECT_QUEUE printk("%s: no packet steering support\n", __FUNCTION__); #else gna->generic_ndo.ndo_select_queue = &generic_ndo_select_queue; #endif ifp->netdev_ops = &gna->generic_ndo; } else { /* Restore the original netdev_ops. */ ifp->netdev_ops = (void *)na->if_transmit; } }
/* Ask the Linux RX subsystem to intercept (or stop intercepting) * the packets incoming from the interface attached to 'na'. */ int nm_os_catch_rx(struct netmap_generic_adapter *gna, int intercept) { #ifndef NETMAP_LINUX_HAVE_RX_REGISTER return 0; #else /* HAVE_RX_REGISTER */ struct netmap_adapter *na = &gna->up.up; struct ifnet *ifp = netmap_generic_getifp(gna); if (intercept) { return -netdev_rx_handler_register(ifp, &linux_generic_rx_handler, na); } else { netdev_rx_handler_unregister(ifp); return 0; } #endif /* HAVE_RX_REGISTER */ }
static int nm_os_catch_qdisc(struct netmap_generic_adapter *gna, int intercept) { struct netmap_adapter *na = &gna->up.up; struct ifnet *ifp = netmap_generic_getifp(gna); struct nm_generic_qdisc *qdiscopt = NULL; struct Qdisc *fqdisc = NULL; struct nlattr *nla = NULL; struct netdev_queue *txq; unsigned int i; if (!gna->txqdisc) { return 0; } if (intercept) { nla = kmalloc(nla_attr_size(sizeof(*qdiscopt)), GFP_KERNEL); if (!nla) { D("Failed to allocate netlink attribute"); return ENOMEM; } nla->nla_type = RTM_NEWQDISC; nla->nla_len = nla_attr_size(sizeof(*qdiscopt)); qdiscopt = (struct nm_generic_qdisc *)nla_data(nla); memset(qdiscopt, 0, sizeof(*qdiscopt)); qdiscopt->limit = na->num_tx_desc; } if (ifp->flags & IFF_UP) { dev_deactivate(ifp); } /* Replace the current qdiscs with our own. */ for (i = 0; i < ifp->real_num_tx_queues; i++) { struct Qdisc *nqdisc = NULL; struct Qdisc *oqdisc; int err; txq = netdev_get_tx_queue(ifp, i); if (intercept) { /* This takes a refcount to netmap module, alloc the * qdisc and calls the init() op with NULL netlink * attribute. */ nqdisc = qdisc_create_dflt( #ifndef NETMAP_LINUX_QDISC_CREATE_DFLT_3ARGS ifp, #endif /* NETMAP_LINUX_QDISC_CREATE_DFLT_3ARGS */ txq, &generic_qdisc_ops, TC_H_UNSPEC); if (!nqdisc) { D("Failed to create qdisc"); goto qdisc_create; } fqdisc = fqdisc ?: nqdisc; /* Call the change() op passing a valid netlink * attribute. This is used to set the queue idx. */ qdiscopt->qidx = i; err = nqdisc->ops->change(nqdisc, nla); if (err) { D("Failed to init qdisc"); goto qdisc_create; } } oqdisc = dev_graft_qdisc(txq, nqdisc); /* We can call this also with * odisc == &noop_qdisc, since the noop * qdisc has the TCQ_F_BUILTIN flag set, * and so qdisc_destroy will skip it. */ qdisc_destroy(oqdisc); } kfree(nla); if (ifp->qdisc) { qdisc_destroy(ifp->qdisc); } if (intercept) { atomic_inc(&fqdisc->refcnt); ifp->qdisc = fqdisc; } else { ifp->qdisc = &noop_qdisc; } if (ifp->flags & IFF_UP) { dev_activate(ifp); } return 0; qdisc_create: if (nla) { kfree(nla); } nm_os_catch_qdisc(gna, 0); return -1; }