diff options
Diffstat (limited to 'sys/dev/netmap/netmap_freebsd.c')
| -rw-r--r-- | sys/dev/netmap/netmap_freebsd.c | 35 |
1 files changed, 22 insertions, 13 deletions
diff --git a/sys/dev/netmap/netmap_freebsd.c b/sys/dev/netmap/netmap_freebsd.c index 4b3b1a6edacd..e67d26d6788f 100644 --- a/sys/dev/netmap/netmap_freebsd.c +++ b/sys/dev/netmap/netmap_freebsd.c @@ -387,15 +387,20 @@ nm_os_catch_tx(struct netmap_generic_adapter *gna, int intercept) * 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) @@ -405,16 +410,21 @@ nm_os_generic_xmit_frame(struct nm_os_gen_arg *a) 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; + M_ASSERTPKTHDR(m); + KASSERT((m->m_flags & M_EXT) != 0, + ("%s: mbuf %p has no cluster", __func__, m)); - m->m_flags |= M_PKTHDR; - m->m_len = m->m_pkthdr.len = 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; + } - /* mbuf refcnt is not contended, no need to use atomic - * (a memory barrier is enough). */ + m_copyback(m, 0, len, a->addr); + m->m_len = m->m_pkthdr.len = len; SET_MBUF_REFCNT(m, 2); M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE); m->m_pkthdr.flowid = a->ring_nr; @@ -425,7 +435,6 @@ nm_os_generic_xmit_frame(struct nm_os_gen_arg *a) return ret ? -1 : 0; } - struct netmap_adapter * netmap_getna(if_t ifp) { |
