If the message was received via a shared memory ring the frame is usable as ofpbuf base directly without copying the buffer unnecessarily. A new destructor() callback is introduced which allows marking the frame unused after the ofpbuf has been uninitialized.
Signed-off-by: Thomas Graf <tg...@suug.ch> --- lib/netlink-socket.c | 26 +++++++++++++++++++++++++- lib/ofpbuf.c | 6 ++++++ lib/ofpbuf.h | 2 ++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/lib/netlink-socket.c b/lib/netlink-socket.c index a264a0a..fd2810d 100644 --- a/lib/netlink-socket.c +++ b/lib/netlink-socket.c @@ -539,6 +539,14 @@ nl_sock_recv_linear(struct nl_sock *sock, struct ofpbuf *buf, bool wait, return 0; } +static void +nl_mmap_ofpbuf_destructor(struct ofpbuf *b) +{ + struct nl_mmap_hdr *hdr = b->destructor_data; + + hdr->nm_status = NL_MMAP_STATUS_UNUSED; +} + static int nl_sock_recv_mmap(struct nl_sock *sock, struct ofpbuf *buf, bool wait, uint8_t *tail, size_t taillen) @@ -558,8 +566,22 @@ restart: goto restart; } - ofpbuf_put(buf, (char *) hdr + NL_MMAP_HDRLEN, hdr->nm_len); COVERAGE_INC(netlink_recv_mmap); + + /* Recycle empty buffers and do copy-on-write by referring to + * the memory ring. The frame of the ring is marked unused as + * the ofpbuf is uninitialized. + */ + if (ofpbuf_size(buf) == 0) { + ofpbuf_uninit(buf); + ofpbuf_use_stub(buf, (char *) hdr + NL_MMAP_HDRLEN, hdr->nm_len); + ofpbuf_set_size(buf, hdr->nm_len); + buf->destructor = nl_mmap_ofpbuf_destructor; + buf->destructor_data = hdr; + goto dont_release; + } else { + ofpbuf_put(buf, (char *) hdr + NL_MMAP_HDRLEN, hdr->nm_len); + } break; case NL_MMAP_STATUS_COPY: @@ -582,6 +604,8 @@ restart: } hdr->nm_status = NL_MMAP_STATUS_UNUSED; + +dont_release: mmap_advance_rx_ring(sock); return retval; diff --git a/lib/ofpbuf.c b/lib/ofpbuf.c index 1f4b61d..88ef6a6 100644 --- a/lib/ofpbuf.c +++ b/lib/ofpbuf.c @@ -29,6 +29,8 @@ ofpbuf_init__(struct ofpbuf *b, size_t allocated, enum ofpbuf_source source) b->source = source; b->frame = NULL; b->l2_5_ofs = b->l3_ofs = b->l4_ofs = UINT16_MAX; + b->destructor = NULL; + b->destructor_data = NULL; list_poison(&b->list_node); } @@ -132,6 +134,10 @@ void ofpbuf_uninit(struct ofpbuf *b) { if (b) { + if (b->destructor) { + b->destructor(b); + b->destructor = NULL; + } if (b->source == OFPBUF_MALLOC) { free(ofpbuf_base(b)); } diff --git a/lib/ofpbuf.h b/lib/ofpbuf.h index 85be899..8757518 100644 --- a/lib/ofpbuf.h +++ b/lib/ofpbuf.h @@ -75,6 +75,8 @@ struct ofpbuf { or UINT16_MAX. */ enum ofpbuf_source source; /* Source of memory allocated as 'base'. */ struct list list_node; /* Private list element for use by owner. */ + void (*destructor)(struct ofpbuf *); /* User specified destructor */ + void *destructor_data; /* Data pointer owned by destructor */ }; static inline void * ofpbuf_data(const struct ofpbuf *); -- 1.8.3.1 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev