diff --git a/sys/dev/netmap/netmap_freebsd.c b/sys/dev/netmap/netmap_freebsd.c --- a/sys/dev/netmap/netmap_freebsd.c +++ b/sys/dev/netmap/netmap_freebsd.c @@ -403,15 +403,20 @@ * addr and len identify the netmap buffer, m is the (preallocated) * mbuf to use for transmissions. * - * We should add a reference to the mbuf so the m_freem() at the end - * of the transmission does not consume resources. + * Zero-copy transmission is possible if netmap is attached directly to a + * hardware interface: when cleaning we simply wait for the mbuf cluster + * refcount to decrement to 1, indicating that the driver has completed + * transmission and is done with the buffer. However, this approach can + * lead to queue deadlocks when attaching to software interfaces (e.g., + * if_bridge) since we cannot rely on member ports to promptly reclaim + * transmitted mbufs. Since there is no easy way to distinguish these + * cases, we currently always copy the buffer. * - * On FreeBSD, and on multiqueue cards, we can force the queue using + * On multiqueue cards, we can force the queue using * if (M_HASHTYPE_GET(m) != M_HASHTYPE_NONE) * i = m->m_pkthdr.flowid % adapter->num_queues; * else * i = curcpu % adapter->num_queues; - * */ int nm_os_generic_xmit_frame(struct nm_os_gen_arg *a) @@ -421,16 +426,18 @@ if_t ifp = a->ifp; struct mbuf *m = a->m; - /* Link the external storage to - * the netmap buffer, so that no copy is necessary. */ - m->m_ext.ext_buf = m->m_data = a->addr; - m->m_ext.ext_size = len; + if (MBUF_REFCNT(m) != 1) { + nm_prerr("invalid refcnt %d for %p", MBUF_REFCNT(m), m); + panic("in generic_xmit_frame"); + } + if (unlikely(m->m_ext.ext_size < len)) { + nm_prlim(2, "size %d < len %d", m->m_ext.ext_size, len); + len = m->m_ext.ext_size; + } + m_copyback(m, 0, len, a->addr); m->m_flags |= M_PKTHDR; m->m_len = m->m_pkthdr.len = len; - - /* mbuf refcnt is not contended, no need to use atomic - * (a memory barrier is enough). */ SET_MBUF_REFCNT(m, 2); M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE); m->m_pkthdr.flowid = a->ring_nr; @@ -438,10 +445,11 @@ CURVNET_SET(if_getvnet(ifp)); ret = NA(ifp)->if_transmit(ifp, m); CURVNET_RESTORE(); + if (ret != 0) + if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1); return ret ? -1 : 0; } - struct netmap_adapter * netmap_getna(if_t ifp) { diff --git a/sys/dev/netmap/netmap_generic.c b/sys/dev/netmap/netmap_generic.c --- a/sys/dev/netmap/netmap_generic.c +++ b/sys/dev/netmap/netmap_generic.c @@ -424,6 +424,7 @@ return error; } +#ifndef __FreeBSD__ /* * Callback invoked when the device driver frees an mbuf used * by netmap to transmit a packet. This usually happens when @@ -481,10 +482,8 @@ /* Second, wake up clients. They will reclaim the event through * txsync. */ netmap_generic_irq(na, r, NULL); -#ifdef __FreeBSD__ - void_mbuf_dtor(m); -#endif } +#endif /* Record completed transmissions and update hwtail. * @@ -537,10 +536,12 @@ } /* The event has been consumed, we can go * ahead. */ - } else if (MBUF_REFCNT(m) != 1) { - /* This mbuf is still busy: its refcnt is 2. */ - break; + /* This mbuf has been submitted but is still busy + * (refcount is 2). + * Leave it to the driver and replenish. */ + m_freem(m); + tx_pool[nm_i] = NULL; } } @@ -577,6 +578,13 @@ return e; } +#ifdef __FreeBSD__ +static void +generic_set_tx_event(struct netmap_kring *kring __unused, u_int hwcur __unused) +{ + netmap_common_irq(kring->na, kring->ring_id, NULL); +} +#else static void generic_set_tx_event(struct netmap_kring *kring, u_int hwcur) { @@ -631,9 +639,8 @@ /* Decrement the refcount. This will free it if we lose the race * with the driver. */ m_freem(m); - smp_mb(); } - +#endif /* * generic_netmap_txsync() transforms netmap buffers into mbufs @@ -698,7 +705,7 @@ /* Tale a mbuf from the tx pool (replenishing the pool * entry if necessary) and copy in the user packet. */ m = kring->tx_pool[nm_i]; - if (unlikely(m == NULL)) { + if (m == NULL) { kring->tx_pool[nm_i] = m = nm_os_get_mbuf(ifp, NETMAP_BUF_SIZE(na)); if (m == NULL) { diff --git a/sys/dev/netmap/netmap_kern.h b/sys/dev/netmap/netmap_kern.h --- a/sys/dev/netmap/netmap_kern.h +++ b/sys/dev/netmap/netmap_kern.h @@ -2394,7 +2394,7 @@ * has a KASSERT(), checking that the mbuf dtor function is not NULL. */ -static void void_mbuf_dtor(struct mbuf *m) { } +static inline void void_mbuf_dtor(struct mbuf *m) { } #define SET_MBUF_DESTRUCTOR(m, fn) do { \ (m)->m_ext.ext_free = (fn != NULL) ? \ @@ -2402,22 +2402,9 @@ } while (0) static inline struct mbuf * -nm_os_get_mbuf(if_t ifp, int len) +nm_os_get_mbuf(if_t ifp __unused, int len) { - struct mbuf *m; - - (void)ifp; - (void)len; - - m = m_gethdr(M_NOWAIT, MT_DATA); - if (m == NULL) { - return m; - } - - m_extadd(m, NULL /* buf */, 0 /* size */, void_mbuf_dtor, - NULL, NULL, 0, EXT_NET_DRV); - - return m; + return (m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, len)); } #endif /* __FreeBSD__ */