예제 #1
0
파일: netmap_linux.c 프로젝트: majek/netmap
/* 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;
    }
}
예제 #2
0
/* 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 */
}
예제 #3
0
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;
}