Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 28 Aug 2020 20:03:55 +0000 (UTC)
From:      Vincenzo Maffione <vmaffione@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r364936 - in head: lib lib/libnetmap share/mk
Message-ID:  <202008282003.07SK3tuD093523@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: vmaffione
Date: Fri Aug 28 20:03:54 2020
New Revision: 364936
URL: https://svnweb.freebsd.org/changeset/base/364936

Log:
  lib: add libnetmap
  
  This changeset introduces the new libnetmap library for writing
  netmap applications.
  Before libnetmap, applications could either use the kernel API
  directly (e.g. NIOCREGIF/NIOCCTRL) or the simple header-only-library
  netmap_user.h (e.g. nm_open(), nm_close(), nm_mmap() etc.)
  
  The new library offers more functionalities than netmap_user.h:
    - Support for complex netmap options, such as external memory
      allocators or per-buffer offsets. This opens the way to future
      extensions.
    - More flexibility in the netmap port bind options, such as
      non-numeric names for pipes, or the ability to specify the netmap
      allocator that must be used for a given port.
    - Automatic tracking of the netmap memory regions in use across the
      open ports.
  
  At the moment there is no man page, but the libnetmap.h header file
  has in-depth documentation.
  
  Reviewed by:	hrs
  MFC after:	2 weeks
  Differential Revision:	https://reviews.freebsd.org/D26171

Added:
  head/lib/libnetmap/
  head/lib/libnetmap/Makefile   (contents, props changed)
  head/lib/libnetmap/libnetmap.h   (contents, props changed)
  head/lib/libnetmap/nmctx-pthreads.c   (contents, props changed)
  head/lib/libnetmap/nmctx.c   (contents, props changed)
  head/lib/libnetmap/nmport.c   (contents, props changed)
  head/lib/libnetmap/nmreq.c   (contents, props changed)
Modified:
  head/lib/Makefile
  head/share/mk/bsd.libnames.mk
  head/share/mk/src.libnames.mk

Modified: head/lib/Makefile
==============================================================================
--- head/lib/Makefile	Fri Aug 28 19:59:02 2020	(r364935)
+++ head/lib/Makefile	Fri Aug 28 20:03:54 2020	(r364936)
@@ -71,6 +71,7 @@ SUBDIR=	${SUBDIR_BOOTSTRAP} \
 	libmt \
 	lib80211 \
 	libnetbsd \
+	libnetmap \
 	libnv \
 	libopenbsd \
 	libopie \

Added: head/lib/libnetmap/Makefile
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lib/libnetmap/Makefile	Fri Aug 28 20:03:54 2020	(r364936)
@@ -0,0 +1,16 @@
+#
+# $FreeBSD$
+#
+
+.include <src.opts.mk>
+
+PACKAGE=	lib${LIB}
+LIB=		netmap
+SRCS=		nmctx.c nmport.c \
+		nmctx-pthreads.c nmreq.c
+INCS=		libnetmap.h
+#MAN=		libnetmap.3
+CFLAGS+=	-I${SRCTOP}/sys/net -I${.CURDIR}
+WARNS?=		2
+
+.include <bsd.lib.mk>

Added: head/lib/libnetmap/libnetmap.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lib/libnetmap/libnetmap.h	Fri Aug 28 20:03:54 2020	(r364936)
@@ -0,0 +1,660 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (C) 2018 Universita` di Pisa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * $FreeBSD$
+ */
+
+#ifndef LIBNETMAP_H_
+#define LIBNETMAP_H_
+/* if thread-safety is not needed, define LIBNETMAP_NOTHREADSAFE before including
+ * this file.
+ */
+
+/* NOTE: we include net/netmap_user.h without defining NETMAP_WITH_LIBS, which
+ * is deprecated. If you still need it, please define NETMAP_WITH_LIBS and
+ * include net/netmap_user.h before including this file.
+ */
+#include <net/netmap_user.h>
+
+struct nmctx;
+struct nmport_d;
+struct nmem_d;
+
+/*
+ * A port open specification (portspec for brevity) has the following syntax
+ * (square brackets delimit optional parts):
+ *
+ *     subsystem:vpname[mode][options]
+ *
+ *  The "subsystem" is denoted by a prefix, possibly followed by an identifier.
+ *  There can be several kinds of subsystems, each one selected by a unique
+ *  prefix.  Currently defined subsystems are:
+ *
+ *  netmap 		(no id allowed)
+ *  			the standard subsystem
+ *
+ *  vale 		(followed by a possibly empty id)
+ *  			the vpname is connected to a VALE switch identified by
+ *  			the id (an empty id selects the default switch)
+ *
+ *  The "vpname" has the following syntax:
+ *
+ *     identifier			or
+ *     identifier1{identifier2		or
+ *     identifier1}identifier2
+ *
+ *  Identifiers are sequences of alphanumeric characters. The part that begins
+ *  with either '{' or '}', when present, denotes a netmap pipe opened in the
+ *  same memory region as the subsystem:indentifier1 port.
+ *
+ * The "mode" can be one of the following:
+ *
+ *	^		bind all host (sw) ring pairs
+ *	^NN		bind individual host ring pair
+ *	*		bind host and NIC ring pairs
+ *	-NN		bind individual NIC ring pair
+ *	@NN		open the port in the NN memory region
+ *	a suffix starting with / and the following flags,
+ *	in any order:
+ *	x		exclusive access
+ *	z		zero copy monitor (both tx and rx)
+ *	t		monitor tx side (copy monitor)
+ *	r		monitor rx side (copy monitor)
+ *	R		bind only RX ring(s)
+ *	T		bind only TX ring(s)
+ *
+ *  The "options" start at the first '@' character not followed by a number.
+ *  Each option starts with '@' and has the following syntax:
+ *
+ *      option					(flag option)
+ *      option=value				(single key option)
+ *      option:key1=value1,key2=value2,...	(multi-key option)
+ *
+ *  For multi-key options, the keys can be assigned in any order, but they
+ *  cannot be assigned more than once. It is not necessary to assign all the
+ *  option keys: unmentioned keys will receive default values.  Some multi-key
+ *  options define a default key and also accept the single-key syntax, by
+ *  assigning the value to this key.
+ *
+ *  NOTE: Options may be silently ignored if the port is already open by some
+ *  other process.
+ *
+ *  The currently available options are (default keys, when defined, are marked
+ *  with '*'):
+ *
+ *  share (single-key)
+ *  			open the port in the same memory region used by the
+ *  			given port name (the port name must be given in
+ *  			subsystem:vpname form)
+ *
+ *  conf  (multi-key)
+ *  			specify the rings/slots numbers (effective only on
+ *  			ports that are created by the open operation itself,
+ *  			and ignored otherwise).
+ *
+ *			The keys are:
+ *
+ *  		       *rings		number of tx and rx rings
+ *  			tx-rings	number of tx rings
+ *  			rx-rings	number of rx rings
+ *			host-rings	number of tx and rx host rings
+ *  			host-tx-rings	number of host tx rings
+ *  			host-rx-rings	number of host rx rings
+ *  			slots		number of slots in each tx and rx
+ *  					ring
+ *  			tx-slots	number of slots in each tx ring
+ *  			rx-slots	number of slots in each rx ring
+ *
+ *  			(more specific keys override the less specific ones)
+ *			All keys default to zero if not assigned, and the
+ *			corresponding value will be chosen by netmap.
+ *
+ *  extmem (multi-key)
+ *			open the port in the memory region obtained by
+ *			mmap()ing the given file.
+ *
+ *			The keys are:
+ *
+ *		       *file		the file to mmap
+ *			if-num		number of pre-allocated netmap_if's
+ *			if-size		size of each netmap_if
+ *			ring-num	number of pre-allocated netmap_ring's
+ *			ring-size	size of each netmap_ring
+ *			buf-num		number of pre-allocated buffers
+ *			buf-size	size of each buffer
+ *
+ *			file must be assigned. The other keys default to zero,
+ *			causing netmap to take the corresponding values from
+ *			the priv_{if,ring,buf}_{num,size} sysctls.
+ *
+ */
+
+
+/* nmport manipulation */
+
+/* struct nmport_d - describes a netmap port */
+struct nmport_d {
+	/* see net/netmap.h for the definition of these fields */
+	struct nmreq_header hdr;
+	struct nmreq_register reg;
+
+	/* all the fields below should be considered read-only */
+
+	/* if the same context is used throughout the program, d1->mem ==
+	 * d2->mem iff d1 and d2 are using the memory region (i.e., zero
+	 * copy is possible between the two ports)
+	 */
+	struct nmem_d *mem;
+
+	/* the nmctx used when this nmport_d was created */
+	struct nmctx *ctx;
+
+	int register_done;	/* nmport_register() has been called */
+	int mmap_done;		/* nmport_mmap() has been called */
+	/* pointer to the extmem option contained in the hdr options, if any */
+	struct nmreq_opt_extmem *extmem;
+
+	/* the fields below are compatible with nm_open() */
+	int fd;				/* "/dev/netmap", -1 if not open */
+	struct netmap_if *nifp;		/* pointer to the netmap_if */
+	uint16_t first_tx_ring;
+	uint16_t last_tx_ring;
+	uint16_t first_rx_ring;
+	uint16_t last_rx_ring;
+	uint16_t cur_tx_ring;		/* used by nmport_inject */
+	uint16_t cur_rx_ring;
+
+	/* LIFO list of cleanup functions (used internally) */
+	struct nmport_cleanup_d *clist;
+};
+
+/* nmport_open - opens a port from a portspec
+ * @portspec	the port opening specification
+ *
+ * If successful, the function returns a new nmport_d describing a netmap
+ * port, opened according to the port specification, ready to be used for rx
+ * and/or tx.
+ *
+ * The rings available for tx are in the [first_tx_ring, last_tx_ring]
+ * interval, and similarly for rx. One or both intervals may be empty.
+ *
+ * When done using it, the nmport_d descriptor must be closed using
+ * nmport_close().
+ *
+ * In case of error, NULL is returned, errno is set to some error, and an
+ * error message is sent through the error() method of the current context.
+ */
+struct nmport_d * nmport_open(const char *portspec);
+
+/* nport_close - close a netmap port
+ * @d		the port we want to close
+ *
+ * Undoes the actions performed by the nmport_open that created d, then
+ * frees the descriptor.
+ */
+void nmport_close(struct nmport_d *d);
+
+/* nmport_inject - sends a packet
+ * @d		the port through which we want to send
+ * @buf		base address of the packet
+ * @size	its size in bytes
+ *
+ * Sends a packet using the cur_tx_ring and updates the index
+ * to use all available tx rings in turn. Note: the packet is copied.
+ *
+ * Returns 0 on success an -1 on error.
+ */
+int nmport_inject(struct nmport_d *d, const void *buf, size_t size);
+
+/*
+ * the functions below can be used to split the functionality of
+ * nmport_open when special features (e.g., extra buffers) are needed
+ *
+ * The relation among the functions is as follows:
+ *
+ *				   |nmport_new
+ * 		|nmport_prepare	 = |
+ *		|		   |nmport_parse
+ * nmport_open =|
+ *		|		   |nmport_register
+ *		|nmport_open_desc =|
+ *				   |nmport_mmap
+ *
+ */
+
+/* nmport_new - create a new nmport_d
+ *
+ * Creates a new nmport_d using the malloc() method of the current default
+ * context. Returns NULL on error, setting errno to an error value.
+ */
+struct nmport_d *nmport_new(void);
+
+/* nmport_parse - fills the nmport_d netmap-register request
+ * @d		the nmport to be filled
+ * @portspec	the port opening specification
+ *
+ * This function parses the portspec and initizalizes the @d->hdr and @d->reg
+ * fields. It may need to allocate a list of options. If an extmem option is
+ * found, it may also mmap() the corresponding file.
+ *
+ * It returns 0 on success. On failure it returns -1, sets errno to an error
+ * value and sends an error message to the error() method of the context used
+ * when @d was created. Moreover, *@d is left unchanged.
+ */
+int nmport_parse(struct nmport_d *d, const char *portspec);
+
+/* nmport_register - registers the port with netmap
+ * @d		the nmport to be registered
+ *
+ * This function obtains a netmap file descriptor and registers the port with
+ * netmap. The @d->hdr and @d->reg data structures must have been previously
+ * initialized (via nmport_parse() or otherwise).
+ *
+ * It returns 0 on success. On failure it returns -1, sets errno to an error
+ * value and sends an error message to the error() method of the context used
+ * when @d was created. Moreover, *@d is left unchanged.
+ */
+int nmport_register(struct nmport_d *);
+
+/* nmport_mmap - maps the port resources into the process memory
+ * @d		the nmport to be mapped
+ *
+ * The port must have been previously been registered using nmport_register.
+ *
+ * Note that if extmem is used (either via an option or by calling an
+ * nmport_extmem_* function before nmport_register()), no new mmap() is issued.
+ *
+ * It returns 0 on success. On failure it returns -1, sets errno to an error
+ * value and sends an error message to the error() method of the context used
+ * when @d was created. Moreover, *@d is left unchanged.
+ */
+int nmport_mmap(struct nmport_d *);
+
+/* the following functions undo the actions of nmport_new(), nmport_parse(),
+ * nmport_register() and nmport_mmap(), respectively.
+ */
+void nmport_delete(struct nmport_d *);
+void nmport_undo_parse(struct nmport_d *);
+void nmport_undo_register(struct nmport_d *);
+void nmport_undo_mmap(struct nmport_d *);
+
+/* nmport_prepare - create a port descriptor, but do not open it
+ * @portspec	the port opening specification
+ *
+ * This functions creates a new nmport_d and initializes it according to
+ * @portspec. It is equivalent to nmport_new() followed by nmport_parse().
+ *
+ * It returns 0 on success. On failure it returns -1, sets errno to an error
+ * value and sends an error message to the error() method of the context used
+ * when @d was created. Moreover, *@d is left unchanged.
+ */
+struct nmport_d *nmport_prepare(const char *portspec);
+
+/* nmport_open_desc - open an initialized port descriptor
+ * @d		the descriptor we want to open
+ *
+ * Registers the port with netmap and maps the rings and buffers into the
+ * process memory. It is equivalent to nmport_register() followed by
+ * nmport_mmap().
+ *
+ * It returns 0 on success. On failure it returns -1, sets errno to an error
+ * value and sends an error message to the error() method of the context used
+ * when @d was created. Moreover, *@d is left unchanged.
+ */
+int nmport_open_desc(struct nmport_d *d);
+
+/* the following functions undo the actions of nmport_prepare()
+ * and nmport_open_desc(), respectively.
+ */
+void nmport_undo_prepare(struct nmport_d *);
+void nmport_undo_open_desc(struct nmport_d *);
+
+/* nmport_clone - copy an nmport_d
+ * @d		the nmport_d we want to copy
+ *
+ * Copying an nmport_d by hand should be avoided, since adjustments are needed
+ * and some part of the state cannot be easily duplicated. This function
+ * creates a copy of @d in a safe way. The returned nmport_d contains
+ * nmreq_header and nmreq_register structures equivalent to those contained in
+ * @d, except for the option list, which is ignored. The returned nmport_d is
+ * already nmport_prepare()d, but it must still be nmport_open_desc()ed. The
+ * new nmport_d uses the same nmctx as @d.
+ *
+ * If extmem was used for @d, then @d cannot be nmport_clone()d until it has
+ * been nmport_register()ed.
+ *
+ * In case of error, the function returns NULL, sets errno to an error value
+ * and sends an error message to the nmctx error() method.
+ */
+struct nmport_d *nmport_clone(struct nmport_d *);
+
+/* nmport_extmem - use extmem for this port
+ * @d		the port we want to use the extmem for
+ * @base	the base address of the extmem region
+ * @size	the size in bytes of the extmem region
+ *
+ * the memory that contains the netmap ifs, rings and buffers is usually
+ * allocated by netmap and later mmap()ed by the applications. It is sometimes
+ * useful to reverse this process, by having the applications allocate some
+ * memory (through mmap() or otherwise) and then let netmap use it.  The extmem
+ * option can be used to implement this latter strategy. The option can be
+ * passed through the portspec using the '@extmem:...' syntax, or
+ * programmatically by calling nmport_extmem() or nmport_extmem_from_file()
+ * between nmport_parse() and nmport_register() (or between nmport_prepare()
+ * and nmport_open_desc()).
+ *
+ * It returns 0 on success. On failure it returns -1, sets errno to an error
+ * value and sends an error message to the error() method of the context used
+ * when @d was created. Moreover, *@d is left unchanged.
+ */
+int nmport_extmem(struct nmport_d *d, void *base, size_t size);
+
+/* nmport_extmem_from_file - use the extmem obtained by mapping a file
+ * @d		the port we want to use the extmem for
+ * @fname	path of the file we want to map
+ *
+ * This works like nmport_extmem, but the extmem memory is obtained by
+ * mmap()ping @fname. nmport_close() will also automatically munmap() the file.
+ *
+ * It returns 0 on success. On failure it returns -1, sets errno to an error
+ * value and sends an error message to the error() method of the context used
+ * when @d was created. Moreover, *@d is left unchanged.
+ */
+int nmport_extmem_from_file(struct nmport_d *d, const char *fname);
+
+/* nmport_extmem_getinfo - opbtai a pointer to the extmem configuration
+ * @d		the port we want to obtain the pointer from
+ *
+ * Returns a pointer to the nmreq_pools_info structure containing the
+ * configuration of the extmem attached to port @d, or NULL if no extmem
+ * is attached. This can be used to set the desired configuration before
+ * registering the port, or to read the actual configuration after
+ * registration.
+ */
+struct nmreq_pools_info* nmport_extmem_getinfo(struct nmport_d *d);
+
+
+/* enable/disable options
+ *
+ * These functions can be used to disable options that the application cannot
+ * or doesn't want to handle, or to enable options that require special support
+ * from the application and are, therefore, disabled by default. Disabled
+ * options will cause an error if encountered during option parsing.
+ *
+ * If the option is unknown, nmport_disable_option is a NOP, while
+ * nmport_enable_option returns -1 and sets errno to EOPNOTSUPP.
+ *
+ * These functions are not threadsafe and are meant to be used at the beginning
+ * of the program.
+ */
+void nmport_disable_option(const char *opt);
+int nmport_enable_option(const char *opt);
+
+/* nmreq manipulation
+ *
+ * nmreq_header_init - initialize an nmreq_header
+ * @hdr		the nmreq_header to initialize
+ * @reqtype	the kind of netmap request
+ * @body	the body of the request
+ *
+ * Initialize the nr_version, nr_reqtype and nr_body fields of *@hdr.
+ * The other fields are set to zero.
+ */
+void nmreq_header_init(struct nmreq_header *hdr, uint16_t reqtype, void *body);
+
+/*
+ * These functions allow for finer grained parsing of portspecs.  They are used
+ * internally by nmport_parse().
+ */
+
+/* nmreq_header_decode - initialize an nmreq_header
+ * @ppspec:	(in/out) pointer to a pointer to the portspec
+ * @hdr:	pointer to the nmreq_header to be initialized
+ * @ctx:	pointer to the nmctx to use (for errors)
+ *
+ * This function fills the @hdr the nr_name field with the port name extracted
+ * from *@pifname.  The other fields of *@hdr are unchanged. The @pifname is
+ * updated to point at the first char past the port name.
+ *
+ * Returns 0 on success.  In case of error, -1 is returned with errno set to
+ * EINVAL, @pifname is unchanged, *@hdr is also unchanged, and an error message
+ * is sent through @ctx->error().
+ */
+int nmreq_header_decode(const char **ppspec, struct nmreq_header *hdr,
+		struct nmctx *ctx);
+
+/* nmreq_regiter_decode - initialize an nmreq_register
+ * @pmode:	(in/out) pointer to a pointer to an opening mode
+ * @reg:	pointer to the nmreq_register to be initialized
+ * @ctx:	pointer to the nmctx to use (for errors)
+ *
+ * This function fills the nr_mode, nr_ringid, nr_flags and nr_mem_id fields of
+ * the structure pointed by @reg, according to the opening mode specified by
+ * *@pmode. The other fields of *@reg are unchanged.  The @pmode is updated to
+ * point at the first char past the opening mode.
+ *
+ * If a '@' is encountered followed by something which is not a number, parsing
+ * stops (without error) and @pmode is left pointing at the '@' char. The
+ * nr_mode, nr_ringid and nr_flags fields are still updated, but nr_mem_id is
+ * not touched and the interpretation of the '@' field is left to the caller.
+ *
+ * Returns 0 on success.  In case of error, -1 is returned with errno set to
+ * EINVAL, @pmode is unchanged, *@reg is also unchanged, and an error message
+ * is sent through @ctx->error().
+ */
+int nmreq_register_decode(const char **pmode, struct nmreq_register *reg,
+		struct nmctx *ctx);
+
+/* nmreq_options_decode - parse the "options" part of the portspec
+ * @opt:	pointer to the option list
+ * @parsers:	list of option parsers
+ * @token:	token to pass to each parser
+ * @ctx:	pointer to the nmctx to use (for errors and malloc/free)
+ *
+ * This function parses each option in @opt. Each option is matched (based on
+ * the "option" prefix) to a corresponding parser in @parsers. The function
+ * checks that the syntax is appropriate for the parser and it assigns all the
+ * keys mentioned in the option. It then passes control to the parser, to
+ * interpret the keys values.
+ *
+ * Returns 0 on success. In case of error, -1 is returned, errno is set to an
+ * error value and a message is sent to @ctx->error(). The effects of partially
+ * interpreted options may not be undone.
+ */
+struct nmreq_opt_parser;
+int nmreq_options_decode(const char *opt, struct nmreq_opt_parser *parsers,
+		void *token, struct nmctx *ctx);
+
+struct nmreq_parse_ctx;
+/* type of the option-parsers callbacks */
+typedef int (*nmreq_opt_parser_cb)(struct nmreq_parse_ctx *);
+
+#define NMREQ_OPT_MAXKEYS 16	/* max nr of recognized keys per option */
+
+/* struct nmreq_opt_key - describes an option key */
+struct nmreq_opt_key {
+	const char *key;	/* the key name */
+	int id;			/* its position in the parse context */
+	unsigned int flags;
+#define NMREQ_OPTK_ALLOWEMPTY 	(1U << 0) /* =value may be omitted */
+#define NMREQ_OPTK_MUSTSET	(1U << 1) /* the key is mandatory */
+#define NMREQ_OPTK_DEFAULT	(1U << 2) /* this is the default key */
+};
+
+/* struct nmreq_opt_parser - describes an option parser */
+struct nmreq_opt_parser {
+	const char *prefix;	/* matches one option prefix */
+	nmreq_opt_parser_cb parse;	/* the parse callback */
+	int default_key;	/* which option is the default if the
+				   parser is multi-key (-1 if none) */
+	int nr_keys;
+	unsigned int flags;
+#define NMREQ_OPTF_DISABLED     (1U << 0)
+#define NMREQ_OPTF_ALLOWEMPTY	(1U << 1)	/* =value can be omitted */
+
+	struct nmreq_opt_parser *next;	/* list of options */
+
+	/* recognized keys */
+	struct nmreq_opt_key keys[NMREQ_OPT_MAXKEYS];
+} __attribute__((aligned(16)));
+
+/* struct nmreq_parse_ctx - the parse context received by the parse callback */
+struct nmreq_parse_ctx {
+	struct nmctx *ctx;	/* the nmctx for errors and malloc/free */
+	void *token;		/* the token passed to nmreq_options_parse */
+
+	/* the value (i.e., the part after the = sign) of each recognized key
+	 * is assigned to the corresponding entry in this array, based on the
+	 * key id. Unassigned keys are left at NULL.
+	 */
+	const char *keys[NMREQ_OPT_MAXKEYS];
+};
+
+/* nmreq_get_mem_id - get the mem_id of the given port
+ * @portname	pointer to a pointer to the portname
+ * @ctx		pointer to the nmctx to use (for errors)
+ *
+ * *@portname must point to a substem:vpname porname, possibly followed by
+ * something else.
+ *
+ * If successful, returns the mem_id of *@portname and moves @portname past the
+ * subsystem:vpname part of the input. In case of error it returns -1, sets
+ * errno to an error value and sends an error message to ctx->error().
+ */
+int32_t nmreq_get_mem_id(const char **portname, struct nmctx *ctx);
+
+/* option list manipulation */
+void nmreq_push_option(struct nmreq_header *, struct nmreq_option *);
+void nmreq_remove_option(struct nmreq_header *, struct nmreq_option *);
+struct nmreq_option *nmreq_find_option(struct nmreq_header *, uint32_t);
+void nmreq_free_options(struct nmreq_header *);
+const char* nmreq_option_name(uint32_t);
+#define nmreq_foreach_option(h_, o_) \
+	for ((o_) = (struct nmreq_option *)((h_)->nr_options);\
+	     (o_) != NULL;\
+	     (o_) = (struct nmreq_option *)((o_)->nro_next))
+
+/* nmctx manipulation */
+
+/* the nmctx serves a few purposes:
+ *
+ * - maintain a list of all memory regions open by the program, so that two
+ *   ports that are using the same region (as identified by the mem_id) will
+ *   point to the same nmem_d instance.
+ *
+ * - allow the user to specify how to lock accesses to the above list, if
+ *   needed (lock() callback)
+ *
+ * - allow the user to specify how error messages should be delivered (error()
+ *   callback)
+ *
+ * - select the verbosity of the library (verbose field); if verbose==0, no
+ *   errors are sent to the error() callback
+ *
+ * - allow the user to override the malloc/free functions used by the library
+ *   (malloc() and free() callbacks)
+ *
+ */
+typedef void  (*nmctx_error_cb)(struct nmctx *, const char *);
+typedef void *(*nmctx_malloc_cb)(struct nmctx *,size_t);
+typedef void  (*nmctx_free_cb)(struct nmctx *,void *);
+typedef void  (*nmctx_lock_cb)(struct nmctx *, int);
+
+struct nmctx {
+	int verbose;
+	nmctx_error_cb 	error;
+	nmctx_malloc_cb	malloc;
+	nmctx_free_cb	free;
+	nmctx_lock_cb	lock;
+
+	struct nmem_d  *mem_descs;
+};
+
+/* nmctx_get - obtain a pointer to the current default context */
+struct nmctx *nmctx_get(void);
+
+/* nmctx_set_default - change the default context
+ * @ctx		pointer to the new context
+ *
+ * Returns a pointer to the previous default context.
+ */
+struct nmctx *nmctx_set_default(struct nmctx *ctx);
+
+/* internal functions and data structures */
+
+/* struct nmem_d - describes a memory region currently used */
+struct nmem_d {
+	uint16_t mem_id;	/* the region netmap identifier */
+	int refcount;		/* how many nmport_d's point here */
+	void *mem;		/* memory region base address */
+	size_t size;		/* memory region size */
+	int is_extmem;		/* was it obtained via extmem? */
+
+	/* pointers for the circular list implementation.
+	 * The list head is the mem_descs filed in the nmctx
+	 */
+	struct nmem_d *next;
+	struct nmem_d *prev;
+};
+
+/* a trick to force the inclusion of libpthread only if requested. If
+ * LIBNETMAP_NOTHREADSAFE is defined, no pthread symbol is imported.
+ *
+ * There is no need to actually call this function: the ((used)) attribute is
+ * sufficient to include it in the image.
+ */
+static  __attribute__((used)) void libnetmap_init(void)
+{
+#ifndef LIBNETMAP_NOTHREADSAFE
+	extern int nmctx_threadsafe;
+	/* dummy assignment to link-in the nmctx-pthread.o object.  The proper
+	 * inizialization is performed only once in the library constructor
+	 * defined there.
+	 */
+	nmctx_threadsafe = 1;
+#endif /* LIBNETMAP_NOTHREADSAFE */
+}
+
+/* nmctx_set_threadsafe - install a threadsafe default context
+ *
+ * called by the constructor in nmctx-pthread.o to initialize a lock and install
+ * the lock() callback in the default context.
+ */
+void nmctx_set_threadsafe(void);
+
+/* nmctx_ferror - format and send an error message */
+void nmctx_ferror(struct nmctx *, const char *, ...);
+/* nmctx_malloc - allocate memory */
+void *nmctx_malloc(struct nmctx *, size_t);
+/* nmctx_free - free memory allocated via nmctx_malloc */
+void nmctx_free(struct nmctx *, void *);
+/* nmctx_lock - lock the list of nmem_d */
+void nmctx_lock(struct nmctx *);
+/* nmctx_unlock - unlock the list of nmem_d */
+void nmctx_unlock(struct nmctx *);
+
+#endif /* LIBNETMAP_H_ */

Added: head/lib/libnetmap/nmctx-pthreads.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lib/libnetmap/nmctx-pthreads.c	Fri Aug 28 20:03:54 2020	(r364936)
@@ -0,0 +1,47 @@
+/* $FreeBSD$ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <net/netmap_user.h>
+#include <pthread.h>
+#include "libnetmap.h"
+
+struct nmctx_pthread {
+	struct nmctx up;
+	pthread_mutex_t mutex;
+};
+
+static struct nmctx_pthread nmctx_pthreadsafe;
+
+static void
+nmctx_pthread_lock(struct nmctx *ctx, int lock)
+{
+	struct nmctx_pthread *ctxp =
+		(struct nmctx_pthread *)ctx;
+	if (lock) {
+		pthread_mutex_lock(&ctxp->mutex);
+	} else {
+		pthread_mutex_unlock(&ctxp->mutex);
+	}
+}
+
+void __attribute__ ((constructor))
+nmctx_set_threadsafe(void)
+{
+	struct nmctx *old;
+
+	pthread_mutex_init(&nmctx_pthreadsafe.mutex, NULL);
+	old = nmctx_set_default(&nmctx_pthreadsafe.up);
+	nmctx_pthreadsafe.up = *old;
+	nmctx_pthreadsafe.up.lock = nmctx_pthread_lock;
+}
+
+int nmctx_threadsafe;

Added: head/lib/libnetmap/nmctx.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lib/libnetmap/nmctx.c	Fri Aug 28 20:03:54 2020	(r364936)
@@ -0,0 +1,111 @@
+/* $FreeBSD$ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <net/netmap_user.h>
+#define LIBNETMAP_NOTHREADSAFE
+#include "libnetmap.h"
+
+static void
+nmctx_default_error(struct nmctx *ctx, const char *errmsg)
+{
+	fprintf(stderr, "%s\n", errmsg);
+}
+
+static void *
+nmctx_default_malloc(struct nmctx *ctx, size_t sz)
+{
+	(void)ctx;
+	return malloc(sz);
+}
+
+static void
+nmctx_default_free(struct nmctx *ctx, void *p)
+{
+	(void)ctx;
+	free(p);
+}
+
+static struct nmctx nmctx_global = {
+	.verbose = 1,
+	.error = nmctx_default_error,
+	.malloc = nmctx_default_malloc,
+	.free = nmctx_default_free,
+	.lock = NULL,
+};
+
+static struct nmctx *nmctx_default = &nmctx_global;
+
+struct nmctx *
+nmctx_get(void)
+{
+	return nmctx_default;
+}
+
+struct nmctx *
+nmctx_set_default(struct nmctx *ctx)
+{
+	struct nmctx *old = nmctx_default;
+	nmctx_default = ctx;
+	return old;
+}
+
+#define MAXERRMSG 1000
+void
+nmctx_ferror(struct nmctx *ctx, const char *fmt, ...)
+{
+	char errmsg[MAXERRMSG];
+	va_list ap;
+	int rv;
+
+	if (!ctx->verbose)
+		return;
+
+	va_start(ap, fmt);
+	rv = vsnprintf(errmsg, MAXERRMSG, fmt, ap);
+	va_end(ap);
+
+	if (rv > 0) {
+		if (rv < MAXERRMSG) {
+			ctx->error(ctx, errmsg);
+		} else {
+			ctx->error(ctx, "error message too long");
+		}
+	} else {
+		ctx->error(ctx, "internal error");
+	}
+}
+
+void *
+nmctx_malloc(struct nmctx *ctx, size_t sz)
+{
+	return ctx->malloc(ctx, sz);
+}
+
+void
+nmctx_free(struct nmctx *ctx, void *p)
+{
+	ctx->free(ctx, p);
+}
+
+void
+nmctx_lock(struct nmctx *ctx)
+{
+	if (ctx->lock != NULL)
+		ctx->lock(ctx, 1);
+}
+
+void
+nmctx_unlock(struct nmctx *ctx)
+{
+	if (ctx->lock != NULL)
+		ctx->lock(ctx, 0);
+}

Added: head/lib/libnetmap/nmport.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lib/libnetmap/nmport.c	Fri Aug 28 20:03:54 2020	(r364936)
@@ -0,0 +1,810 @@
+/* $FreeBSD$ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <net/netmap_user.h>
+#define LIBNETMAP_NOTHREADSAFE
+#include "libnetmap.h"
+
+struct nmport_cleanup_d {
+	struct nmport_cleanup_d *next;
+	void (*cleanup)(struct nmport_cleanup_d *, struct nmport_d *);
+};
+
+static void
+nmport_push_cleanup(struct nmport_d *d, struct nmport_cleanup_d *c)
+{
+	c->next = d->clist;
+	d->clist = c;
+}
+
+static void
+nmport_pop_cleanup(struct nmport_d *d)
+{
+	struct nmport_cleanup_d *top;
+
+	top = d->clist;
+	d->clist = d->clist->next;
+	(*top->cleanup)(top, d);
+	nmctx_free(d->ctx, top);
+}
+
+void nmport_do_cleanup(struct nmport_d *d)
+{
+	while (d->clist != NULL) {
+		nmport_pop_cleanup(d);
+	}
+}
+
+static struct nmport_d *
+nmport_new_with_ctx(struct nmctx *ctx)
+{
+	struct nmport_d *d;
+
+	/* allocate a descriptor */
+	d = nmctx_malloc(ctx, sizeof(*d));
+	if (d == NULL) {
+		nmctx_ferror(ctx, "cannot allocate nmport descriptor");
+		goto out;
+	}
+	memset(d, 0, sizeof(*d));
+
+	nmreq_header_init(&d->hdr, NETMAP_REQ_REGISTER, &d->reg);
+
+	d->ctx = ctx;
+	d->fd = -1;
+
+out:
+	return d;
+}
+
+struct nmport_d *
+nmport_new(void)
+{
+	struct nmctx *ctx = nmctx_get();
+	return nmport_new_with_ctx(ctx);
+}
+
+
+void
+nmport_delete(struct nmport_d *d)
+{
+	nmctx_free(d->ctx, d);
+}
+
+void
+nmport_extmem_cleanup(struct nmport_cleanup_d *c, struct nmport_d *d)
+{
+	(void)c;
+
+	if (d->extmem == NULL)
+		return;
+
+	nmreq_remove_option(&d->hdr, &d->extmem->nro_opt);
+	nmctx_free(d->ctx, d->extmem);
+	d->extmem = NULL;
+}
+
+
+int
+nmport_extmem(struct nmport_d *d, void *base, size_t size)
+{
+	struct nmctx *ctx = d->ctx;
+	struct nmport_cleanup_d *clnup = NULL;
+
+	if (d->register_done) {
+		nmctx_ferror(ctx, "%s: cannot set extmem of an already registered port", d->hdr.nr_name);
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (d->extmem != NULL) {
+		nmctx_ferror(ctx, "%s: extmem already in use", d->hdr.nr_name);
+		errno = EINVAL;
+		return -1;
+	}
+
+	clnup = (struct nmport_cleanup_d *)nmctx_malloc(ctx, sizeof(*clnup));
+	if (clnup == NULL) {
+		nmctx_ferror(ctx, "failed to allocate cleanup descriptor");
+		errno = ENOMEM;
+		return -1;
+	}
+
+	d->extmem = nmctx_malloc(ctx, sizeof(*d->extmem));
+	if (d->extmem == NULL) {
+		nmctx_ferror(ctx, "%s: cannot allocate extmem option", d->hdr.nr_name);
+		nmctx_free(ctx, clnup);
+		errno = ENOMEM;
+		return -1;
+	}
+	memset(d->extmem, 0, sizeof(*d->extmem));
+	d->extmem->nro_usrptr = (uintptr_t)base;
+	d->extmem->nro_opt.nro_reqtype = NETMAP_REQ_OPT_EXTMEM;
+	d->extmem->nro_info.nr_memsize = size;
+	nmreq_push_option(&d->hdr, &d->extmem->nro_opt);
+
+	clnup->cleanup = nmport_extmem_cleanup;
+	nmport_push_cleanup(d, clnup);
+
+	return 0;
+}
+

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202008282003.07SK3tuD093523>