better handling of netmap emulation over standard device drivers:
plug a potential mbuf leak, and detect bogus drivers that return ENOBUFS even when the packet has been queued. MFC after: 3 days
This commit is contained in:
parent
cdd68772f8
commit
1971caf17c
@ -61,7 +61,8 @@
|
||||
|
||||
/* ======================== FREEBSD-SPECIFIC ROUTINES ================== */
|
||||
|
||||
rawsum_t nm_csum_raw(uint8_t *data, size_t len, rawsum_t cur_sum)
|
||||
rawsum_t
|
||||
nm_csum_raw(uint8_t *data, size_t len, rawsum_t cur_sum)
|
||||
{
|
||||
/* TODO XXX please use the FreeBSD implementation for this. */
|
||||
uint16_t *words = (uint16_t *)data;
|
||||
@ -80,7 +81,8 @@ rawsum_t nm_csum_raw(uint8_t *data, size_t len, rawsum_t cur_sum)
|
||||
/* Fold a raw checksum: 'cur_sum' is in host byte order, while the
|
||||
* return value is in network byte order.
|
||||
*/
|
||||
uint16_t nm_csum_fold(rawsum_t cur_sum)
|
||||
uint16_t
|
||||
nm_csum_fold(rawsum_t cur_sum)
|
||||
{
|
||||
/* TODO XXX please use the FreeBSD implementation for this. */
|
||||
while (cur_sum >> 16)
|
||||
@ -89,7 +91,8 @@ uint16_t nm_csum_fold(rawsum_t cur_sum)
|
||||
return htobe16((~cur_sum) & 0xFFFF);
|
||||
}
|
||||
|
||||
uint16_t nm_csum_ipv4(struct nm_iphdr *iph)
|
||||
uint16_t
|
||||
nm_csum_ipv4(struct nm_iphdr *iph)
|
||||
{
|
||||
#if 0
|
||||
return in_cksum_hdr((void *)iph);
|
||||
@ -98,7 +101,8 @@ uint16_t nm_csum_ipv4(struct nm_iphdr *iph)
|
||||
#endif
|
||||
}
|
||||
|
||||
void nm_csum_tcpudp_ipv4(struct nm_iphdr *iph, void *data,
|
||||
void
|
||||
nm_csum_tcpudp_ipv4(struct nm_iphdr *iph, void *data,
|
||||
size_t datalen, uint16_t *check)
|
||||
{
|
||||
#ifdef INET
|
||||
@ -120,7 +124,8 @@ void nm_csum_tcpudp_ipv4(struct nm_iphdr *iph, void *data,
|
||||
#endif
|
||||
}
|
||||
|
||||
void nm_csum_tcpudp_ipv6(struct nm_ipv6hdr *ip6h, void *data,
|
||||
void
|
||||
nm_csum_tcpudp_ipv6(struct nm_ipv6hdr *ip6h, void *data,
|
||||
size_t datalen, uint16_t *check)
|
||||
{
|
||||
#ifdef INET6
|
||||
@ -143,7 +148,8 @@ void nm_csum_tcpudp_ipv6(struct nm_ipv6hdr *ip6h, void *data,
|
||||
int
|
||||
netmap_catch_rx(struct netmap_adapter *na, int intercept)
|
||||
{
|
||||
struct netmap_generic_adapter *gna = (struct netmap_generic_adapter *)na;
|
||||
struct netmap_generic_adapter *gna =
|
||||
(struct netmap_generic_adapter *)na;
|
||||
struct ifnet *ifp = na->ifp;
|
||||
|
||||
if (intercept) {
|
||||
@ -209,11 +215,29 @@ generic_xmit_frame(struct ifnet *ifp, struct mbuf *m,
|
||||
{
|
||||
int ret;
|
||||
|
||||
m->m_len = m->m_pkthdr.len = 0;
|
||||
/*
|
||||
* The mbuf should be a cluster from our special pool,
|
||||
* so we do not need to do an m_copyback but just copy
|
||||
* (and eventually, just reference the netmap buffer)
|
||||
*/
|
||||
|
||||
// copy data to the mbuf
|
||||
m_copyback(m, 0, len, addr);
|
||||
// inc refcount. We are alone, so we can skip the atomic
|
||||
if (*m->m_ext.ref_cnt != 1) {
|
||||
D("invalid refcnt %d for %p",
|
||||
*m->m_ext.ref_cnt, m);
|
||||
panic("in generic_xmit_frame");
|
||||
}
|
||||
// XXX the ext_size check is unnecessary if we link the netmap buf
|
||||
if (m->m_ext.ext_size < len) {
|
||||
RD(5, "size %d < len %d", m->m_ext.ext_size, len);
|
||||
len = m->m_ext.ext_size;
|
||||
}
|
||||
if (0) { /* XXX seems to have negligible benefits */
|
||||
m->m_ext.ext_buf = m->m_data = addr;
|
||||
} else {
|
||||
bcopy(addr, m->m_data, len);
|
||||
}
|
||||
m->m_len = m->m_pkthdr.len = len;
|
||||
// inc refcount. All ours, we could skip the atomic
|
||||
atomic_fetchadd_int(m->m_ext.ref_cnt, 1);
|
||||
m->m_flags |= M_FLOWID;
|
||||
m->m_pkthdr.flowid = ring_nr;
|
||||
@ -238,7 +262,7 @@ netmap_getna(if_t ifp)
|
||||
int
|
||||
generic_find_num_desc(struct ifnet *ifp, unsigned int *tx, unsigned int *rx)
|
||||
{
|
||||
D("called");
|
||||
D("called, in tx %d rx %d", *tx, *rx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -246,13 +270,14 @@ generic_find_num_desc(struct ifnet *ifp, unsigned int *tx, unsigned int *rx)
|
||||
void
|
||||
generic_find_num_queues(struct ifnet *ifp, u_int *txq, u_int *rxq)
|
||||
{
|
||||
D("called");
|
||||
D("called, in txq %d rxq %d", *txq, *rxq);
|
||||
*txq = netmap_generic_rings;
|
||||
*rxq = netmap_generic_rings;
|
||||
}
|
||||
|
||||
|
||||
void netmap_mitigation_init(struct nm_generic_mit *mit, struct netmap_adapter *na)
|
||||
void
|
||||
netmap_mitigation_init(struct nm_generic_mit *mit, struct netmap_adapter *na)
|
||||
{
|
||||
ND("called");
|
||||
mit->mit_pending = 0;
|
||||
@ -260,26 +285,30 @@ void netmap_mitigation_init(struct nm_generic_mit *mit, struct netmap_adapter *n
|
||||
}
|
||||
|
||||
|
||||
void netmap_mitigation_start(struct nm_generic_mit *mit)
|
||||
void
|
||||
netmap_mitigation_start(struct nm_generic_mit *mit)
|
||||
{
|
||||
ND("called");
|
||||
}
|
||||
|
||||
|
||||
void netmap_mitigation_restart(struct nm_generic_mit *mit)
|
||||
void
|
||||
netmap_mitigation_restart(struct nm_generic_mit *mit)
|
||||
{
|
||||
ND("called");
|
||||
}
|
||||
|
||||
|
||||
int netmap_mitigation_active(struct nm_generic_mit *mit)
|
||||
int
|
||||
netmap_mitigation_active(struct nm_generic_mit *mit)
|
||||
{
|
||||
ND("called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void netmap_mitigation_cleanup(struct nm_generic_mit *mit)
|
||||
void
|
||||
netmap_mitigation_cleanup(struct nm_generic_mit *mit)
|
||||
{
|
||||
ND("called");
|
||||
}
|
||||
|
@ -81,8 +81,8 @@ __FBSDID("$FreeBSD$");
|
||||
#include <dev/netmap/netmap_kern.h>
|
||||
#include <dev/netmap/netmap_mem2.h>
|
||||
|
||||
#define rtnl_lock() ND("rtnl_lock called");
|
||||
#define rtnl_unlock() ND("rtnl_unlock called");
|
||||
#define rtnl_lock() ND("rtnl_lock called")
|
||||
#define rtnl_unlock() ND("rtnl_unlock called")
|
||||
#define MBUF_TXQ(m) ((m)->m_pkthdr.flowid)
|
||||
#define MBUF_RXQ(m) ((m)->m_pkthdr.flowid)
|
||||
#define smp_mb()
|
||||
@ -101,7 +101,6 @@ __FBSDID("$FreeBSD$");
|
||||
/*
|
||||
* mbuf wrappers
|
||||
*/
|
||||
#define netmap_get_mbuf(len) m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR|M_NOFREE)
|
||||
|
||||
/* mbuf destructor, also need to change the type to EXT_EXTREF,
|
||||
* add an M_NOFREE flag, and then clear the flag and
|
||||
@ -113,6 +112,32 @@ __FBSDID("$FreeBSD$");
|
||||
(m)->m_ext.ext_type = EXT_EXTREF; \
|
||||
} while (0)
|
||||
|
||||
static void
|
||||
netmap_default_mbuf_destructor(struct mbuf *m)
|
||||
{
|
||||
/* restore original mbuf */
|
||||
m->m_ext.ext_buf = m->m_data = m->m_ext.ext_arg1;
|
||||
m->m_ext.ext_arg1 = NULL;
|
||||
m->m_ext.ext_type = EXT_PACKET;
|
||||
m->m_ext.ext_free = NULL;
|
||||
if (*(m->m_ext.ref_cnt) == 0)
|
||||
*(m->m_ext.ref_cnt) = 1;
|
||||
uma_zfree(zone_pack, m);
|
||||
}
|
||||
|
||||
static inline struct mbuf *
|
||||
netmap_get_mbuf(int len)
|
||||
{
|
||||
struct mbuf *m;
|
||||
m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR | M_NOFREE);
|
||||
if (m) {
|
||||
m->m_ext.ext_arg1 = m->m_ext.ext_buf; // XXX save
|
||||
m->m_ext.ext_free = (void *)netmap_default_mbuf_destructor;
|
||||
m->m_ext.ext_type = EXT_EXTREF;
|
||||
ND(5, "create m %p refcnt %d", m, *m->m_ext.ref_cnt);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
#define GET_MBUF_REFCNT(m) ((m)->m_ext.ref_cnt ? *(m)->m_ext.ref_cnt : -1)
|
||||
|
||||
@ -230,7 +255,7 @@ generic_netmap_register(struct netmap_adapter *na, int enable)
|
||||
#endif /* REG_RESET */
|
||||
|
||||
if (enable) { /* Enable netmap mode. */
|
||||
/* Init the mitigation support. */
|
||||
/* Init the mitigation support on all the rx queues. */
|
||||
gna->mit = malloc(na->num_rx_rings * sizeof(struct nm_generic_mit),
|
||||
M_DEVBUF, M_NOWAIT | M_ZERO);
|
||||
if (!gna->mit) {
|
||||
@ -380,15 +405,11 @@ out:
|
||||
static void
|
||||
generic_mbuf_destructor(struct mbuf *m)
|
||||
{
|
||||
if (netmap_verbose)
|
||||
D("Tx irq (%p) queue %d", m, MBUF_TXQ(m));
|
||||
netmap_generic_irq(MBUF_IFP(m), MBUF_TXQ(m), NULL);
|
||||
#ifdef __FreeBSD__
|
||||
m->m_ext.ext_type = EXT_PACKET;
|
||||
m->m_ext.ext_free = NULL;
|
||||
if (*(m->m_ext.ref_cnt) == 0)
|
||||
*(m->m_ext.ref_cnt) = 1;
|
||||
uma_zfree(zone_pack, m);
|
||||
if (netmap_verbose)
|
||||
RD(5, "Tx irq (%p) queue %d index %d" , m, MBUF_TXQ(m), (int)(uintptr_t)m->m_ext.ext_arg1);
|
||||
netmap_default_mbuf_destructor(m);
|
||||
#endif /* __FreeBSD__ */
|
||||
IFRATE(rate_ctx.new.txirq++);
|
||||
}
|
||||
@ -478,12 +499,12 @@ generic_set_tx_event(struct netmap_kring *kring, u_int hwcur)
|
||||
e = generic_tx_event_middle(kring, hwcur);
|
||||
|
||||
m = kring->tx_pool[e];
|
||||
ND(5, "Request Event at %d mbuf %p refcnt %d", e, m, m ? GET_MBUF_REFCNT(m) : -2 );
|
||||
if (m == NULL) {
|
||||
/* This can happen if there is already an event on the netmap
|
||||
slot 'e': There is nothing to do. */
|
||||
return;
|
||||
}
|
||||
ND("Event at %d mbuf %p refcnt %d", e, m, GET_MBUF_REFCNT(m));
|
||||
kring->tx_pool[e] = NULL;
|
||||
SET_MBUF_DESTRUCTOR(m, generic_mbuf_destructor);
|
||||
|
||||
@ -777,6 +798,10 @@ generic_netmap_attach(struct ifnet *ifp)
|
||||
|
||||
generic_find_num_desc(ifp, &num_tx_desc, &num_rx_desc);
|
||||
ND("Netmap ring size: TX = %d, RX = %d", num_tx_desc, num_rx_desc);
|
||||
if (num_tx_desc == 0 || num_rx_desc == 0) {
|
||||
D("Device has no hw slots (tx %u, rx %u)", num_tx_desc, num_rx_desc);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
gna = malloc(sizeof(*gna), M_DEVBUF, M_NOWAIT | M_ZERO);
|
||||
if (gna == NULL) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user