From owner-svn-src-all@freebsd.org Fri Aug 28 20:03:56 2020 Return-Path: Delivered-To: svn-src-all@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id 5D8AC3BF2D4; Fri, 28 Aug 2020 20:03:56 +0000 (UTC) (envelope-from vmaffione@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4BdVsm2CP9z4JRl; Fri, 28 Aug 2020 20:03:56 +0000 (UTC) (envelope-from vmaffione@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 302431A3E0; Fri, 28 Aug 2020 20:03:56 +0000 (UTC) (envelope-from vmaffione@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id 07SK3uja093530; Fri, 28 Aug 2020 20:03:56 GMT (envelope-from vmaffione@FreeBSD.org) Received: (from vmaffione@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id 07SK3tuD093523; Fri, 28 Aug 2020 20:03:55 GMT (envelope-from vmaffione@FreeBSD.org) Message-Id: <202008282003.07SK3tuD093523@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: vmaffione set sender to vmaffione@FreeBSD.org using -f From: Vincenzo Maffione Date: Fri, 28 Aug 2020 20:03:55 +0000 (UTC) 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 X-SVN-Group: head X-SVN-Commit-Author: vmaffione X-SVN-Commit-Paths: in head: lib lib/libnetmap share/mk X-SVN-Commit-Revision: 364936 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.33 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 28 Aug 2020 20:03:56 -0000 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 + +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 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 + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 ***