/* Transmit routine used by generic_netmap_txsync(). Returns 0 on success and -1 on error (which may be packet drops or other errors). */ int generic_xmit_frame(struct ifnet *ifp, struct mbuf *m, void *addr, u_int len, u_int ring_nr) { netdev_tx_t ret; /* Empty the sk_buff. */ if (unlikely(skb_headroom(m))) skb_push(m, skb_headroom(m)); skb_trim(m, 0); /* TODO Support the slot flags (NS_MOREFRAG, NS_INDIRECT). */ skb_copy_to_linear_data(m, addr, len); // skb_store_bits(m, 0, addr, len); skb_put(m, len); NM_ATOMIC_INC(&m->users); m->dev = ifp; /* Tell generic_ndo_start_xmit() to pass this mbuf to the driver. */ m->priority = NM_MAGIC_PRIORITY_TX; skb_set_queue_mapping(m, ring_nr); ret = dev_queue_xmit(m); if (likely(ret == NET_XMIT_SUCCESS)) { return 0; } if (unlikely(ret != NET_XMIT_DROP)) { /* If something goes wrong in the TX path, there is nothing intelligent we can do (for now) apart from error reporting. */ RD(5, "dev_queue_xmit failed: HARD ERROR %d", ret); } return -1; }
/* Transmit routine used by generic_netmap_txsync(). Returns 0 on success and -1 on error (which may be packet drops or other errors). */ int nm_os_generic_xmit_frame(struct nm_os_gen_arg *a) { struct mbuf *m = a->m; struct ifnet *ifp = a->ifp; u_int len = a->len; netdev_tx_t ret; /* We know that the driver needs to prepend ifp->needed_headroom bytes * to each packet to be transmitted. We then reset the mbuf pointers * to the correct initial state: * ___________________________________________ * ^ ^ ^ * | | | * head data end * tail * * which correspond to an empty buffer with exactly * ifp->needed_headroom bytes between head and data. */ m->len = 0; m->data = m->head + ifp->needed_headroom; skb_reset_tail_pointer(m); skb_reset_mac_header(m); skb_reset_network_header(m); /* Copy a netmap buffer into the mbuf. * TODO Support the slot flags (NS_MOREFRAG, NS_INDIRECT). */ skb_copy_to_linear_data(m, a->addr, len); // skb_store_bits(m, 0, addr, len); skb_put(m, len); /* Hold a reference on this, we are going to recycle mbufs as * much as possible. */ NM_ATOMIC_INC(&m->users); /* On linux m->dev is not reliable, since it can be changed by the * ndo_start_xmit() callback. This happens, for instance, with veth * and bridge drivers. For this reason, the nm_os_generic_xmit_frame() * implementation for linux stores a copy of m->dev into the * destructor_arg field. */ m->dev = ifp; skb_shinfo(m)->destructor_arg = m->dev; /* Tell generic_ndo_start_xmit() to pass this mbuf to the driver. */ skb_set_queue_mapping(m, a->ring_nr); m->priority = a->qevent ? NM_MAGIC_PRIORITY_TXQE : NM_MAGIC_PRIORITY_TX; ret = dev_queue_xmit(m); if (unlikely(ret != NET_XMIT_SUCCESS)) { /* Reset priority, so that generic_netmap_tx_clean() can * reclaim this mbuf. */ m->priority = 0; /* Qdisc queue is full (this cannot happen with * the netmap-aware qdisc, see exaplanation in * netmap_generic_txsync), or qdisc is being * deactivated. In the latter case dev_queue_xmit() * does not call the enqueue method and returns * NET_XMIT_DROP. * If there is no carrier, the generic qdisc is * not yet active (is pending in the qdisc_sleeping * field), and so the temporary noop qdisc enqueue * method will drop the packet and return NET_XMIT_CN. */ RD(3, "Warning: dev_queue_xmit() is dropping [%d]", ret); return -1; } return 0; }