Patchwork [libmnl,2/9] mmap: initial

login
register
mail settings
Submitter Ken-ichirou MATSUZAWA
Date Dec. 7, 2013, 3:09 p.m.
Message ID <87r49o8zpe.wl%chamaken@gmail.com>
Download mbox | patch
Permalink /patch/298689/
State Superseded
Headers show

Comments

Ken-ichirou MATSUZAWA - Dec. 7, 2013, 3:09 p.m.
Signed-off-by: Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp>

---
 include/libmnl/libmnl.h |  44 ++++++++++
 src/Makefile.am         |   2 +-
 src/libmnl.map          |   7 ++
 src/mmap.c              | 227 ++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 279 insertions(+), 1 deletion(-)
 create mode 100644 src/mmap.c

Patch

diff --git a/include/libmnl/libmnl.h b/include/libmnl/libmnl.h
index 223709c..11abb04 100644
--- a/include/libmnl/libmnl.h
+++ b/include/libmnl/libmnl.h
@@ -180,6 +180,50 @@  extern int mnl_cb_run2(const void *buf, size_t numbytes, unsigned int seq,
 		       mnl_cb_t *cb_ctl_array, unsigned int cb_ctl_array_len);
 
 /*
+ * mmapped API
+ */
+
+/*
+ * ring contains #nr blocks, block contains #nr frames
+ * ring size == nm_block_size * nm_block_nr
+ *           == nm_frame_size * nm_frame_nr
+ *
+ * frames per block: nm_block_size / nm_frame_size
+ * nm_frame_nr     : frames per block * nm_block_nr
+ * nm_block_size   : n * getpagesize()
+ * nm_frame_size   : >= NL_MMAP_HDRLEN to hold at least the frame header
+ * nm_frame_size   : <= nm_block_size
+ */
+#define MNL_MMAP_DEFAULT_REQ {	\
+	.nm_block_size	= MNL_SOCKET_BUFFER_SIZE * 16, \
+	.nm_block_nr	= 64, \
+	.nm_frame_size	= 16384, \
+	.nm_frame_nr	= 64 * MNL_SOCKET_BUFFER_SIZE * 16 / 16384, \
+}
+
+#define MNL_MMAP_SMALL_REQ { \
+	.nm_block_size	= getpagesize(),	\
+	.nm_block_nr	= 4, \
+	.nm_frame_size	= getpagesize() / 2, \
+	.nm_frame_nr	= (4 * getpagesize()) / (getpagesize() / 2), \
+}
+
+#define MNL_MMAP_MSGHDR(nlmhdr) ((void *)(nlmhdr) + NL_MMAP_HDRLEN)
+
+enum mnl_ring_types {
+       MNL_RING_RX,
+       MNL_RING_TX,
+};
+
+struct mnl_ring_socket;
+
+extern struct mnl_ring_socket *
+mnl_ring_map(const struct mnl_socket *nl, struct nl_mmap_req *tx_req, struct nl_mmap_req *rx_req);
+extern int mnl_ring_unmap(struct mnl_ring_socket *nlm);
+extern struct nl_mmap_hdr *mnl_ring_get_frame(struct mnl_ring_socket *nlm, enum mnl_ring_types type);
+extern int mnl_ring_advance(struct mnl_ring_socket *nlm, enum mnl_ring_types type);
+
+/*
  * other declarations
  */
 
diff --git a/src/Makefile.am b/src/Makefile.am
index 9aab516..f97958c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -2,4 +2,4 @@  include $(top_srcdir)/Make_global.am
 lib_LTLIBRARIES = libmnl.la
 
 libmnl_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libmnl.map -version-info $(LIBVERSION)
-libmnl_la_SOURCES = socket.c callback.c nlmsg.c attr.c internal.h libmnl.map
+libmnl_la_SOURCES = socket.c callback.c nlmsg.c attr.c mmap.c internal.h libmnl.map
diff --git a/src/libmnl.map b/src/libmnl.map
index dbc332e..73de08f 100644
--- a/src/libmnl.map
+++ b/src/libmnl.map
@@ -72,3 +72,10 @@  local: *;
 LIBMNL_1.1 {
   mnl_attr_parse_payload;
 } LIBMNL_1.0;
+
+LIBMNL_1.2 {
+  mnl_ring_map;
+  mnl_ring_unmap;
+  mnl_ring_get_frame;
+  mnl_ring_advance;
+} LIBMNL_1.1;
diff --git a/src/mmap.c b/src/mmap.c
new file mode 100644
index 0000000..348fa89
--- /dev/null
+++ b/src/mmap.c
@@ -0,0 +1,227 @@ 
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <libmnl/libmnl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/mman.h>
+#include <poll.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+#include "internal.h"
+
+
+struct mnl_ring {
+	void			*ring;
+	unsigned int		frame_size;
+	unsigned int		frame_max;
+	unsigned int		block_size;
+	unsigned int		head;
+};
+
+/*
+ * ring descriptor
+ */
+struct mnl_ring_socket {
+	const struct mnl_socket	*sock;
+
+	struct mnl_ring		*tx_ring;
+	struct mnl_ring		*rx_ring;
+};
+
+
+static inline struct mnl_ring *get_mnl_ring(struct mnl_ring_socket *nlm, enum mnl_ring_types type)
+{
+	return type == MNL_RING_TX ? nlm->tx_ring : nlm->rx_ring;
+}
+
+
+static struct mnl_ring *alloc_ring(const struct nl_mmap_req *req)
+{
+	struct mnl_ring *ring;
+
+	ring = calloc(sizeof(struct mnl_ring), 1);
+	if (ring == NULL)
+		return NULL;
+
+	ring->frame_size	= req->nm_frame_size;
+	ring->frame_max		= req->nm_frame_nr - 1;
+	ring->block_size	= req->nm_block_size;
+
+	return ring;
+}
+
+/**
+ * \defgroup socket Netlink mmapped socket helpers
+ * @{
+ */
+
+/**
+ * mnl_ring_map - setup a ring descriptor
+ * \param nl opend mnl_socket
+ * \param tx_req setsockopt param for tx ring
+ * \param rx_req setsockopt param for rx ring
+ *
+ * return NULL if fail
+ */
+struct mnl_ring_socket *
+mnl_ring_map(const struct mnl_socket *nl, struct nl_mmap_req *tx_req, struct nl_mmap_req *rx_req)
+{
+	void *ring;
+	struct mnl_ring_socket *nlm;
+	size_t tx_size = 0, rx_size = 0;
+
+	nlm = calloc(sizeof(struct mnl_ring_socket), 1);
+	if (nlm == NULL)
+		return NULL;
+	nlm->sock = nl;
+
+	if (tx_req != NULL) {
+		nlm->tx_ring = alloc_ring(tx_req);
+		if (nlm->tx_ring == NULL)
+			goto error;
+		if (mnl_socket_setsockopt(nl, NETLINK_TX_RING, tx_req, sizeof(*tx_req)) < 0)
+			goto error;
+		tx_size = tx_req->nm_block_nr * tx_req->nm_block_size;
+	}
+
+	if (rx_req != NULL) {
+		nlm->rx_ring = alloc_ring(rx_req);
+		if (nlm->rx_ring == NULL)
+			goto error;
+		if (mnl_socket_setsockopt(nl, NETLINK_RX_RING, rx_req, sizeof(*rx_req)) < 0)
+			goto error;
+		rx_size += rx_req->nm_block_nr * rx_req->nm_block_size;
+	}
+
+	ring = mmap(NULL, tx_size + rx_size, PROT_READ | PROT_WRITE, MAP_SHARED,
+		    mnl_socket_get_fd(nlm->sock), 0);
+	if (ring == MAP_FAILED)
+		goto error;
+
+	if (rx_req != NULL && tx_req != NULL) {
+		nlm->rx_ring->ring = ring;
+		nlm->tx_ring->ring = ring + rx_size;
+	} else if (rx_req != NULL) {
+		nlm->rx_ring->ring = ring;
+	} else {
+		nlm->tx_ring->ring = ring;
+	}
+
+	return nlm;
+
+error:
+	if (nlm->tx_ring != NULL)
+		free(nlm->tx_ring);
+	if (nlm->rx_ring != NULL)
+		free(nlm->rx_ring);
+	free(nlm);
+	return NULL;
+}
+EXPORT_SYMBOL(mnl_ring_map);
+
+
+static int unmap_ring(struct mnl_ring *ring)
+{
+	size_t ring_size;
+	unsigned int frames_per_block;
+	int ret;
+
+	frames_per_block = ring->block_size / ring->frame_size;
+	ring_size = (ring->frame_max + 1 + frames_per_block - 1) / frames_per_block * ring->block_size;
+
+	ret = munmap(ring->ring, ring_size);
+	if (ret < 0)
+		return ret;
+
+	ring->ring = NULL;
+	ring->frame_size = 0;
+	ring->frame_max = 0;
+	ring->block_size = 0;
+	ring->head = 0;
+
+	return ret;
+}
+
+/**
+ * mnl_ring_unmap - free a given ring descriptor
+ * \param nlm ring descriptor obtained via mnl_ring_map()
+ */
+int mnl_ring_unmap(struct mnl_ring_socket *nlm)
+{
+	int ret;
+
+	/* XXX: how's sockopt going? */
+	if (nlm->tx_ring != NULL) {
+		ret = unmap_ring(nlm->tx_ring);
+		if (ret < 0)
+			return ret;
+		free(nlm->tx_ring);
+		nlm->tx_ring = NULL;
+	}
+	if (nlm->rx_ring != NULL) {
+		ret = unmap_ring(nlm->rx_ring);
+		if (ret < 0)
+			return ret;
+		free(nlm->rx_ring);
+		nlm->rx_ring = NULL;
+	}
+
+	free(nlm);
+	return 1;
+}
+EXPORT_SYMBOL(mnl_ring_unmap);
+
+/**
+ * mnl_ring_get_frame - get current frame
+ * \param nlm ring descriptor
+ * \param type ring type either MNL_RING_RX or MNL_RING_TX
+ */
+struct nl_mmap_hdr *mnl_ring_get_frame(struct mnl_ring_socket *nlm, enum mnl_ring_types type)
+{
+	unsigned int frames_per_block, block_pos, frame_off;
+	struct mnl_ring *ring;
+
+	ring = get_mnl_ring(nlm, type);
+	if (ring == NULL) {
+		errno = EBADR;
+		return NULL;
+	}
+
+	frames_per_block = ring->block_size / ring->frame_size;
+	block_pos = ring->head / frames_per_block;
+	frame_off = ring->head % frames_per_block;
+
+	return (struct nl_mmap_hdr *)(ring->ring + block_pos * ring->block_size + frame_off * ring->frame_size);
+}
+EXPORT_SYMBOL(mnl_ring_get_frame);
+
+/**
+ * mnl_ring_advance - set forward frame pointer
+ * \param nlm ring descriptor
+ * \param type ring type either MNL_RING_RX or MNL_RING_TX
+ */
+int mnl_ring_advance(struct mnl_ring_socket *nlm,  enum mnl_ring_types type)
+{
+	struct mnl_ring *ring;
+
+	ring = get_mnl_ring(nlm, type);
+	if (ring == NULL) {
+		errno = EBADR;
+		return -1;
+	}
+	ring->head = ring->head != ring->frame_max ? ring->head + 1 : 0;
+
+	return 1; /* ring->head; */
+}
+EXPORT_SYMBOL(mnl_ring_advance);
+
+/**
+ * @}
+ */