Index: ObsoleteFiles.inc =================================================================== --- ObsoleteFiles.inc +++ ObsoleteFiles.inc @@ -78,6 +78,72 @@ OLD_FILES+=usr/share/man/man9/MFREE.9.gz # 20151023: unused sgsmsg utility is removed OLD_FILES+=usr/bin/sgsmsg +# 20150702: Remove duplicated nvlist includes. +OLD_FILES+=usr/include/dnv.h +OLD_FILES+=usr/include/nv.h +# 20150604: Move nvlist man pages to section 9. +OLD_FILES+=usr/share/man/man3/libnv.3.gz +OLD_FILES+=usr/share/man/man3/nvlist.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_add_binary.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_add_bool.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_add_descriptor.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_add_null.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_add_number.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_add_nvlist.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_add_string.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_add_stringf.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_add_stringv.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_clone.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_create.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_destroy.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_dump.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_empty.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_error.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_exists.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_exists_binary.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_exists_bool.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_exists_descriptor.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_exists_null.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_exists_number.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_exists_nvlist.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_exists_string.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_exists_type.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_fdump.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_flags.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_free.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_free_binary.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_free_bool.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_free_descriptor.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_free_null.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_free_number.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_free_nvlist.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_free_string.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_free_type.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_get_binary.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_get_bool.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_get_descriptor.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_get_number.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_get_nvlist.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_get_parent.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_get_string.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_move_binary.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_move_descriptor.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_move_nvlist.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_move_string.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_next.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_pack.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_recv.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_send.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_set_error.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_size.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_take_binary.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_take_bool.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_take_descriptor.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_take_number.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_take_nvlist.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_take_string.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_unpack.3.gz +OLD_FILES+=usr/share/man/man3/nvlist_xfer.3.gz # 20150506 OLD_FILES+=usr/share/man/man9/NDHASGIANT.9.gz # 20141223: remove in6_gif.h and in_gif.h Index: etc/defaults/rc.conf =================================================================== --- etc/defaults/rc.conf +++ etc/defaults/rc.conf @@ -683,6 +683,8 @@ rctl_enable="NO" # Load rctl(8) rules on boot rctl_rules="/etc/rctl.conf" # rctl(8) ruleset. See rctl.conf(5). +iovctl_files="" # Config files for iovctl(8) + ############################################################## ### Jail Configuration (see rc.conf(5) manual page) ########## ############################################################## Index: etc/rc.d/Makefile =================================================================== --- etc/rc.d/Makefile +++ etc/rc.d/Makefile @@ -44,6 +44,7 @@ hostid_save \ hostname \ initrandom \ + iovctl \ ip6addrctl \ ipfilter \ ipfs \ Index: etc/rc.d/iovctl =================================================================== --- etc/rc.d/iovctl +++ etc/rc.d/iovctl @@ -0,0 +1,39 @@ +#!/bin/sh +# +# $FreeBSD$ +# + +# PROVIDE: iovctl +# REQUIRE: FILESYSTEMS sysctl + +. /etc/rc.subr + +name="iovctl" +command="/usr/sbin/iovctl" +start_cmd="iovctl_start" +stop_cmd="iovctl_stop" + +run_iovctl() +{ + local _f flag + + flag=$1 + for _f in ${iovctl_files} ; do + if [ -r ${_f} ]; then + ${command} ${flag} -f ${_f} > /dev/null + fi + done +} + +iovctl_start() +{ + run_iovctl -C +} + +iovctl_stop() +{ + run_iovctl -D +} + +load_rc_config $name +run_rc_command "$1" Index: etc/rc.d/netif =================================================================== --- etc/rc.d/netif +++ etc/rc.d/netif @@ -26,7 +26,7 @@ # # PROVIDE: netif -# REQUIRE: atm1 FILESYSTEMS serial sppp sysctl +# REQUIRE: atm1 FILESYSTEMS iovctl serial sppp sysctl # REQUIRE: ipfilter ipfs # KEYWORD: nojailvnet Index: lib/libnv/Makefile =================================================================== --- lib/libnv/Makefile +++ lib/libnv/Makefile @@ -7,82 +7,14 @@ LIB= nv SHLIB_MAJOR= 0 -.PATH: ${.CURDIR}/../../sys/kern ${.CURDIR}/../../sys/sys +.PATH: ${.CURDIR}/../../sys/contrib/libnv ${.CURDIR}/../../sys/sys CFLAGS+=-I${.CURDIR}/../../sys -I${.CURDIR} -SRCS= subr_dnvlist.c +SRCS= dnvlist.c SRCS+= msgio.c -SRCS+= subr_nvlist.c -SRCS+= subr_nvpair.c +SRCS+= nvlist.c +SRCS+= nvpair.c -INCS= dnv.h -INCS+= nv.h - -MAN+= nv.3 - -MLINKS+=nv.3 libnv.3 \ - nv.3 nvlist.3 -MLINKS+=nv.3 nvlist_add_binary.3 \ - nv.3 nvlist_add_bool.3 \ - nv.3 nvlist_add_descriptor.3 \ - nv.3 nvlist_add_null.3 \ - nv.3 nvlist_add_number.3 \ - nv.3 nvlist_add_nvlist.3 \ - nv.3 nvlist_add_string.3 \ - nv.3 nvlist_add_stringf.3 \ - nv.3 nvlist_add_stringv.3 \ - nv.3 nvlist_clone.3 \ - nv.3 nvlist_create.3 \ - nv.3 nvlist_destroy.3 \ - nv.3 nvlist_dump.3 \ - nv.3 nvlist_empty.3 \ - nv.3 nvlist_error.3 \ - nv.3 nvlist_exists.3 \ - nv.3 nvlist_exists_binary.3 \ - nv.3 nvlist_exists_bool.3 \ - nv.3 nvlist_exists_descriptor.3 \ - nv.3 nvlist_exists_null.3 \ - nv.3 nvlist_exists_number.3 \ - nv.3 nvlist_exists_nvlist.3 \ - nv.3 nvlist_exists_string.3 \ - nv.3 nvlist_exists_type.3 \ - nv.3 nvlist_fdump.3 \ - nv.3 nvlist_flags.3 \ - nv.3 nvlist_free.3 \ - nv.3 nvlist_free_binary.3 \ - nv.3 nvlist_free_bool.3 \ - nv.3 nvlist_free_descriptor.3 \ - nv.3 nvlist_free_null.3 \ - nv.3 nvlist_free_number.3 \ - nv.3 nvlist_free_nvlist.3 \ - nv.3 nvlist_free_string.3 \ - nv.3 nvlist_free_type.3 \ - nv.3 nvlist_get_binary.3 \ - nv.3 nvlist_get_bool.3 \ - nv.3 nvlist_get_descriptor.3 \ - nv.3 nvlist_get_number.3 \ - nv.3 nvlist_get_nvlist.3 \ - nv.3 nvlist_get_parent.3 \ - nv.3 nvlist_get_string.3 \ - nv.3 nvlist_move_binary.3 \ - nv.3 nvlist_move_descriptor.3 \ - nv.3 nvlist_move_nvlist.3 \ - nv.3 nvlist_move_string.3 \ - nv.3 nvlist_next.3 \ - nv.3 nvlist_pack.3 \ - nv.3 nvlist_recv.3 \ - nv.3 nvlist_send.3 \ - nv.3 nvlist_set_error.3 \ - nv.3 nvlist_size.3 \ - nv.3 nvlist_take_binary.3 \ - nv.3 nvlist_take_bool.3 \ - nv.3 nvlist_take_descriptor.3 \ - nv.3 nvlist_take_number.3 \ - nv.3 nvlist_take_nvlist.3 \ - nv.3 nvlist_take_string.3 \ - nv.3 nvlist_unpack.3 \ - nv.3 nvlist_xfer.3 - WARNS?= 6 .if ${MK_TESTS} != "no" Index: lib/libnv/nv.3 =================================================================== --- lib/libnv/nv.3 +++ lib/libnv/nv.3 @@ -1,653 +0,0 @@ -.\" -.\" Copyright (c) 2013 The FreeBSD Foundation -.\" All rights reserved. -.\" -.\" This documentation was written by Pawel Jakub Dawidek under sponsorship -.\" the FreeBSD Foundation. -.\" -.\" 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$ -.\" -.Dd May 1, 2015 -.Dt NV 3 -.Os -.Sh NAME -.Nm nvlist_create , -.Nm nvlist_destroy , -.Nm nvlist_error , -.Nm nvlist_set_error , -.Nm nvlist_empty , -.Nm nvlist_flags , -.Nm nvlist_exists , -.Nm nvlist_free , -.Nm nvlist_clone , -.Nm nvlist_dump , -.Nm nvlist_fdump , -.Nm nvlist_size , -.Nm nvlist_pack , -.Nm nvlist_unpack , -.Nm nvlist_send , -.Nm nvlist_recv , -.Nm nvlist_xfer , -.Nm nvlist_next , -.Nm nvlist_add , -.Nm nvlist_move , -.Nm nvlist_get , -.Nm nvlist_take -.Nd "library for name/value pairs" -.Sh LIBRARY -.Lb libnv -.Sh SYNOPSIS -.In nv.h -.Ft "nvlist_t *" -.Fn nvlist_create "int flags" -.Ft void -.Fn nvlist_destroy "nvlist_t *nvl" -.Ft int -.Fn nvlist_error "const nvlist_t *nvl" -.Ft void -.Fn nvlist_set_error "nvlist_t *nvl, int error" -.Ft bool -.Fn nvlist_empty "const nvlist_t *nvl" -.Ft int -.Fn nvlist_flags "const nvlist_t *nvl" -.\" -.Ft "nvlist_t *" -.Fn nvlist_clone "const nvlist_t *nvl" -.\" -.Ft void -.Fn nvlist_dump "const nvlist_t *nvl, int fd" -.Ft void -.Fn nvlist_fdump "const nvlist_t *nvl, FILE *fp" -.\" -.Ft size_t -.Fn nvlist_size "const nvlist_t *nvl" -.Ft "void *" -.Fn nvlist_pack "const nvlist_t *nvl" "size_t *sizep" -.Ft "nvlist_t *" -.Fn nvlist_unpack "const void *buf" "size_t size" -.\" -.Ft int -.Fn nvlist_send "int sock" "const nvlist_t *nvl" -.Ft "nvlist_t *" -.Fn nvlist_recv "int sock" -.Ft "nvlist_t *" -.Fn nvlist_xfer "int sock" "nvlist_t *nvl" -.\" -.Ft "const char *" -.Fn nvlist_next "const nvlist_t *nvl" "int *typep" "void **cookiep" -.\" -.Ft bool -.Fn nvlist_exists "const nvlist_t *nvl" "const char *name" -.Ft bool -.Fn nvlist_exists_type "const nvlist_t *nvl" "const char *name" "int type" -.Ft bool -.Fn nvlist_exists_null "const nvlist_t *nvl" "const char *name" -.Ft bool -.Fn nvlist_exists_bool "const nvlist_t *nvl" "const char *name" -.Ft bool -.Fn nvlist_exists_number "const nvlist_t *nvl" "const char *name" -.Ft bool -.Fn nvlist_exists_string "const nvlist_t *nvl" "const char *name" -.Ft bool -.Fn nvlist_exists_nvlist "const nvlist_t *nvl" "const char *name" -.Ft bool -.Fn nvlist_exists_descriptor "const nvlist_t *nvl" "const char *name" -.Ft bool -.Fn nvlist_exists_binary "const nvlist_t *nvl" "const char *name" -.\" -.Ft void -.Fn nvlist_add_null "nvlist_t *nvl" "const char *name" -.Ft void -.Fn nvlist_add_bool "nvlist_t *nvl" "const char *name" "bool value" -.Ft void -.Fn nvlist_add_number "nvlist_t *nvl" "const char *name" "uint64_t value" -.Ft void -.Fn nvlist_add_string "nvlist_t *nvl" "const char *name" "const char *value" -.Ft void -.Fn nvlist_add_stringf "nvlist_t *nvl" "const char *name" "const char *valuefmt" "..." -.Ft void -.Fn nvlist_add_stringv "nvlist_t *nvl" "const char *name" "const char *valuefmt" "va_list valueap" -.Ft void -.Fn nvlist_add_nvlist "nvlist_t *nvl" "const char *name" "const nvlist_t *value" -.Ft void -.Fn nvlist_add_descriptor "nvlist_t *nvl" "const char *name" "int value" -.Ft void -.Fn nvlist_add_binary "nvlist_t *nvl" "const char *name" "const void *value" "size_t size" -.\" -.Ft void -.Fn nvlist_move_string "nvlist_t *nvl" "const char *name" "char *value" -.Ft void -.Fn nvlist_move_nvlist "nvlist_t *nvl" "const char *name" "nvlist_t *value" -.Ft void -.Fn nvlist_move_descriptor "nvlist_t *nvl" "const char *name" "int value" -.Ft void -.Fn nvlist_move_binary "nvlist_t *nvl" "const char *name" "void *value" "size_t size" -.\" -.Ft bool -.Fn nvlist_get_bool "const nvlist_t *nvl" "const char *name" -.Ft uint64_t -.Fn nvlist_get_number "const nvlist_t *nvl" "const char *name" -.Ft "const char *" -.Fn nvlist_get_string "const nvlist_t *nvl" "const char *name" -.Ft "const nvlist_t *" -.Fn nvlist_get_nvlist "const nvlist_t *nvl" "const char *name" -.Ft int -.Fn nvlist_get_descriptor "const nvlist_t *nvl" "const char *name" -.Ft "const void *" -.Fn nvlist_get_binary "const nvlist_t *nvl" "const char *name" "size_t *sizep" -.Ft "const nvlist_t *" -.Fn nvlist_get_parent "const nvlist_t *nvl" "void **cookiep" -.\" -.Ft bool -.Fn nvlist_take_bool "nvlist_t *nvl" "const char *name" -.Ft uint64_t -.Fn nvlist_take_number "nvlist_t *nvl" "const char *name" -.Ft "char *" -.Fn nvlist_take_string "nvlist_t *nvl" "const char *name" -.Ft "nvlist_t *" -.Fn nvlist_take_nvlist "nvlist_t *nvl" "const char *name" -.Ft int -.Fn nvlist_take_descriptor "nvlist_t *nvl" "const char *name" -.Ft "void *" -.Fn nvlist_take_binary "nvlist_t *nvl" "const char *name" "size_t *sizep" -.\" -.Ft void -.Fn nvlist_free "nvlist_t *nvl" "const char *name" -.Ft void -.Fn nvlist_free_type "nvlist_t *nvl" "const char *name" "int type" -.\" -.Ft void -.Fn nvlist_free_null "nvlist_t *nvl" "const char *name" -.Ft void -.Fn nvlist_free_bool "nvlist_t *nvl" "const char *name" -.Ft void -.Fn nvlist_free_number "nvlist_t *nvl" "const char *name" -.Ft void -.Fn nvlist_free_string "nvlist_t *nvl" "const char *name" -.Ft void -.Fn nvlist_free_nvlist "nvlist_t *nvl" "const char *name" -.Ft void -.Fn nvlist_free_descriptor "nvlist_t *nvl" "const char *name" -.Ft void -.Fn nvlist_free_binary "nvlist_t *nvl" "const char *name" -.Sh DESCRIPTION -The -.Nm libnv -library allows to easily manage name value pairs as well as send and receive -them over sockets. -A group (list) of name value pairs is called an -.Nm nvlist . -The API supports the following data types: -.Bl -ohang -offset indent -.It Sy null ( NV_TYPE_NULL ) -There is no data associated with the name. -.It Sy bool ( NV_TYPE_BOOL ) -The value can be either -.Dv true -or -.Dv false . -.It Sy number ( NV_TYPE_NUMBER ) -The value is a number stored as -.Vt uint64_t . -.It Sy string ( NV_TYPE_STRING ) -The value is a C string. -.It Sy nvlist ( NV_TYPE_NVLIST ) -The value is a nested nvlist. -.It Sy descriptor ( NV_TYPE_DESCRIPTOR ) -The value is a file descriptor. -Note that file descriptors can be sent only over -.Xr unix 4 -domain sockets. -.It Sy binary ( NV_TYPE_BINARY ) -The value is a binary buffer. -.El -.Pp -The -.Fn nvlist_create -function allocates memory and initializes an nvlist. -.Pp -The following flag can be provided: -.Pp -.Bl -tag -width "NV_FLAG_IGNORE_CASE" -compact -offset indent -.It Dv NV_FLAG_IGNORE_CASE -Perform case-insensitive lookups of provided names. -.El -.Pp -The -.Fn nvlist_destroy -function destroys the given nvlist. -Function does nothing if -.Dv NULL -nvlist is provided. -Function never modifies the -.Va errno -global variable. -.Pp -The -.Fn nvlist_error -function returns any error value that the nvlist accumulated. -If the given nvlist is -.Dv NULL -the -.Er ENOMEM -error will be returned. -.Pp -The -.Fn nvlist_set_error -function sets an nvlist to be in the error state. -Subsequent calls to -.Fn nvlist_error -will return the given error value. -This function cannot be used to clear the error state from an nvlist. -This function does nothing if the nvlist is already in the error state. -.Pp -The -.Fn nvlist_empty -function returns -.Dv true -if the given nvlist is empty and -.Dv false -otherwise. -The nvlist must not be in error state. -.Pp -The -.Fn nvlist_flags -function returns flags used to create the nvlist with the -.Fn nvlist_create -function. -.Pp -The -.Fn nvlist_clone -functions clones the given nvlist. -The clone shares no resources with its origin. -This also means that all file descriptors that are part of the nvlist will be -duplicated with the -.Xr dup 2 -system call before placing them in the clone. -.Pp -The -.Fn nvlist_dump -dumps nvlist content for debugging purposes to the given file descriptor -.Fa fd . -.Pp -The -.Fn nvlist_fdump -dumps nvlist content for debugging purposes to the given file stream -.Fa fp . -.Pp -The -.Fn nvlist_size -function returns the size of the given nvlist after converting it to binary -buffer with the -.Fn nvlist_pack -function. -.Pp -The -.Fn nvlist_pack -function converts the given nvlist to a binary buffer. -The function allocates memory for the buffer, which should be freed with the -.Xr free 3 -function. -If the -.Fa sizep -argument is not -.Dv NULL , -the size of the buffer will be stored there. -The function returns -.Dv NULL -in case of an error (allocation failure). -If the nvlist contains any file descriptors -.Dv NULL -will be returned. -The nvlist must not be in error state. -.Pp -The -.Fn nvlist_unpack -function converts the given buffer to the nvlist. -The function returns -.Dv NULL -in case of an error. -.Pp -The -.Fn nvlist_send -function sends the given nvlist over the socket given by the -.Fa sock -argument. -Note that nvlist that contains file descriptors can only be send over -.Xr unix 4 -domain sockets. -.Pp -The -.Fn nvlist_recv -function receives nvlist over the socket given by the -.Fa sock -argument. -.Pp -The -.Fn nvlist_xfer -function sends the given nvlist over the socket given by the -.Fa sock -argument and receives nvlist over the same socket. -The given nvlist is always destroyed. -.Pp -The -.Fn nvlist_next -function iterates over the given nvlist returning names and types of subsequent -elements. -The -.Fa cookiep -argument allows the function to figure out which element should be returned -next. -The -.Va *cookiep -should be set to -.Dv NULL -for the first call and should not be changed later. -Returning -.Dv NULL -means there are no more elements on the nvlist. -The -.Fa typep -argument can be NULL. -Elements may not be removed from the nvlist while traversing it. -The nvlist must not be in error state. -.Pp -The -.Fn nvlist_exists -function returns -.Dv true -if element of the given name exists (besides of its type) or -.Dv false -otherwise. -The nvlist must not be in error state. -.Pp -The -.Fn nvlist_exists_type -function returns -.Dv true -if element of the given name and the given type exists or -.Dv false -otherwise. -The nvlist must not be in error state. -.Pp -The -.Fn nvlist_exists_null , -.Fn nvlist_exists_bool , -.Fn nvlist_exists_number , -.Fn nvlist_exists_string , -.Fn nvlist_exists_nvlist , -.Fn nvlist_exists_descriptor , -.Fn nvlist_exists_binary -functions return -.Dv true -if element of the given name and the given type determined by the function name -exists or -.Dv false -otherwise. -The nvlist must not be in error state. -.Pp -The -.Fn nvlist_add_null , -.Fn nvlist_add_bool , -.Fn nvlist_add_number , -.Fn nvlist_add_string , -.Fn nvlist_add_stringf , -.Fn nvlist_add_stringv , -.Fn nvlist_add_nvlist , -.Fn nvlist_add_descriptor , -.Fn nvlist_add_binary -functions add element to the given nvlist. -When adding string or binary buffor the functions will allocate memory -and copy the data over. -When adding nvlist, the nvlist will be cloned and clone will be added. -When adding descriptor, the descriptor will be duplicated using the -.Xr dup 2 -system call and the new descriptor will be added. -If an error occurs while adding new element, internal error is set which can be -examined using the -.Fn nvlist_error -function. -.Pp -The -.Fn nvlist_move_string , -.Fn nvlist_move_nvlist , -.Fn nvlist_move_descriptor , -.Fn nvlist_move_binary -functions add new element to the given nvlist, but unlike -.Fn nvlist_add_ -functions they will consume the given resource. -If an error occurs while adding new element, the resource is destroyed and -internal error is set which can be examined using the -.Fn nvlist_error -function. -.Pp -The -.Fn nvlist_get_bool , -.Fn nvlist_get_number , -.Fn nvlist_get_string , -.Fn nvlist_get_nvlist , -.Fn nvlist_get_descriptor , -.Fn nvlist_get_binary -functions allow to obtain value of the given name. -In case of string, nvlist, descriptor or binary, returned resource should -not be modified - it still belongs to the nvlist. -If element of the given name does not exist, the program will be aborted. -To avoid that the caller should check for existence before trying to obtain -the value or use -.Xr dnvlist 3 -extension, which allows to provide default value for a missing element. -The nvlist must not be in error state. -.Pp -The -.Fn nvlist_get_parent -function allows to obtain the parent nvlist from the nested nvlist. -.Pp -The -.Fn nvlist_take_bool , -.Fn nvlist_take_number , -.Fn nvlist_take_string , -.Fn nvlist_take_nvlist , -.Fn nvlist_take_descriptor , -.Fn nvlist_take_binary -functions return value associated with the given name and remove the element -from the nvlist. -In case of string and binary values, the caller is responsible for free returned -memory using the -.Xr free 3 -function. -In case of nvlist, the caller is responsible for destroying returned nvlist -using the -.Fn nvlist_destroy -function. -In case of descriptor, the caller is responsible for closing returned descriptor -using the -.Fn close 2 -system call. -If element of the given name does not exist, the program will be aborted. -To avoid that the caller should check for existence before trying to obtain -the value or use -.Xr dnvlist 3 -extension, which allows to provide default value for a missing element. -The nvlist must not be in error state. -.Pp -The -.Fn nvlist_free -function removes element of the given name from the nvlist (besides of its type) -and frees all resources associated with it. -If element of the given name does not exist, the program will be aborted. -The nvlist must not be in error state. -.Pp -The -.Fn nvlist_free_type -function removes element of the given name and the given type from the nvlist -and frees all resources associated with it. -If element of the given name and the given type does not exist, the program -will be aborted. -The nvlist must not be in error state. -.Pp -The -.Fn nvlist_free_null , -.Fn nvlist_free_bool , -.Fn nvlist_free_number , -.Fn nvlist_free_string , -.Fn nvlist_free_nvlist , -.Fn nvlist_free_descriptor , -.Fn nvlist_free_binary -functions remove element of the given name and the given type determined by the -function name from the nvlist and free all resources associated with it. -If element of the given name and the given type does not exist, the program -will be aborted. -The nvlist must not be in error state. -.Sh EXAMPLES -The following example demonstrates how to prepare an nvlist and send it over -.Xr unix 4 -domain socket. -.Bd -literal -nvlist_t *nvl; -int fd; - -fd = open("/tmp/foo", O_RDONLY); -if (fd < 0) - err(1, "open(\\"/tmp/foo\\") failed"); - -nvl = nvlist_create(0); -/* - * There is no need to check if nvlist_create() succeeded, - * as the nvlist_add_() functions can cope. - * If it failed, nvlist_send() will fail. - */ -nvlist_add_string(nvl, "filename", "/tmp/foo"); -nvlist_add_number(nvl, "flags", O_RDONLY); -/* - * We just want to send the descriptor, so we can give it - * for the nvlist to consume (that's why we use nvlist_move - * not nvlist_add). - */ -nvlist_move_descriptor(nvl, "fd", fd); -if (nvlist_send(sock, nvl) < 0) { - nvlist_destroy(nvl); - err(1, "nvlist_send() failed"); -} -nvlist_destroy(nvl); -.Ed -.Pp -Receiving nvlist and getting data: -.Bd -literal -nvlist_t *nvl; -const char *command; -char *filename; -int fd; - -nvl = nvlist_recv(sock); -if (nvl == NULL) - err(1, "nvlist_recv() failed"); - -/* For command we take pointer to nvlist's buffer. */ -command = nvlist_get_string(nvl, "command"); -/* - * For filename we remove it from the nvlist and take - * ownership of the buffer. - */ -filename = nvlist_take_string(nvl, "filename"); -/* The same for the descriptor. */ -fd = nvlist_take_descriptor(nvl, "fd"); - -printf("command=%s filename=%s fd=%d\n", command, filename, fd); - -nvlist_destroy(nvl); -free(filename); -close(fd); -/* command was freed by nvlist_destroy() */ -.Ed -.Pp -Iterating over nvlist: -.Bd -literal -nvlist_t *nvl; -const char *name; -void *cookie; -int type; - -nvl = nvlist_recv(sock); -if (nvl == NULL) - err(1, "nvlist_recv() failed"); - -cookie = NULL; -while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) { - printf("%s=", name); - switch (type) { - case NV_TYPE_NUMBER: - printf("%ju", (uintmax_t)nvlist_get_number(nvl, name)); - break; - case NV_TYPE_STRING: - printf("%s", nvlist_get_string(nvl, name)); - break; - default: - printf("N/A"); - break; - } - printf("\\n"); -} -.Ed -.Pp -Iterating over every nested nvlist: -.Bd -literal -nvlist_t *nvl; -const char *name; -void *cookie; -int type; - -nvl = nvlist_recv(sock); -if (nvl == NULL) - err(1, "nvlist_recv() failed"); - -cookie = NULL; -do { - while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) { - if (type == NV_TYPE_NVLIST) { - nvl = nvlist_get_nvlist(nvl, name); - cookie = NULL; - } - } -} while ((nvl = nvlist_get_parent(nvl, &cookie)) != NULL); -.Ed -.Sh SEE ALSO -.Xr close 2 , -.Xr dup 2 , -.Xr open 2 , -.Xr err 3 , -.Xr free 3 , -.Xr printf 3 , -.Xr unix 4 -.Sh HISTORY -The -.Nm libnv -library appeared in -.Fx 11.0 . -.Sh AUTHORS -.An -nosplit -The -.Nm libnv -library was implemented by -.An Pawel Jakub Dawidek Aq pawel@dawidek.net -under sponsorship from the FreeBSD Foundation. Index: lib/libnv/tests/Makefile =================================================================== --- lib/libnv/tests/Makefile +++ lib/libnv/tests/Makefile @@ -4,6 +4,7 @@ ATF_TESTS_CXX= \ dnv_tests \ + nv_array_tests \ nv_tests \ TAP_TESTS_C+= nvlist_add_test Index: lib/libnv/tests/dnv_tests.cc =================================================================== --- lib/libnv/tests/dnv_tests.cc +++ lib/libnv/tests/dnv_tests.cc @@ -27,9 +27,11 @@ #include __FBSDID("$FreeBSD$"); +#include +#include +#include + #include -#include -#include ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_get_bool__present); ATF_TEST_CASE_BODY(dnvlist_get_bool__present) Index: lib/libnv/tests/nv_array_tests.cc =================================================================== --- lib/libnv/tests/nv_array_tests.cc +++ lib/libnv/tests/nv_array_tests.cc @@ -0,0 +1,1196 @@ +/*- + * Copyright (c) 2015 Mariusz Zaborski + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define fd_is_valid(fd) (fcntl((fd), F_GETFL) != -1 || errno != EBADF) + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_bool_array__basic); +ATF_TEST_CASE_BODY(nvlist_bool_array__basic) +{ + bool testbool[16]; + const bool *const_result; + bool *result; + nvlist_t *nvl; + size_t num_items; + unsigned int i; + const char *key; + + key = "nvl/bool"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + + for (i = 0; i < 16; i++) + testbool[i] = (i % 2 == 0); + + nvlist_add_bool_array(nvl, key, testbool, 16); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_bool_array(nvl, key)); + ATF_REQUIRE(nvlist_exists_bool_array(nvl, "nvl/bool")); + + const_result = nvlist_get_bool_array(nvl, key, &num_items); + ATF_REQUIRE_EQ(num_items, 16); + ATF_REQUIRE(const_result != NULL); + for (i = 0; i < num_items; i++) + ATF_REQUIRE_EQ(const_result[i], testbool[i]); + + result = nvlist_take_bool_array(nvl, key, &num_items); + ATF_REQUIRE_EQ(num_items, 16); + ATF_REQUIRE(const_result != NULL); + for (i = 0; i < num_items; i++) + ATF_REQUIRE_EQ(result[i], testbool[i]); + + ATF_REQUIRE(!nvlist_exists_bool_array(nvl, key)); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + + free(result); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_string_array__basic); +ATF_TEST_CASE_BODY(nvlist_string_array__basic) +{ + const char * const *const_result; + char **result; + nvlist_t *nvl; + size_t num_items; + unsigned int i; + const char *key; + const char *string_arr[8] = { "a", "b", "kot", "foo", + "tests", "nice test", "", "abcdef" }; + + key = "nvl/string"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + + nvlist_add_string_array(nvl, key, string_arr, nitems(string_arr)); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_string_array(nvl, key)); + ATF_REQUIRE(nvlist_exists_string_array(nvl, "nvl/string")); + + const_result = nvlist_get_string_array(nvl, key, &num_items); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(const_result != NULL); + ATF_REQUIRE(num_items == nitems(string_arr)); + for (i = 0; i < num_items; i++) { + if (string_arr[i] != NULL) { + ATF_REQUIRE(strcmp(const_result[i], + string_arr[i]) == 0); + } else { + ATF_REQUIRE(const_result[i] == string_arr[i]); + } + } + + result = nvlist_take_string_array(nvl, key, &num_items); + ATF_REQUIRE(result != NULL); + ATF_REQUIRE_EQ(num_items, nitems(string_arr)); + for (i = 0; i < num_items; i++) { + if (string_arr[i] != NULL) { + ATF_REQUIRE_EQ(strcmp(result[i], string_arr[i]), 0); + } else { + ATF_REQUIRE_EQ(result[i], string_arr[i]); + } + } + + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + + for (i = 0; i < num_items; i++) + free(result[i]); + free(result); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_descriptor_array__basic); +ATF_TEST_CASE_BODY(nvlist_descriptor_array__basic) +{ + int fd[32], *result; + const int *const_result; + nvlist_t *nvl; + size_t num_items; + unsigned int i; + const char *key; + + for (i = 0; i < nitems(fd); i++) { + fd[i] = dup(STDERR_FILENO); + ATF_REQUIRE(fd_is_valid(fd[i])); + } + + key = "nvl/descriptor"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_descriptor_array(nvl, key)); + + nvlist_add_descriptor_array(nvl, key, fd, nitems(fd)); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_descriptor_array(nvl, key)); + ATF_REQUIRE(nvlist_exists_descriptor_array(nvl, "nvl/descriptor")); + + const_result = nvlist_get_descriptor_array(nvl, key, &num_items); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(const_result != NULL); + ATF_REQUIRE(num_items == nitems(fd)); + for (i = 0; i < num_items; i++) { + ATF_REQUIRE(fd_is_valid(const_result[i])); + if (i > 0) + ATF_REQUIRE(const_result[i] != const_result[i - 1]); + } + + result = nvlist_take_descriptor_array(nvl, key, &num_items); + ATF_REQUIRE(result != NULL); + ATF_REQUIRE_EQ(num_items, nitems(fd)); + for (i = 0; i < num_items; i++) { + ATF_REQUIRE(fd_is_valid(result[i])); + if (i > 0) + ATF_REQUIRE(const_result[i] != const_result[i - 1]); + } + + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + + for (i = 0; i < num_items; i++) { + close(result[i]); + close(fd[i]); + } + free(result); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_number_array__basic); +ATF_TEST_CASE_BODY(nvlist_number_array__basic) +{ + const uint64_t *const_result; + uint64_t *result; + nvlist_t *nvl; + size_t num_items; + unsigned int i; + const char *key; + const uint64_t number[8] = { 0, UINT_MAX, 7, 123, 90, + 100000, 8, 1 }; + + key = "nvl/number"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + + nvlist_add_number_array(nvl, key, number, nitems(number)); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_number_array(nvl, key)); + ATF_REQUIRE(nvlist_exists_number_array(nvl, "nvl/number")); + + const_result = nvlist_get_number_array(nvl, key, &num_items); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(const_result != NULL); + ATF_REQUIRE(num_items == nitems(number)); + for (i = 0; i < num_items; i++) + ATF_REQUIRE_EQ(const_result[i], number[i]); + + result = nvlist_take_number_array(nvl, key, &num_items); + ATF_REQUIRE(result != NULL); + ATF_REQUIRE_EQ(num_items, nitems(number)); + for (i = 0; i < num_items; i++) + ATF_REQUIRE_EQ(result[i], number[i]); + + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + + free(result); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_nvlist_array__basic); +ATF_TEST_CASE_BODY(nvlist_nvlist_array__basic) +{ + nvlist_t *testnvl[8]; + const nvlist_t * const *const_result; + nvlist_t **result; + nvlist_t *nvl; + size_t num_items; + unsigned int i; + const char *somestr[8] = { "a", "b", "c", "d", "e", "f", "g", "h" }; + const char *key; + + for (i = 0; i < 8; i++) { + testnvl[i] = nvlist_create(0); + ATF_REQUIRE(testnvl[i] != NULL); + ATF_REQUIRE_EQ(nvlist_error(testnvl[i]), 0); + nvlist_add_string(testnvl[i], "nvl/string", somestr[i]); + ATF_REQUIRE_EQ(nvlist_error(testnvl[i]), 0); + ATF_REQUIRE(nvlist_exists_string(testnvl[i], "nvl/string")); + } + + key = "nvl/nvlist"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + + nvlist_add_nvlist_array(nvl, key, (const nvlist_t * const *)testnvl, 8); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_nvlist_array(nvl, key)); + ATF_REQUIRE(nvlist_exists_nvlist_array(nvl, "nvl/nvlist")); + + const_result = nvlist_get_nvlist_array(nvl, key, &num_items); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(const_result != NULL); + ATF_REQUIRE(num_items == nitems(testnvl)); + + for (i = 0; i < num_items; i++) { + ATF_REQUIRE_EQ(nvlist_error(const_result[i]), 0); + if (i < num_items - 1) { + ATF_REQUIRE(nvlist_get_array_next(const_result[i]) == + const_result[i + 1]); + } else { + ATF_REQUIRE(nvlist_get_array_next(const_result[i]) == + NULL); + } + ATF_REQUIRE(nvlist_get_parent(const_result[i], NULL) == nvl); + ATF_REQUIRE(nvlist_in_array(const_result[i])); + ATF_REQUIRE(nvlist_exists_string(const_result[i], + "nvl/string")); + ATF_REQUIRE(strcmp(nvlist_get_string(const_result[i], + "nvl/string"), somestr[i]) == 0); + } + + result = nvlist_take_nvlist_array(nvl, key, &num_items); + ATF_REQUIRE(result != NULL); + ATF_REQUIRE_EQ(num_items, 8); + for (i = 0; i < num_items; i++) { + ATF_REQUIRE_EQ(nvlist_error(result[i]), 0); + ATF_REQUIRE(nvlist_get_array_next(result[i]) == NULL); + ATF_REQUIRE(nvlist_get_array_next(const_result[i]) == NULL); + ATF_REQUIRE(!nvlist_in_array(const_result[i])); + } + + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + + for (i = 0; i < 8; i++) { + nvlist_destroy(result[i]); + nvlist_destroy(testnvl[i]); + } + + free(result); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_clone_array); +ATF_TEST_CASE_BODY(nvlist_clone_array) +{ + nvlist_t *testnvl[8]; + nvlist_t *src, *dst; + const nvlist_t *nvl; + bool testbool[16]; + int testfd[16]; + size_t i, num_items; + const char *string_arr[8] = { "a", "b", "kot", "foo", + "tests", "nice test", "", "abcdef" }; + const char *somestr[8] = { "a", "b", "c", "d", "e", "f", "g", "h" }; + const uint64_t number[8] = { 0, UINT_MAX, 7, 123, 90, + 100000, 8, 1 }; + + for (i = 0; i < nitems(testfd); i++) { + testbool[i] = (i % 2 == 0); + testfd[i] = dup(STDERR_FILENO); + ATF_REQUIRE(fd_is_valid(testfd[i])); + } + for (i = 0; i < nitems(testnvl); i++) { + testnvl[i] = nvlist_create(0); + ATF_REQUIRE(nvlist_error(testnvl[i]) == 0); + nvlist_add_string(testnvl[i], "nvl/nvl/teststr", somestr[i]); + ATF_REQUIRE(nvlist_error(testnvl[i]) == 0); + } + + src = nvlist_create(0); + ATF_REQUIRE(nvlist_error(src) == 0); + + ATF_REQUIRE(!nvlist_exists_bool_array(src, "nvl/bool")); + nvlist_add_bool_array(src, "nvl/bool", testbool, nitems(testbool)); + ATF_REQUIRE_EQ(nvlist_error(src), 0); + ATF_REQUIRE(nvlist_exists_bool_array(src, "nvl/bool")); + + ATF_REQUIRE(!nvlist_exists_string_array(src, "nvl/string")); + nvlist_add_string_array(src, "nvl/string", string_arr, + nitems(string_arr)); + ATF_REQUIRE_EQ(nvlist_error(src), 0); + ATF_REQUIRE(nvlist_exists_string_array(src, "nvl/string")); + + ATF_REQUIRE(!nvlist_exists_descriptor_array(src, "nvl/fd")); + nvlist_add_descriptor_array(src, "nvl/fd", testfd, nitems(testfd)); + ATF_REQUIRE_EQ(nvlist_error(src), 0); + ATF_REQUIRE(nvlist_exists_descriptor_array(src, "nvl/fd")); + + ATF_REQUIRE(!nvlist_exists_number_array(src, "nvl/number")); + nvlist_add_number_array(src, "nvl/number", number, + nitems(number)); + ATF_REQUIRE_EQ(nvlist_error(src), 0); + ATF_REQUIRE(nvlist_exists_number_array(src, "nvl/number")); + + ATF_REQUIRE(!nvlist_exists_nvlist_array(src, "nvl/array")); + nvlist_add_nvlist_array(src, "nvl/array", + (const nvlist_t * const *)testnvl, nitems(testnvl)); + ATF_REQUIRE_EQ(nvlist_error(src), 0); + ATF_REQUIRE(nvlist_exists_nvlist_array(src, "nvl/array")); + + dst = nvlist_clone(src); + ATF_REQUIRE(dst != NULL); + + ATF_REQUIRE(nvlist_exists_bool_array(dst, "nvl/bool")); + (void) nvlist_get_bool_array(dst, "nvl/bool", &num_items); + ATF_REQUIRE_EQ(num_items, nitems(testbool)); + for (i = 0; i < num_items; i++) { + ATF_REQUIRE( + nvlist_get_bool_array(dst, "nvl/bool", &num_items)[i] == + nvlist_get_bool_array(src, "nvl/bool", &num_items)[i]); + } + + ATF_REQUIRE(nvlist_exists_string_array(dst, "nvl/string")); + (void) nvlist_get_string_array(dst, "nvl/string", &num_items); + ATF_REQUIRE_EQ(num_items, nitems(string_arr)); + for (i = 0; i < num_items; i++) { + if (nvlist_get_string_array(dst, "nvl/string", + &num_items)[i] == NULL) { + ATF_REQUIRE(nvlist_get_string_array(dst, "nvl/string", + &num_items)[i] == nvlist_get_string_array(src, + "nvl/string", &num_items)[i]); + } else { + ATF_REQUIRE(strcmp(nvlist_get_string_array(dst, + "nvl/string", &num_items)[i], nvlist_get_string_array( + src, "nvl/string", &num_items)[i]) == 0); + } + } + + ATF_REQUIRE(nvlist_exists_descriptor_array(dst, "nvl/fd")); + (void) nvlist_get_descriptor_array(dst, "nvl/fd", &num_items); + ATF_REQUIRE_EQ(num_items, nitems(testfd)); + for (i = 0; i < num_items; i++) { + ATF_REQUIRE(fd_is_valid( + nvlist_get_descriptor_array(dst, "nvl/fd", &num_items)[i])); + } + ATF_REQUIRE(nvlist_exists_number_array(dst, "nvl/number")); + (void) nvlist_get_number_array(dst, "nvl/number", &num_items); + ATF_REQUIRE_EQ(num_items, nitems(number)); + + for (i = 0; i < num_items; i++) { + ATF_REQUIRE( + nvlist_get_number_array(dst, "nvl/number", &num_items)[i] == + nvlist_get_number_array(src, "nvl/number", &num_items)[i]); + } + + ATF_REQUIRE(nvlist_exists_nvlist_array(dst, "nvl/array")); + (void) nvlist_get_nvlist_array(dst, "nvl/array", &num_items); + ATF_REQUIRE_EQ(num_items, nitems(testnvl)); + for (i = 0; i < num_items; i++) { + nvl = nvlist_get_nvlist_array(dst, "nvl/array", &num_items)[i]; + ATF_REQUIRE(nvlist_exists_string(nvl, "nvl/nvl/teststr")); + ATF_REQUIRE(strcmp(nvlist_get_string(nvl, "nvl/nvl/teststr"), + somestr[i]) == 0); + } + + for (i = 0; i < nitems(testfd); i++) { + close(testfd[i]); + } + for (i = 0; i < nitems(testnvl); i++) { + nvlist_destroy(testnvl[i]); + } + nvlist_destroy(src); + nvlist_destroy(dst); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_bool_array__move); +ATF_TEST_CASE_BODY(nvlist_bool_array__move) +{ + bool *testbool; + const bool *const_result; + nvlist_t *nvl; + size_t num_items, count; + unsigned int i; + const char *key; + + key = "nvl/bool"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + + count = 16; + testbool = (bool*)malloc(sizeof(*testbool) * count); + ATF_REQUIRE(testbool != NULL); + for (i = 0; i < count; i++) + testbool[i] = (i % 2 == 0); + + nvlist_move_bool_array(nvl, key, testbool, count); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_bool_array(nvl, key)); + + const_result = nvlist_get_bool_array(nvl, key, &num_items); + ATF_REQUIRE_EQ(num_items, count); + ATF_REQUIRE(const_result != NULL); + ATF_REQUIRE(const_result == testbool); + for (i = 0; i < num_items; i++) + ATF_REQUIRE_EQ(const_result[i], (i % 2 == 0)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_string_array__move); +ATF_TEST_CASE_BODY(nvlist_string_array__move) +{ + char **teststr; + const char * const *const_result; + nvlist_t *nvl; + size_t num_items, count; + unsigned int i; + const char *key; + + key = "nvl/string"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + + count = 26; + teststr = (char**)malloc(sizeof(*teststr) * count); + ATF_REQUIRE(teststr != NULL); + for (i = 0; i < count; i++) { + teststr[i] = (char*)malloc(sizeof(**teststr) * 2); + ATF_REQUIRE(teststr[i] != NULL); + teststr[i][0] = 'a' + i; + teststr[i][1] = '\0'; + } + + nvlist_move_string_array(nvl, key, teststr, count); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_string_array(nvl, key)); + + const_result = nvlist_get_string_array(nvl, key, &num_items); + ATF_REQUIRE_EQ(num_items, count); + ATF_REQUIRE(const_result != NULL); + ATF_REQUIRE((intptr_t)const_result == (intptr_t)teststr); + for (i = 0; i < num_items; i++) { + ATF_REQUIRE_EQ(const_result[i][0], (char)('a' + i)); + ATF_REQUIRE_EQ(const_result[i][1], '\0'); + } + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_nvlist_array__move); +ATF_TEST_CASE_BODY(nvlist_nvlist_array__move) +{ + nvlist **testnv; + const nvlist * const *const_result; + nvlist_t *nvl; + size_t num_items, count; + unsigned int i; + const char *key; + + key = "nvl/nvlist"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_nvlist_array(nvl, key)); + + count = 26; + testnv = (nvlist**)malloc(sizeof(*testnv) * count); + ATF_REQUIRE(testnv != NULL); + for (i = 0; i < count; i++) { + testnv[i] = nvlist_create(0); + ATF_REQUIRE(testnv[i] != NULL); + } + + nvlist_move_nvlist_array(nvl, key, testnv, count); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_nvlist_array(nvl, key)); + + const_result = nvlist_get_nvlist_array(nvl, key, &num_items); + ATF_REQUIRE_EQ(num_items, count); + ATF_REQUIRE(const_result != NULL); + ATF_REQUIRE((intptr_t)const_result == (intptr_t)testnv); + for (i = 0; i < num_items; i++) { + ATF_REQUIRE_EQ(nvlist_error(const_result[i]), 0); + ATF_REQUIRE(nvlist_empty(const_result[i])); + if (i < num_items - 1) { + ATF_REQUIRE(nvlist_get_array_next(const_result[i]) == + const_result[i + 1]); + } else { + ATF_REQUIRE(nvlist_get_array_next(const_result[i]) == + NULL); + } + ATF_REQUIRE(nvlist_get_parent(const_result[i], NULL) == nvl); + ATF_REQUIRE(nvlist_in_array(const_result[i])); + } + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_number_array__move); +ATF_TEST_CASE_BODY(nvlist_number_array__move) +{ + uint64_t *testnumber; + const uint64_t *const_result; + nvlist_t *nvl; + size_t num_items, count; + unsigned int i; + const char *key; + + key = "nvl/number"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + + count = 1000; + testnumber = (uint64_t*)malloc(sizeof(*testnumber) * count); + ATF_REQUIRE(testnumber != NULL); + for (i = 0; i < count; i++) + testnumber[i] = i; + + nvlist_move_number_array(nvl, key, testnumber, count); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_number_array(nvl, key)); + + const_result = nvlist_get_number_array(nvl, key, &num_items); + ATF_REQUIRE_EQ(num_items, count); + ATF_REQUIRE(const_result != NULL); + ATF_REQUIRE(const_result == testnumber); + for (i = 0; i < num_items; i++) + ATF_REQUIRE_EQ(const_result[i], i); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_descriptor_array__move); +ATF_TEST_CASE_BODY(nvlist_descriptor_array__move) +{ + int *testfd; + const int *const_result; + nvlist_t *nvl; + size_t num_items, count; + unsigned int i; + const char *key; + + key = "nvl/fd"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + + count = 50; + testfd = (int*)malloc(sizeof(*testfd) * count); + ATF_REQUIRE(testfd != NULL); + for (i = 0; i < count; i++) { + testfd[i] = dup(STDERR_FILENO); + ATF_REQUIRE(fd_is_valid(testfd[i])); + } + + nvlist_move_descriptor_array(nvl, key, testfd, count); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_descriptor_array(nvl, key)); + + const_result = nvlist_get_descriptor_array(nvl, key, &num_items); + ATF_REQUIRE_EQ(num_items, count); + ATF_REQUIRE(const_result != NULL); + ATF_REQUIRE(const_result == testfd); + for (i = 0; i < num_items; i++) + ATF_REQUIRE(fd_is_valid(const_result[i])); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_arrays__error_null); +ATF_TEST_CASE_BODY(nvlist_arrays__error_null) +{ + nvlist_t *nvl; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_add_number_array(nvl, "nvl/number", NULL, 0); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_move_number_array(nvl, "nvl/number", NULL, 0); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_add_descriptor_array(nvl, "nvl/fd", NULL, 0); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_move_descriptor_array(nvl, "nvl/fd", NULL, 0); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_add_string_array(nvl, "nvl/string", NULL, 0); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_move_string_array(nvl, "nvl/string", NULL, 0); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_add_nvlist_array(nvl, "nvl/nvlist", NULL, 0); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_move_nvlist_array(nvl, "nvl/nvlist", NULL, 0); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_add_bool_array(nvl, "nvl/bool", NULL, 0); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_move_bool_array(nvl, "nvl/bool", NULL, 0); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_arrays__bad_value); +ATF_TEST_CASE_BODY(nvlist_arrays__bad_value) +{ + nvlist_t *nvl, *nvladd[1], **nvlmove; + int fdadd[1], *fdmove; + + nvladd[0] = NULL; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_add_nvlist_array(nvl, "nvl/nvlist", nvladd, 1); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); + + nvlmove = (nvlist_t**)malloc(sizeof(*nvlmove)); + ATF_REQUIRE(nvlmove != NULL); + nvlmove[0] = NULL; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_move_nvlist_array(nvl, "nvl/nvlist", nvlmove, 1); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); + + fdadd[0] = -2; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_add_descriptor_array(nvl, "nvl/fd", fdadd, 1); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); + + fdmove = (int*)malloc(sizeof(*fdmove)); + ATF_REQUIRE(fdmove != NULL); + fdmove[0] = -2; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_move_descriptor_array(nvl, "nvl/fd", fdmove, 1); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_nvlist_array__travel); +ATF_TEST_CASE_BODY(nvlist_nvlist_array__travel) +{ + nvlist_t *nvl, *test[5], *nasted; + const nvlist_t *travel; + const char *name; + void *cookie; + int type; + unsigned int i, index; + + for (i = 0; i < nitems(test); i++) { + test[i] = nvlist_create(0); + ATF_REQUIRE(test[i] != NULL); + nvlist_add_number(test[i], "nvl/number", i); + ATF_REQUIRE(nvlist_error(test[i]) == 0); + } + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_add_nvlist_array(nvl, "nvl/nvlist_array", test, nitems(test)); + ATF_REQUIRE(nvlist_error(nvl) == 0); + nasted = nvlist_create(0); + ATF_REQUIRE(nasted != NULL); + nvlist_add_nvlist_array(nasted, "nvl/nvl/nvlist_array", test, + nitems(test)); + ATF_REQUIRE(nvlist_error(nasted) == 0); + nvlist_move_nvlist(nvl, "nvl/nvl", nasted); + ATF_REQUIRE(nvlist_error(nvl) == 0); + nvlist_add_string(nvl, "nvl/string", "END"); + ATF_REQUIRE(nvlist_error(nvl) == 0); + + cookie = NULL; + index = 0; + travel = nvl; + do { + while ((name = nvlist_next(travel, &type, &cookie)) != NULL) { + if (index == 0) { + ATF_REQUIRE(type == NV_TYPE_NVLIST_ARRAY); + } else if (index >= 1 && index <= nitems(test)) { + ATF_REQUIRE(type == NV_TYPE_NUMBER); + } else if (index == nitems(test) + 1) { + ATF_REQUIRE(type == NV_TYPE_NVLIST); + } else if (index == nitems(test) + 2) { + ATF_REQUIRE(type == NV_TYPE_NVLIST_ARRAY); + } else if (index >= nitems(test) + 3 && + index <= 2 * nitems(test) + 2) { + ATF_REQUIRE(type == NV_TYPE_NUMBER); + } else if (index == 2 * nitems(test) + 3) { + ATF_REQUIRE(type == NV_TYPE_STRING); + } + + if (type == NV_TYPE_NVLIST) { + travel = nvlist_get_nvlist(travel, name); + cookie = NULL; + } else if (type == NV_TYPE_NVLIST_ARRAY) { + travel = nvlist_get_nvlist_array(travel, name, + NULL)[0]; + cookie = NULL; + } + index ++; + } + } while ((travel = nvlist_get_pararr(travel, &cookie)) != NULL); + + for (i = 0; i < nitems(test); i++) + nvlist_destroy(test[i]); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_nvlist_array__travel_alternative); +ATF_TEST_CASE_BODY(nvlist_nvlist_array__travel_alternative) +{ + nvlist_t *nvl, *test[5], *nasted; + const nvlist_t *travel, *tmp; + void *cookie; + int index, i, type; + const char *name; + + for (i = 0; i < 5; i++) { + test[i] = nvlist_create(0); + ATF_REQUIRE(test[i] != NULL); + nvlist_add_number(test[i], "nvl/number", i); + ATF_REQUIRE(nvlist_error(test[i]) == 0); + } + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_add_nvlist_array(nvl, "nvl/nvlist_array", test, 5); + ATF_REQUIRE(nvlist_error(nvl) == 0); + nasted = nvlist_create(0); + ATF_REQUIRE(nasted != NULL); + nvlist_add_nvlist_array(nasted, "nvl/nvl/nvlist_array", test, 5); + ATF_REQUIRE(nvlist_error(nasted) == 0); + nvlist_move_nvlist(nvl, "nvl/nvl", nasted); + ATF_REQUIRE(nvlist_error(nvl) == 0); + nvlist_add_string(nvl, "nvl/string", "END"); + ATF_REQUIRE(nvlist_error(nvl) == 0); + + cookie = NULL; + index = 0; + tmp = travel = nvl; + do { + do { + travel = tmp; + while ((name = nvlist_next(travel, &type, &cookie)) != + NULL) { + if (index == 0) { + ATF_REQUIRE(type == + NV_TYPE_NVLIST_ARRAY); + } else if (index >= 1 && index <= 5) { + ATF_REQUIRE(type == NV_TYPE_NUMBER); + } else if (index == 6) { + ATF_REQUIRE(type == NV_TYPE_NVLIST); + } else if (index == 7) { + ATF_REQUIRE(type == + NV_TYPE_NVLIST_ARRAY); + } else if (index >= 8 && index <= 12) { + ATF_REQUIRE(type == NV_TYPE_NUMBER); + } else if (index == 13) { + ATF_REQUIRE(type == NV_TYPE_STRING); + } + + if (type == NV_TYPE_NVLIST) { + travel = nvlist_get_nvlist(travel, + name); + cookie = NULL; + } else if (type == NV_TYPE_NVLIST_ARRAY) { + travel = nvlist_get_nvlist_array(travel, + name, NULL)[0]; + cookie = NULL; + } + index ++; + } + cookie = NULL; + } while ((tmp = nvlist_get_array_next(travel)) != NULL); + } while ((tmp = nvlist_get_parent(travel, &cookie)) != NULL); + + for (i = 0; i < 5; i++) + nvlist_destroy(test[i]); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_bool_array__pack); +ATF_TEST_CASE_BODY(nvlist_bool_array__pack) +{ + nvlist_t *nvl, *unpacked; + const char *key; + size_t packed_size, count; + void *packed; + unsigned int i; + const bool *const_result; + bool testbool[16]; + + for (i = 0; i < nitems(testbool); i++) + testbool[i] = (i % 2 == 0); + + key = "nvl/bool"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + + nvlist_add_bool_array(nvl, key, testbool, nitems(testbool)); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_bool_array(nvl, key)); + + packed = nvlist_pack(nvl, &packed_size); + ATF_REQUIRE(packed != NULL); + + unpacked = nvlist_unpack(packed, packed_size, 0); + ATF_REQUIRE(unpacked != NULL); + ATF_REQUIRE_EQ(nvlist_error(unpacked), 0); + ATF_REQUIRE(nvlist_exists_bool_array(unpacked, key)); + + const_result = nvlist_get_bool_array(unpacked, key, &count); + ATF_REQUIRE_EQ(count, nitems(testbool)); + for (i = 0; i < count; i++) { + ATF_REQUIRE_EQ(testbool[i], const_result[i]); + } + + nvlist_destroy(nvl); + nvlist_destroy(unpacked); + free(packed); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_number_array__pack); +ATF_TEST_CASE_BODY(nvlist_number_array__pack) +{ + nvlist_t *nvl, *unpacked; + const char *key; + size_t packed_size, count; + void *packed; + unsigned int i; + const uint64_t *const_result; + const uint64_t number[8] = { 0, UINT_MAX, 7, 123, 90, + 100000, 8, 1 }; + + key = "nvl/number"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + + nvlist_add_number_array(nvl, key, number, 8); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_number_array(nvl, key)); + + packed = nvlist_pack(nvl, &packed_size); + ATF_REQUIRE(packed != NULL); + + unpacked = nvlist_unpack(packed, packed_size, 0); + ATF_REQUIRE(unpacked != NULL); + ATF_REQUIRE_EQ(nvlist_error(unpacked), 0); + ATF_REQUIRE(nvlist_exists_number_array(unpacked, key)); + + const_result = nvlist_get_number_array(unpacked, key, &count); + ATF_REQUIRE_EQ(count, nitems(number)); + for (i = 0; i < count; i++) { + ATF_REQUIRE_EQ(number[i], const_result[i]); + } + + nvlist_destroy(nvl); + nvlist_destroy(unpacked); + free(packed); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_descriptor_array__pack); +ATF_TEST_CASE_BODY(nvlist_descriptor_array__pack) +{ + nvlist_t *nvl; + const char *key; + size_t num_items; + unsigned int i; + const int *const_result; + int desc[32], fd, socks[2]; + pid_t pid; + + key = "nvl/descriptor"; + + ATF_REQUIRE_EQ(socketpair(PF_UNIX, SOCK_STREAM, 0, socks), 0); + + pid = atf::utils::fork(); + ATF_REQUIRE(pid >= 0); + if (pid == 0) { + /* Child. */ + fd = socks[0]; + close(socks[1]); + for (i = 0; i < nitems(desc); i++) { + desc[i] = dup(STDERR_FILENO); + ATF_REQUIRE(fd_is_valid(desc[i])); + } + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_descriptor_array(nvl, key)); + + nvlist_add_descriptor_array(nvl, key, desc, nitems(desc)); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_descriptor_array(nvl, key)); + + ATF_REQUIRE(nvlist_send(fd, nvl) >= 0); + + for (i = 0; i < nitems(desc); i++) + close(desc[i]); + } else { + /* Parent */ + fd = socks[1]; + close(socks[0]); + + errno = 0; + nvl = nvlist_recv(fd, 0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(nvlist_exists_descriptor_array(nvl, key)); + + const_result = nvlist_get_descriptor_array(nvl, key, &num_items); + ATF_REQUIRE(const_result != NULL); + ATF_REQUIRE_EQ(num_items, nitems(desc)); + for (i = 0; i < num_items; i++) + ATF_REQUIRE(fd_is_valid(const_result[i])); + + atf::utils::wait(pid, 0, "", ""); + } + + nvlist_destroy(nvl); + close(fd); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_string_array__pack); +ATF_TEST_CASE_BODY(nvlist_string_array__pack) +{ + nvlist_t *nvl, *unpacked; + const char *key; + size_t packed_size, count; + void *packed; + unsigned int i; + const char * const *const_result; + const char *string_arr[8] = { "a", "b", "kot", "foo", + "tests", "nice test", "", "abcdef" }; + + key = "nvl/string"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + + nvlist_add_string_array(nvl, key, string_arr, nitems(string_arr)); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_string_array(nvl, key)); + + packed = nvlist_pack(nvl, &packed_size); + ATF_REQUIRE(packed != NULL); + + unpacked = nvlist_unpack(packed, packed_size, 0); + ATF_REQUIRE(unpacked != NULL); + ATF_REQUIRE_EQ(nvlist_error(unpacked), 0); + ATF_REQUIRE(nvlist_exists_string_array(unpacked, key)); + + const_result = nvlist_get_string_array(unpacked, key, &count); + ATF_REQUIRE_EQ(count, nitems(string_arr)); + for (i = 0; i < count; i++) { + ATF_REQUIRE_EQ(strcmp(string_arr[i], const_result[i]), 0); + } + + nvlist_destroy(nvl); + nvlist_destroy(unpacked); + free(packed); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_nvlist_array__pack); +ATF_TEST_CASE_BODY(nvlist_nvlist_array__pack) +{ + nvlist_t *testnvl[8], *unpacked; + const nvlist_t * const *const_result; + nvlist_t *nvl; + size_t num_items, packed_size; + unsigned int i; + void *packed; + const char *somestr[8] = { "a", "b", "c", "d", "e", "f", "g", "h" }; + const char *key; + + for (i = 0; i < nitems(testnvl); i++) { + testnvl[i] = nvlist_create(0); + ATF_REQUIRE(testnvl[i] != NULL); + ATF_REQUIRE_EQ(nvlist_error(testnvl[i]), 0); + nvlist_add_string(testnvl[i], "nvl/string", somestr[i]); + ATF_REQUIRE_EQ(nvlist_error(testnvl[i]), 0); + ATF_REQUIRE(nvlist_exists_string(testnvl[i], "nvl/string")); + } + + key = "nvl/nvlist"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + + nvlist_add_nvlist_array(nvl, key, (const nvlist_t * const *)testnvl, 8); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_nvlist_array(nvl, key)); + ATF_REQUIRE(nvlist_exists_nvlist_array(nvl, "nvl/nvlist")); + packed = nvlist_pack(nvl, &packed_size); + ATF_REQUIRE(packed != NULL); + + unpacked = nvlist_unpack(packed, packed_size, 0); + ATF_REQUIRE(unpacked != NULL); + ATF_REQUIRE_EQ(nvlist_error(unpacked), 0); + ATF_REQUIRE(nvlist_exists_nvlist_array(unpacked, key)); + + const_result = nvlist_get_nvlist_array(unpacked, key, &num_items); + ATF_REQUIRE(const_result != NULL); + ATF_REQUIRE_EQ(num_items, nitems(testnvl)); + for (i = 0; i < num_items; i++) { + ATF_REQUIRE_EQ(nvlist_error(const_result[i]), 0); + if (i < num_items - 1) { + ATF_REQUIRE(nvlist_get_array_next(const_result[i]) == + const_result[i + 1]); + } else { + ATF_REQUIRE(nvlist_get_array_next(const_result[i]) == + NULL); + } + ATF_REQUIRE(nvlist_get_parent(const_result[i], NULL) == unpacked); + ATF_REQUIRE(nvlist_in_array(const_result[i])); + ATF_REQUIRE(nvlist_exists_string(const_result[i], + "nvl/string")); + ATF_REQUIRE(strcmp(nvlist_get_string(const_result[i], + "nvl/string"), somestr[i]) == 0); + } + + for (i = 0; i < nitems(testnvl); i++) + nvlist_destroy(testnvl[i]); + nvlist_destroy(nvl); + nvlist_destroy(unpacked); + free(packed); +} + +ATF_INIT_TEST_CASES(tp) +{ + + ATF_ADD_TEST_CASE(tp, nvlist_bool_array__basic); + ATF_ADD_TEST_CASE(tp, nvlist_string_array__basic); + ATF_ADD_TEST_CASE(tp, nvlist_descriptor_array__basic); + ATF_ADD_TEST_CASE(tp, nvlist_number_array__basic); + ATF_ADD_TEST_CASE(tp, nvlist_nvlist_array__basic) + + ATF_ADD_TEST_CASE(tp, nvlist_clone_array) + + ATF_ADD_TEST_CASE(tp, nvlist_bool_array__move); + ATF_ADD_TEST_CASE(tp, nvlist_string_array__move); + ATF_ADD_TEST_CASE(tp, nvlist_nvlist_array__move); + ATF_ADD_TEST_CASE(tp, nvlist_number_array__move); + ATF_ADD_TEST_CASE(tp, nvlist_descriptor_array__move); + + ATF_ADD_TEST_CASE(tp, nvlist_arrays__error_null); + + ATF_ADD_TEST_CASE(tp, nvlist_arrays__bad_value) + + ATF_ADD_TEST_CASE(tp, nvlist_nvlist_array__travel) + ATF_ADD_TEST_CASE(tp, nvlist_nvlist_array__travel_alternative) + + ATF_ADD_TEST_CASE(tp, nvlist_bool_array__pack) + ATF_ADD_TEST_CASE(tp, nvlist_number_array__pack) + ATF_ADD_TEST_CASE(tp, nvlist_descriptor_array__pack) + ATF_ADD_TEST_CASE(tp, nvlist_string_array__pack) + ATF_ADD_TEST_CASE(tp, nvlist_nvlist_array__pack) +} + Index: lib/libnv/tests/nv_tests.cc =================================================================== --- lib/libnv/tests/nv_tests.cc +++ lib/libnv/tests/nv_tests.cc @@ -27,8 +27,9 @@ #include __FBSDID("$FreeBSD$"); +#include + #include -#include #include #include @@ -440,7 +441,7 @@ packed = nvlist_pack(nvl, &packed_size); ATF_REQUIRE(packed != NULL); - unpacked = nvlist_unpack(packed, packed_size); + unpacked = nvlist_unpack(packed, packed_size, 0); ATF_REQUIRE(unpacked != NULL); ATF_REQUIRE(unpacked != nvl); ATF_REQUIRE(nvlist_empty(unpacked)); @@ -450,6 +451,40 @@ free(packed); } +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_unpack__flags_nvlist); +ATF_TEST_CASE_BODY(nvlist_unpack__flags_nvlist) +{ + nvlist_t *nvl, *unpacked; + void *packed; + size_t packed_size; + + nvl = nvlist_create(NV_FLAG_NO_UNIQUE); + ATF_REQUIRE(nvl != NULL); + + nvlist_add_bool(nvl, "name", true); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_bool(nvl, "name")); + + packed = nvlist_pack(nvl, &packed_size); + ATF_REQUIRE(packed != NULL); + + unpacked = nvlist_unpack(packed, packed_size, 0); + ATF_REQUIRE(unpacked == NULL); + + unpacked = nvlist_unpack(packed, packed_size, NV_FLAG_IGNORE_CASE); + ATF_REQUIRE(unpacked == NULL); + + unpacked = nvlist_unpack(packed, packed_size, NV_FLAG_NO_UNIQUE); + ATF_REQUIRE(unpacked != NULL); + ATF_REQUIRE(unpacked != nvl); + ATF_REQUIRE(!nvlist_empty(unpacked)); + ATF_REQUIRE(nvlist_exists_bool(unpacked, "name")); + + nvlist_destroy(unpacked); + nvlist_destroy(nvl); + free(packed); +} + static void verify_null(const nvlist_t *nvl, int type) { @@ -534,7 +569,7 @@ packed = nvlist_pack(nvl, &packed_size); ATF_REQUIRE(packed != NULL); - unpacked = nvlist_unpack(packed, packed_size); + unpacked = nvlist_unpack(packed, packed_size, 0); ATF_REQUIRE(unpacked != 0); it = NULL; @@ -614,7 +649,7 @@ ATF_REQUIRE(keypos != NULL); memcpy(keypos, key2, keylen); - unpacked = nvlist_unpack(packed, size); + unpacked = nvlist_unpack(packed, size, 0); ATF_REQUIRE(nvlist_error(unpacked) != 0); free(packed); @@ -1206,6 +1241,7 @@ ATF_ADD_TEST_CASE(tp, nvlist_pack__multiple_values); ATF_ADD_TEST_CASE(tp, nvlist_pack__error_nvlist); ATF_ADD_TEST_CASE(tp, nvlist_unpack__duplicate_key); + ATF_ADD_TEST_CASE(tp, nvlist_unpack__flags_nvlist); ATF_ADD_TEST_CASE(tp, nvlist_move_string__single_insert); ATF_ADD_TEST_CASE(tp, nvlist_move_nvlist__single_insert); Index: lib/libnv/tests/nvlist_add_test.c =================================================================== --- lib/libnv/tests/nvlist_add_test.c +++ lib/libnv/tests/nvlist_add_test.c @@ -29,12 +29,12 @@ * $FreeBSD$ */ +#include + #include #include #include -#include - static int ntest = 1; #define CHECK(expr) do { \ Index: lib/libnv/tests/nvlist_exists_test.c =================================================================== --- lib/libnv/tests/nvlist_exists_test.c +++ lib/libnv/tests/nvlist_exists_test.c @@ -29,11 +29,11 @@ * $FreeBSD$ */ +#include + #include #include -#include - static int ntest = 1; #define CHECK(expr) do { \ Index: lib/libnv/tests/nvlist_free_test.c =================================================================== --- lib/libnv/tests/nvlist_free_test.c +++ lib/libnv/tests/nvlist_free_test.c @@ -29,11 +29,11 @@ * $FreeBSD$ */ +#include + #include #include -#include - static int ntest = 1; #define CHECK(expr) do { \ Index: lib/libnv/tests/nvlist_get_test.c =================================================================== --- lib/libnv/tests/nvlist_get_test.c +++ lib/libnv/tests/nvlist_get_test.c @@ -28,6 +28,7 @@ * * $FreeBSD$ */ +#include #include #include @@ -35,8 +36,6 @@ #include #include -#include - static int ntest = 1; #define CHECK(expr) do { \ Index: lib/libnv/tests/nvlist_move_test.c =================================================================== --- lib/libnv/tests/nvlist_move_test.c +++ lib/libnv/tests/nvlist_move_test.c @@ -29,6 +29,8 @@ * $FreeBSD$ */ +#include + #include #include #include @@ -35,8 +37,6 @@ #include #include -#include - static int ntest = 1; #define CHECK(expr) do { \ Index: lib/libnv/tests/nvlist_send_recv_test.c =================================================================== --- lib/libnv/tests/nvlist_send_recv_test.c +++ lib/libnv/tests/nvlist_send_recv_test.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -40,8 +41,6 @@ #include #include -#include - static int ntest = 1; #define CHECK(expr) do { \ @@ -95,7 +94,7 @@ int type, ctype; size_t size; - nvl = nvlist_recv(sock); + nvl = nvlist_recv(sock, 0); CHECK(nvlist_error(nvl) == 0); if (nvlist_error(nvl) != 0) err(1, "nvlist_recv() failed"); Index: share/man/man5/rc.conf.5 =================================================================== --- share/man/man5/rc.conf.5 +++ share/man/man5/rc.conf.5 @@ -4524,6 +4524,11 @@ .Xr rctl.conf 5 ruleset to load for .Xr rctl 8 . +.It Va iovctl_files +.Pq Vt str +A space-separated list of configuration files used by +.Xr iovctl 8 . +The default value is an empty string. .El .Sh FILES .Bl -tag -width ".Pa /etc/defaults/rc.conf" -compact @@ -4577,6 +4582,7 @@ .Xr hcsecd 8 , .Xr ifconfig 8 , .Xr inetd 8 , +.Xr iovctl 8 , .Xr ipf 8 , .Xr ipfw 8 , .Xr ipnat 8 , Index: share/man/man9/Makefile =================================================================== --- share/man/man9/Makefile +++ share/man/man9/Makefile @@ -188,6 +188,7 @@ mutex.9 \ namei.9 \ netisr.9 \ + nv.9 \ osd.9 \ panic.9 \ pbuf.9 \ @@ -194,6 +195,10 @@ p_candebug.9 \ p_cansee.9 \ pci.9 \ + PCI_IOV_ADD_VF.9 \ + PCI_IOV_INIT.9 \ + pci_iov_schema.9 \ + PCI_IOV_UNINIT.9 \ pfil.9 \ pfind.9 \ pget.9 \ @@ -999,6 +1004,68 @@ mutex.9 mtx_unlock_spin_flags.9 MLINKS+=namei.9 NDFREE.9 \ namei.9 NDINIT.9 +MLINKS+=nv.9 libnv.9 \ + nv.9 nvlist.9 \ + nv.9 nvlist_add_binary.9 \ + nv.9 nvlist_add_bool.9 \ + nv.9 nvlist_add_descriptor.9 \ + nv.9 nvlist_add_null.9 \ + nv.9 nvlist_add_number.9 \ + nv.9 nvlist_add_nvlist.9 \ + nv.9 nvlist_add_string.9 \ + nv.9 nvlist_add_stringf.9 \ + nv.9 nvlist_add_stringv.9 \ + nv.9 nvlist_clone.9 \ + nv.9 nvlist_create.9 \ + nv.9 nvlist_destroy.9 \ + nv.9 nvlist_dump.9 \ + nv.9 nvlist_empty.9 \ + nv.9 nvlist_error.9 \ + nv.9 nvlist_exists.9 \ + nv.9 nvlist_exists_binary.9 \ + nv.9 nvlist_exists_bool.9 \ + nv.9 nvlist_exists_descriptor.9 \ + nv.9 nvlist_exists_null.9 \ + nv.9 nvlist_exists_number.9 \ + nv.9 nvlist_exists_nvlist.9 \ + nv.9 nvlist_exists_string.9 \ + nv.9 nvlist_exists_type.9 \ + nv.9 nvlist_fdump.9 \ + nv.9 nvlist_flags.9 \ + nv.9 nvlist_free.9 \ + nv.9 nvlist_free_binary.9 \ + nv.9 nvlist_free_bool.9 \ + nv.9 nvlist_free_descriptor.9 \ + nv.9 nvlist_free_null.9 \ + nv.9 nvlist_free_number.9 \ + nv.9 nvlist_free_nvlist.9 \ + nv.9 nvlist_free_string.9 \ + nv.9 nvlist_free_type.9 \ + nv.9 nvlist_get_binary.9 \ + nv.9 nvlist_get_bool.9 \ + nv.9 nvlist_get_descriptor.9 \ + nv.9 nvlist_get_number.9 \ + nv.9 nvlist_get_nvlist.9 \ + nv.9 nvlist_get_parent.9 \ + nv.9 nvlist_get_string.9 \ + nv.9 nvlist_move_binary.9 \ + nv.9 nvlist_move_descriptor.9 \ + nv.9 nvlist_move_nvlist.9 \ + nv.9 nvlist_move_string.9 \ + nv.9 nvlist_next.9 \ + nv.9 nvlist_pack.9 \ + nv.9 nvlist_recv.9 \ + nv.9 nvlist_send.9 \ + nv.9 nvlist_set_error.9 \ + nv.9 nvlist_size.9 \ + nv.9 nvlist_take_binary.9 \ + nv.9 nvlist_take_bool.9 \ + nv.9 nvlist_take_descriptor.9 \ + nv.9 nvlist_take_number.9 \ + nv.9 nvlist_take_nvlist.9 \ + nv.9 nvlist_take_string.9 \ + nv.9 nvlist_unpack.9 \ + nv.9 nvlist_xfer.9 MLINKS+=panic.9 vpanic.9 MLINKS+=pbuf.9 getpbuf.9 \ pbuf.9 relpbuf.9 \ @@ -1020,6 +1087,8 @@ pci.9 pci_get_powerstate.9 \ pci.9 pci_get_vpd_ident.9 \ pci.9 pci_get_vpd_readonly.9 \ + pci.9 pci_iov_attach.9 \ + pci.9 pci_iov_detach.9 \ pci.9 pci_msi_count.9 \ pci.9 pci_msix_count.9 \ pci.9 pci_pending_msix.9 \ @@ -1034,6 +1103,14 @@ pci.9 pcie_adjust_config.9 \ pci.9 pcie_read_config.9 \ pci.9 pcie_write_config.9 +MLINKS+=pci_iov_schema.9 pci_iov_schema_alloc_node.9 \ + pci_iov_schema.9 pci_iov_schema_add_bool.9 \ + pci_iov_schema.9 pci_iov_schema_add_string.9 \ + pci_iov_schema.9 pci_iov_schema_add_uint8.9 \ + pci_iov_schema.9 pci_iov_schema_add_uint16.9 \ + pci_iov_schema.9 pci_iov_schema_add_uint32.9 \ + pci_iov_schema.9 pci_iov_schema_add_uint64.9 \ + pci_iov_schema.9 pci_iov_schema_add_unicast_mac.9 MLINKS+=pfil.9 pfil_add_hook.9 \ pfil.9 pfil_hook_get.9 \ pfil.9 pfil_remove_hook.9 Index: share/man/man9/PCI_IOV_ADD_VF.9 =================================================================== --- share/man/man9/PCI_IOV_ADD_VF.9 +++ share/man/man9/PCI_IOV_ADD_VF.9 @@ -0,0 +1,112 @@ +.\" +.\" Copyright (c) 2014 Sandvine Inc. +.\" 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$ +.\" +.Dd May 28, 2015 +.Dt PCI_IOV_ADD_VF 9 +.Os +.Sh NAME +.Nm PCI_IOV_ADD_VF +.Nd inform a PF driver that a VF is being created +.Sh SYNOPSIS +.In sys/bus.h +.In machine/stdarg.h +.In sys/nv.h +.In dev/pci/pci_iov.h +.Ft int +.Fn PCI_IOV_ADD_VF "device_t dev" "uint16_t vfnum" "const nvlist_t *vf_config" +.Sh DESCRIPTION +The +.Fn PCI_IOV_ADD_VF +method is called by the PCI Single-Root I/O Virtualization +.Pq SR-IOV +infrastructure when it is initializating a new Virtual Function (VF) as a child +of the given Physical Function (PF) device. +This method will not be called until a successful call to +.Xr PCI_IOV_INIT 9 +has been made. +It is not guaranteed that this method will be called following a successful call +to +.Xr PCI_IOV_INIT 9 . +If the infrastructure encounters a failure to allocate resources following the +call to +.Xr PCI_IOV_INIT 9 , +the VF creation will be aborted and +.Xr PCI_IOV_UNINIT 9 +will be called immediately without any preceding calls to +.Nm . +.Pp +The index of the VF being initialized is passed in the +.Fa vfnum +argument. +VFs are always numbered sequentially starting at 0. +.Pp +If the driver requested device-specific configuration parameters via a VF schema +in its call to +.Xr pci_iov_attach 9 , +those parameters will be contained in the +.Pa vf_config +argument. +All configuration parameters that were either set as required parameters or that +had a default value set in the VF schema are guaranteed to be present in +.Fa vf_config . +Configuration parameters that were neither set as required nor were given a +default value are optional and may or may not be present in +.Fa vf_config . +.Fa vf_config +will not contain any configuration parameters that were not specified in the VF +schema. +All configuration parameters will have the correct type and will be in the range +of valid values specified in the schema. +.Pp +Note that it is possible for the user to set different configuration values on +different VF devices that are children of the same PF. +The PF driver must not cache configuration parameters passed in previous calls +to +.Fn PCI_IOV_ADD_VF +for other VFs and apply those parameters to the current VF. +.Pp +This function will not be called twice for the same +.Fa vf_num +on the same PF device without +.Xr PCI_IOV_UNINIT 9 +and +.Xr PCI_IOV_INIT 9 +first being called, in that order. +.Sh RETURN VALUES +This method returns 0 on success, otherwise an appropriate error is returned. +If this method returns an error then the current VF device will be destroyed +but the rest of the VF devices will be created and SR-IOV will be enabled on +the PF. +.Sh SEE ALSO +.Xr nv 9 , +.Xr pci 9 , +.Xr pci_iov_schema 9 , +.Xr PCI_IOV_INIT 9 , +.Xr PCI_IOV_UNINIT 9 +.Sh AUTHORS +This manual page was written by +.An Ryan Stone Aq Mt rstone@FreeBSD.org . Index: share/man/man9/PCI_IOV_INIT.9 =================================================================== --- share/man/man9/PCI_IOV_INIT.9 +++ share/man/man9/PCI_IOV_INIT.9 @@ -0,0 +1,85 @@ +.\" +.\" Copyright (c) 2014 Sandvine Inc. +.\" 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$ +.\" +.Dd May 28, 2015 +.Dt PCI_IOV_INIT 9 +.Os +.Sh NAME +.Nm PCI_IOV_INIT +.Nd enable SR-IOV on a PF device +.Sh SYNOPSIS +.In sys/bus.h +.In machine/stdarg.h +.In sys/nv.h +.In dev/pci/pci_iov.h +.Ft int +.Fn PCI_IOV_INIT "device_t dev" "uint16_t num_vfs" "const nvlist_t *pf_config" +.Sh DESCRIPTION +The +.Fn PCI_IOV_INIT +method is called by the PCI Single-Root I/O Virtualization (SR-IOV) +infrastucture when the user requests that SR-IOV be enabled on a Physical +Function (PF). +The number of Virtual Functions (VFs) that will be created is passed to this +method in the +.Fa num_vfs +argument. +.Pp +If the driver requested device-specific PF configuration parameters via a PF +schema in its call to +.Xr pci_iov_attach 9 , +those parameters will be available in the +.Fa pf_config +argument. +All configuration parameters that were either set as required parameters or that +had a default value set in the PF schema are guaranteed to be present in +.Fa pf_config . +Configuration parameters that were neither set as required nor were given a +default value are optional and may or may not be present in +.Fa pf_config . +.Fa pf_config +will not contain any configuration parameters that were not specified in the PF +schema. +All configuration parameters will have the correct type and are in the range of +valid values specified in the schema. +.Pp +If this method returns successfully, then this method will not be called again +on the same device until after a call to +.Xr PCI_IOV_UNINIT . +.Sh RETURN VALUES +Returns 0 on success, otherwise an appropriate error is returned. +If this method returns an error then the SR-IOV configuration will be aborted +and no VFs will be created. +.Sh SEE ALSO +.Xr nv 9 , +.Xr pci 9 , +.Xr pci_iov_schema 9 , +.Xr PCI_IOV_ADD_VF 9 , +.Xr PCI_IOV_UNINIT 9 +.Sh AUTHORS +This manual page was written by +.An Ryan Stone Aq Mt rstone@FreeBSD.org . Index: share/man/man9/PCI_IOV_UNINIT.9 =================================================================== --- share/man/man9/PCI_IOV_UNINIT.9 +++ share/man/man9/PCI_IOV_UNINIT.9 @@ -0,0 +1,63 @@ +.\" +.\" Copyright (c) 2014 Sandvine Inc. +.\" 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$ +.\" +.Dd May 28, 2015 +.Dt PCI_IOV_UNINIT 9 +.Os +.Sh NAME +.Nm PCI_IOV_UNINIT +.Nd disable SR-IOV on a PF device +.Sh SYNOPSIS +.In sys/bus.h +.In dev/pci/pci_iov.h +.Ft void +.Fn PCI_IOV_UNINIT "device_t dev" +.Sh DESCRIPTION +The +.Fn PCI_IOV_UNINIT +method is called by the PCI Single-Root I/O Virtualization (SR-IOV) +infrastructure when the user requests that SR-IOV be disabled on a Physical +Function (PF). +When this method is called, the PF driver must release any SR-IOV-related +resources that it has allocated and disable any device-specific SR-IOV +configuration in the device. +.Pp +This method will only be called following a successful call to +.Xr PCI_IOV_INIT . +It is not guaranteed that +.Xr PCI_IOV_ADD_VF +will have been called for any Virtual Function (VF) after the call to +.Xr PCI_IOV_INIT +and before the call to +.Nm . +.Sh SEE ALSO +.Xr pci 9 , +.Xr PCI_IOV_ADD_VF 9 , +.Xr PCI_IOV_INIT 9 +.Sh AUTHORS +This manual page was written by +.An Ryan Stone Aq Mt rstone@FreeBSD.org . Index: share/man/man9/nv.9 =================================================================== --- share/man/man9/nv.9 +++ share/man/man9/nv.9 @@ -0,0 +1,935 @@ +.\" +.\" Copyright (c) 2013 The FreeBSD Foundation +.\" Copyright (c) 2013-2015 Mariusz Zaborski +.\" All rights reserved. +.\" +.\" This documentation was written by Pawel Jakub Dawidek under sponsorship +.\" the FreeBSD Foundation. +.\" +.\" 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$ +.\" +.Dd August 15, 2015 +.Dt NV 9 +.Os +.Sh NAME +.Nm nvlist_create , +.Nm nvlist_destroy , +.Nm nvlist_error , +.Nm nvlist_set_error , +.Nm nvlist_empty , +.Nm nvlist_flags , +.Nm nvlist_exists , +.Nm nvlist_free , +.Nm nvlist_clone , +.Nm nvlist_dump , +.Nm nvlist_fdump , +.Nm nvlist_size , +.Nm nvlist_pack , +.Nm nvlist_unpack , +.Nm nvlist_send , +.Nm nvlist_recv , +.Nm nvlist_xfer , +.Nm nvlist_in_array , +.Nm nvlist_next , +.Nm nvlist_add , +.Nm nvlist_move , +.Nm nvlist_get , +.Nm nvlist_take +.Nd "library for name/value pairs" +.Sh LIBRARY +.Lb libnv +.Sh SYNOPSIS +.In nv.h +.Ft "nvlist_t *" +.Fn nvlist_create "int flags" +.Ft void +.Fn nvlist_destroy "nvlist_t *nvl" +.Ft int +.Fn nvlist_error "const nvlist_t *nvl" +.Ft void +.Fn nvlist_set_error "nvlist_t *nvl, int error" +.Ft bool +.Fn nvlist_empty "const nvlist_t *nvl" +.Ft int +.Fn nvlist_flags "const nvlist_t *nvl" +.Ft bool +.Fn nvlist_in_array "const nvlist_t *nvl" +.\" +.Ft "nvlist_t *" +.Fn nvlist_clone "const nvlist_t *nvl" +.\" +.Ft void +.Fn nvlist_dump "const nvlist_t *nvl, int fd" +.Ft void +.Fn nvlist_fdump "const nvlist_t *nvl, FILE *fp" +.\" +.Ft size_t +.Fn nvlist_size "const nvlist_t *nvl" +.Ft "void *" +.Fn nvlist_pack "const nvlist_t *nvl" "size_t *sizep" +.Ft "nvlist_t *" +.Fn nvlist_unpack "const void *buf" "size_t size" "int flags" +.\" +.Ft int +.Fn nvlist_send "int sock" "const nvlist_t *nvl" +.Ft "nvlist_t *" +.Fn nvlist_recv "int sock" "int flags" +.Ft "nvlist_t *" +.Fn nvlist_xfer "int sock" "nvlist_t *nvl" "int flags" +.\" +.Ft "const char *" +.Fn nvlist_next "const nvlist_t *nvl" "int *typep" "void **cookiep" +.\" +.Ft bool +.Fn nvlist_exists "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_type "const nvlist_t *nvl" "const char *name" "int type" +.Ft bool +.Fn nvlist_exists_null "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_bool "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_number "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_string "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_nvlist "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_descriptor "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_binary "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_bool_array "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_number_array "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_string_array "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_nvlist_array "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_descriptor_array "const nvlist_t *nvl" "const char *name" +.\" +.Ft void +.Fn nvlist_add_null "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_add_bool "nvlist_t *nvl" "const char *name" "bool value" +.Ft void +.Fn nvlist_add_number "nvlist_t *nvl" "const char *name" "uint64_t value" +.Ft void +.Fn nvlist_add_string "nvlist_t *nvl" "const char *name" "const char *value" +.Ft void +.Fn nvlist_add_stringf "nvlist_t *nvl" "const char *name" "const char *valuefmt" "..." +.Ft void +.Fn nvlist_add_stringv "nvlist_t *nvl" "const char *name" "const char *valuefmt" "va_list valueap" +.Ft void +.Fn nvlist_add_nvlist "nvlist_t *nvl" "const char *name" "const nvlist_t *value" +.Ft void +.Fn nvlist_add_descriptor "nvlist_t *nvl" "const char *name" "int value" +.Ft void +.Fn nvlist_add_binary "nvlist_t *nvl" "const char *name" "const void *value" "size_t size" +.Ft void +.Fn nvlist_add_bool_array "nvlist_t *nvl" "const char *name" "const bool *value" "size_t nitems" +. +.Ft void +.Fn nvlist_add_number_array "nvlist_t *nvl" "const char *name" "const uint64_t *value" "size_t nitems" +. +.Ft void +.Fn nvlist_add_string_array "nvlist_t *nvl" "const char *name" "const char * const * value" "size_t nitems" +. +.Ft void +.Fn nvlist_add_nvlist_array "nvlist_t *nvl" "const char *name" "const nvlist_t * const * value" "size_t nitems" +. +.Ft void +.Fn nvlist_add_descriptor_array "nvlist_t *nvl" "const char *name" "const int *value" "size_t nitems" +.\" +.Ft void +.Fn nvlist_move_string "nvlist_t *nvl" "const char *name" "char *value" +.Ft void +.Fn nvlist_move_nvlist "nvlist_t *nvl" "const char *name" "nvlist_t *value" +.Ft void +.Fn nvlist_move_descriptor "nvlist_t *nvl" "const char *name" "int value" +.Ft void +.Fn nvlist_move_binary "nvlist_t *nvl" "const char *name" "void *value" "size_t size" +.Ft void +.Fn nvlist_move_bool_array "nvlist_t *nvl" "const char *name" "bool *value" "size_t nitems" +. +.Ft void +.Fn nvlist_move_number_array "nvlist_t *nvl" "const char *name" "uint64_t *value" "size_t nitems" +. +.Ft void +.Fn nvlist_move_string_array "nvlist_t *nvl" "const char *name" "char **value" "size_t nitems" +. +.Ft void +.Fn nvlist_move_nvlist_array "nvlist_t *nvl" "const char *name" "nvlist_t **value" "size_t nitems" +. +.Ft void +.Fn nvlist_move_descriptor_array "nvlist_t *nvl" "const char *name" "int *value" "size_t nitems" +.\" +.Ft bool +.Fn nvlist_get_bool "const nvlist_t *nvl" "const char *name" +.Ft uint64_t +.Fn nvlist_get_number "const nvlist_t *nvl" "const char *name" +.Ft "const char *" +.Fn nvlist_get_string "const nvlist_t *nvl" "const char *name" +.Ft "const nvlist_t *" +.Fn nvlist_get_nvlist "const nvlist_t *nvl" "const char *name" +.Ft int +.Fn nvlist_get_descriptor "const nvlist_t *nvl" "const char *name" +.Ft "const void *" +.Fn nvlist_get_binary "const nvlist_t *nvl" "const char *name" "size_t *sizep" +.Ft "const bool *" +.Fn nvlist_get_bool_array "const nvlist_t *nvl" "const char *name" "size_t *nitems" +.Ft "const uint64_t *" +.Fn nvlist_get_number "const nvlist_t *nvl" "const char *name" "size_t *nitems" +.Ft "const char * const *" +.Fn nvlist_get_string "const nvlist_t *nvl" "const char *name" "size_t *nitems" +.Ft "const nvlist_t * const *" +.Fn nvlist_get_nvlist "const nvlist_t *nvl" "const char *name" "size_t *nitems" +.Ft "const int *" +.Fn nvlist_get_descriptor_array "const nvlist_t *nvl" "const char *name" "size_t *nitems" +.Ft "const nvlist_t *" +.Fn nvlist_get_parent "const nvlist_t *nvl" "void **cookiep" +.Ft "const nvlist_t *" +.Fn nvlist_get_array_next "const nvlist_t *nvl" +.Ft "const nvlist_t *" +.Fn nvlist_get_pararr "const nvlist_t *nvl" "void **cookiep" +.\" +.Ft bool +.Fn nvlist_take_bool "nvlist_t *nvl" "const char *name" +.Ft uint64_t +.Fn nvlist_take_number "nvlist_t *nvl" "const char *name" +.Ft "char *" +.Fn nvlist_take_string "nvlist_t *nvl" "const char *name" +.Ft "nvlist_t *" +.Fn nvlist_take_nvlist "nvlist_t *nvl" "const char *name" +.Ft int +.Fn nvlist_take_descriptor "nvlist_t *nvl" "const char *name" +.Ft "void *" +.Fn nvlist_take_binary "nvlist_t *nvl" "const char *name" "size_t *sizep" +.Ft "bool *" +.Fn nvlist_take_bool_array "nvlist_t *nvl" "const char *name" "size_t *nitems" +.Ft "uint64_t **" +.Fn nvlist_take_number "nvlist_t *nvl" "const char *name" "size_t *nitems" +.Ft "char **" +.Fn nvlist_take_string "nvlist_t *nvl" "const char *name" "size_t *nitems" +.Ft "nvlist_t **" +.Fn nvlist_take_nvlist "nvlist_t *nvl" "const char *name" "size_t *nitems" +.Ft "int *" +.Fn nvlist_take_descriptor "nvlist_t *nvl" "const char *name" "size_t *nitems" +.\" +.Ft void +.Fn nvlist_free "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_type "nvlist_t *nvl" "const char *name" "int type" +.\" +.Ft void +.Fn nvlist_free_null "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_bool "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_number "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_string "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_nvlist "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_descriptor "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_binary "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_bool_array "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_number_array "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_string_array "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_nvlist_array "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_descriptor_array "nvlist_t *nvl" "const char *name" +.Sh DESCRIPTION +The +.Nm libnv +library allows to easily manage name value pairs as well as send and receive +them over sockets. +A group (list) of name value pairs is called an +.Nm nvlist . +The API supports the following data types: +.Bl -ohang -offset indent +.It Sy null ( NV_TYPE_NULL ) +There is no data associated with the name. +.It Sy bool ( NV_TYPE_BOOL ) +The value can be either +.Dv true +or +.Dv false . +.It Sy number ( NV_TYPE_NUMBER ) +The value is a number stored as +.Vt uint64_t . +.It Sy string ( NV_TYPE_STRING ) +The value is a C string. +.It Sy nvlist ( NV_TYPE_NVLIST ) +The value is a nested nvlist. +.It Sy descriptor ( NV_TYPE_DESCRIPTOR ) +The value is a file descriptor. +Note that file descriptors can be sent only over +.Xr unix 4 +domain sockets. +.It Sy binary ( NV_TYPE_BINARY ) +The value is a binary buffer. +.It Sy bool array ( NV_TYPE_BOOL_ARRAY ) +The value is an array of boolean values. +.It Sy number array ( NV_TYPE_NUMBER_ARRAY ) +The value is an array of numbers, each stored as +.Vt uint64_t . +.It Sy string array ( NV_TYPE_STRING_ARRAY ) +The value is an array of C strings. +.It Sy nvlist array ( NV_TYPE_NVLIST_ARRAY ) +The value is an array of nvlists. +When an nvlist is added to an array, it becomes part of the primary nvlist. +Traversing these arrays can be done using the +.Fn nvlist_get_array_next +and +.Fn nvlist_get_pararr +functions. +.It Sy descriptor array ( NV_TYPE_DESCRIPTOR_ARRAY ) +The value is an array of files descriptors. +.El +.Pp +The +.Fn nvlist_create +function allocates memory and initializes an nvlist. +.Pp +The following flag can be provided: +.Pp +.Bl -tag -width "NV_FLAG_IGNORE_CASE" -compact -offset indent +.It Dv NV_FLAG_IGNORE_CASE +Perform case-insensitive lookups of provided names. +.It Dv NV_FLAG_NO_UNIQUE +Names in the nvlist do not have to be unique. +.El +.Pp +The +.Fn nvlist_destroy +function destroys the given nvlist. +Function does nothing if +.Dv NULL +nvlist is provided. +Function never modifies the +.Va errno +global variable. +.Pp +The +.Fn nvlist_error +function returns any error value that the nvlist accumulated. +If the given nvlist is +.Dv NULL +the +.Er ENOMEM +error will be returned. +.Pp +The +.Fn nvlist_set_error +function sets an nvlist to be in the error state. +Subsequent calls to +.Fn nvlist_error +will return the given error value. +This function cannot be used to clear the error state from an nvlist. +This function does nothing if the nvlist is already in the error state. +.Pp +The +.Fn nvlist_empty +function returns +.Dv true +if the given nvlist is empty and +.Dv false +otherwise. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_flags +function returns flags used to create the nvlist with the +.Fn nvlist_create +function. +.Pp +The +.Fn nvlist_in_array +function returns +.Dv true +if +.Fa nvl +is part of an array that is a member of another nvlist. +.Pp +The +.Fn nvlist_clone +functions clones the given nvlist. +The clone shares no resources with its origin. +This also means that all file descriptors that are part of the nvlist will be +duplicated with the +.Xr dup 2 +system call before placing them in the clone. +.Pp +The +.Fn nvlist_dump +dumps nvlist content for debugging purposes to the given file descriptor +.Fa fd . +.Pp +The +.Fn nvlist_fdump +dumps nvlist content for debugging purposes to the given file stream +.Fa fp . +.Pp +The +.Fn nvlist_size +function returns the size of the given nvlist after converting it to binary +buffer with the +.Fn nvlist_pack +function. +.Pp +The +.Fn nvlist_pack +function converts the given nvlist to a binary buffer. +The function allocates memory for the buffer, which should be freed with the +.Xr free 3 +function. +If the +.Fa sizep +argument is not +.Dv NULL , +the size of the buffer will be stored there. +The function returns +.Dv NULL +in case of an error (allocation failure). +If the nvlist contains any file descriptors +.Dv NULL +will be returned. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_unpack +function converts the given buffer to the nvlist. +The +.Fa flags +argument defines what type of the top level nvlist is expected to be. +Flags are set up using the +.Fn nvlist_create +function. +If the nvlist flags do not match the flags passed to +.Fn nvlist_unpack , +the nvlist will not be returned. +Every nested nvlist list should be checked using +.Fn nvlist_flags +function. +The function returns +.Dv NULL +in case of an error. +.Pp +The +.Fn nvlist_send +function sends the given nvlist over the socket given by the +.Fa sock +argument. +Note that nvlist that contains file descriptors can only be send over +.Xr unix 4 +domain sockets. +.Pp +The +.Fn nvlist_recv +function receives nvlist over the socket given by the +.Fa sock +argument. +The +.Fa flags +argument defines what type of the top level nvlist is expected to be. +Flags are set up using the +.Fn nvlist_create +function. +If the nvlist flags do not match the flags passed to +.Fn nvlist_recv , +the nvlist will not be returned. +Every nested nvlist list should be checked using +.Fn nvlist_flags +function. +.Pp +The +.Fn nvlist_xfer +function sends the given nvlist over the socket given by the +.Fa sock +argument and receives nvlist over the same socket. +The +.Fa flags +argument defines what type of the top level nvlist is expected to be. +Flags are set up using the +.Fn nvlist_create +function. +If the nvlist flags do not match the flags passed to +.Fn nvlist_xfer , +the nvlist will not be returned. +Every nested nvlist list should be checked using +.Fn nvlist_flags +function. +The given nvlist is always destroyed. +.Pp +The +.Fn nvlist_next +function iterates over the given nvlist returning names and types of subsequent +elements. +The +.Fa cookiep +argument allows the function to figure out which element should be returned +next. +The +.Va *cookiep +should be set to +.Dv NULL +for the first call and should not be changed later. +Returning +.Dv NULL +means there are no more elements on the nvlist. +The +.Fa typep +argument can be NULL. +Elements may not be removed from the nvlist while traversing it. +The nvlist must not be in error state. +Note that +.Fn nvlist_next +will handle +.Va cookiep +being set to +.Dv NULL . +In this case first element is returned or +.Dv NULL +if nvlist is empty. +This behavior simplifies removing the first element from the list. +.Pp +The +.Fn nvlist_exists +function returns +.Dv true +if element of the given name exists (besides of its type) or +.Dv false +otherwise. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_exists_type +function returns +.Dv true +if element of the given name and the given type exists or +.Dv false +otherwise. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_exists_null , +.Fn nvlist_exists_bool , +.Fn nvlist_exists_number , +.Fn nvlist_exists_string , +.Fn nvlist_exists_nvlist , +.Fn nvlist_exists_descriptor , +.Fn nvlist_exists_binary , +.Fn nvlist_exists_bool_array , +.Fn nvlist_exists_number_array , +.Fn nvlist_exists_string_array , +.Fn nvlist_exists_nvlist_array , +.Fn nvlist_exists_descriptor_array +functions return +.Dv true +if element of the given name and the given type determined by the function name +exists or +.Dv false +otherwise. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_add_null , +.Fn nvlist_add_bool , +.Fn nvlist_add_number , +.Fn nvlist_add_string , +.Fn nvlist_add_stringf , +.Fn nvlist_add_stringv , +.Fn nvlist_add_nvlist , +.Fn nvlist_add_descriptor , +.Fn nvlist_add_binary , +.Fn nvlist_add_bool_array , +.Fn nvlist_add_number_array , +.Fn nvlist_add_string_array , +.Fn nvlist_add_nvlist_array , +.Fn nvlist_add_descriptor_array +functions add element to the given nvlist. +When adding string or binary buffor the functions will allocate memory +and copy the data over. +When adding nvlist, the nvlist will be cloned and clone will be added. +When adding descriptor, the descriptor will be duplicated using the +.Xr dup 2 +system call and the new descriptor will be added. +The array functions will fail if there are any +.Dv NULL +elements in the array, or if the array pointer is +.Dv NULL . +If an error occurs while adding new element, internal error is set which can be +examined using the +.Fn nvlist_error +function. +.Pp +The +.Fn nvlist_move_string , +.Fn nvlist_move_nvlist , +.Fn nvlist_move_descriptor , +.Fn nvlist_move_binary , +.Fn nvlist_move_bool_array , +.Fn nvlist_move_number_array , +.Fn nvlist_move_string_array , +.Fn nvlist_move_nvlist_array , +.Fn nvlist_move_descriptor_array +functions add new element to the given nvlist, but unlike +.Fn nvlist_add_ +functions they will consume the given resource. +In the case of strings, descriptors, or nvlists every elements must be +unique, or it could cause a double free. +The array functions will fail if there are any +.Dv NULL +elements, or if the array pointer is +.Dv NULL . +If an error occurs while adding new element, the resource is destroyed and +internal error is set which can be examined using the +.Fn nvlist_error +function. +.Pp +The +.Fn nvlist_get_bool , +.Fn nvlist_get_number , +.Fn nvlist_get_string , +.Fn nvlist_get_nvlist , +.Fn nvlist_get_descriptor , +.Fn nvlist_get_binary , +.Fn nvlist_get_bool_array , +.Fn nvlist_get_number_array , +.Fn nvlist_get_string_array , +.Fn nvlist_get_nvlist_array , +.Fn nvlist_get_descriptor_array +functions return the value that corresponds to the given key name. +In the case of strings, nvlists, descriptors, binary, or arrays, the returned +resource should not be modified - they still belong to the nvlist. +If an element of the given name does not exist, the program will be aborted. +To avoid this, the caller should check for the existence of the name before +trying to obtain the value, or use the +.Xr dnvlist 3 +extension, which can provide a default value in the case of a missing element. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_get_parent +function returns the parent nvlist of the nested nvlist. +.Pp +The +.Fn nvlist_get_array_next +function returns the next element from the array or +.Dv NULL +if the nvlist is not in array or it is the last element. +Note that +.Fn nvlist_get_array_next +only works if you added the nvlist array using the +.Fn nvlist_move_nvlist_array +or +.Fn nvlist_add_nvlist_array +functions. +.Pp +The +.Fn nvlist_get_pararr +function returns the next element in the array, or if not available +the parent of the nested nvlist. +.Pp +The +.Fn nvlist_take_bool , +.Fn nvlist_take_number , +.Fn nvlist_take_string , +.Fn nvlist_take_nvlist , +.Fn nvlist_take_descriptor , +.Fn nvlist_take_binary , +.Fn nvlist_take_bool_array , +.Fn nvlist_take_number_array , +.Fn nvlist_take_string_array , +.Fn nvlist_take_nvlist_array , +.Fn nvlist_take_descriptor_array +functions return value associated with the given name and remove the element +from the nvlist. +In case of string and binary values, the caller is responsible for free returned +memory using the +.Xr free 3 +function. +In case of nvlist, the caller is responsible for destroying returned nvlist +using the +.Fn nvlist_destroy +function. +In case of descriptor, the caller is responsible for closing returned descriptor +using the +.Fn close 2 +system call. +If an element of the given name does not exist, the program will be aborted. +To avoid that the caller should check for the existence of the given name +before trying to obtain the value, or use the +.Xr dnvlist 3 +extension, which can provide a default value in the case of a missing element. +In the case of an array of strings or binary values, the caller is responsible +for freeing every element of the array using the +.Xr free 3 +function. +In the case of an array of nvlists, the caller is responsible for destroying +every element of array using the +.Fn nvlist_destroy +function. +In the case of descriptors, the caller is responsible for closing every +element of array using the +.Fn close 2 +system call. +In every case involving an array, the caller must also free the pointer to +the array using the +.Xr free 3 +function. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_free +function removes element of the given name from the nvlist (besides of its type) +and frees all resources associated with it. +If element of the given name does not exist, the program will be aborted. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_free_type +function removes element of the given name and the given type from the nvlist +and frees all resources associated with it. +If element of the given name and the given type does not exist, the program +will be aborted. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_free_null , +.Fn nvlist_free_bool , +.Fn nvlist_free_number , +.Fn nvlist_free_string , +.Fn nvlist_free_nvlist , +.Fn nvlist_free_descriptor , +.Fn nvlist_free_binary , +.Fn nvlist_free_bool_array , +.Fn nvlist_free_number_array , +.Fn nvlist_free_string_array , +.Fn nvlist_free_nvlist_array , +.Fn nvlist_free_descriptor_array +functions remove element of the given name and the given type determined by the +function name from the nvlist and free all resources associated with it. +If element of the given name and the given type does not exist, the program +will be aborted. +The nvlist must not be in error state. +.Sh EXAMPLES +The following example demonstrates how to prepare an nvlist and send it over +.Xr unix 4 +domain socket. +.Bd -literal +nvlist_t *nvl; +int fd; + +fd = open("/tmp/foo", O_RDONLY); +if (fd < 0) + err(1, "open(\\"/tmp/foo\\") failed"); + +nvl = nvlist_create(0); +/* + * There is no need to check if nvlist_create() succeeded, + * as the nvlist_add_() functions can cope. + * If it failed, nvlist_send() will fail. + */ +nvlist_add_string(nvl, "filename", "/tmp/foo"); +nvlist_add_number(nvl, "flags", O_RDONLY); +/* + * We just want to send the descriptor, so we can give it + * for the nvlist to consume (that's why we use nvlist_move + * not nvlist_add). + */ +nvlist_move_descriptor(nvl, "fd", fd); +if (nvlist_send(sock, nvl) < 0) { + nvlist_destroy(nvl); + err(1, "nvlist_send() failed"); +} +nvlist_destroy(nvl); +.Ed +.Pp +Receiving nvlist and getting data: +.Bd -literal +nvlist_t *nvl; +const char *command; +char *filename; +int fd; + +nvl = nvlist_recv(sock, 0); +if (nvl == NULL) + err(1, "nvlist_recv() failed"); + +/* For command we take pointer to nvlist's buffer. */ +command = nvlist_get_string(nvl, "command"); +/* + * For filename we remove it from the nvlist and take + * ownership of the buffer. + */ +filename = nvlist_take_string(nvl, "filename"); +/* The same for the descriptor. */ +fd = nvlist_take_descriptor(nvl, "fd"); + +printf("command=%s filename=%s fd=%d\n", command, filename, fd); + +nvlist_destroy(nvl); +free(filename); +close(fd); +/* command was freed by nvlist_destroy() */ +.Ed +.Pp +Iterating over nvlist: +.Bd -literal +nvlist_t *nvl; +const char *name; +void *cookie; +int type; + +nvl = nvlist_recv(sock, 0); +if (nvl == NULL) + err(1, "nvlist_recv() failed"); + +cookie = NULL; +while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) { + printf("%s=", name); + switch (type) { + case NV_TYPE_NUMBER: + printf("%ju", (uintmax_t)nvlist_get_number(nvl, name)); + break; + case NV_TYPE_STRING: + printf("%s", nvlist_get_string(nvl, name)); + break; + default: + printf("N/A"); + break; + } + printf("\\n"); +} +.Ed +.Pp +Iterating over every nested nvlist: +.Bd -literal +nvlist_t *nvl; +const char *name; +void *cookie; +int type; + +nvl = nvlist_recv(sock, 0); +if (nvl == NULL) + err(1, "nvlist_recv() failed"); + +cookie = NULL; +do { + while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) { + if (type == NV_TYPE_NVLIST) { + nvl = nvlist_get_nvlist(nvl, name); + cookie = NULL; + } + } +} while ((nvl = nvlist_get_parent(nvl, &cookie)) != NULL); +.Ed +.Pp +Iterating over every nested nvlist and every nvlist element: +.Bd -literal +nvlist_t *nvl; +const nvlist_t * const *array; +const char *name; +void *cookie; +int type; + +nvl = nvlist_recv(sock, 0); +if (nvl == null) + err(1, "nvlist_recv() failed"); + +cookie = null; +do { + while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) { + if (type == NV_TYPE_NVLIST) { + nvl = nvlist_get_nvlist(nvl, name); + cookie = NULL; + } else if (type == NV_TYPE_NVLIST_ARRAY) { + nvl = nvlist_get_nvlist_array(nvl, name, NULL)[0]; + cookie = NULL; + } + } +} while ((nvl = nvlist_get_pararr(nvl, &cookie)) != NULL); +.Ed +.Pp +Or alternatively: +.Bd -literal +nvlist_t *nvl, *tmp; +const nvlist_t * const *array; +const char *name; +void *cookie; +int type; + +nvl = nvlist_recv(sock, 0); +if (nvl == null) + err(1, "nvlist_recv() failed"); + +cooke = NULL; +tmp = nvl; +do { + do { + nvl = tmp; + while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) { + if (type == NV_TYPE_NVLIST) { + nvl = nvlist_get_nvlist(nvl, + name); + cookie = NULL; + } else if (type == NV_TYPE_NVLIST_ARRAY) { + nvl = nvlist_get_nvlist_array(nvl, name, + NULL)[0]; + cookie = NULL; + } + } + cookie = NULL; + } while ((tmp = nvlist_get_array_next(nvl)) != NULL); +} while ((tmp = nvlist_get_parent(nvl, &cookie)) != NULL); +.Ed +.Sh SEE ALSO +.Xr close 2 , +.Xr dup 2 , +.Xr open 2 , +.Xr err 3 , +.Xr free 3 , +.Xr printf 3 , +.Xr unix 4 +.Sh HISTORY +The +.Nm libnv +library appeared in +.Fx 11.0 . +.Sh AUTHORS +.An -nosplit +The +.Nm libnv +library was implemented by +.An Pawel Jakub Dawidek Aq Mt pawel@dawidek.net +under sponsorship from the FreeBSD Foundation. Index: share/man/man9/pci.9 =================================================================== --- share/man/man9/pci.9 +++ share/man/man9/pci.9 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd November 5, 2015 +.Dd January 3, 2015 .Dt PCI 9 .Os .Sh NAME @@ -47,6 +47,8 @@ .Nm pci_get_powerstate , .Nm pci_get_vpd_ident , .Nm pci_get_vpd_readonly , +.Nm pci_iov_attach , +.Nm pci_iov_detach , .Nm pci_msi_count , .Nm pci_msix_count , .Nm pci_pending_msix , @@ -134,6 +136,11 @@ .Fn pcie_read_config "device_t dev" "int reg" "int width" .Ft void .Fn pcie_write_config "device_t dev" "int reg" "uint32_t val" "int width" +.In dev/pci/pci_iov.h +.Ft int +.Fn pci_iov_attach "device_t dev" "nvlist_t *pf_schema" "nvlist_t *vf_schema" +.Ft int +.Fn pci_iov_detach "device_t dev" .Sh DESCRIPTION The .Nm @@ -504,6 +511,75 @@ .Er EOPNOTSUPP . .Pp The +.Fn pci_iov_attach +function is used to advertise that the given device +.Pq and associated device driver +supports PCI Single-Root I/O Virtualization +.Pq SR-IOV . +A driver that supports SR-IOV must implement the +.Xr PCI_IOV_INIT 9 , +.Xr PCI_IOV_ADD_VF 9 +and +.Xr PCI_IOV_UNINIT 9 +methods. +This function should be called during the +.Xr DEVICE_ATTACH 9 +method. +If this function returns an error, it is recommended that the device driver +still successfully attaches, but runs with SR-IOV disabled. +The +.Fa pf_schema +and +.Fa vf_schema +parameters are used to define what device-specific configuration parameters the +device driver accepts when SR-IOV is enabled for the Physical Function +.Pq PF +and for individual Virtual Functions +.Pq VFs +respectively. +See +.Xr pci_iov_schema 9 +for details on how to construct the schema. +If either the +.Pa pf_schema +or +.Pa vf_schema +is invalid or specifies parameter names that conflict with parameter names that +are already in use, +.Fn pci_iov_attach +will return an error and SR-IOV will not be available on the PF device. +If a driver does not accept configuration parameters for either the PF device +or the VF devices, the driver must pass an empty schema for that device. +The SR-IOV infrastructure takes ownership of the +.Fa pf_schema +and +.Fa vf_schema +and is responsible for freeing them. +The driver must never free the schemas itself. +.Pp +The +.Fn pci_iov_detach +function is used to advise the SR-IOV infrastructure that the driver for the +given device is attempting to detach and that all SR-IOV resources for the +device must be released. +This function must be called during the +.Xr DEVICE_DETACH 9 +method if +.Fn pci_iov_attach +was successfully called on the device and +.Fn pci_iov_detach +has not subsequently been called on the device and returned no error. +If this function returns an error, the +.Xr DEVICE_DETACH 9 +method must fail and return an error, as detaching the PF driver while VF +devices are active would cause system instability. +This function is safe to call and will always succeed if +.Fn pci_iov_attach +previously failed with an error on the given device, or if +.Fn pci_iov_attach +was never called on the device. +.Pp +The .Fn pci_save_state and .Fn pci_restore_state Index: share/man/man9/pci_iov_schema.9 =================================================================== --- share/man/man9/pci_iov_schema.9 +++ share/man/man9/pci_iov_schema.9 @@ -0,0 +1,265 @@ +.\" +.\" Copyright (c) 2014 Sandvine Inc. +.\" 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$ +.\" +.Dd July 8, 2015 +.Dt PCI_IOV_SCHEMA 9 +.Os +.Sh NAME +.Nm pci_iov_schema , +.Nm pci_iov_schema_alloc_node , +.Nm pci_iov_schema_add_bool , +.Nm pci_iov_schema_add_string , +.Nm pci_iov_schema_add_uint8 , +.Nm pci_iov_schema_add_uint16 , +.Nm pci_iov_schema_add_uint32 , +.Nm pci_iov_schema_add_uint64 , +.Nm pci_iov_schema_add_unicast_mac +.Nd PCI SR-IOV config schema interface +.Sh SYNOPSIS +.In machine/stdarg.h +.In sys/nv.h +.In sys/iov_schema.h +.Ft nvlist_t * +.Fn pci_iov_schema_alloc_node "void" +.Ft void +.Fn pci_iov_schema_add_bool "nvlist_t *schema" "const char *name" \ +"uint32_t flags" "int defaultVal" +.Ft void +.Fn pci_iov_schema_add_string "nvlist_t *schema" "const char *name" \ +"uint32_t flags" "const char *defaultVal" +.Ft void +.Fn pci_iov_schema_add_uint8 "nvlist_t *schema" "const char *name" \ +"uint32_t flags" "uint8_t defaultVal" +.Ft void +.Fn pci_iov_schema_add_uint16 "nvlist_t *schema" "const char *name" \ +"uint32_t flags" "uint16_t defaultVal" +.Ft void +.Fn pci_iov_schema_add_uint32 "nvlist_t *schema" "const char *name" \ +"uint32_t flags" "uint32_t defaultVal" +.Ft void +.Fn pci_iov_schema_add_uint64 "nvlist_t *schema" "const char *name" \ +"uint32_t flags" "uint64_t defaultVal" +.Ft void +.Fn pci_iov_schema_add_unicast_mac "nvlist_t *schema" "const char *name" \ +"uint32_t flags" "const uint8_t *defaultVal" +.Sh DESCRIPTION +The PCI Single-Root I/O Virtualization +.Pq SR-IOV +configuration schema is a data +structure that describes the device-specific configuration parameters that a PF +driver will accept when SR-IOV is enabled on the PF device. +Each PF driver defines two schema instances: the PF schema and the VF schema. +The PF schema describes configuration that applies to the PF device as a whole. +The VF schema describes configuration that applies to an individual VF device. +Different VF devices may have different configuration applied to them, as long +as the configuration for each VF conforms to the VF schema. +.Pp +A PF driver builds a configuration schema by first allocating a schema node and +then adding configuration parameter specifications to the schema. +The configuration parameter specification consists of a name and a value type. +.Pp +Configuration parameter names are case-insensitive. +It is an error to specify two or more configuration parameters with the same +name. +It is also an error to specific a configuration parameter that uses the same +name as a configuration parameter used by the SR-IOV infrastructure. +See +.Xr iovctl.conf 5 +for documentation of all configuration parameters used by the SR-IOV +infrastructure. +.Pp +The parameter type constrains the possible values that the configuration +parameter may take. +.Pp +A configuration parameter may be specified as a required parameter by setting +the +.Dv IOV_SCHEMA_REQUIRED +flag in the +.Pa flags +argument. +Required parameters must be specified by the user when SR-IOV is enabled. +If the user does not specify a required parameter, the SR-IOV infrastructure +will abort the request to enable SR-IOV and return an error to the user. +.Pp +Alternatively, a configuration parameter may be given a default value by +setting the +.Dv IOV_SCHEMA_HASDEFAULT +flag in the +.Pa flags +argument. +If a configuration parameter has a default value but the user has not specified +a value for that parameter, then the SR-IOV infrastructure will apply +.Pa defaultVal +for that parameter in the configuration before passing it to the PF driver. +It is an error for the value of the +.Pa defaultVal +parameter to not conform to the restrictions of the specified type. +If this flag is not specified then the +.Pa defaultVal +argument is ignored. +This flag is not compatible with the +.Dv IOV_SCHEMA_REQUIRED +flag; it is an error to specify both on the same parameter. +.Pp +The SR-IOV infrastructure guarantees that all configuration parameters that are +either specified as required or given a default value will be present in the +configuration passed to the PF driver. +Configuration parameters that are neither specified as required nor given a +default value are optional and may or may not be present in the configuration +passed to the PF driver. +.Pp +It is highly recommended that a PF driver reserve the use of optional parameters +for configuration that is truly optional. +For example, a Network Interface PF device might have the option to encapsulate +all traffic to and from a VF device in a vlan tag. +The PF driver could expose that option as a "vlan" parameter accepting an +integer argument specifying the vlan tag. +In this case, it would be appropriate to set the "vlan" parameter as an optional +parameter as it would be legitimate for a VF to be configured to have no vlan +tagging enabled at all. +.Pp +Alternatively, if the PF device had an boolean option that controlled whether +the VF was allowed to change its MAC address, it would not be appropriate to +set this parameter as optional. +The PF driver must either allow the MAC to change or not, so it would be more +appropriate for the PF driver to document the default behaviour by specifying +a default value in the schema +.Po or potentially force the user to make the choice by setting the parameter +to be required +.Pc . +.Pp +Configuration parameters that have security implications must default to the +most secure configuration possible. +.Pp +All device-specific configuration parameters must be documented in the manual +page for the PF driver, or in a separate manual page that is cross-referenced +from the main driver manual page. +.Pp +It is not necessary for a PF driver to check for failure from any of these +functions. +If an error occurs, it is flagged in the schema. +The +.Xr pci_iov_attach 9 +function checks for this error and will fail to initialize SR-IOV on the PF +device if an error is set in the schema. +If this occurs, it is recommended that the PF driver still succeed in attaching +and run with SR-IOV disabled on the device. +.Pp +The +.Fn pci_iov_schema_alloc_node +function is used to allocate an empty configuration schema. +It is not necessary to check for failure from this function. +The SR-IOV infrastructure will gracefully handle failure to allocate a schema +and will simply not enable SR-IOV on the PF device. +.Pp +The +.Fn pci_iov_schema_add_bool +function is used to specify a configuration parameter in the given schema with +the name +.Pa name +and having a boolean type. +Boolean values can only take the value true or false (1 or 0, respectively). +.Pp +The +.Fn pci_iov_schema_add_string +function is used to specify a configuration parameter in the given schema with +the name +.Pa name +and having a string type. +String values are standard C strings. +.Pp +The +.Fn pci_iov_schema_add_uint8 +function is used to specify a configuration parameter in the given schema with +the name +.Pa name +and having a +.Vt uint8_t +type. +Values of type +.Vt uint8_t +are unsigned integers in the range 0 to 255, inclusive. +.Pp +The +.Fn pci_iov_schema_add_uint16 +function is used to specify a configuration parameter in the given schema with +the name +.Pa name +and having a +.Vt uint16_t +type. +Values of type +.Vt uint16_t +are unsigned integers in the range 0 to 65535, inclusive. +.Pp +The +.Fn pci_iov_schema_add_uint32 +function is used to specify a configuration parameter in the given schema with +the name +.Pa name +and having a +.Vt uint32_t +type. +Values of type +.Vt uint32_t +are unsigned integers in the range 0 to +.Pq 2**32 - 1 , +inclusive. +.Pp +The +.Fn pci_iov_schema_add_uint64 +function is used to specify a configuration parameter in the given schema with +the name +.Pa name +and having a +.Vt uint64_t +type. +Values of type +.Vt uint64_t +are unsigned integers in the range 0 to +.Pq 2**64 - 1 , +inclusive. +.Pp +The +.Fn pci_iov_schema_add_unicast_mac +function is used to specify a configuration parameter in the given schema with +the name +.Pa name +and having a unicast-mac type. +Values of type unicast-mac are binary values exactly 6 bytes long. +The MAC address is guaranteed to not be a multicast or broadcast address. +.Sh RETURN VALUES +The +.Fn pci_iov_schema_alloc_node +function returns a pointer to the allocated schema, or NULL if a failure occurs. +.Sh SEE ALSO +.Xr pci 9 , +.Xr PCI_IOV_ADD_VF 9 , +.Xr PCI_IOV_INIT 9 +.Sh AUTHORS +This manual page was written by +.An Ryan Stone Aq rstone@FreeBSD.org . Index: sys/amd64/conf/GENERIC =================================================================== --- sys/amd64/conf/GENERIC +++ sys/amd64/conf/GENERIC @@ -90,6 +90,7 @@ device acpi options ACPI_DMAR device pci +options PCI_IOV # PCI SR-IOV support # Floppy drives device fdc Index: sys/cddl/compat/opensolaris/sys/nvpair.h =================================================================== --- sys/cddl/compat/opensolaris/sys/nvpair.h +++ sys/cddl/compat/opensolaris/sys/nvpair.h @@ -42,29 +42,19 @@ */ #define nvlist_add_binary illumos_nvlist_add_binary #define nvlist_add_bool illumos_nvlist_add_bool +#define nvlist_add_bool_array illumos_nvlist_add_bool_array #define nvlist_add_descriptor illumos_nvlist_add_descriptor +#define nvlist_add_descriptor_array illumos_nvlist_add_descriptor_array #define nvlist_add_null illumos_nvlist_add_null #define nvlist_add_number illumos_nvlist_add_number +#define nvlist_add_number_array illumos_nvlist_add_number_array #define nvlist_add_nvlist illumos_nvlist_add_nvlist +#define nvlist_add_nvlist_array illumos_nvlist_add_nvlist_array #define nvlist_add_nvpair illumos_nvlist_add_nvpair #define nvlist_add_string illumos_nvlist_add_string +#define nvlist_add_string_array illumos_nvlist_add_string_array #define nvlist_add_stringf illumos_nvlist_add_stringf #define nvlist_add_stringv illumos_nvlist_add_stringv -#define nvlist_addf_binary illumos_nvlist_addf_binary -#define nvlist_addf_bool illumos_nvlist_addf_bool -#define nvlist_addf_descriptor illumos_nvlist_addf_descriptor -#define nvlist_addf_null illumos_nvlist_addf_null -#define nvlist_addf_number illumos_nvlist_addf_number -#define nvlist_addf_nvlist illumos_nvlist_addf_nvlist -#define nvlist_addf_string illumos_nvlist_addf_string -#define nvlist_addv_binary illumos_nvlist_addv_binary -#define nvlist_addv_bool illumos_nvlist_addv_bool -#define nvlist_addv_descriptor illumos_nvlist_addv_descriptor -#define nvlist_addv_null illumos_nvlist_addv_null -#define nvlist_addv_number illumos_nvlist_addv_number -#define nvlist_addv_nvlist illumos_nvlist_addv_nvlist -#define nvlist_addv_string illumos_nvlist_addv_string -#define nvlist_check_header illumos_nvlist_check_header #define nvlist_clone illumos_nvlist_clone #define nvlist_create illumos_nvlist_create #define nvlist_descriptors illumos_nvlist_descriptors @@ -75,92 +65,61 @@ #define nvlist_exists illumos_nvlist_exists #define nvlist_exists_binary illumos_nvlist_exists_binary #define nvlist_exists_bool illumos_nvlist_exists_bool +#define nvlist_exists_bool_array illumos_nvlist_exists_bool_array #define nvlist_exists_descriptor illumos_nvlist_exists_descriptor +#define nvlist_exists_descriptor_array illumos_nvlist_exists_descriptor_array #define nvlist_exists_null illumos_nvlist_exists_null #define nvlist_exists_number illumos_nvlist_exists_number +#define nvlist_exists_number_array illumos_nvlist_exists_number_array #define nvlist_exists_nvlist illumos_nvlist_exists_nvlist +#define nvlist_exists_nvlist_array illumos_nvlist_exists_nvlist_array #define nvlist_exists_string illumos_nvlist_exists_string +#define nvlist_exists_string_array illumos_nvlist_exists_string_array #define nvlist_exists_type illumos_nvlist_exists_type -#define nvlist_existsf illumos_nvlist_existsf -#define nvlist_existsf_binary illumos_nvlist_existsf_binary -#define nvlist_existsf_bool illumos_nvlist_existsf_bool -#define nvlist_existsf_descriptor illumos_nvlist_existsf_descriptor -#define nvlist_existsf_null illumos_nvlist_existsf_null -#define nvlist_existsf_number illumos_nvlist_existsf_number -#define nvlist_existsf_nvlist illumos_nvlist_existsf_nvlist -#define nvlist_existsf_string illumos_nvlist_existsf_string -#define nvlist_existsf_type illumos_nvlist_existsf_type -#define nvlist_existsv illumos_nvlist_existsv -#define nvlist_existsv_binary illumos_nvlist_existsv_binary -#define nvlist_existsv_bool illumos_nvlist_existsv_bool -#define nvlist_existsv_descriptor illumos_nvlist_existsv_descriptor -#define nvlist_existsv_null illumos_nvlist_existsv_null -#define nvlist_existsv_number illumos_nvlist_existsv_number -#define nvlist_existsv_nvlist illumos_nvlist_existsv_nvlist -#define nvlist_existsv_string illumos_nvlist_existsv_string -#define nvlist_existsv_type illumos_nvlist_existsv_type #define nvlist_fdump illumos_nvlist_fdump #define nvlist_first_nvpair illumos_nvlist_first_nvpair +#define nvlist_flags illumos_nvlist_flags #define nvlist_free illumos_nvlist_free #define nvlist_free_binary illumos_nvlist_free_binary +#define nvlist_free_binary_array illumos_nvlist_free_binary_array #define nvlist_free_bool illumos_nvlist_free_bool +#define nvlist_free_bool_array illumos_nvlist_free_bool_array #define nvlist_free_descriptor illumos_nvlist_free_descriptor +#define nvlist_free_descriptor_array illumos_nvlist_free_descriptor_array #define nvlist_free_null illumos_nvlist_free_null #define nvlist_free_number illumos_nvlist_free_number +#define nvlist_free_number_array illumos_nvlist_free_number_array #define nvlist_free_nvlist illumos_nvlist_free_nvlist +#define nvlist_free_nvlist_array illumos_nvlist_free_nvlist_array #define nvlist_free_nvpair illumos_nvlist_free_nvpair #define nvlist_free_string illumos_nvlist_free_string +#define nvlist_free_string_array illumos_nvlist_free_string_array #define nvlist_free_type illumos_nvlist_free_type -#define nvlist_freef illumos_nvlist_freef -#define nvlist_freef_binary illumos_nvlist_freef_binary -#define nvlist_freef_bool illumos_nvlist_freef_bool -#define nvlist_freef_descriptor illumos_nvlist_freef_descriptor -#define nvlist_freef_null illumos_nvlist_freef_null -#define nvlist_freef_number illumos_nvlist_freef_number -#define nvlist_freef_nvlist illumos_nvlist_freef_nvlist -#define nvlist_freef_string illumos_nvlist_freef_string -#define nvlist_freef_type illumos_nvlist_freef_type -#define nvlist_freev illumos_nvlist_freev -#define nvlist_freev_binary illumos_nvlist_freev_binary -#define nvlist_freev_bool illumos_nvlist_freev_bool -#define nvlist_freev_descriptor illumos_nvlist_freev_descriptor -#define nvlist_freev_null illumos_nvlist_freev_null -#define nvlist_freev_number illumos_nvlist_freev_number -#define nvlist_freev_nvlist illumos_nvlist_freev_nvlist -#define nvlist_freev_string illumos_nvlist_freev_string -#define nvlist_freev_type illumos_nvlist_freev_type +#define nvlist_get_array_next illumos_nvlist_get_array_next #define nvlist_get_binary illumos_nvlist_get_binary #define nvlist_get_bool illumos_nvlist_get_bool +#define nvlist_get_bool_array illumos_nvlist_get_bool_array #define nvlist_get_descriptor illumos_nvlist_get_descriptor +#define nvlist_get_descriptor_array illumos_nvlist_get_descriptor_array #define nvlist_get_number illumos_nvlist_get_number +#define nvlist_get_number_array illumos_nvlist_get_number_array #define nvlist_get_nvlist illumos_nvlist_get_nvlist #define nvlist_get_nvpair illumos_nvlist_get_nvpair +#define nvlist_get_nvpair_parent illumos_nvlist_get_nvpair_parent +#define nvlist_get_pararr illumos_nvlist_get_pararr +#define nvlist_get_parent illumos_nvlist_get_parent #define nvlist_get_string illumos_nvlist_get_string -#define nvlist_getf_binary illumos_nvlist_getf_binary -#define nvlist_getf_bool illumos_nvlist_getf_bool -#define nvlist_getf_descriptor illumos_nvlist_getf_descriptor -#define nvlist_getf_number illumos_nvlist_getf_number -#define nvlist_getf_nvlist illumos_nvlist_getf_nvlist -#define nvlist_getf_string illumos_nvlist_getf_string -#define nvlist_getv_binary illumos_nvlist_getv_binary -#define nvlist_getv_bool illumos_nvlist_getv_bool -#define nvlist_getv_descriptor illumos_nvlist_getv_descriptor -#define nvlist_getv_number illumos_nvlist_getv_number -#define nvlist_getv_nvlist illumos_nvlist_getv_nvlist -#define nvlist_getv_string illumos_nvlist_getv_string +#define nvlist_in_array illumos_nvlist_in_array #define nvlist_move_binary illumos_nvlist_move_binary +#define nvlist_move_bool_array illumos_nvlist_move_bool_array #define nvlist_move_descriptor illumos_nvlist_move_descriptor +#define nvlist_move_descriptor_array illumos_nvlist_move_descriptor_array +#define nvlist_move_number_array illumos_nvlist_move_number_array #define nvlist_move_nvlist illumos_nvlist_move_nvlist +#define nvlist_move_nvlist_array illumos_nvlist_move_nvlist_array #define nvlist_move_nvpair illumos_nvlist_move_nvpair #define nvlist_move_string illumos_nvlist_move_string -#define nvlist_movef_binary illumos_nvlist_movef_binary -#define nvlist_movef_descriptor illumos_nvlist_movef_descriptor -#define nvlist_movef_nvlist illumos_nvlist_movef_nvlist -#define nvlist_movef_string illumos_nvlist_movef_string -#define nvlist_movev_binary illumos_nvlist_movev_binary -#define nvlist_movev_descriptor illumos_nvlist_movev_descriptor -#define nvlist_movev_nvlist illumos_nvlist_movev_nvlist -#define nvlist_movev_string illumos_nvlist_movev_string +#define nvlist_move_string_array illumos_nvlist_move_string_array #define nvlist_ndescriptors illumos_nvlist_ndescriptors #define nvlist_next illumos_nvlist_next #define nvlist_next_nvpair illumos_nvlist_next_nvpair @@ -168,86 +127,82 @@ #define nvlist_prev_nvpair illumos_nvlist_prev_nvpair #define nvlist_recv illumos_nvlist_recv #define nvlist_remove_nvpair illumos_nvlist_remove_nvpair -#define nvlist_report_missing illumos_nvlist_report_missing #define nvlist_send illumos_nvlist_send +#define nvlist_set_array_next illumos_nvlist_set_array_next #define nvlist_set_error illumos_nvlist_set_error +#define nvlist_set_flags illumos_nvlist_set_flags +#define nvlist_set_parent illumos_nvlist_set_parent #define nvlist_size illumos_nvlist_size #define nvlist_take_binary illumos_nvlist_take_binary #define nvlist_take_bool illumos_nvlist_take_bool +#define nvlist_take_bool_array illumos_nvlist_take_bool_array #define nvlist_take_descriptor illumos_nvlist_take_descriptor +#define nvlist_take_descriptor_array illumos_nvlist_take_descriptor_array #define nvlist_take_number illumos_nvlist_take_number +#define nvlist_take_number_array illumos_nvlist_take_number_array #define nvlist_take_nvlist illumos_nvlist_take_nvlist +#define nvlist_take_nvlist_array illumos_nvlist_take_nvlist_array #define nvlist_take_nvpair illumos_nvlist_take_nvpair #define nvlist_take_string illumos_nvlist_take_string -#define nvlist_takef_binary illumos_nvlist_takef_binary -#define nvlist_takef_bool illumos_nvlist_takef_bool -#define nvlist_takef_descriptor illumos_nvlist_takef_descriptor -#define nvlist_takef_number illumos_nvlist_takef_number -#define nvlist_takef_nvlist illumos_nvlist_takef_nvlist -#define nvlist_takef_string illumos_nvlist_takef_string -#define nvlist_takev_binary illumos_nvlist_takev_binary -#define nvlist_takev_bool illumos_nvlist_takev_bool -#define nvlist_takev_descriptor illumos_nvlist_takev_descriptor -#define nvlist_takev_number illumos_nvlist_takev_number -#define nvlist_takev_nvlist illumos_nvlist_takev_nvlist -#define nvlist_takev_string illumos_nvlist_takev_string +#define nvlist_take_string_array illumos_nvlist_take_string_array #define nvlist_unpack illumos_nvlist_unpack +#define nvlist_unpack_header illumos_nvlist_unpack_header #define nvlist_xfer illumos_nvlist_xfer -#define nvlist_xpack illumos_nvlist_xpack -#define nvlist_xunpack illumos_nvlist_xunpack -#define nvpair_allocv illumos_nvpair_allocv #define nvpair_assert illumos_nvpair_assert #define nvpair_clone illumos_nvpair_clone #define nvpair_create_binary illumos_nvpair_create_binary #define nvpair_create_bool illumos_nvpair_create_bool +#define nvpair_create_bool_array illumos_nvpair_create_bool_array #define nvpair_create_descriptor illumos_nvpair_create_descriptor +#define nvpair_create_descriptor_array illumos_nvpair_create_descriptor_array #define nvpair_create_null illumos_nvpair_create_null #define nvpair_create_number illumos_nvpair_create_number +#define nvpair_create_number_array illumos_nvpair_create_number_array #define nvpair_create_nvlist illumos_nvpair_create_nvlist +#define nvpair_create_nvlist_array illumos_nvpair_create_nvlist_array #define nvpair_create_string illumos_nvpair_create_string +#define nvpair_create_string_array illumos_nvpair_create_string_array #define nvpair_create_stringf illumos_nvpair_create_stringf #define nvpair_create_stringv illumos_nvpair_create_stringv -#define nvpair_createf_binary illumos_nvpair_createf_binary -#define nvpair_createf_bool illumos_nvpair_createf_bool -#define nvpair_createf_descriptor illumos_nvpair_createf_descriptor -#define nvpair_createf_null illumos_nvpair_createf_null -#define nvpair_createf_number illumos_nvpair_createf_number -#define nvpair_createf_nvlist illumos_nvpair_createf_nvlist -#define nvpair_createf_string illumos_nvpair_createf_string -#define nvpair_createv_binary illumos_nvpair_createv_binary -#define nvpair_createv_bool illumos_nvpair_createv_bool -#define nvpair_createv_descriptor illumos_nvpair_createv_descriptor -#define nvpair_createv_null illumos_nvpair_createv_null -#define nvpair_createv_number illumos_nvpair_createv_number -#define nvpair_createv_nvlist illumos_nvpair_createv_nvlist -#define nvpair_createv_string illumos_nvpair_createv_string #define nvpair_free illumos_nvpair_free #define nvpair_free_structure illumos_nvpair_free_structure #define nvpair_get_binary illumos_nvpair_get_binary #define nvpair_get_bool illumos_nvpair_get_bool +#define nvpair_get_bool_array illumos_nvpair_get_bool_array #define nvpair_get_descriptor illumos_nvpair_get_descriptor +#define nvpair_get_descriptor_array illumos_nvpair_get_descriptor_array #define nvpair_get_number illumos_nvpair_get_number +#define nvpair_get_number_array illumos_nvpair_get_number_array #define nvpair_get_nvlist illumos_nvpair_get_nvlist #define nvpair_get_string illumos_nvpair_get_string #define nvpair_header_size illumos_nvpair_header_size +#define nvpair_init_datasize illumos_nvpair_init_datasize #define nvpair_insert illumos_nvpair_insert #define nvpair_move_binary illumos_nvpair_move_binary +#define nvpair_move_bool_array illumos_nvpair_move_bool_array #define nvpair_move_descriptor illumos_nvpair_move_descriptor +#define nvpair_move_descriptor_array illumos_nvpair_move_descriptor_array +#define nvpair_move_number_array illumos_nvpair_move_number_array #define nvpair_move_nvlist illumos_nvpair_move_nvlist +#define nvpair_move_nvlist_array illumos_nvpair_move_nvlist_array #define nvpair_move_string illumos_nvpair_move_string -#define nvpair_movef_binary illumos_nvpair_movef_binary -#define nvpair_movef_descriptor illumos_nvpair_movef_descriptor -#define nvpair_movef_nvlist illumos_nvpair_movef_nvlist -#define nvpair_movef_string illumos_nvpair_movef_string -#define nvpair_movev_binary illumos_nvpair_movev_binary -#define nvpair_movev_descriptor illumos_nvpair_movev_descriptor -#define nvpair_movev_nvlist illumos_nvpair_movev_nvlist -#define nvpair_movev_string illumos_nvpair_movev_string +#define nvpair_move_string_array illumos_nvpair_move_string_array #define nvpair_name illumos_nvpair_name #define nvpair_next illumos_nvpair_next #define nvpair_nvlist illumos_nvpair_nvlist -#define nvpair_pack illumos_nvpair_pack +#define nvpair_pack_binary illumos_nvpair_pack_binary +#define nvpair_pack_bool illumos_nvpair_pack_bool +#define nvpair_pack_bool_array illumos_nvpair_pack_bool_array #define nvpair_pack_descriptor illumos_nvpair_pack_descriptor +#define nvpair_pack_descriptor_array illumos_nvpair_pack_descriptor_array +#define nvpair_pack_header illumos_nvpair_pack_header +#define nvpair_pack_null illumos_nvpair_pack_null +#define nvpair_pack_number illumos_nvpair_pack_number +#define nvpair_pack_number_array illumos_nvpair_pack_number_array +#define nvpair_pack_nvlist_array_next illumos_nvpair_pack_nvlist_array_next +#define nvpair_pack_nvlist_up illumos_nvpair_pack_nvlist_up +#define nvpair_pack_string illumos_nvpair_pack_string +#define nvpair_pack_string_array illumos_nvpair_pack_string_array #define nvpair_prev illumos_nvpair_prev #define nvpair_remove illumos_nvpair_remove #define nvpair_size illumos_nvpair_size @@ -254,7 +209,19 @@ #define nvpair_type illumos_nvpair_type #define nvpair_type_string illumos_nvpair_type_string #define nvpair_unpack illumos_nvpair_unpack +#define nvpair_unpack_binary illumos_nvpair_unpack_binary +#define nvpair_unpack_bool illumos_nvpair_unpack_bool +#define nvpair_unpack_bool_array illumos_nvpair_unpack_bool_array #define nvpair_unpack_descriptor illumos_nvpair_unpack_descriptor +#define nvpair_unpack_descriptor_array illumos_nvpair_unpack_descriptor_array +#define nvpair_unpack_header illumos_nvpair_unpack_header +#define nvpair_unpack_null illumos_nvpair_unpack_null +#define nvpair_unpack_number illumos_nvpair_unpack_number +#define nvpair_unpack_number_array illumos_nvpair_unpack_number_array +#define nvpair_unpack_nvlist illumos_nvpair_unpack_nvlist +#define nvpair_unpack_nvlist_array illumos_nvpair_unpack_nvlist_array +#define nvpair_unpack_string illumos_nvpair_unpack_string +#define nvpair_unpack_string_array illumos_nvpair_unpack_string_array #endif /* _KERNEL */ Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -476,6 +476,9 @@ contrib/libfdt/fdt_strerror.c optional fdt contrib/libfdt/fdt_sw.c optional fdt contrib/libfdt/fdt_wip.c optional fdt +contrib/libnv/dnvlist.c standard +contrib/libnv/nvlist.c standard +contrib/libnv/nvpair.c standard contrib/ngatm/netnatm/api/cc_conn.c optional ngatm_ccatm \ compile-with "${NORMAL_C_NOWERROR} -I$S/contrib/ngatm" contrib/ngatm/netnatm/api/cc_data.c optional ngatm_ccatm \ @@ -2003,6 +2006,9 @@ dev/pci/isa_pci.c optional pci isa dev/pci/pci.c optional pci dev/pci/pci_if.m standard +dev/pci/pci_iov.c optional pci pci_iov +dev/pci/pci_iov_if.m standard +dev/pci/pci_iov_schema.c optional pci pci_iov dev/pci/pci_pci.c optional pci dev/pci/pci_subr.c optional pci dev/pci/pci_user.c optional pci @@ -3076,7 +3082,6 @@ kern/subr_counter.c standard kern/subr_devstat.c standard kern/subr_disk.c standard -kern/subr_dnvlist.c standard kern/subr_eventhandler.c standard kern/subr_fattime.c standard kern/subr_firmware.c optional firmware @@ -3090,8 +3095,6 @@ kern/subr_mchain.c optional libmchain kern/subr_module.c standard kern/subr_msgbuf.c standard -kern/subr_nvlist.c standard -kern/subr_nvpair.c standard kern/subr_param.c standard kern/subr_pcpu.c standard kern/subr_pctrie.c standard Index: sys/conf/kmod.mk =================================================================== --- sys/conf/kmod.mk +++ sys/conf/kmod.mk @@ -357,7 +357,7 @@ dev/mmc/mmcbr_if.m dev/mmc/mmcbus_if.m \ dev/mii/miibus_if.m dev/mvs/mvs_if.m dev/ofw/ofw_bus_if.m \ dev/pccard/card_if.m dev/pccard/power_if.m dev/pci/pci_if.m \ - dev/pci/pcib_if.m dev/ppbus/ppbus_if.m \ + dev/pci/pci_iov_if.m dev/pci/pcib_if.m dev/ppbus/ppbus_if.m \ dev/sdhci/sdhci_if.m dev/smbus/smbus_if.m dev/spibus/spibus_if.m \ dev/sound/pci/hda/hdac_if.m \ dev/sound/pcm/ac97_if.m dev/sound/pcm/channel_if.m \ Index: sys/conf/options =================================================================== --- sys/conf/options +++ sys/conf/options @@ -166,6 +166,7 @@ NSWBUF_MIN opt_swap.h MBUF_PACKET_ZONE_DISABLE opt_global.h PANIC_REBOOT_WAIT_TIME opt_panic.h +PCI_IOV opt_global.h PPC_DEBUG opt_ppc.h PPC_PROBE_CHIPSET opt_ppc.h PPS_SYNC opt_ntp.h Index: sys/contrib/libnv/dnvlist.c =================================================================== --- sys/contrib/libnv/dnvlist.c +++ sys/contrib/libnv/dnvlist.c @@ -0,0 +1,128 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#ifdef _KERNEL + +#include +#include +#include +#include +#include + +#include + +#else +#include +#include +#include +#include +#endif + +#include +#include + +#include "nv_impl.h" + +#define DNVLIST_GET(ftype, type) \ +ftype \ +dnvlist_get_##type(const nvlist_t *nvl, const char *name, ftype defval) \ +{ \ + \ + if (nvlist_exists_##type(nvl, name)) \ + return (nvlist_get_##type(nvl, name)); \ + else \ + return (defval); \ +} + +DNVLIST_GET(bool, bool) +DNVLIST_GET(uint64_t, number) +DNVLIST_GET(const char *, string) +DNVLIST_GET(const nvlist_t *, nvlist) +#ifndef _KERNEL +DNVLIST_GET(int, descriptor) +#endif + +#undef DNVLIST_GET + +const void * +dnvlist_get_binary(const nvlist_t *nvl, const char *name, size_t *sizep, + const void *defval, size_t defsize) +{ + const void *value; + + if (nvlist_exists_binary(nvl, name)) + value = nvlist_get_binary(nvl, name, sizep); + else { + if (sizep != NULL) + *sizep = defsize; + value = defval; + } + return (value); +} + +#define DNVLIST_TAKE(ftype, type) \ +ftype \ +dnvlist_take_##type(nvlist_t *nvl, const char *name, ftype defval) \ +{ \ + \ + if (nvlist_exists_##type(nvl, name)) \ + return (nvlist_take_##type(nvl, name)); \ + else \ + return (defval); \ +} + +DNVLIST_TAKE(bool, bool) +DNVLIST_TAKE(uint64_t, number) +DNVLIST_TAKE(char *, string) +DNVLIST_TAKE(nvlist_t *, nvlist) +#ifndef _KERNEL +DNVLIST_TAKE(int, descriptor) +#endif + +#undef DNVLIST_TAKE + +void * +dnvlist_take_binary(nvlist_t *nvl, const char *name, size_t *sizep, + void *defval, size_t defsize) +{ + void *value; + + if (nvlist_exists_binary(nvl, name)) + value = nvlist_take_binary(nvl, name, sizep); + else { + if (sizep != NULL) + *sizep = defsize; + value = defval; + } + return (value); +} + Index: sys/contrib/libnv/nv_impl.h =================================================================== --- sys/contrib/libnv/nv_impl.h +++ sys/contrib/libnv/nv_impl.h @@ -0,0 +1,158 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * Copyright (c) 2013-2015 Mariusz Zaborski + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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 _NV_IMPL_H_ +#define _NV_IMPL_H_ + +#ifndef _NVPAIR_T_DECLARED +#define _NVPAIR_T_DECLARED +struct nvpair; + +typedef struct nvpair nvpair_t; +#endif + +#define NV_TYPE_NVLIST_ARRAY_NEXT 254 +#define NV_TYPE_NVLIST_UP 255 + +#define NV_TYPE_FIRST NV_TYPE_NULL +#define NV_TYPE_LAST NV_TYPE_DESCRIPTOR_ARRAY + +#define NV_FLAG_BIG_ENDIAN 0x080 +#define NV_FLAG_IN_ARRAY 0x100 + +#ifdef _KERNEL +#define nv_malloc(size) malloc((size), M_NVLIST, M_WAITOK) +#define nv_calloc(n, size) malloc((n) * (size), M_NVLIST, \ + M_WAITOK | M_ZERO) +#define nv_realloc(buf, size) realloc((buf), (size), M_NVLIST, \ + M_WAITOK) +#define nv_free(buf) free((buf), M_NVLIST) +#define nv_strdup(buf) strdup((buf), M_NVLIST) +#define nv_vasprintf(ptr, ...) vasprintf(ptr, M_NVLIST, __VA_ARGS__) + +#define ERRNO_SET(var) do { } while (0) +#define ERRNO_SAVE() do { do { } while(0) +#define ERRNO_RESTORE() } while (0) + +#define ERRNO_OR_DEFAULT(default) (default) + +#else + +#define nv_malloc(size) malloc((size)) +#define nv_calloc(n, size) calloc((n), (size)) +#define nv_realloc(buf, size) realloc((buf), (size)) +#define nv_free(buf) free((buf)) +#define nv_strdup(buf) strdup((buf)) +#define nv_vasprintf(ptr, ...) vasprintf(ptr, __VA_ARGS__) + +#define ERRNO_SET(var) do { errno = (var); } while (0) +#define ERRNO_SAVE() do { \ + int _serrno; \ + \ + _serrno = errno + +#define ERRNO_RESTORE() errno = _serrno; \ + } while (0) + +#define ERRNO_OR_DEFAULT(default) (errno == 0 ? (default) : errno) + +#endif + +int *nvlist_descriptors(const nvlist_t *nvl, size_t *nitemsp); +size_t nvlist_ndescriptors(const nvlist_t *nvl); +void nvlist_set_flags(nvlist_t *nvl, int flags); + +nvpair_t *nvlist_first_nvpair(const nvlist_t *nvl); +nvpair_t *nvlist_next_nvpair(const nvlist_t *nvl, const nvpair_t *nvp); +nvpair_t *nvlist_prev_nvpair(const nvlist_t *nvl, const nvpair_t *nvp); + +void nvlist_add_nvpair(nvlist_t *nvl, const nvpair_t *nvp); + +bool nvlist_move_nvpair(nvlist_t *nvl, nvpair_t *nvp); + +void nvlist_set_parent(nvlist_t *nvl, nvpair_t *parent); +void nvlist_set_array_next(nvlist_t *nvl, nvpair_t *ele); + +const nvpair_t *nvlist_get_nvpair(const nvlist_t *nvl, const char *name); + +nvpair_t *nvlist_take_nvpair(nvlist_t *nvl, const char *name); + +/* Function removes the given nvpair from the nvlist. */ +void nvlist_remove_nvpair(nvlist_t *nvl, nvpair_t *nvp); + +void nvlist_free_nvpair(nvlist_t *nvl, nvpair_t *nvp); + +int nvpair_type(const nvpair_t *nvp); +const char *nvpair_name(const nvpair_t *nvp); + +nvpair_t *nvpair_clone(const nvpair_t *nvp); + +nvpair_t *nvpair_create_null(const char *name); +nvpair_t *nvpair_create_bool(const char *name, bool value); +nvpair_t *nvpair_create_number(const char *name, uint64_t value); +nvpair_t *nvpair_create_string(const char *name, const char *value); +nvpair_t *nvpair_create_stringf(const char *name, const char *valuefmt, ...) __printflike(2, 3); +nvpair_t *nvpair_create_stringv(const char *name, const char *valuefmt, va_list valueap) __printflike(2, 0); +nvpair_t *nvpair_create_nvlist(const char *name, const nvlist_t *value); +nvpair_t *nvpair_create_descriptor(const char *name, int value); +nvpair_t *nvpair_create_binary(const char *name, const void *value, size_t size); +nvpair_t *nvpair_create_bool_array(const char *name, const bool *value, size_t nitems); +nvpair_t *nvpair_create_number_array(const char *name, const uint64_t *value, size_t nitems); +nvpair_t *nvpair_create_string_array(const char *name, const char * const *value, size_t nitems); +nvpair_t *nvpair_create_nvlist_array(const char *name, const nvlist_t * const *value, size_t nitems); +nvpair_t *nvpair_create_descriptor_array(const char *name, const int *value, size_t nitems); + +nvpair_t *nvpair_move_string(const char *name, char *value); +nvpair_t *nvpair_move_nvlist(const char *name, nvlist_t *value); +nvpair_t *nvpair_move_descriptor(const char *name, int value); +nvpair_t *nvpair_move_binary(const char *name, void *value, size_t size); +nvpair_t *nvpair_move_bool_array(const char *name, bool *value, size_t nitems); +nvpair_t *nvpair_move_nvlist_array(const char *name, nvlist_t **value, size_t nitems); +nvpair_t *nvpair_move_descriptor_array(const char *name, int *value, size_t nitems); +nvpair_t *nvpair_move_number_array(const char *name, uint64_t *value, size_t nitems); +nvpair_t *nvpair_move_string_array(const char *name, char **value, size_t nitems); + +bool nvpair_get_bool(const nvpair_t *nvp); +uint64_t nvpair_get_number(const nvpair_t *nvp); +const char *nvpair_get_string(const nvpair_t *nvp); +const nvlist_t *nvpair_get_nvlist(const nvpair_t *nvp); +int nvpair_get_descriptor(const nvpair_t *nvp); +const void *nvpair_get_binary(const nvpair_t *nvp, size_t *sizep); +const bool *nvpair_get_bool_array(const nvpair_t *nvp, size_t *nitemsp); +const uint64_t *nvpair_get_number_array(const nvpair_t *nvp, size_t *nitemsp); +const char * const *nvpair_get_string_array(const nvpair_t *nvp, size_t *nitemsp); +const nvlist_t * const *nvpair_get_nvlist_array(const nvpair_t *nvp, size_t *nitemsp); +const int *nvpair_get_descriptor_array(const nvpair_t *nvp, size_t *nitemsp); + +void nvpair_free(nvpair_t *nvp); + +#endif /* !_NV_IMPL_H_ */ Index: sys/contrib/libnv/nvlist.c =================================================================== --- sys/contrib/libnv/nvlist.c +++ sys/contrib/libnv/nvlist.c @@ -0,0 +1,2024 @@ +/*- + * Copyright (c) 2009-2013 The FreeBSD Foundation + * Copyright (c) 2013-2015 Mariusz Zaborski + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#ifdef _KERNEL + +#include +#include +#include +#include +#include + +#include + +#else +#include + +#include +#include +#include +#include +#define _WITH_DPRINTF +#include +#include +#include +#include + +#include "msgio.h" +#endif + +#ifdef HAVE_PJDLOG +#include +#endif + +#include + +#include "nv_impl.h" +#include "nvlist_impl.h" +#include "nvpair_impl.h" + +#ifndef HAVE_PJDLOG +#ifdef _KERNEL +#define PJDLOG_ASSERT(...) MPASS(__VA_ARGS__) +#define PJDLOG_RASSERT(expr, ...) KASSERT(expr, (__VA_ARGS__)) +#define PJDLOG_ABORT(...) panic(__VA_ARGS__) +#else +#include +#define PJDLOG_ASSERT(...) assert(__VA_ARGS__) +#define PJDLOG_RASSERT(expr, ...) assert(expr) +#define PJDLOG_ABORT(...) do { \ + fprintf(stderr, "%s:%u: ", __FILE__, __LINE__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + abort(); \ +} while (0) +#endif +#endif + +#define NV_FLAG_PRIVATE_MASK (NV_FLAG_BIG_ENDIAN | NV_FLAG_IN_ARRAY) +#define NV_FLAG_PUBLIC_MASK (NV_FLAG_IGNORE_CASE | NV_FLAG_NO_UNIQUE) +#define NV_FLAG_ALL_MASK (NV_FLAG_PRIVATE_MASK | NV_FLAG_PUBLIC_MASK) + +#define NVLIST_MAGIC 0x6e766c /* "nvl" */ +struct nvlist { + int nvl_magic; + int nvl_error; + int nvl_flags; + nvpair_t *nvl_parent; + nvpair_t *nvl_array_next; + struct nvl_head nvl_head; +}; + +#define NVLIST_ASSERT(nvl) do { \ + PJDLOG_ASSERT((nvl) != NULL); \ + PJDLOG_ASSERT((nvl)->nvl_magic == NVLIST_MAGIC); \ +} while (0) + +#ifdef _KERNEL +MALLOC_DEFINE(M_NVLIST, "nvlist", "kernel nvlist"); +#endif + +#define NVPAIR_ASSERT(nvp) nvpair_assert(nvp) + +#define NVLIST_HEADER_MAGIC 0x6c +#define NVLIST_HEADER_VERSION 0x00 +struct nvlist_header { + uint8_t nvlh_magic; + uint8_t nvlh_version; + uint8_t nvlh_flags; + uint64_t nvlh_descriptors; + uint64_t nvlh_size; +} __packed; + +nvlist_t * +nvlist_create(int flags) +{ + nvlist_t *nvl; + + PJDLOG_ASSERT((flags & ~(NV_FLAG_PUBLIC_MASK)) == 0); + + nvl = nv_malloc(sizeof(*nvl)); + if (nvl == NULL) + return (NULL); + nvl->nvl_error = 0; + nvl->nvl_flags = flags; + nvl->nvl_parent = NULL; + nvl->nvl_array_next = NULL; + TAILQ_INIT(&nvl->nvl_head); + nvl->nvl_magic = NVLIST_MAGIC; + + return (nvl); +} + +void +nvlist_destroy(nvlist_t *nvl) +{ + nvpair_t *nvp; + + if (nvl == NULL) + return; + + ERRNO_SAVE(); + + NVLIST_ASSERT(nvl); + + while ((nvp = nvlist_first_nvpair(nvl)) != NULL) { + nvlist_remove_nvpair(nvl, nvp); + nvpair_free(nvp); + } + if (nvl->nvl_array_next != NULL) + nvpair_free_structure(nvl->nvl_array_next); + nvl->nvl_array_next = NULL; + nvl->nvl_parent = NULL; + nvl->nvl_magic = 0; + nv_free(nvl); + + ERRNO_RESTORE(); +} + +void +nvlist_set_error(nvlist_t *nvl, int error) +{ + + PJDLOG_ASSERT(error != 0); + + /* + * Check for error != 0 so that we don't do the wrong thing if somebody + * tries to abuse this API when asserts are disabled. + */ + if (nvl != NULL && error != 0 && nvl->nvl_error == 0) + nvl->nvl_error = error; +} + +int +nvlist_error(const nvlist_t *nvl) +{ + + if (nvl == NULL) + return (ENOMEM); + + NVLIST_ASSERT(nvl); + + return (nvl->nvl_error); +} + +nvpair_t * +nvlist_get_nvpair_parent(const nvlist_t *nvl) +{ + + NVLIST_ASSERT(nvl); + + return (nvl->nvl_parent); +} + +const nvlist_t * +nvlist_get_parent(const nvlist_t *nvl, void **cookiep) +{ + nvpair_t *nvp; + + NVLIST_ASSERT(nvl); + + nvp = nvl->nvl_parent; + if (cookiep != NULL) + *cookiep = nvp; + if (nvp == NULL) + return (NULL); + + return (nvpair_nvlist(nvp)); +} + +void +nvlist_set_parent(nvlist_t *nvl, nvpair_t *parent) +{ + + NVLIST_ASSERT(nvl); + + nvl->nvl_parent = parent; +} + +void +nvlist_set_array_next(nvlist_t *nvl, nvpair_t *ele) +{ + + NVLIST_ASSERT(nvl); + + if (ele != NULL) + nvl->nvl_flags |= NV_FLAG_IN_ARRAY; + else + nvl->nvl_flags &= ~NV_FLAG_IN_ARRAY; + + nvl->nvl_array_next = ele; +} + +bool +nvlist_in_array(const nvlist_t *nvl) +{ + + NVLIST_ASSERT(nvl); + + return ((nvl->nvl_flags & NV_FLAG_IN_ARRAY) != 0); +} + +const nvlist_t * +nvlist_get_array_next(const nvlist_t *nvl) +{ + nvpair_t *nvp; + + NVLIST_ASSERT(nvl); + + nvp = nvl->nvl_array_next; + if (nvp == NULL) + return (NULL); + + return (nvpair_get_nvlist(nvp)); +} + +const nvlist_t * +nvlist_get_pararr(const nvlist_t *nvl, void **cookiep) +{ + const nvlist_t *ret; + + ret = nvlist_get_array_next(nvl); + if (ret != NULL) { + if (cookiep != NULL) + *cookiep = NULL; + return (ret); + } + + ret = nvlist_get_parent(nvl, cookiep); + return (ret); +} + +bool +nvlist_empty(const nvlist_t *nvl) +{ + + NVLIST_ASSERT(nvl); + PJDLOG_ASSERT(nvl->nvl_error == 0); + + return (nvlist_first_nvpair(nvl) == NULL); +} + +int +nvlist_flags(const nvlist_t *nvl) +{ + + NVLIST_ASSERT(nvl); + PJDLOG_ASSERT(nvl->nvl_error == 0); + + return (nvl->nvl_flags & NV_FLAG_PUBLIC_MASK); +} + +void +nvlist_set_flags(nvlist_t *nvl, int flags) +{ + + NVLIST_ASSERT(nvl); + PJDLOG_ASSERT(nvl->nvl_error == 0); + + nvl->nvl_flags = flags; +} + +static void +nvlist_report_missing(int type, const char *name) +{ + + PJDLOG_ABORT("Element '%s' of type %s doesn't exist.", + name, nvpair_type_string(type)); +} + +static nvpair_t * +nvlist_find(const nvlist_t *nvl, int type, const char *name) +{ + nvpair_t *nvp; + + NVLIST_ASSERT(nvl); + PJDLOG_ASSERT(nvl->nvl_error == 0); + PJDLOG_ASSERT(type == NV_TYPE_NONE || + (type >= NV_TYPE_FIRST && type <= NV_TYPE_LAST)); + + for (nvp = nvlist_first_nvpair(nvl); nvp != NULL; + nvp = nvlist_next_nvpair(nvl, nvp)) { + if (type != NV_TYPE_NONE && nvpair_type(nvp) != type) + continue; + if ((nvl->nvl_flags & NV_FLAG_IGNORE_CASE) != 0) { + if (strcasecmp(nvpair_name(nvp), name) != 0) + continue; + } else { + if (strcmp(nvpair_name(nvp), name) != 0) + continue; + } + break; + } + + if (nvp == NULL) + ERRNO_SET(ENOENT); + + return (nvp); +} + +bool +nvlist_exists_type(const nvlist_t *nvl, const char *name, int type) +{ + + NVLIST_ASSERT(nvl); + PJDLOG_ASSERT(nvl->nvl_error == 0); + PJDLOG_ASSERT(type == NV_TYPE_NONE || + (type >= NV_TYPE_FIRST && type <= NV_TYPE_LAST)); + + return (nvlist_find(nvl, type, name) != NULL); +} + +void +nvlist_free_type(nvlist_t *nvl, const char *name, int type) +{ + nvpair_t *nvp; + + NVLIST_ASSERT(nvl); + PJDLOG_ASSERT(nvl->nvl_error == 0); + PJDLOG_ASSERT(type == NV_TYPE_NONE || + (type >= NV_TYPE_FIRST && type <= NV_TYPE_LAST)); + + nvp = nvlist_find(nvl, type, name); + if (nvp != NULL) + nvlist_free_nvpair(nvl, nvp); + else + nvlist_report_missing(type, name); +} + +nvlist_t * +nvlist_clone(const nvlist_t *nvl) +{ + nvlist_t *newnvl; + nvpair_t *nvp, *newnvp; + + NVLIST_ASSERT(nvl); + + if (nvl->nvl_error != 0) { + ERRNO_SET(nvl->nvl_error); + return (NULL); + } + + newnvl = nvlist_create(nvl->nvl_flags & NV_FLAG_PUBLIC_MASK); + for (nvp = nvlist_first_nvpair(nvl); nvp != NULL; + nvp = nvlist_next_nvpair(nvl, nvp)) { + newnvp = nvpair_clone(nvp); + if (newnvp == NULL) + break; + (void)nvlist_move_nvpair(newnvl, newnvp); + } + if (nvp != NULL) { + nvlist_destroy(newnvl); + return (NULL); + } + return (newnvl); +} + +#ifndef _KERNEL +static bool +nvlist_dump_error_check(const nvlist_t *nvl, int fd, int level) +{ + + if (nvlist_error(nvl) != 0) { + dprintf(fd, "%*serror: %d\n", level * 4, "", + nvlist_error(nvl)); + return (true); + } + + return (false); +} + +/* + * Dump content of nvlist. + */ +void +nvlist_dump(const nvlist_t *nvl, int fd) +{ + const nvlist_t *tmpnvl; + nvpair_t *nvp, *tmpnvp; + void *cookie; + int level; + + level = 0; + if (nvlist_dump_error_check(nvl, fd, level)) + return; + + nvp = nvlist_first_nvpair(nvl); + while (nvp != NULL) { + dprintf(fd, "%*s%s (%s):", level * 4, "", nvpair_name(nvp), + nvpair_type_string(nvpair_type(nvp))); + switch (nvpair_type(nvp)) { + case NV_TYPE_NULL: + dprintf(fd, " null\n"); + break; + case NV_TYPE_BOOL: + dprintf(fd, " %s\n", nvpair_get_bool(nvp) ? + "TRUE" : "FALSE"); + break; + case NV_TYPE_NUMBER: + dprintf(fd, " %ju (%jd) (0x%jx)\n", + (uintmax_t)nvpair_get_number(nvp), + (intmax_t)nvpair_get_number(nvp), + (uintmax_t)nvpair_get_number(nvp)); + break; + case NV_TYPE_STRING: + dprintf(fd, " [%s]\n", nvpair_get_string(nvp)); + break; + case NV_TYPE_NVLIST: + dprintf(fd, "\n"); + tmpnvl = nvpair_get_nvlist(nvp); + if (nvlist_dump_error_check(tmpnvl, fd, level + 1)) + break; + tmpnvp = nvlist_first_nvpair(tmpnvl); + if (tmpnvp != NULL) { + nvl = tmpnvl; + nvp = tmpnvp; + level++; + continue; + } + break; + case NV_TYPE_DESCRIPTOR: + dprintf(fd, " %d\n", nvpair_get_descriptor(nvp)); + break; + case NV_TYPE_BINARY: + { + const unsigned char *binary; + unsigned int ii; + size_t size; + + binary = nvpair_get_binary(nvp, &size); + dprintf(fd, " %zu ", size); + for (ii = 0; ii < size; ii++) + dprintf(fd, "%02hhx", binary[ii]); + dprintf(fd, "\n"); + break; + } + case NV_TYPE_BOOL_ARRAY: + { + const bool *value; + unsigned int ii; + size_t nitems; + + value = nvpair_get_bool_array(nvp, &nitems); + dprintf(fd, " [ "); + for (ii = 0; ii < nitems; ii++) { + dprintf(fd, "%s", value[ii] ? "TRUE" : "FALSE"); + if (ii != nitems - 1) + dprintf(fd, ", "); + } + dprintf(fd, " ]\n"); + break; + } + case NV_TYPE_STRING_ARRAY: + { + const char * const *value; + unsigned int ii; + size_t nitems; + + value = nvpair_get_string_array(nvp, &nitems); + dprintf(fd, " [ "); + for (ii = 0; ii < nitems; ii++) { + if (value[ii] == NULL) + dprintf(fd, "NULL"); + else + dprintf(fd, "\"%s\"", value[ii]); + if (ii != nitems - 1) + dprintf(fd, ", "); + } + dprintf(fd, " ]\n"); + break; + } + case NV_TYPE_NUMBER_ARRAY: + { + const uint64_t *value; + unsigned int ii; + size_t nitems; + + value = nvpair_get_number_array(nvp, &nitems); + dprintf(fd, " [ "); + for (ii = 0; ii < nitems; ii++) { + dprintf(fd, "%ju (%jd) (0x%jx)", + value[ii], value[ii], value[ii]); + if (ii != nitems - 1) + dprintf(fd, ", "); + } + dprintf(fd, " ]\n"); + break; + } + case NV_TYPE_DESCRIPTOR_ARRAY: + { + const int *value; + unsigned int ii; + size_t nitems; + + value = nvpair_get_descriptor_array(nvp, &nitems); + dprintf(fd, " [ "); + for (ii = 0; ii < nitems; ii++) { + dprintf(fd, "%d", value[ii]); + if (ii != nitems - 1) + dprintf(fd, ", "); + } + dprintf(fd, " ]\n"); + break; + } + case NV_TYPE_NVLIST_ARRAY: + { + const nvlist_t * const *value; + unsigned int ii; + size_t nitems; + + value = nvpair_get_nvlist_array(nvp, &nitems); + dprintf(fd, " %zu\n", nitems); + tmpnvl = NULL; + tmpnvp = NULL; + for (ii = 0; ii < nitems; ii++) { + if (nvlist_dump_error_check(value[ii], fd, + level + 1)) { + break; + } + + if (tmpnvl == NULL) { + tmpnvp = nvlist_first_nvpair(value[ii]); + if (tmpnvp != NULL) { + tmpnvl = value[ii]; + } else { + dprintf(fd, "%*s,\n", + (level + 1) * 4, ""); + } + } + } + if (tmpnvp != NULL) { + nvl = tmpnvl; + nvp = tmpnvp; + level++; + continue; + } + break; + } + default: + PJDLOG_ABORT("Unknown type: %d.", nvpair_type(nvp)); + } + + while ((nvp = nvlist_next_nvpair(nvl, nvp)) == NULL) { + do { + cookie = NULL; + if (nvlist_in_array(nvl)) + dprintf(fd, "%*s,\n", level * 4, ""); + nvl = nvlist_get_pararr(nvl, &cookie); + if (nvl == NULL) + return; + if (nvlist_in_array(nvl) && cookie == NULL) { + nvp = nvlist_first_nvpair(nvl); + } else { + nvp = cookie; + level--; + } + } while (nvp == NULL); + if (nvlist_in_array(nvl) && cookie == NULL) + break; + } + } +} + +void +nvlist_fdump(const nvlist_t *nvl, FILE *fp) +{ + + fflush(fp); + nvlist_dump(nvl, fileno(fp)); +} +#endif + +/* + * The function obtains size of the nvlist after nvlist_pack(). + */ +size_t +nvlist_size(const nvlist_t *nvl) +{ + const nvlist_t *tmpnvl; + const nvlist_t * const *nvlarray; + const nvpair_t *nvp, *tmpnvp; + void *cookie; + size_t size, nitems; + unsigned int ii; + + NVLIST_ASSERT(nvl); + PJDLOG_ASSERT(nvl->nvl_error == 0); + + size = sizeof(struct nvlist_header); + nvp = nvlist_first_nvpair(nvl); + while (nvp != NULL) { + size += nvpair_header_size(); + size += strlen(nvpair_name(nvp)) + 1; + if (nvpair_type(nvp) == NV_TYPE_NVLIST) { + size += sizeof(struct nvlist_header); + size += nvpair_header_size() + 1; + tmpnvl = nvpair_get_nvlist(nvp); + PJDLOG_ASSERT(tmpnvl->nvl_error == 0); + tmpnvp = nvlist_first_nvpair(tmpnvl); + if (tmpnvp != NULL) { + nvl = tmpnvl; + nvp = tmpnvp; + continue; + } + } else if (nvpair_type(nvp) == NV_TYPE_NVLIST_ARRAY) { + nvlarray = nvpair_get_nvlist_array(nvp, &nitems); + PJDLOG_ASSERT(nitems > 0); + + size += (nvpair_header_size() + 1) * nitems; + size += sizeof(struct nvlist_header) * nitems; + + tmpnvl = NULL; + tmpnvp = NULL; + for (ii = 0; ii < nitems; ii++) { + PJDLOG_ASSERT(nvlarray[ii]->nvl_error == 0); + tmpnvp = nvlist_first_nvpair(nvlarray[ii]); + if (tmpnvp != NULL) { + tmpnvl = nvlarray[ii]; + break; + } + } + if (tmpnvp != NULL) { + nvp = tmpnvp; + nvl = tmpnvl; + continue; + } + + } else { + size += nvpair_size(nvp); + } + + while ((nvp = nvlist_next_nvpair(nvl, nvp)) == NULL) { + do { + cookie = NULL; + nvl = nvlist_get_pararr(nvl, &cookie); + if (nvl == NULL) + goto out; + if (nvlist_in_array(nvl) && cookie == NULL) { + nvp = nvlist_first_nvpair(nvl); + } else { + nvp = cookie; + } + } while (nvp == NULL); + if (nvlist_in_array(nvl) && cookie == NULL) + break; + } + } + +out: + return (size); +} + +#ifndef _KERNEL +static int * +nvlist_xdescriptors(const nvlist_t *nvl, int *descs) +{ + nvpair_t *nvp; + const char *name; + int type; + + NVLIST_ASSERT(nvl); + PJDLOG_ASSERT(nvl->nvl_error == 0); + + nvp = NULL; + do { + while ((name = nvlist_next(nvl, &type, (void**)&nvp)) != NULL) { + switch (type) { + case NV_TYPE_DESCRIPTOR: + *descs = nvpair_get_descriptor(nvp); + descs++; + break; + case NV_TYPE_DESCRIPTOR_ARRAY: + { + const int *value; + size_t nitems; + unsigned int ii; + + value = nvpair_get_descriptor_array(nvp, + &nitems); + for (ii = 0; ii < nitems; ii++) { + *descs = value[ii]; + descs++; + } + break; + } + case NV_TYPE_NVLIST: + nvl = nvpair_get_nvlist(nvp); + nvp = NULL; + break; + case NV_TYPE_NVLIST_ARRAY: + { + const nvlist_t * const *value; + size_t nitems; + + value = nvpair_get_nvlist_array(nvp, &nitems); + PJDLOG_ASSERT(value != NULL); + PJDLOG_ASSERT(nitems > 0); + + nvl = value[0]; + nvp = NULL; + break; + } + } + } + } while ((nvl = nvlist_get_pararr(nvl, (void**)&nvp)) != NULL); + + return (descs); +} +#endif + +#ifndef _KERNEL +int * +nvlist_descriptors(const nvlist_t *nvl, size_t *nitemsp) +{ + size_t nitems; + int *fds; + + nitems = nvlist_ndescriptors(nvl); + fds = nv_malloc(sizeof(fds[0]) * (nitems + 1)); + if (fds == NULL) + return (NULL); + if (nitems > 0) + nvlist_xdescriptors(nvl, fds); + fds[nitems] = -1; + if (nitemsp != NULL) + *nitemsp = nitems; + return (fds); +} +#endif + +size_t +nvlist_ndescriptors(const nvlist_t *nvl) +{ +#ifndef _KERNEL + nvpair_t *nvp; + const char *name; + size_t ndescs; + int type; + + NVLIST_ASSERT(nvl); + PJDLOG_ASSERT(nvl->nvl_error == 0); + + ndescs = 0; + nvp = NULL; + do { + while ((name = nvlist_next(nvl, &type, (void**)&nvp)) != NULL) { + switch (type) { + case NV_TYPE_DESCRIPTOR: + ndescs++; + break; + case NV_TYPE_NVLIST: + nvl = nvpair_get_nvlist(nvp); + nvp = NULL; + break; + case NV_TYPE_NVLIST_ARRAY: + { + const nvlist_t * const *value; + size_t nitems; + + value = nvpair_get_nvlist_array(nvp, &nitems); + PJDLOG_ASSERT(value != NULL); + PJDLOG_ASSERT(nitems > 0); + + nvl = value[0]; + nvp = NULL; + break; + } + case NV_TYPE_DESCRIPTOR_ARRAY: + { + size_t nitems; + + (void)nvpair_get_descriptor_array(nvp, + &nitems); + ndescs += nitems; + break; + } + } + } + } while ((nvl = nvlist_get_pararr(nvl, (void**)&nvp)) != NULL); + + return (ndescs); +#else + return (0); +#endif +} + +static unsigned char * +nvlist_pack_header(const nvlist_t *nvl, unsigned char *ptr, size_t *leftp) +{ + struct nvlist_header nvlhdr; + + NVLIST_ASSERT(nvl); + + nvlhdr.nvlh_magic = NVLIST_HEADER_MAGIC; + nvlhdr.nvlh_version = NVLIST_HEADER_VERSION; + nvlhdr.nvlh_flags = nvl->nvl_flags; +#if BYTE_ORDER == BIG_ENDIAN + nvlhdr.nvlh_flags |= NV_FLAG_BIG_ENDIAN; +#endif + nvlhdr.nvlh_descriptors = nvlist_ndescriptors(nvl); + nvlhdr.nvlh_size = *leftp - sizeof(nvlhdr); + PJDLOG_ASSERT(*leftp >= sizeof(nvlhdr)); + memcpy(ptr, &nvlhdr, sizeof(nvlhdr)); + ptr += sizeof(nvlhdr); + *leftp -= sizeof(nvlhdr); + + return (ptr); +} + +static void * +nvlist_xpack(const nvlist_t *nvl, int64_t *fdidxp, size_t *sizep) +{ + unsigned char *buf, *ptr; + size_t left, size; + const nvlist_t *tmpnvl; + nvpair_t *nvp, *tmpnvp; + void *cookie; + + NVLIST_ASSERT(nvl); + + if (nvl->nvl_error != 0) { + ERRNO_SET(nvl->nvl_error); + return (NULL); + } + + size = nvlist_size(nvl); + buf = nv_malloc(size); + if (buf == NULL) + return (NULL); + + ptr = buf; + left = size; + + ptr = nvlist_pack_header(nvl, ptr, &left); + + nvp = nvlist_first_nvpair(nvl); + while (nvp != NULL) { + NVPAIR_ASSERT(nvp); + + nvpair_init_datasize(nvp); + ptr = nvpair_pack_header(nvp, ptr, &left); + if (ptr == NULL) + goto fail; + switch (nvpair_type(nvp)) { + case NV_TYPE_NULL: + ptr = nvpair_pack_null(nvp, ptr, &left); + break; + case NV_TYPE_BOOL: + ptr = nvpair_pack_bool(nvp, ptr, &left); + break; + case NV_TYPE_NUMBER: + ptr = nvpair_pack_number(nvp, ptr, &left); + break; + case NV_TYPE_STRING: + ptr = nvpair_pack_string(nvp, ptr, &left); + break; + case NV_TYPE_NVLIST: + tmpnvl = nvpair_get_nvlist(nvp); + ptr = nvlist_pack_header(tmpnvl, ptr, &left); + if (ptr == NULL) + goto fail; + tmpnvp = nvlist_first_nvpair(tmpnvl); + if (tmpnvp != NULL) { + nvl = tmpnvl; + nvp = tmpnvp; + continue; + } + ptr = nvpair_pack_nvlist_up(ptr, &left); + break; +#ifndef _KERNEL + case NV_TYPE_DESCRIPTOR: + ptr = nvpair_pack_descriptor(nvp, ptr, fdidxp, &left); + break; + case NV_TYPE_DESCRIPTOR_ARRAY: + ptr = nvpair_pack_descriptor_array(nvp, ptr, fdidxp, + &left); + break; +#endif + case NV_TYPE_BINARY: + ptr = nvpair_pack_binary(nvp, ptr, &left); + break; + case NV_TYPE_BOOL_ARRAY: + ptr = nvpair_pack_bool_array(nvp, ptr, &left); + break; + case NV_TYPE_NUMBER_ARRAY: + ptr = nvpair_pack_number_array(nvp, ptr, &left); + break; + case NV_TYPE_STRING_ARRAY: + ptr = nvpair_pack_string_array(nvp, ptr, &left); + break; + case NV_TYPE_NVLIST_ARRAY: + { + const nvlist_t * const * value; + size_t nitems; + unsigned int ii; + + tmpnvl = NULL; + value = nvpair_get_nvlist_array(nvp, &nitems); + for (ii = 0; ii < nitems; ii++) { + ptr = nvlist_pack_header(value[ii], ptr, &left); + if (ptr == NULL) + goto out; + tmpnvp = nvlist_first_nvpair(value[ii]); + if (tmpnvp != NULL) { + tmpnvl = value[ii]; + break; + } + ptr = nvpair_pack_nvlist_array_next(ptr, &left); + if (ptr == NULL) + goto out; + } + if (tmpnvl != NULL) { + nvl = tmpnvl; + nvp = tmpnvp; + continue; + } + break; + } + default: + PJDLOG_ABORT("Invalid type (%d).", nvpair_type(nvp)); + } + if (ptr == NULL) + goto fail; + while ((nvp = nvlist_next_nvpair(nvl, nvp)) == NULL) { + do { + cookie = NULL; + if (nvlist_in_array(nvl)) { + ptr = nvpair_pack_nvlist_array_next(ptr, + &left); + if (ptr == NULL) + goto fail; + } + nvl = nvlist_get_pararr(nvl, &cookie); + if (nvl == NULL) + goto out; + if (nvlist_in_array(nvl) && cookie == NULL) { + nvp = nvlist_first_nvpair(nvl); + ptr = nvlist_pack_header(nvl, ptr, + &left); + if (ptr == NULL) + goto fail; + } else if (nvpair_type((nvpair_t *)cookie) != + NV_TYPE_NVLIST_ARRAY) { + ptr = nvpair_pack_nvlist_up(ptr, &left); + if (ptr == NULL) + goto fail; + nvp = cookie; + } else { + nvp = cookie; + } + } while (nvp == NULL); + if (nvlist_in_array(nvl) && cookie == NULL) + break; + } + } + +out: + if (sizep != NULL) + *sizep = size; + return (buf); +fail: + nv_free(buf); + return (NULL); +} + +void * +nvlist_pack(const nvlist_t *nvl, size_t *sizep) +{ + + NVLIST_ASSERT(nvl); + + if (nvl->nvl_error != 0) { + ERRNO_SET(nvl->nvl_error); + return (NULL); + } + + if (nvlist_ndescriptors(nvl) > 0) { + ERRNO_SET(EOPNOTSUPP); + return (NULL); + } + + return (nvlist_xpack(nvl, NULL, sizep)); +} + +static bool +nvlist_check_header(struct nvlist_header *nvlhdrp) +{ + + if (nvlhdrp->nvlh_magic != NVLIST_HEADER_MAGIC) { + ERRNO_SET(EINVAL); + return (false); + } + if ((nvlhdrp->nvlh_flags & ~NV_FLAG_ALL_MASK) != 0) { + ERRNO_SET(EINVAL); + return (false); + } +#if BYTE_ORDER == BIG_ENDIAN + if ((nvlhdrp->nvlh_flags & NV_FLAG_BIG_ENDIAN) == 0) { + nvlhdrp->nvlh_size = le64toh(nvlhdrp->nvlh_size); + nvlhdrp->nvlh_descriptors = le64toh(nvlhdrp->nvlh_descriptors); + } +#else + if ((nvlhdrp->nvlh_flags & NV_FLAG_BIG_ENDIAN) != 0) { + nvlhdrp->nvlh_size = be64toh(nvlhdrp->nvlh_size); + nvlhdrp->nvlh_descriptors = be64toh(nvlhdrp->nvlh_descriptors); + } +#endif + return (true); +} + +const unsigned char * +nvlist_unpack_header(nvlist_t *nvl, const unsigned char *ptr, size_t nfds, + bool *isbep, size_t *leftp) +{ + struct nvlist_header nvlhdr; + int inarrayf; + + if (*leftp < sizeof(nvlhdr)) + goto failed; + + memcpy(&nvlhdr, ptr, sizeof(nvlhdr)); + + if (!nvlist_check_header(&nvlhdr)) + goto failed; + + if (nvlhdr.nvlh_size != *leftp - sizeof(nvlhdr)) + goto failed; + + /* + * nvlh_descriptors might be smaller than nfds in embedded nvlists. + */ + if (nvlhdr.nvlh_descriptors > nfds) + goto failed; + + if ((nvlhdr.nvlh_flags & ~NV_FLAG_ALL_MASK) != 0) + goto failed; + + inarrayf = (nvl->nvl_flags & NV_FLAG_IN_ARRAY); + nvl->nvl_flags = (nvlhdr.nvlh_flags & NV_FLAG_PUBLIC_MASK) | inarrayf; + + ptr += sizeof(nvlhdr); + if (isbep != NULL) + *isbep = (((int)nvlhdr.nvlh_flags & NV_FLAG_BIG_ENDIAN) != 0); + *leftp -= sizeof(nvlhdr); + + return (ptr); +failed: + ERRNO_SET(EINVAL); + return (NULL); +} + +static nvlist_t * +nvlist_xunpack(const void *buf, size_t size, const int *fds, size_t nfds, + int flags) +{ + const unsigned char *ptr; + nvlist_t *nvl, *retnvl, *tmpnvl, *array; + nvpair_t *nvp; + size_t left; + bool isbe; + + PJDLOG_ASSERT((flags & ~(NV_FLAG_PUBLIC_MASK)) == 0); + + left = size; + ptr = buf; + + tmpnvl = array = NULL; + nvl = retnvl = nvlist_create(0); + if (nvl == NULL) + goto failed; + + ptr = nvlist_unpack_header(nvl, ptr, nfds, &isbe, &left); + if (ptr == NULL) + goto failed; + if (nvl->nvl_flags != flags) { + ERRNO_SET(EILSEQ); + goto failed; + } + + while (left > 0) { + ptr = nvpair_unpack(isbe, ptr, &left, &nvp); + if (ptr == NULL) + goto failed; + switch (nvpair_type(nvp)) { + case NV_TYPE_NULL: + ptr = nvpair_unpack_null(isbe, nvp, ptr, &left); + break; + case NV_TYPE_BOOL: + ptr = nvpair_unpack_bool(isbe, nvp, ptr, &left); + break; + case NV_TYPE_NUMBER: + ptr = nvpair_unpack_number(isbe, nvp, ptr, &left); + break; + case NV_TYPE_STRING: + ptr = nvpair_unpack_string(isbe, nvp, ptr, &left); + break; + case NV_TYPE_NVLIST: + ptr = nvpair_unpack_nvlist(isbe, nvp, ptr, &left, nfds, + &tmpnvl); + if (tmpnvl == NULL || ptr == NULL) + goto failed; + nvlist_set_parent(tmpnvl, nvp); + break; +#ifndef _KERNEL + case NV_TYPE_DESCRIPTOR: + ptr = nvpair_unpack_descriptor(isbe, nvp, ptr, &left, + fds, nfds); + break; + case NV_TYPE_DESCRIPTOR_ARRAY: + ptr = nvpair_unpack_descriptor_array(isbe, nvp, ptr, + &left, fds, nfds); + break; +#endif + case NV_TYPE_BINARY: + ptr = nvpair_unpack_binary(isbe, nvp, ptr, &left); + break; + case NV_TYPE_NVLIST_UP: + if (nvl->nvl_parent == NULL) + goto failed; + nvl = nvpair_nvlist(nvl->nvl_parent); + nvpair_free_structure(nvp); + continue; + case NV_TYPE_NVLIST_ARRAY_NEXT: + if (nvl->nvl_array_next == NULL) { + if (nvl->nvl_parent == NULL) + goto failed; + nvl = nvpair_nvlist(nvl->nvl_parent); + } else { + nvl = __DECONST(nvlist_t *, + nvlist_get_array_next(nvl)); + ptr = nvlist_unpack_header(nvl, ptr, nfds, + &isbe, &left); + if (ptr == NULL) + goto failed; + } + nvpair_free_structure(nvp); + continue; + case NV_TYPE_BOOL_ARRAY: + ptr = nvpair_unpack_bool_array(isbe, nvp, ptr, &left); + break; + case NV_TYPE_NUMBER_ARRAY: + ptr = nvpair_unpack_number_array(isbe, nvp, ptr, &left); + break; + case NV_TYPE_STRING_ARRAY: + ptr = nvpair_unpack_string_array(isbe, nvp, ptr, &left); + break; + case NV_TYPE_NVLIST_ARRAY: + ptr = nvpair_unpack_nvlist_array(isbe, nvp, ptr, &left, + &array); + if (ptr == NULL) + goto failed; + tmpnvl = array; + while (array != NULL) { + nvlist_set_parent(array, nvp); + array = __DECONST(nvlist_t *, + nvlist_get_array_next(array)); + } + ptr = nvlist_unpack_header(tmpnvl, ptr, nfds, &isbe, + &left); + break; + default: + PJDLOG_ABORT("Invalid type (%d).", nvpair_type(nvp)); + } + if (ptr == NULL) + goto failed; + if (!nvlist_move_nvpair(nvl, nvp)) + goto failed; + if (tmpnvl != NULL) { + nvl = tmpnvl; + tmpnvl = NULL; + } + } + + return (retnvl); +failed: + nvlist_destroy(retnvl); + return (NULL); +} + +nvlist_t * +nvlist_unpack(const void *buf, size_t size, int flags) +{ + + return (nvlist_xunpack(buf, size, NULL, 0, flags)); +} + +#ifndef _KERNEL +int +nvlist_send(int sock, const nvlist_t *nvl) +{ + size_t datasize, nfds; + int *fds; + void *data; + int64_t fdidx; + int ret; + + if (nvlist_error(nvl) != 0) { + ERRNO_SET(nvlist_error(nvl)); + return (-1); + } + + fds = nvlist_descriptors(nvl, &nfds); + if (fds == NULL) + return (-1); + + ret = -1; + data = NULL; + fdidx = 0; + + data = nvlist_xpack(nvl, &fdidx, &datasize); + if (data == NULL) + goto out; + + if (buf_send(sock, data, datasize) == -1) + goto out; + + if (nfds > 0) { + if (fd_send(sock, fds, nfds) == -1) + goto out; + } + + ret = 0; +out: + ERRNO_SAVE(); + nv_free(fds); + nv_free(data); + ERRNO_RESTORE(); + return (ret); +} + +nvlist_t * +nvlist_recv(int sock, int flags) +{ + struct nvlist_header nvlhdr; + nvlist_t *nvl, *ret; + unsigned char *buf; + size_t nfds, size, i; + int *fds; + + if (buf_recv(sock, &nvlhdr, sizeof(nvlhdr)) == -1) + return (NULL); + + if (!nvlist_check_header(&nvlhdr)) + return (NULL); + + nfds = (size_t)nvlhdr.nvlh_descriptors; + size = sizeof(nvlhdr) + (size_t)nvlhdr.nvlh_size; + + buf = nv_malloc(size); + if (buf == NULL) + return (NULL); + + memcpy(buf, &nvlhdr, sizeof(nvlhdr)); + + ret = NULL; + fds = NULL; + + if (buf_recv(sock, buf + sizeof(nvlhdr), size - sizeof(nvlhdr)) == -1) + goto out; + + if (nfds > 0) { + fds = nv_malloc(nfds * sizeof(fds[0])); + if (fds == NULL) + goto out; + if (fd_recv(sock, fds, nfds) == -1) + goto out; + } + + nvl = nvlist_xunpack(buf, size, fds, nfds, flags); + if (nvl == NULL) { + ERRNO_SAVE(); + for (i = 0; i < nfds; i++) + close(fds[i]); + ERRNO_RESTORE(); + goto out; + } + + ret = nvl; +out: + ERRNO_SAVE(); + nv_free(buf); + nv_free(fds); + ERRNO_RESTORE(); + + return (ret); +} + +nvlist_t * +nvlist_xfer(int sock, nvlist_t *nvl, int flags) +{ + + if (nvlist_send(sock, nvl) < 0) { + nvlist_destroy(nvl); + return (NULL); + } + nvlist_destroy(nvl); + return (nvlist_recv(sock, flags)); +} +#endif + +nvpair_t * +nvlist_first_nvpair(const nvlist_t *nvl) +{ + + NVLIST_ASSERT(nvl); + + return (TAILQ_FIRST(&nvl->nvl_head)); +} + +nvpair_t * +nvlist_next_nvpair(const nvlist_t *nvl, const nvpair_t *nvp) +{ + nvpair_t *retnvp; + + NVLIST_ASSERT(nvl); + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvpair_nvlist(nvp) == nvl); + + retnvp = nvpair_next(nvp); + PJDLOG_ASSERT(retnvp == NULL || nvpair_nvlist(retnvp) == nvl); + + return (retnvp); + +} + +nvpair_t * +nvlist_prev_nvpair(const nvlist_t *nvl, const nvpair_t *nvp) +{ + nvpair_t *retnvp; + + NVLIST_ASSERT(nvl); + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvpair_nvlist(nvp) == nvl); + + retnvp = nvpair_prev(nvp); + PJDLOG_ASSERT(nvpair_nvlist(retnvp) == nvl); + + return (retnvp); +} + +const char * +nvlist_next(const nvlist_t *nvl, int *typep, void **cookiep) +{ + nvpair_t *nvp; + + NVLIST_ASSERT(nvl); + + if (cookiep == NULL || *cookiep == NULL) + nvp = nvlist_first_nvpair(nvl); + else + nvp = nvlist_next_nvpair(nvl, *cookiep); + if (nvp == NULL) + return (NULL); + if (typep != NULL) + *typep = nvpair_type(nvp); + if (cookiep != NULL) + *cookiep = nvp; + return (nvpair_name(nvp)); +} + +bool +nvlist_exists(const nvlist_t *nvl, const char *name) +{ + + return (nvlist_find(nvl, NV_TYPE_NONE, name) != NULL); +} + +#define NVLIST_EXISTS(type, TYPE) \ +bool \ +nvlist_exists_##type(const nvlist_t *nvl, const char *name) \ +{ \ + \ + return (nvlist_find(nvl, NV_TYPE_##TYPE, name) != NULL); \ +} + +NVLIST_EXISTS(null, NULL) +NVLIST_EXISTS(bool, BOOL) +NVLIST_EXISTS(number, NUMBER) +NVLIST_EXISTS(string, STRING) +NVLIST_EXISTS(nvlist, NVLIST) +NVLIST_EXISTS(binary, BINARY) +NVLIST_EXISTS(bool_array, BOOL_ARRAY) +NVLIST_EXISTS(number_array, NUMBER_ARRAY) +NVLIST_EXISTS(string_array, STRING_ARRAY) +NVLIST_EXISTS(nvlist_array, NVLIST_ARRAY) +#ifndef _KERNEL +NVLIST_EXISTS(descriptor, DESCRIPTOR) +NVLIST_EXISTS(descriptor_array, DESCRIPTOR_ARRAY) +#endif + +#undef NVLIST_EXISTS + +void +nvlist_add_nvpair(nvlist_t *nvl, const nvpair_t *nvp) +{ + nvpair_t *newnvp; + + NVPAIR_ASSERT(nvp); + + if (nvlist_error(nvl) != 0) { + ERRNO_SET(nvlist_error(nvl)); + return; + } + if ((nvl->nvl_flags & NV_FLAG_NO_UNIQUE) == 0) { + if (nvlist_exists(nvl, nvpair_name(nvp))) { + nvl->nvl_error = EEXIST; + ERRNO_SET(nvlist_error(nvl)); + return; + } + } + + newnvp = nvpair_clone(nvp); + if (newnvp == NULL) { + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); + ERRNO_SET(nvlist_error(nvl)); + return; + } + + nvpair_insert(&nvl->nvl_head, newnvp, nvl); +} + +void +nvlist_add_stringf(nvlist_t *nvl, const char *name, const char *valuefmt, ...) +{ + va_list valueap; + + va_start(valueap, valuefmt); + nvlist_add_stringv(nvl, name, valuefmt, valueap); + va_end(valueap); +} + +void +nvlist_add_stringv(nvlist_t *nvl, const char *name, const char *valuefmt, + va_list valueap) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + ERRNO_SET(nvlist_error(nvl)); + return; + } + + nvp = nvpair_create_stringv(name, valuefmt, valueap); + if (nvp == NULL) { + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); + ERRNO_SET(nvl->nvl_error); + } else { + (void)nvlist_move_nvpair(nvl, nvp); + } +} + +void +nvlist_add_null(nvlist_t *nvl, const char *name) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + ERRNO_SET(nvlist_error(nvl)); + return; + } + + nvp = nvpair_create_null(name); + if (nvp == NULL) { + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); + ERRNO_SET(nvl->nvl_error); + } else { + (void)nvlist_move_nvpair(nvl, nvp); + } +} + +void +nvlist_add_binary(nvlist_t *nvl, const char *name, const void *value, + size_t size) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + ERRNO_SET(nvlist_error(nvl)); + return; + } + + nvp = nvpair_create_binary(name, value, size); + if (nvp == NULL) { + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); + ERRNO_SET(nvl->nvl_error); + } else { + (void)nvlist_move_nvpair(nvl, nvp); + } +} + + +#define NVLIST_ADD(vtype, type) \ +void \ +nvlist_add_##type(nvlist_t *nvl, const char *name, vtype value) \ +{ \ + nvpair_t *nvp; \ + \ + if (nvlist_error(nvl) != 0) { \ + ERRNO_SET(nvlist_error(nvl)); \ + return; \ + } \ + \ + nvp = nvpair_create_##type(name, value); \ + if (nvp == NULL) { \ + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); \ + ERRNO_SET(nvl->nvl_error); \ + } else { \ + (void)nvlist_move_nvpair(nvl, nvp); \ + } \ +} + +NVLIST_ADD(bool, bool) +NVLIST_ADD(uint64_t, number) +NVLIST_ADD(const char *, string) +NVLIST_ADD(const nvlist_t *, nvlist) +#ifndef _KERNEL +NVLIST_ADD(int, descriptor); +#endif + +#undef NVLIST_ADD + +#define NVLIST_ADD_ARRAY(vtype, type) \ +void \ +nvlist_add_##type##_array(nvlist_t *nvl, const char *name, vtype value, \ + size_t nitems) \ +{ \ + nvpair_t *nvp; \ + \ + if (nvlist_error(nvl) != 0) { \ + ERRNO_SET(nvlist_error(nvl)); \ + return; \ + } \ + \ + nvp = nvpair_create_##type##_array(name, value, nitems); \ + if (nvp == NULL) { \ + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); \ + ERRNO_SET(nvl->nvl_error); \ + } else { \ + (void)nvlist_move_nvpair(nvl, nvp); \ + } \ +} + +NVLIST_ADD_ARRAY(const bool *, bool) +NVLIST_ADD_ARRAY(const uint64_t *, number) +NVLIST_ADD_ARRAY(const char * const *, string) +NVLIST_ADD_ARRAY(const nvlist_t * const *, nvlist) +#ifndef _KERNEL +NVLIST_ADD_ARRAY(const int *, descriptor) +#endif + +#undef NVLIST_ADD_ARRAY + +bool +nvlist_move_nvpair(nvlist_t *nvl, nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvpair_nvlist(nvp) == NULL); + + if (nvlist_error(nvl) != 0) { + nvpair_free(nvp); + ERRNO_SET(nvlist_error(nvl)); + return (false); + } + if ((nvl->nvl_flags & NV_FLAG_NO_UNIQUE) == 0) { + if (nvlist_exists(nvl, nvpair_name(nvp))) { + nvpair_free(nvp); + nvl->nvl_error = EEXIST; + ERRNO_SET(nvl->nvl_error); + return (false); + } + } + + nvpair_insert(&nvl->nvl_head, nvp, nvl); + return (true); +} + +void +nvlist_move_string(nvlist_t *nvl, const char *name, char *value) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + nv_free(value); + ERRNO_SET(nvlist_error(nvl)); + return; + } + + nvp = nvpair_move_string(name, value); + if (nvp == NULL) { + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); + ERRNO_SET(nvl->nvl_error); + } else { + (void)nvlist_move_nvpair(nvl, nvp); + } +} + +void +nvlist_move_nvlist(nvlist_t *nvl, const char *name, nvlist_t *value) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + if (value != NULL && nvlist_get_nvpair_parent(value) != NULL) + nvlist_destroy(value); + ERRNO_SET(nvlist_error(nvl)); + return; + } + + nvp = nvpair_move_nvlist(name, value); + if (nvp == NULL) { + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); + ERRNO_SET(nvl->nvl_error); + } else { + (void)nvlist_move_nvpair(nvl, nvp); + } +} + +#ifndef _KERNEL +void +nvlist_move_descriptor(nvlist_t *nvl, const char *name, int value) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + close(value); + ERRNO_SET(nvlist_error(nvl)); + return; + } + + nvp = nvpair_move_descriptor(name, value); + if (nvp == NULL) { + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); + ERRNO_SET(nvl->nvl_error); + } else { + (void)nvlist_move_nvpair(nvl, nvp); + } +} +#endif + +void +nvlist_move_binary(nvlist_t *nvl, const char *name, void *value, size_t size) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + nv_free(value); + ERRNO_SET(nvlist_error(nvl)); + return; + } + + nvp = nvpair_move_binary(name, value, size); + if (nvp == NULL) { + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); + ERRNO_SET(nvl->nvl_error); + } else { + (void)nvlist_move_nvpair(nvl, nvp); + } +} + +void +nvlist_move_bool_array(nvlist_t *nvl, const char *name, bool *value, + size_t nitems) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + nv_free(value); + ERRNO_SET(nvlist_error(nvl)); + return; + } + + nvp = nvpair_move_bool_array(name, value, nitems); + if (nvp == NULL) { + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); + ERRNO_SET(nvl->nvl_error); + } else { + (void)nvlist_move_nvpair(nvl, nvp); + } +} + +void +nvlist_move_string_array(nvlist_t *nvl, const char *name, char **value, + size_t nitems) +{ + nvpair_t *nvp; + size_t i; + + if (nvlist_error(nvl) != 0) { + if (value != NULL) { + for (i = 0; i < nitems; i++) + nv_free(value[i]); + nv_free(value); + } + ERRNO_SET(nvlist_error(nvl)); + return; + } + + nvp = nvpair_move_string_array(name, value, nitems); + if (nvp == NULL) { + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); + ERRNO_SET(nvl->nvl_error); + } else { + (void)nvlist_move_nvpair(nvl, nvp); + } +} + +void +nvlist_move_nvlist_array(nvlist_t *nvl, const char *name, nvlist_t **value, + size_t nitems) +{ + nvpair_t *nvp; + size_t i; + + if (nvlist_error(nvl) != 0) { + if (value != NULL) { + for (i = 0; i < nitems; i++) { + if (nvlist_get_pararr(value[i], NULL) == NULL) + nvlist_destroy(value[i]); + } + } + nv_free(value); + ERRNO_SET(nvlist_error(nvl)); + return; + } + + nvp = nvpair_move_nvlist_array(name, value, nitems); + if (nvp == NULL) { + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); + ERRNO_SET(nvl->nvl_error); + } else { + (void)nvlist_move_nvpair(nvl, nvp); + } +} + +void +nvlist_move_number_array(nvlist_t *nvl, const char *name, uint64_t *value, + size_t nitems) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + nv_free(value); + ERRNO_SET(nvlist_error(nvl)); + return; + } + + nvp = nvpair_move_number_array(name, value, nitems); + if (nvp == NULL) { + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); + ERRNO_SET(nvl->nvl_error); + } else { + (void)nvlist_move_nvpair(nvl, nvp); + } +} + +#ifndef _KERNEL +void +nvlist_move_descriptor_array(nvlist_t *nvl, const char *name, int *value, + size_t nitems) +{ + nvpair_t *nvp; + size_t i; + + if (nvlist_error(nvl) != 0) { + if (value != 0) { + for (i = 0; i < nitems; i++) + close(value[i]); + nv_free(value); + } + + ERRNO_SET(nvlist_error(nvl)); + return; + } + + nvp = nvpair_move_descriptor_array(name, value, nitems); + if (nvp == NULL) { + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); + ERRNO_SET(nvl->nvl_error); + } else { + (void)nvlist_move_nvpair(nvl, nvp); + } +} +#endif + +const nvpair_t * +nvlist_get_nvpair(const nvlist_t *nvl, const char *name) +{ + + return (nvlist_find(nvl, NV_TYPE_NONE, name)); +} + +#define NVLIST_GET(ftype, type, TYPE) \ +ftype \ +nvlist_get_##type(const nvlist_t *nvl, const char *name) \ +{ \ + const nvpair_t *nvp; \ + \ + nvp = nvlist_find(nvl, NV_TYPE_##TYPE, name); \ + if (nvp == NULL) \ + nvlist_report_missing(NV_TYPE_##TYPE, name); \ + return (nvpair_get_##type(nvp)); \ +} + +NVLIST_GET(bool, bool, BOOL) +NVLIST_GET(uint64_t, number, NUMBER) +NVLIST_GET(const char *, string, STRING) +NVLIST_GET(const nvlist_t *, nvlist, NVLIST) +#ifndef _KERNEL +NVLIST_GET(int, descriptor, DESCRIPTOR) +#endif + +#undef NVLIST_GET + +const void * +nvlist_get_binary(const nvlist_t *nvl, const char *name, size_t *sizep) +{ + nvpair_t *nvp; + + nvp = nvlist_find(nvl, NV_TYPE_BINARY, name); + if (nvp == NULL) + nvlist_report_missing(NV_TYPE_BINARY, name); + + return (nvpair_get_binary(nvp, sizep)); +} + +#define NVLIST_GET_ARRAY(ftype, type, TYPE) \ +ftype \ +nvlist_get_##type##_array(const nvlist_t *nvl, const char *name, \ + size_t *nitems) \ +{ \ + const nvpair_t *nvp; \ + \ + nvp = nvlist_find(nvl, NV_TYPE_##TYPE##_ARRAY, name); \ + if (nvp == NULL) \ + nvlist_report_missing(NV_TYPE_##TYPE##_ARRAY, name); \ + return (nvpair_get_##type##_array(nvp, nitems)); \ +} + +NVLIST_GET_ARRAY(const bool *, bool, BOOL) +NVLIST_GET_ARRAY(const uint64_t *, number, NUMBER) +NVLIST_GET_ARRAY(const char * const *, string, STRING) +NVLIST_GET_ARRAY(const nvlist_t * const *, nvlist, NVLIST) +#ifndef _KERNEL +NVLIST_GET_ARRAY(const int *, descriptor, DESCRIPTOR) +#endif + +#undef NVLIST_GET_ARRAY + +#define NVLIST_TAKE(ftype, type, TYPE) \ +ftype \ +nvlist_take_##type(nvlist_t *nvl, const char *name) \ +{ \ + nvpair_t *nvp; \ + ftype value; \ + \ + nvp = nvlist_find(nvl, NV_TYPE_##TYPE, name); \ + if (nvp == NULL) \ + nvlist_report_missing(NV_TYPE_##TYPE, name); \ + value = (ftype)(intptr_t)nvpair_get_##type(nvp); \ + nvlist_remove_nvpair(nvl, nvp); \ + nvpair_free_structure(nvp); \ + return (value); \ +} + +NVLIST_TAKE(bool, bool, BOOL) +NVLIST_TAKE(uint64_t, number, NUMBER) +NVLIST_TAKE(char *, string, STRING) +NVLIST_TAKE(nvlist_t *, nvlist, NVLIST) +#ifndef _KERNEL +NVLIST_TAKE(int, descriptor, DESCRIPTOR) +#endif + +#undef NVLIST_TAKE + +void * +nvlist_take_binary(nvlist_t *nvl, const char *name, size_t *sizep) +{ + nvpair_t *nvp; + void *value; + + nvp = nvlist_find(nvl, NV_TYPE_BINARY, name); + if (nvp == NULL) + nvlist_report_missing(NV_TYPE_BINARY, name); + + value = (void *)(intptr_t)nvpair_get_binary(nvp, sizep); + nvlist_remove_nvpair(nvl, nvp); + nvpair_free_structure(nvp); + return (value); +} + +#define NVLIST_TAKE_ARRAY(ftype, type, TYPE) \ +ftype \ +nvlist_take_##type##_array(nvlist_t *nvl, const char *name, \ + size_t *nitems) \ +{ \ + nvpair_t *nvp; \ + ftype value; \ + \ + nvp = nvlist_find(nvl, NV_TYPE_##TYPE##_ARRAY, name); \ + if (nvp == NULL) \ + nvlist_report_missing(NV_TYPE_##TYPE##_ARRAY, name); \ + value = (ftype)(intptr_t)nvpair_get_##type##_array(nvp, nitems);\ + nvlist_remove_nvpair(nvl, nvp); \ + nvpair_free_structure(nvp); \ + return (value); \ +} + +NVLIST_TAKE_ARRAY(bool *, bool, BOOL) +NVLIST_TAKE_ARRAY(uint64_t *, number, NUMBER) +NVLIST_TAKE_ARRAY(char **, string, STRING) +NVLIST_TAKE_ARRAY(nvlist_t **, nvlist, NVLIST) +#ifndef _KERNEL +NVLIST_TAKE_ARRAY(int *, descriptor, DESCRIPTOR) +#endif + +void +nvlist_remove_nvpair(nvlist_t *nvl, nvpair_t *nvp) +{ + + NVLIST_ASSERT(nvl); + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvpair_nvlist(nvp) == nvl); + + nvpair_remove(&nvl->nvl_head, nvp, nvl); +} + +void +nvlist_free(nvlist_t *nvl, const char *name) +{ + + nvlist_free_type(nvl, name, NV_TYPE_NONE); +} + +#define NVLIST_FREE(type, TYPE) \ +void \ +nvlist_free_##type(nvlist_t *nvl, const char *name) \ +{ \ + \ + nvlist_free_type(nvl, name, NV_TYPE_##TYPE); \ +} + +NVLIST_FREE(null, NULL) +NVLIST_FREE(bool, BOOL) +NVLIST_FREE(number, NUMBER) +NVLIST_FREE(string, STRING) +NVLIST_FREE(nvlist, NVLIST) +NVLIST_FREE(binary, BINARY) +NVLIST_FREE(bool_array, BOOL_ARRAY) +NVLIST_FREE(number_array, NUMBER_ARRAY) +NVLIST_FREE(string_array, STRING_ARRAY) +NVLIST_FREE(nvlist_array, NVLIST_ARRAY) +#ifndef _KERNEL +NVLIST_FREE(descriptor, DESCRIPTOR) +NVLIST_FREE(descriptor_array, DESCRIPTOR_ARRAY) +#endif + +#undef NVLIST_FREE + +void +nvlist_free_nvpair(nvlist_t *nvl, nvpair_t *nvp) +{ + + NVLIST_ASSERT(nvl); + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvpair_nvlist(nvp) == nvl); + + nvlist_remove_nvpair(nvl, nvp); + nvpair_free(nvp); +} + Index: sys/contrib/libnv/nvlist_impl.h =================================================================== --- sys/contrib/libnv/nvlist_impl.h +++ sys/contrib/libnv/nvlist_impl.h @@ -0,0 +1,46 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * Copyright (c) 2013-2015 Mariusz Zaborski + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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 _NVLIST_IMPL_H_ +#define _NVLIST_IMPL_H_ + +#include + +#ifndef _KERNEL +#include +#endif + +nvpair_t *nvlist_get_nvpair_parent(const nvlist_t *nvl); +const unsigned char *nvlist_unpack_header(nvlist_t *nvl, + const unsigned char *ptr, size_t nfds, bool *isbep, size_t *leftp); + +#endif /* !_NVLIST_IMPL_H_ */ Index: sys/contrib/libnv/nvpair.c =================================================================== --- sys/contrib/libnv/nvpair.c +++ sys/contrib/libnv/nvpair.c @@ -0,0 +1,1997 @@ +/*- + * Copyright (c) 2009-2013 The FreeBSD Foundation + * Copyright (c) 2013-2015 Mariusz Zaborski + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#ifdef _KERNEL + +#include +#include +#include +#include + +#include + +#else +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common_impl.h" +#endif + +#ifdef HAVE_PJDLOG +#include +#endif + +#include + +#include "nv_impl.h" +#include "nvlist_impl.h" +#include "nvpair_impl.h" + +#ifndef HAVE_PJDLOG +#ifdef _KERNEL +#define PJDLOG_ASSERT(...) MPASS(__VA_ARGS__) +#define PJDLOG_RASSERT(expr, ...) KASSERT(expr, (__VA_ARGS__)) +#define PJDLOG_ABORT(...) panic(__VA_ARGS__) +#else +#include +#define PJDLOG_ASSERT(...) assert(__VA_ARGS__) +#define PJDLOG_RASSERT(expr, ...) assert(expr) +#define PJDLOG_ABORT(...) abort() +#endif +#endif + +#define NVPAIR_MAGIC 0x6e7670 /* "nvp" */ +struct nvpair { + int nvp_magic; + char *nvp_name; + int nvp_type; + uint64_t nvp_data; + size_t nvp_datasize; + size_t nvp_nitems; /* Used only for array types. */ + nvlist_t *nvp_list; + TAILQ_ENTRY(nvpair) nvp_next; +}; + +#define NVPAIR_ASSERT(nvp) do { \ + PJDLOG_ASSERT((nvp) != NULL); \ + PJDLOG_ASSERT((nvp)->nvp_magic == NVPAIR_MAGIC); \ +} while (0) + +struct nvpair_header { + uint8_t nvph_type; + uint16_t nvph_namesize; + uint64_t nvph_datasize; + uint64_t nvph_nitems; +} __packed; + + +void +nvpair_assert(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); +} + +static nvpair_t * +nvpair_allocv(const char *name, int type, uint64_t data, size_t datasize, + size_t nitems) +{ + nvpair_t *nvp; + size_t namelen; + + PJDLOG_ASSERT(type >= NV_TYPE_FIRST && type <= NV_TYPE_LAST); + + namelen = strlen(name); + if (namelen >= NV_NAME_MAX) { + ERRNO_SET(ENAMETOOLONG); + return (NULL); + } + + nvp = nv_calloc(1, sizeof(*nvp) + namelen + 1); + if (nvp != NULL) { + nvp->nvp_name = (char *)(nvp + 1); + memcpy(nvp->nvp_name, name, namelen); + nvp->nvp_name[namelen] = '\0'; + nvp->nvp_type = type; + nvp->nvp_data = data; + nvp->nvp_datasize = datasize; + nvp->nvp_nitems = nitems; + nvp->nvp_magic = NVPAIR_MAGIC; + } + + return (nvp); +} + +nvlist_t * +nvpair_nvlist(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + + return (nvp->nvp_list); +} + +nvpair_t * +nvpair_next(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_list != NULL); + + return (TAILQ_NEXT(nvp, nvp_next)); +} + +nvpair_t * +nvpair_prev(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_list != NULL); + + return (TAILQ_PREV(nvp, nvl_head, nvp_next)); +} + +void +nvpair_insert(struct nvl_head *head, nvpair_t *nvp, nvlist_t *nvl) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_list == NULL); + PJDLOG_ASSERT((nvlist_flags(nvl) & NV_FLAG_NO_UNIQUE) != 0 || + !nvlist_exists(nvl, nvpair_name(nvp))); + + TAILQ_INSERT_TAIL(head, nvp, nvp_next); + nvp->nvp_list = nvl; +} + +static void +nvpair_remove_nvlist(nvpair_t *nvp) +{ + nvlist_t *nvl; + + /* XXX: DECONST is bad, mkay? */ + nvl = __DECONST(nvlist_t *, nvpair_get_nvlist(nvp)); + PJDLOG_ASSERT(nvl != NULL); + nvlist_set_parent(nvl, NULL); +} + +static void +nvpair_remove_nvlist_array(nvpair_t *nvp) +{ + nvlist_t **nvlarray; + size_t count, i; + + /* XXX: DECONST is bad, mkay? */ + nvlarray = __DECONST(nvlist_t **, + nvpair_get_nvlist_array(nvp, &count)); + for (i = 0; i < count; i++) + nvlist_set_array_next(nvlarray[i], NULL); +} + +void +nvpair_remove(struct nvl_head *head, nvpair_t *nvp, const nvlist_t *nvl) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_list == nvl); + + if (nvpair_type(nvp) == NV_TYPE_NVLIST) + nvpair_remove_nvlist(nvp); + else if (nvpair_type(nvp) == NV_TYPE_NVLIST_ARRAY) + nvpair_remove_nvlist_array(nvp); + + TAILQ_REMOVE(head, nvp, nvp_next); + nvp->nvp_list = NULL; +} + +nvpair_t * +nvpair_clone(const nvpair_t *nvp) +{ + nvpair_t *newnvp; + const char *name; + const void *data; + size_t datasize; + + NVPAIR_ASSERT(nvp); + + name = nvpair_name(nvp); + + switch (nvpair_type(nvp)) { + case NV_TYPE_NULL: + newnvp = nvpair_create_null(name); + break; + case NV_TYPE_BOOL: + newnvp = nvpair_create_bool(name, nvpair_get_bool(nvp)); + break; + case NV_TYPE_NUMBER: + newnvp = nvpair_create_number(name, nvpair_get_number(nvp)); + break; + case NV_TYPE_STRING: + newnvp = nvpair_create_string(name, nvpair_get_string(nvp)); + break; + case NV_TYPE_NVLIST: + newnvp = nvpair_create_nvlist(name, nvpair_get_nvlist(nvp)); + break; + case NV_TYPE_BINARY: + data = nvpair_get_binary(nvp, &datasize); + newnvp = nvpair_create_binary(name, data, datasize); + break; + case NV_TYPE_BOOL_ARRAY: + data = nvpair_get_bool_array(nvp, &datasize); + newnvp = nvpair_create_bool_array(name, data, datasize); + break; + case NV_TYPE_NUMBER_ARRAY: + data = nvpair_get_number_array(nvp, &datasize); + newnvp = nvpair_create_number_array(name, data, datasize); + break; + case NV_TYPE_STRING_ARRAY: + data = nvpair_get_string_array(nvp, &datasize); + newnvp = nvpair_create_string_array(name, data, datasize); + break; + case NV_TYPE_NVLIST_ARRAY: + data = nvpair_get_nvlist_array(nvp, &datasize); + newnvp = nvpair_create_nvlist_array(name, data, datasize); + break; +#ifndef _KERNEL + case NV_TYPE_DESCRIPTOR: + newnvp = nvpair_create_descriptor(name, + nvpair_get_descriptor(nvp)); + break; + case NV_TYPE_DESCRIPTOR_ARRAY: + data = nvpair_get_descriptor_array(nvp, &datasize); + newnvp = nvpair_create_descriptor_array(name, data, datasize); + break; +#endif + default: + PJDLOG_ABORT("Unknown type: %d.", nvpair_type(nvp)); + } + + return (newnvp); +} + +size_t +nvpair_header_size(void) +{ + + return (sizeof(struct nvpair_header)); +} + +size_t +nvpair_size(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + + return (nvp->nvp_datasize); +} + +unsigned char * +nvpair_pack_header(const nvpair_t *nvp, unsigned char *ptr, size_t *leftp) +{ + struct nvpair_header nvphdr; + size_t namesize; + + NVPAIR_ASSERT(nvp); + + nvphdr.nvph_type = nvp->nvp_type; + namesize = strlen(nvp->nvp_name) + 1; + PJDLOG_ASSERT(namesize > 0 && namesize <= UINT16_MAX); + nvphdr.nvph_namesize = namesize; + nvphdr.nvph_datasize = nvp->nvp_datasize; + nvphdr.nvph_nitems = nvp->nvp_nitems; + PJDLOG_ASSERT(*leftp >= sizeof(nvphdr)); + memcpy(ptr, &nvphdr, sizeof(nvphdr)); + ptr += sizeof(nvphdr); + *leftp -= sizeof(nvphdr); + + PJDLOG_ASSERT(*leftp >= namesize); + memcpy(ptr, nvp->nvp_name, namesize); + ptr += namesize; + *leftp -= namesize; + + return (ptr); +} + +unsigned char * +nvpair_pack_null(const nvpair_t *nvp, unsigned char *ptr, + size_t *leftp __unused) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NULL); + + return (ptr); +} + +unsigned char * +nvpair_pack_bool(const nvpair_t *nvp, unsigned char *ptr, size_t *leftp) +{ + uint8_t value; + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BOOL); + + value = (uint8_t)nvp->nvp_data; + + PJDLOG_ASSERT(*leftp >= sizeof(value)); + memcpy(ptr, &value, sizeof(value)); + ptr += sizeof(value); + *leftp -= sizeof(value); + + return (ptr); +} + +unsigned char * +nvpair_pack_number(const nvpair_t *nvp, unsigned char *ptr, size_t *leftp) +{ + uint64_t value; + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NUMBER); + + value = (uint64_t)nvp->nvp_data; + + PJDLOG_ASSERT(*leftp >= sizeof(value)); + memcpy(ptr, &value, sizeof(value)); + ptr += sizeof(value); + *leftp -= sizeof(value); + + return (ptr); +} + +unsigned char * +nvpair_pack_string(const nvpair_t *nvp, unsigned char *ptr, size_t *leftp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_STRING); + + PJDLOG_ASSERT(*leftp >= nvp->nvp_datasize); + memcpy(ptr, (const void *)(intptr_t)nvp->nvp_data, nvp->nvp_datasize); + ptr += nvp->nvp_datasize; + *leftp -= nvp->nvp_datasize; + + return (ptr); +} + +unsigned char * +nvpair_pack_nvlist_up(unsigned char *ptr, size_t *leftp) +{ + struct nvpair_header nvphdr; + size_t namesize; + const char *name = ""; + + namesize = 1; + nvphdr.nvph_type = NV_TYPE_NVLIST_UP; + nvphdr.nvph_namesize = namesize; + nvphdr.nvph_datasize = 0; + nvphdr.nvph_nitems = 0; + PJDLOG_ASSERT(*leftp >= sizeof(nvphdr)); + memcpy(ptr, &nvphdr, sizeof(nvphdr)); + ptr += sizeof(nvphdr); + *leftp -= sizeof(nvphdr); + + PJDLOG_ASSERT(*leftp >= namesize); + memcpy(ptr, name, namesize); + ptr += namesize; + *leftp -= namesize; + + return (ptr); +} + +unsigned char * +nvpair_pack_nvlist_array_next(unsigned char *ptr, size_t *leftp) +{ + struct nvpair_header nvphdr; + size_t namesize; + const char *name = ""; + + namesize = 1; + nvphdr.nvph_type = NV_TYPE_NVLIST_ARRAY_NEXT; + nvphdr.nvph_namesize = namesize; + nvphdr.nvph_datasize = 0; + nvphdr.nvph_nitems = 0; + PJDLOG_ASSERT(*leftp >= sizeof(nvphdr)); + memcpy(ptr, &nvphdr, sizeof(nvphdr)); + ptr += sizeof(nvphdr); + *leftp -= sizeof(nvphdr); + + PJDLOG_ASSERT(*leftp >= namesize); + memcpy(ptr, name, namesize); + ptr += namesize; + *leftp -= namesize; + + return (ptr); +} + +#ifndef _KERNEL +unsigned char * +nvpair_pack_descriptor(const nvpair_t *nvp, unsigned char *ptr, int64_t *fdidxp, + size_t *leftp) +{ + int64_t value; + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_DESCRIPTOR); + + value = (int64_t)nvp->nvp_data; + if (value != -1) { + /* + * If there is a real descriptor here, we change its number + * to position in the array of descriptors send via control + * message. + */ + PJDLOG_ASSERT(fdidxp != NULL); + + value = *fdidxp; + (*fdidxp)++; + } + + PJDLOG_ASSERT(*leftp >= sizeof(value)); + memcpy(ptr, &value, sizeof(value)); + ptr += sizeof(value); + *leftp -= sizeof(value); + + return (ptr); +} +#endif + +unsigned char * +nvpair_pack_binary(const nvpair_t *nvp, unsigned char *ptr, size_t *leftp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BINARY); + + PJDLOG_ASSERT(*leftp >= nvp->nvp_datasize); + memcpy(ptr, (const void *)(intptr_t)nvp->nvp_data, nvp->nvp_datasize); + ptr += nvp->nvp_datasize; + *leftp -= nvp->nvp_datasize; + + return (ptr); +} + +unsigned char * +nvpair_pack_bool_array(const nvpair_t *nvp, unsigned char *ptr, size_t *leftp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BOOL_ARRAY); + PJDLOG_ASSERT(*leftp >= nvp->nvp_datasize); + + memcpy(ptr, (const void *)(intptr_t)nvp->nvp_data, nvp->nvp_datasize); + ptr += nvp->nvp_datasize; + *leftp -= nvp->nvp_datasize; + + return (ptr); +} + +unsigned char * +nvpair_pack_number_array(const nvpair_t *nvp, unsigned char *ptr, size_t *leftp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NUMBER_ARRAY); + PJDLOG_ASSERT(*leftp >= nvp->nvp_datasize); + + memcpy(ptr, (const void *)(intptr_t)nvp->nvp_data, nvp->nvp_datasize); + ptr += nvp->nvp_datasize; + *leftp -= nvp->nvp_datasize; + + return (ptr); +} + +unsigned char * +nvpair_pack_string_array(const nvpair_t *nvp, unsigned char *ptr, size_t *leftp) +{ + unsigned int ii; + size_t size, len; + const char * const *array; + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_STRING_ARRAY); + PJDLOG_ASSERT(*leftp >= nvp->nvp_datasize); + + size = 0; + array = nvpair_get_string_array(nvp, NULL); + PJDLOG_ASSERT(array != NULL); + + for (ii = 0; ii < nvp->nvp_nitems; ii++) { + len = strlen(array[ii]) + 1; + PJDLOG_ASSERT(*leftp >= len); + + memcpy(ptr, (const void *)array[ii], len); + size += len; + ptr += len; + *leftp -= len; + } + + PJDLOG_ASSERT(size == nvp->nvp_datasize); + + return (ptr); +} + +#ifndef _KERNEL +unsigned char * +nvpair_pack_descriptor_array(const nvpair_t *nvp, unsigned char *ptr, + int64_t *fdidxp, size_t *leftp) +{ + int64_t value; + const int *array; + unsigned int ii; + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_DESCRIPTOR_ARRAY); + PJDLOG_ASSERT(*leftp >= nvp->nvp_datasize); + + array = nvpair_get_descriptor_array(nvp, NULL); + PJDLOG_ASSERT(array != NULL); + + for (ii = 0; ii < nvp->nvp_nitems; ii++) { + PJDLOG_ASSERT(*leftp >= sizeof(value)); + + value = array[ii]; + if (value != -1) { + /* + * If there is a real descriptor here, we change its + * number to position in the array of descriptors send + * via control message. + */ + PJDLOG_ASSERT(fdidxp != NULL); + + value = *fdidxp; + (*fdidxp)++; + } + memcpy(ptr, &value, sizeof(value)); + ptr += sizeof(value); + *leftp -= sizeof(value); + } + + return (ptr); +} +#endif + +void +nvpair_init_datasize(nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + + if (nvp->nvp_type == NV_TYPE_NVLIST) { + if (nvp->nvp_data == 0) { + nvp->nvp_datasize = 0; + } else { + nvp->nvp_datasize = + nvlist_size((const nvlist_t *)(intptr_t)nvp->nvp_data); + } + } +} + +const unsigned char * +nvpair_unpack_header(bool isbe, nvpair_t *nvp, const unsigned char *ptr, + size_t *leftp) +{ + struct nvpair_header nvphdr; + + if (*leftp < sizeof(nvphdr)) + goto failed; + + memcpy(&nvphdr, ptr, sizeof(nvphdr)); + ptr += sizeof(nvphdr); + *leftp -= sizeof(nvphdr); + +#if NV_TYPE_FIRST > 0 + if (nvphdr.nvph_type < NV_TYPE_FIRST) + goto failed; +#endif + if (nvphdr.nvph_type > NV_TYPE_LAST && + nvphdr.nvph_type != NV_TYPE_NVLIST_UP && + nvphdr.nvph_type != NV_TYPE_NVLIST_ARRAY_NEXT) { + goto failed; + } + +#if BYTE_ORDER == BIG_ENDIAN + if (!isbe) { + nvphdr.nvph_namesize = le16toh(nvphdr.nvph_namesize); + nvphdr.nvph_datasize = le64toh(nvphdr.nvph_datasize); + } +#else + if (isbe) { + nvphdr.nvph_namesize = be16toh(nvphdr.nvph_namesize); + nvphdr.nvph_datasize = be64toh(nvphdr.nvph_datasize); + } +#endif + + if (nvphdr.nvph_namesize > NV_NAME_MAX) + goto failed; + if (*leftp < nvphdr.nvph_namesize) + goto failed; + if (nvphdr.nvph_namesize < 1) + goto failed; + if (strnlen((const char *)ptr, nvphdr.nvph_namesize) != + (size_t)(nvphdr.nvph_namesize - 1)) { + goto failed; + } + + memcpy(nvp->nvp_name, ptr, nvphdr.nvph_namesize); + ptr += nvphdr.nvph_namesize; + *leftp -= nvphdr.nvph_namesize; + + if (*leftp < nvphdr.nvph_datasize) + goto failed; + + nvp->nvp_type = nvphdr.nvph_type; + nvp->nvp_data = 0; + nvp->nvp_datasize = nvphdr.nvph_datasize; + nvp->nvp_nitems = nvphdr.nvph_nitems; + + return (ptr); +failed: + ERRNO_SET(EINVAL); + return (NULL); +} + +const unsigned char * +nvpair_unpack_null(bool isbe __unused, nvpair_t *nvp, const unsigned char *ptr, + size_t *leftp __unused) +{ + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NULL); + + if (nvp->nvp_datasize != 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + return (ptr); +} + +const unsigned char * +nvpair_unpack_bool(bool isbe __unused, nvpair_t *nvp, const unsigned char *ptr, + size_t *leftp) +{ + uint8_t value; + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BOOL); + + if (nvp->nvp_datasize != sizeof(value)) { + ERRNO_SET(EINVAL); + return (NULL); + } + if (*leftp < sizeof(value)) { + ERRNO_SET(EINVAL); + return (NULL); + } + + memcpy(&value, ptr, sizeof(value)); + ptr += sizeof(value); + *leftp -= sizeof(value); + + if (value != 0 && value != 1) { + ERRNO_SET(EINVAL); + return (NULL); + } + + nvp->nvp_data = (uint64_t)value; + + return (ptr); +} + +const unsigned char * +nvpair_unpack_number(bool isbe, nvpair_t *nvp, const unsigned char *ptr, + size_t *leftp) +{ + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NUMBER); + + if (nvp->nvp_datasize != sizeof(uint64_t)) { + ERRNO_SET(EINVAL); + return (NULL); + } + if (*leftp < sizeof(uint64_t)) { + ERRNO_SET(EINVAL); + return (NULL); + } + + if (isbe) + nvp->nvp_data = be64dec(ptr); + else + nvp->nvp_data = le64dec(ptr); + + ptr += sizeof(uint64_t); + *leftp -= sizeof(uint64_t); + + return (ptr); +} + +const unsigned char * +nvpair_unpack_string(bool isbe __unused, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp) +{ + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_STRING); + + if (*leftp < nvp->nvp_datasize || nvp->nvp_datasize == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + if (strnlen((const char *)ptr, nvp->nvp_datasize) != + nvp->nvp_datasize - 1) { + ERRNO_SET(EINVAL); + return (NULL); + } + + nvp->nvp_data = (uint64_t)(uintptr_t)nv_strdup((const char *)ptr); + if (nvp->nvp_data == 0) + return (NULL); + + ptr += nvp->nvp_datasize; + *leftp -= nvp->nvp_datasize; + + return (ptr); +} + +const unsigned char * +nvpair_unpack_nvlist(bool isbe __unused, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp, size_t nfds, nvlist_t **child) +{ + nvlist_t *value; + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NVLIST); + + if (*leftp < nvp->nvp_datasize || nvp->nvp_datasize == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + value = nvlist_create(0); + if (value == NULL) + return (NULL); + + ptr = nvlist_unpack_header(value, ptr, nfds, NULL, leftp); + if (ptr == NULL) + return (NULL); + + nvp->nvp_data = (uint64_t)(uintptr_t)value; + *child = value; + + return (ptr); +} + +#ifndef _KERNEL +const unsigned char * +nvpair_unpack_descriptor(bool isbe, nvpair_t *nvp, const unsigned char *ptr, + size_t *leftp, const int *fds, size_t nfds) +{ + int64_t idx; + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_DESCRIPTOR); + + if (nvp->nvp_datasize != sizeof(idx)) { + ERRNO_SET(EINVAL); + return (NULL); + } + if (*leftp < sizeof(idx)) { + ERRNO_SET(EINVAL); + return (NULL); + } + + if (isbe) + idx = be64dec(ptr); + else + idx = le64dec(ptr); + + if (idx < 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + if ((size_t)idx >= nfds) { + ERRNO_SET(EINVAL); + return (NULL); + } + + nvp->nvp_data = (uint64_t)fds[idx]; + + ptr += sizeof(idx); + *leftp -= sizeof(idx); + + return (ptr); +} +#endif + +const unsigned char * +nvpair_unpack_binary(bool isbe __unused, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp) +{ + void *value; + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BINARY); + + if (*leftp < nvp->nvp_datasize || nvp->nvp_datasize == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + value = nv_malloc(nvp->nvp_datasize); + if (value == NULL) + return (NULL); + + memcpy(value, ptr, nvp->nvp_datasize); + ptr += nvp->nvp_datasize; + *leftp -= nvp->nvp_datasize; + + nvp->nvp_data = (uint64_t)(uintptr_t)value; + + return (ptr); +} + +const unsigned char * +nvpair_unpack_bool_array(bool isbe __unused, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp) +{ + uint8_t *value; + size_t size; + unsigned int i; + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BOOL_ARRAY); + + size = sizeof(*value) * nvp->nvp_nitems; + if (nvp->nvp_datasize != size || *leftp < size || + nvp->nvp_nitems == 0 || size < nvp->nvp_nitems) { + ERRNO_SET(EINVAL); + return (NULL); + } + + value = nv_malloc(size); + if (value == NULL) + return (NULL); + + for (i = 0; i < nvp->nvp_nitems; i++) { + value[i] = *(const uint8_t *)ptr; + + ptr += sizeof(*value); + *leftp -= sizeof(*value); + } + + nvp->nvp_data = (uint64_t)(uintptr_t)value; + + return (ptr); +} + +const unsigned char * +nvpair_unpack_number_array(bool isbe, nvpair_t *nvp, const unsigned char *ptr, + size_t *leftp) +{ + uint64_t *value; + size_t size; + unsigned int i; + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NUMBER_ARRAY); + + size = sizeof(*value) * nvp->nvp_nitems; + if (nvp->nvp_datasize != size || *leftp < size || + nvp->nvp_nitems == 0 || size < nvp->nvp_nitems) { + ERRNO_SET(EINVAL); + return (NULL); + } + + value = nv_malloc(size); + if (value == NULL) + return (NULL); + + for (i = 0; i < nvp->nvp_nitems; i++) { + if (isbe) + value[i] = be64dec(ptr); + else + value[i] = le64dec(ptr); + + ptr += sizeof(*value); + *leftp -= sizeof(*value); + } + + nvp->nvp_data = (uint64_t)(uintptr_t)value; + + return (ptr); +} + +const unsigned char * +nvpair_unpack_string_array(bool isbe __unused, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp) +{ + ssize_t size; + size_t len; + const char *tmp; + char **value; + unsigned int ii, j; + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_STRING_ARRAY); + + if (*leftp < nvp->nvp_datasize || nvp->nvp_datasize == 0 || + nvp->nvp_nitems == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + size = nvp->nvp_datasize; + tmp = (const char *)ptr; + for (ii = 0; ii < nvp->nvp_nitems; ii++) { + len = strnlen(tmp, size - 1) + 1; + size -= len; + if (size < 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + tmp += len; + } + if (size != 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + value = nv_malloc(sizeof(*value) * nvp->nvp_nitems); + if (value == NULL) + return (NULL); + + for (ii = 0; ii < nvp->nvp_nitems; ii++) { + value[ii] = nv_strdup((const char *)ptr); + if (value[ii] == NULL) + goto out; + len = strlen(value[ii]) + 1; + ptr += len; + *leftp -= len; + } + nvp->nvp_data = (uint64_t)(uintptr_t)value; + + return (ptr); +out: + for (j = 0; j < ii; j++) + nv_free(value[j]); + nv_free(value); + return (NULL); +} + +#ifndef _KERNEL +const unsigned char * +nvpair_unpack_descriptor_array(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp, const int *fds, size_t nfds) +{ + int64_t idx; + size_t size; + unsigned int ii; + int *array; + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_DESCRIPTOR_ARRAY); + + size = sizeof(idx) * nvp->nvp_nitems; + if (nvp->nvp_datasize != size || *leftp < size || + nvp->nvp_nitems == 0 || size < nvp->nvp_nitems) { + ERRNO_SET(EINVAL); + return (NULL); + } + + array = (int *)nv_malloc(size); + if (array == NULL) + return (NULL); + + for (ii = 0; ii < nvp->nvp_nitems; ii++) { + if (isbe) + idx = be64dec(ptr); + else + idx = le64dec(ptr); + + if (idx < 0) { + ERRNO_SET(EINVAL); + nv_free(array); + return (NULL); + } + + if ((size_t)idx >= nfds) { + ERRNO_SET(EINVAL); + nv_free(array); + return (NULL); + } + + array[ii] = (uint64_t)fds[idx]; + + ptr += sizeof(idx); + *leftp -= sizeof(idx); + } + + nvp->nvp_data = (uint64_t)(uintptr_t)array; + + return (ptr); +} +#endif + +const unsigned char * +nvpair_unpack_nvlist_array(bool isbe __unused, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp, nvlist_t **firstel) +{ + nvlist_t **value; + nvpair_t *tmpnvp; + unsigned int ii, j; + size_t sizeup; + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NVLIST_ARRAY); + + sizeup = sizeof(struct nvpair_header) * nvp->nvp_nitems; + if (nvp->nvp_nitems == 0 || sizeup < nvp->nvp_nitems || + sizeup > *leftp) { + ERRNO_SET(EINVAL); + return (NULL); + } + + value = nv_malloc(nvp->nvp_nitems * sizeof(*value)); + if (value == NULL) + return (NULL); + + for (ii = 0; ii < nvp->nvp_nitems; ii++) { + value[ii] = nvlist_create(0); + if (value[ii] == NULL) + goto fail; + if (ii > 0) { + tmpnvp = nvpair_allocv(" ", NV_TYPE_NVLIST, + (uint64_t)(uintptr_t)value[ii], 0, 0); + if (tmpnvp == NULL) + goto fail; + nvlist_set_array_next(value[ii - 1], tmpnvp); + } + } + nvlist_set_flags(value[nvp->nvp_nitems - 1], NV_FLAG_IN_ARRAY); + + nvp->nvp_data = (uint64_t)(uintptr_t)value; + *firstel = value[0]; + + return (ptr); +fail: + ERRNO_SAVE(); + for (j = 0; j < ii; j++) + nvlist_destroy(value[j]); + nv_free(value); + ERRNO_RESTORE(); + + return (NULL); +} + +const unsigned char * +nvpair_unpack(bool isbe, const unsigned char *ptr, size_t *leftp, + nvpair_t **nvpp) +{ + nvpair_t *nvp, *tmp; + + nvp = nv_calloc(1, sizeof(*nvp) + NV_NAME_MAX); + if (nvp == NULL) + return (NULL); + nvp->nvp_name = (char *)(nvp + 1); + + ptr = nvpair_unpack_header(isbe, nvp, ptr, leftp); + if (ptr == NULL) + goto failed; + tmp = nv_realloc(nvp, sizeof(*nvp) + strlen(nvp->nvp_name) + 1); + if (tmp == NULL) + goto failed; + nvp = tmp; + + /* Update nvp_name after realloc(). */ + nvp->nvp_name = (char *)(nvp + 1); + nvp->nvp_data = 0x00; + nvp->nvp_magic = NVPAIR_MAGIC; + *nvpp = nvp; + return (ptr); +failed: + nv_free(nvp); + return (NULL); +} + +int +nvpair_type(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + + return (nvp->nvp_type); +} + +const char * +nvpair_name(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + + return (nvp->nvp_name); +} + +nvpair_t * +nvpair_create_stringf(const char *name, const char *valuefmt, ...) +{ + va_list valueap; + nvpair_t *nvp; + + va_start(valueap, valuefmt); + nvp = nvpair_create_stringv(name, valuefmt, valueap); + va_end(valueap); + + return (nvp); +} + +nvpair_t * +nvpair_create_stringv(const char *name, const char *valuefmt, va_list valueap) +{ + nvpair_t *nvp; + char *str; + int len; + + len = nv_vasprintf(&str, valuefmt, valueap); + if (len < 0) + return (NULL); + nvp = nvpair_create_string(name, str); + if (nvp == NULL) + nv_free(str); + return (nvp); +} + +nvpair_t * +nvpair_create_null(const char *name) +{ + + return (nvpair_allocv(name, NV_TYPE_NULL, 0, 0, 0)); +} + +nvpair_t * +nvpair_create_bool(const char *name, bool value) +{ + + return (nvpair_allocv(name, NV_TYPE_BOOL, value ? 1 : 0, + sizeof(uint8_t), 0)); +} + +nvpair_t * +nvpair_create_number(const char *name, uint64_t value) +{ + + return (nvpair_allocv(name, NV_TYPE_NUMBER, value, sizeof(value), 0)); +} + +nvpair_t * +nvpair_create_string(const char *name, const char *value) +{ + nvpair_t *nvp; + size_t size; + char *data; + + if (value == NULL) { + ERRNO_SET(EINVAL); + return (NULL); + } + + data = nv_strdup(value); + if (data == NULL) + return (NULL); + size = strlen(value) + 1; + + nvp = nvpair_allocv(name, NV_TYPE_STRING, (uint64_t)(uintptr_t)data, + size, 0); + if (nvp == NULL) + nv_free(data); + + return (nvp); +} + +nvpair_t * +nvpair_create_nvlist(const char *name, const nvlist_t *value) +{ + nvlist_t *nvl; + nvpair_t *nvp; + + if (value == NULL) { + ERRNO_SET(EINVAL); + return (NULL); + } + + nvl = nvlist_clone(value); + if (nvl == NULL) + return (NULL); + + nvp = nvpair_allocv(name, NV_TYPE_NVLIST, (uint64_t)(uintptr_t)nvl, 0, + 0); + if (nvp == NULL) + nvlist_destroy(nvl); + else + nvlist_set_parent(nvl, nvp); + + return (nvp); +} + +#ifndef _KERNEL +nvpair_t * +nvpair_create_descriptor(const char *name, int value) +{ + nvpair_t *nvp; + + if (value < 0 || !fd_is_valid(value)) { + ERRNO_SET(EBADF); + return (NULL); + } + + value = fcntl(value, F_DUPFD_CLOEXEC, 0); + if (value < 0) + return (NULL); + + nvp = nvpair_allocv(name, NV_TYPE_DESCRIPTOR, (uint64_t)value, + sizeof(int64_t), 0); + if (nvp == NULL) { + ERRNO_SAVE(); + close(value); + ERRNO_RESTORE(); + } + + return (nvp); +} +#endif + +nvpair_t * +nvpair_create_binary(const char *name, const void *value, size_t size) +{ + nvpair_t *nvp; + void *data; + + if (value == NULL || size == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + data = nv_malloc(size); + if (data == NULL) + return (NULL); + memcpy(data, value, size); + + nvp = nvpair_allocv(name, NV_TYPE_BINARY, (uint64_t)(uintptr_t)data, + size, 0); + if (nvp == NULL) + nv_free(data); + + return (nvp); +} + +nvpair_t * +nvpair_create_bool_array(const char *name, const bool *value, size_t nitems) +{ + nvpair_t *nvp; + size_t size; + void *data; + + if (value == NULL || nitems == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + size = sizeof(value[0]) * nitems; + data = nv_malloc(size); + if (data == NULL) + return (NULL); + + memcpy(data, value, size); + nvp = nvpair_allocv(name, NV_TYPE_BOOL_ARRAY, (uint64_t)(uintptr_t)data, + size, nitems); + if (nvp == NULL) { + ERRNO_SAVE(); + nv_free(data); + ERRNO_RESTORE(); + } + + return (nvp); +} + +nvpair_t * +nvpair_create_number_array(const char *name, const uint64_t *value, + size_t nitems) +{ + nvpair_t *nvp; + size_t size; + void *data; + + if (value == NULL || nitems == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + size = sizeof(value[0]) * nitems; + data = nv_malloc(size); + if (data == NULL) + return (NULL); + + memcpy(data, value, size); + nvp = nvpair_allocv(name, NV_TYPE_NUMBER_ARRAY, + (uint64_t)(uintptr_t)data, size, nitems); + if (nvp == NULL) { + ERRNO_SAVE(); + nv_free(data); + ERRNO_RESTORE(); + } + + return (nvp); +} + +nvpair_t * +nvpair_create_string_array(const char *name, const char * const *value, + size_t nitems) +{ + nvpair_t *nvp; + unsigned int ii; + size_t datasize, size; + char **data; + + if (value == NULL || nitems == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + nvp = NULL; + datasize = 0; + data = nv_malloc(sizeof(value[0]) * nitems); + if (data == NULL) + return (NULL); + + for (ii = 0; ii < nitems; ii++) { + if (value[ii] == NULL) { + ERRNO_SET(EINVAL); + goto fail; + } + + size = strlen(value[ii]) + 1; + datasize += size; + data[ii] = nv_strdup(value[ii]); + if (data[ii] == NULL) + goto fail; + } + nvp = nvpair_allocv(name, NV_TYPE_STRING_ARRAY, + (uint64_t)(uintptr_t)data, datasize, nitems); + +fail: + if (nvp == NULL) { + ERRNO_SAVE(); + for (; ii > 0; ii--) + nv_free(data[ii - 1]); + nv_free(data); + ERRNO_RESTORE(); + } + + return (nvp); +} + +nvpair_t * +nvpair_create_nvlist_array(const char *name, const nvlist_t * const *value, + size_t nitems) +{ + unsigned int ii; + nvlist_t **nvls; + nvpair_t *nvp; + int flags; + + nvp = NULL; + nvls = NULL; + ii = 0; + + if (value == NULL || nitems == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + nvls = nv_malloc(sizeof(value[0]) * nitems); + if (nvls == NULL) + return (NULL); + + for (ii = 0; ii < nitems; ii++) { + if (value[ii] == NULL) { + ERRNO_SET(EINVAL); + goto fail; + } + + nvls[ii] = nvlist_clone(value[ii]); + if (nvls[ii] == NULL) + goto fail; + + if (ii > 0) { + nvp = nvpair_allocv(" ", NV_TYPE_NVLIST, + (uint64_t)(uintptr_t)nvls[ii], 0, 0); + if (nvp == NULL) + goto fail; + nvlist_set_array_next(nvls[ii - 1], nvp); + } + } + flags = nvlist_flags(nvls[nitems - 1]) | NV_FLAG_IN_ARRAY; + nvlist_set_flags(nvls[nitems - 1], flags); + + nvp = nvpair_allocv(name, NV_TYPE_NVLIST_ARRAY, + (uint64_t)(uintptr_t)nvls, 0, nitems); + +fail: + if (nvp == NULL) { + ERRNO_SAVE(); + for (; ii > 0; ii--) + nvlist_destroy(nvls[ii - 1]); + + nv_free(nvls); + ERRNO_RESTORE(); + } else { + for (ii = 0; ii < nitems; ii++) + nvlist_set_parent(nvls[ii], nvp); + } + + return (nvp); +} + +#ifndef _KERNEL +nvpair_t * +nvpair_create_descriptor_array(const char *name, const int *value, + size_t nitems) +{ + unsigned int ii; + nvpair_t *nvp; + int *fds; + + if (value == NULL) { + ERRNO_SET(EINVAL); + return (NULL); + } + + nvp = NULL; + + fds = nv_malloc(sizeof(value[0]) * nitems); + if (fds == NULL) + return (NULL); + for (ii = 0; ii < nitems; ii++) { + if (value[ii] == -1) { + fds[ii] = -1; + } else { + if (!fd_is_valid(value[ii])) { + ERRNO_SET(EBADF); + goto fail; + } + + fds[ii] = fcntl(value[ii], F_DUPFD_CLOEXEC, 0); + if (fds[ii] == -1) + goto fail; + } + } + + nvp = nvpair_allocv(name, NV_TYPE_DESCRIPTOR_ARRAY, + (uint64_t)(uintptr_t)fds, sizeof(int64_t) * nitems, nitems); + +fail: + if (nvp == NULL) { + ERRNO_SAVE(); + for (; ii > 0; ii--) { + if (fds[ii - 1] != -1) + close(fds[ii - 1]); + } + nv_free(fds); + ERRNO_RESTORE(); + } + + return (nvp); +} +#endif + +nvpair_t * +nvpair_move_string(const char *name, char *value) +{ + nvpair_t *nvp; + + if (value == NULL) { + ERRNO_SET(EINVAL); + return (NULL); + } + + nvp = nvpair_allocv(name, NV_TYPE_STRING, (uint64_t)(uintptr_t)value, + strlen(value) + 1, 0); + if (nvp == NULL) { + ERRNO_SAVE(); + nv_free(value); + ERRNO_RESTORE(); + } + + return (nvp); +} + +nvpair_t * +nvpair_move_nvlist(const char *name, nvlist_t *value) +{ + nvpair_t *nvp; + + if (value == NULL || nvlist_get_nvpair_parent(value) != NULL) { + ERRNO_SET(EINVAL); + return (NULL); + } + + if (nvlist_error(value) != 0) { + ERRNO_SET(nvlist_error(value)); + nvlist_destroy(value); + return (NULL); + } + + nvp = nvpair_allocv(name, NV_TYPE_NVLIST, (uint64_t)(uintptr_t)value, + 0, 0); + if (nvp == NULL) + nvlist_destroy(value); + else + nvlist_set_parent(value, nvp); + + return (nvp); +} + +#ifndef _KERNEL +nvpair_t * +nvpair_move_descriptor(const char *name, int value) +{ + nvpair_t *nvp; + + if (value < 0 || !fd_is_valid(value)) { + ERRNO_SET(EBADF); + return (NULL); + } + + nvp = nvpair_allocv(name, NV_TYPE_DESCRIPTOR, (uint64_t)value, + sizeof(int64_t), 0); + if (nvp == NULL) { + ERRNO_SAVE(); + close(value); + ERRNO_RESTORE(); + } + + return (nvp); +} +#endif + +nvpair_t * +nvpair_move_binary(const char *name, void *value, size_t size) +{ + nvpair_t *nvp; + + if (value == NULL || size == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + nvp = nvpair_allocv(name, NV_TYPE_BINARY, (uint64_t)(uintptr_t)value, + size, 0); + if (nvp == NULL) { + ERRNO_SAVE(); + nv_free(value); + ERRNO_RESTORE(); + } + + return (nvp); +} + +nvpair_t * +nvpair_move_bool_array(const char *name, bool *value, size_t nitems) +{ + nvpair_t *nvp; + + if (value == NULL || nitems == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + nvp = nvpair_allocv(name, NV_TYPE_BOOL_ARRAY, + (uint64_t)(uintptr_t)value, sizeof(value[0]) * nitems, nitems); + if (nvp == NULL) { + ERRNO_SAVE(); + nv_free(value); + ERRNO_RESTORE(); + } + + return (nvp); +} + +nvpair_t * +nvpair_move_string_array(const char *name, char **value, size_t nitems) +{ + nvpair_t *nvp; + size_t i, size; + + if (value == NULL || nitems == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + size = 0; + for (i = 0; i < nitems; i++) { + if (value[i] == NULL) { + ERRNO_SET(EINVAL); + return (NULL); + } + + size += strlen(value[i]) + 1; + } + + nvp = nvpair_allocv(name, NV_TYPE_STRING_ARRAY, + (uint64_t)(uintptr_t)value, size, nitems); + if (nvp == NULL) { + ERRNO_SAVE(); + for (i = 0; i < nitems; i++) + nv_free(value[i]); + nv_free(value); + ERRNO_RESTORE(); + } + + return (nvp); +} + +nvpair_t * +nvpair_move_number_array(const char *name, uint64_t *value, size_t nitems) +{ + nvpair_t *nvp; + + if (value == NULL || nitems == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + nvp = nvpair_allocv(name, NV_TYPE_NUMBER_ARRAY, + (uint64_t)(uintptr_t)value, sizeof(value[0]) * nitems, nitems); + if (nvp == NULL) { + ERRNO_SAVE(); + nv_free(value); + ERRNO_RESTORE(); + } + + return (nvp); +} + +nvpair_t * +nvpair_move_nvlist_array(const char *name, nvlist_t **value, size_t nitems) +{ + unsigned int ii; + nvpair_t *nvp; + int flags; + + nvp = NULL; + if (value == NULL || nitems == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + for (ii = 0; ii < nitems; ii++) { + if (value == NULL || nvlist_error(value[ii]) != 0 || + nvlist_get_pararr(value[ii], NULL) != NULL) { + ERRNO_SET(EINVAL); + goto fail; + } + if (ii > 0) { + nvp = nvpair_allocv(" ", NV_TYPE_NVLIST, + (uint64_t)(uintptr_t)value[ii], 0, 0); + if (nvp == NULL) + goto fail; + nvlist_set_array_next(value[ii - 1], nvp); + } + } + flags = nvlist_flags(value[nitems - 1]) | NV_FLAG_IN_ARRAY; + nvlist_set_flags(value[nitems - 1], flags); + + nvp = nvpair_allocv(name, NV_TYPE_NVLIST_ARRAY, + (uint64_t)(uintptr_t)value, 0, nitems); +fail: + if (nvp == NULL) { + ERRNO_SAVE(); + for (ii = 0; ii < nitems; ii++) { + if (value[ii] != NULL && + nvlist_get_pararr(value[ii], NULL) != NULL) { + nvlist_destroy(value[ii]); + } + nv_free(value); + } + ERRNO_RESTORE(); + } else { + for (ii = 0; ii < nitems; ii++) + nvlist_set_parent(value[ii], nvp); + } + + return (nvp); +} + +#ifndef _KERNEL +nvpair_t * +nvpair_move_descriptor_array(const char *name, int *value, size_t nitems) +{ + nvpair_t *nvp; + size_t i; + + nvp = NULL; + if (value == NULL || nitems == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + for (i = 0; i < nitems; i++) { + if (value[i] != -1 && !fd_is_valid(value[i])) { + ERRNO_SET(EBADF); + goto fail; + } + } + + nvp = nvpair_allocv(name, NV_TYPE_DESCRIPTOR_ARRAY, + (uint64_t)(uintptr_t)value, sizeof(value[0]) * nitems, nitems); + +fail: + if (nvp == NULL) { + ERRNO_SAVE(); + for (i = 0; i < nitems; i++) { + if (fd_is_valid(value[i])) + close(value[i]); + } + nv_free(value); + ERRNO_RESTORE(); + } + + return (nvp); +} +#endif + +bool +nvpair_get_bool(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + + return (nvp->nvp_data == 1); +} + +uint64_t +nvpair_get_number(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + + return (nvp->nvp_data); +} + +const char * +nvpair_get_string(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_STRING); + + return ((const char *)(intptr_t)nvp->nvp_data); +} + +const nvlist_t * +nvpair_get_nvlist(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NVLIST); + + return ((const nvlist_t *)(intptr_t)nvp->nvp_data); +} + +#ifndef _KERNEL +int +nvpair_get_descriptor(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_DESCRIPTOR); + + return ((int)nvp->nvp_data); +} +#endif + +const void * +nvpair_get_binary(const nvpair_t *nvp, size_t *sizep) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BINARY); + + if (sizep != NULL) + *sizep = nvp->nvp_datasize; + + return ((const void *)(intptr_t)nvp->nvp_data); +} + +const bool * +nvpair_get_bool_array(const nvpair_t *nvp, size_t *nitems) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BOOL_ARRAY); + + if (nitems != NULL) + *nitems = nvp->nvp_nitems; + + return ((const bool *)(intptr_t)nvp->nvp_data); +} + +const uint64_t * +nvpair_get_number_array(const nvpair_t *nvp, size_t *nitems) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NUMBER_ARRAY); + + if (nitems != NULL) + *nitems = nvp->nvp_nitems; + + return ((const uint64_t *)(intptr_t)nvp->nvp_data); +} + +const char * const * +nvpair_get_string_array(const nvpair_t *nvp, size_t *nitems) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_STRING_ARRAY); + + if (nitems != NULL) + *nitems = nvp->nvp_nitems; + + return ((const char * const *)(intptr_t)nvp->nvp_data); +} + +const nvlist_t * const * +nvpair_get_nvlist_array(const nvpair_t *nvp, size_t *nitems) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NVLIST_ARRAY); + + if (nitems != NULL) + *nitems = nvp->nvp_nitems; + + return ((const nvlist_t * const *)((intptr_t)nvp->nvp_data)); +} + +#ifndef _KERNEL +const int * +nvpair_get_descriptor_array(const nvpair_t *nvp, size_t *nitems) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_DESCRIPTOR_ARRAY); + + if (nitems != NULL) + *nitems = nvp->nvp_nitems; + + return ((const int *)(intptr_t)nvp->nvp_data); +} +#endif + +void +nvpair_free(nvpair_t *nvp) +{ + size_t i; + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_list == NULL); + + nvp->nvp_magic = 0; + switch (nvp->nvp_type) { +#ifndef _KERNEL + case NV_TYPE_DESCRIPTOR: + close((int)nvp->nvp_data); + break; + case NV_TYPE_DESCRIPTOR_ARRAY: + for (i = 0; i < nvp->nvp_nitems; i++) + close(((int *)(intptr_t)nvp->nvp_data)[i]); + break; +#endif + case NV_TYPE_NVLIST: + nvlist_destroy((nvlist_t *)(intptr_t)nvp->nvp_data); + break; + case NV_TYPE_STRING: + nv_free((char *)(intptr_t)nvp->nvp_data); + break; + case NV_TYPE_BINARY: + nv_free((void *)(intptr_t)nvp->nvp_data); + break; + case NV_TYPE_NVLIST_ARRAY: + for (i = 0; i < nvp->nvp_nitems; i++) { + nvlist_destroy( + ((nvlist_t **)(intptr_t)nvp->nvp_data)[i]); + } + nv_free(((nvlist_t **)(intptr_t)nvp->nvp_data)); + break; + case NV_TYPE_NUMBER_ARRAY: + nv_free((uint64_t *)(intptr_t)nvp->nvp_data); + break; + case NV_TYPE_BOOL_ARRAY: + nv_free((bool *)(intptr_t)nvp->nvp_data); + break; + case NV_TYPE_STRING_ARRAY: + for (i = 0; i < nvp->nvp_nitems; i++) + nv_free(((char **)(intptr_t)nvp->nvp_data)[i]); + break; + } + nv_free(nvp); +} + +void +nvpair_free_structure(nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_list == NULL); + + nvp->nvp_magic = 0; + nv_free(nvp); +} + +const char * +nvpair_type_string(int type) +{ + + switch (type) { + case NV_TYPE_NULL: + return ("NULL"); + case NV_TYPE_BOOL: + return ("BOOL"); + case NV_TYPE_NUMBER: + return ("NUMBER"); + case NV_TYPE_STRING: + return ("STRING"); + case NV_TYPE_NVLIST: + return ("NVLIST"); + case NV_TYPE_DESCRIPTOR: + return ("DESCRIPTOR"); + case NV_TYPE_BINARY: + return ("BINARY"); + case NV_TYPE_BOOL_ARRAY: + return ("BOOL ARRAY"); + case NV_TYPE_NUMBER_ARRAY: + return ("NUMBER ARRAY"); + case NV_TYPE_STRING_ARRAY: + return ("STRING ARRAY"); + case NV_TYPE_NVLIST_ARRAY: + return ("NVLIST ARRAY"); + case NV_TYPE_DESCRIPTOR_ARRAY: + return ("DESCRIPTOR ARRAY"); + default: + return (""); + } +} + Index: sys/contrib/libnv/nvpair_impl.h =================================================================== --- sys/contrib/libnv/nvpair_impl.h +++ sys/contrib/libnv/nvpair_impl.h @@ -0,0 +1,113 @@ +/*- + * Copyright (c) 2009-2013 The FreeBSD Foundation + * Copyright (c) 2013-2015 Mariusz Zaborski + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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 _NVPAIR_IMPL_H_ +#define _NVPAIR_IMPL_H_ + +#include +#include + +#ifndef _KERNEL +#include +#endif + +TAILQ_HEAD(nvl_head, nvpair); + +void nvpair_assert(const nvpair_t *nvp); +nvlist_t *nvpair_nvlist(const nvpair_t *nvp); +nvpair_t *nvpair_next(const nvpair_t *nvp); +nvpair_t *nvpair_prev(const nvpair_t *nvp); +void nvpair_insert(struct nvl_head *head, nvpair_t *nvp, nvlist_t *nvl); +void nvpair_remove(struct nvl_head *head, nvpair_t *nvp, const nvlist_t *nvl); +size_t nvpair_header_size(void); +size_t nvpair_size(const nvpair_t *nvp); +const unsigned char *nvpair_unpack(bool isbe, const unsigned char *ptr, + size_t *leftp, nvpair_t **nvpp); +void nvpair_free_structure(nvpair_t *nvp); +void nvpair_init_datasize(nvpair_t *nvp); +const char *nvpair_type_string(int type); + +/* Pack functions. */ +unsigned char *nvpair_pack_header(const nvpair_t *nvp, unsigned char *ptr, + size_t *leftp); +unsigned char *nvpair_pack_null(const nvpair_t *nvp, unsigned char *ptr, + size_t *leftp); +unsigned char *nvpair_pack_bool(const nvpair_t *nvp, unsigned char *ptr, + size_t *leftp); +unsigned char *nvpair_pack_number(const nvpair_t *nvp, unsigned char *ptr, + size_t *leftp); +unsigned char *nvpair_pack_string(const nvpair_t *nvp, unsigned char *ptr, + size_t *leftp); +unsigned char *nvpair_pack_descriptor(const nvpair_t *nvp, unsigned char *ptr, + int64_t *fdidxp, size_t *leftp); +unsigned char *nvpair_pack_binary(const nvpair_t *nvp, unsigned char *ptr, + size_t *leftp); +unsigned char *nvpair_pack_nvlist_up(unsigned char *ptr, size_t *leftp); +unsigned char *nvpair_pack_bool_array(const nvpair_t *nvp, unsigned char *ptr, + size_t *leftp); +unsigned char *nvpair_pack_number_array(const nvpair_t *nvp, unsigned char *ptr, + size_t *leftp); +unsigned char *nvpair_pack_string_array(const nvpair_t *nvp, unsigned char *ptr, + size_t *leftp); +unsigned char *nvpair_pack_descriptor_array(const nvpair_t *nvp, + unsigned char *ptr, int64_t *fdidxp, size_t *leftp); +unsigned char *nvpair_pack_nvlist_array_next(unsigned char *ptr, size_t *leftp); + +/* Unpack data functions. */ +const unsigned char *nvpair_unpack_header(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp); +const unsigned char *nvpair_unpack_null(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp); +const unsigned char *nvpair_unpack_bool(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp); +const unsigned char *nvpair_unpack_number(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp); +const unsigned char *nvpair_unpack_string(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp); +const unsigned char *nvpair_unpack_nvlist(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp, size_t nfds, nvlist_t **child); +const unsigned char *nvpair_unpack_descriptor(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp, const int *fds, size_t nfds); +const unsigned char *nvpair_unpack_binary(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp); +const unsigned char *nvpair_unpack_bool_array(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp); +const unsigned char *nvpair_unpack_number_array(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp); +const unsigned char *nvpair_unpack_string_array(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp); +const unsigned char *nvpair_unpack_descriptor_array(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp, const int *fds, size_t nfds); +const unsigned char *nvpair_unpack_nvlist_array(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp, nvlist_t **firstel); + +#endif /* !_NVPAIR_IMPL_H_ */ Index: sys/dev/acpica/acpi_pci.c =================================================================== --- sys/dev/acpica/acpi_pci.c +++ sys/dev/acpica/acpi_pci.c @@ -84,6 +84,11 @@ static void acpi_pci_update_device(ACPI_HANDLE handle, device_t pci_child); static bus_dma_tag_t acpi_pci_get_dma_tag(device_t bus, device_t child); +#ifdef PCI_IOV +static device_t acpi_pci_create_iov_child(device_t bus, device_t pf, + uint16_t rid, uint16_t vid, uint16_t did); +#endif + static device_method_t acpi_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, acpi_pci_probe), @@ -98,6 +103,9 @@ /* PCI interface */ DEVMETHOD(pci_set_powerstate, acpi_pci_set_powerstate_method), +#ifdef PCI_IOV + DEVMETHOD(pci_create_iov_child, acpi_pci_create_iov_child), +#endif DEVMETHOD_END }; @@ -345,3 +353,23 @@ return (pci_get_dma_tag(bus, child)); } #endif + +#ifdef PCI_IOV +static device_t +acpi_pci_create_iov_child(device_t bus, device_t pf, uint16_t rid, uint16_t vid, + uint16_t did) +{ + struct acpi_pci_devinfo *dinfo; + device_t vf; + + vf = pci_add_iov_child(bus, pf, sizeof(struct acpi_pci_devinfo), rid, + vid, did); + if (vf == NULL) + return (NULL); + + dinfo = device_get_ivars(vf); + dinfo->ap_handle = NULL; + return (vf); +} +#endif + Index: sys/dev/ixl/if_ixl.c =================================================================== --- sys/dev/ixl/if_ixl.c +++ sys/dev/ixl/if_ixl.c @@ -207,8 +207,8 @@ #ifdef PCI_IOV static int ixl_adminq_err_to_errno(enum i40e_admin_queue_err err); -static int ixl_init_iov(device_t dev, uint16_t num_vfs, const nvlist_t*); -static void ixl_uninit_iov(device_t dev); +static int ixl_iov_init(device_t dev, uint16_t num_vfs, const nvlist_t*); +static void ixl_iov_uninit(device_t dev); static int ixl_add_vf(device_t dev, uint16_t vfnum, const nvlist_t*); static void ixl_handle_vf_msg(struct ixl_pf *, @@ -230,9 +230,9 @@ DEVMETHOD(device_detach, ixl_detach), DEVMETHOD(device_shutdown, ixl_shutdown), #ifdef PCI_IOV - DEVMETHOD(pci_init_iov, ixl_init_iov), - DEVMETHOD(pci_uninit_iov, ixl_uninit_iov), - DEVMETHOD(pci_add_vf, ixl_add_vf), + DEVMETHOD(pci_iov_init, ixl_iov_init), + DEVMETHOD(pci_iov_uninit, ixl_iov_uninit), + DEVMETHOD(pci_iov_add_vf, ixl_add_vf), #endif {0, 0} }; @@ -6546,7 +6546,7 @@ } static int -ixl_init_iov(device_t dev, uint16_t num_vfs, const nvlist_t *params) +ixl_iov_init(device_t dev, uint16_t num_vfs, const nvlist_t *params) { struct ixl_pf *pf; struct i40e_hw *hw; @@ -6594,7 +6594,7 @@ } static void -ixl_uninit_iov(device_t dev) +ixl_iov_uninit(device_t dev) { struct ixl_pf *pf; struct i40e_hw *hw; @@ -6619,7 +6619,11 @@ pf->veb_seid = 0; } +#if __FreeBSD_version > 1100022 if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) +#else + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) +#endif ixl_disable_intr(vsi); vfs = pf->vfs; Index: sys/dev/ixl/ixl.h =================================================================== --- sys/dev/ixl/ixl.h +++ sys/dev/ixl/ixl.h @@ -93,6 +93,7 @@ #ifdef PCI_IOV #include #include +#include #endif #include "i40e_type.h" Index: sys/dev/pci/pci.c =================================================================== --- sys/dev/pci/pci.c +++ sys/dev/pci/pci.c @@ -77,7 +77,6 @@ static int pci_has_quirk(uint32_t devid, int quirk); static pci_addr_t pci_mapbase(uint64_t mapreg); static const char *pci_maptype(uint64_t mapreg); -static int pci_mapsize(uint64_t testval); static int pci_maprange(uint64_t mapreg); static pci_addr_t pci_rombase(uint64_t mapreg); static int pci_romsize(uint64_t testval); @@ -121,6 +120,9 @@ static uint16_t pci_get_rid_method(device_t dev, device_t child); +static struct pci_devinfo * pci_fill_devinfo(device_t pcib, int d, int b, int s, + int f, uint16_t vid, uint16_t did, size_t size); + static device_method_t pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pci_probe), @@ -184,6 +186,11 @@ DEVMETHOD(pci_msix_count, pci_msix_count_method), DEVMETHOD(pci_get_rid, pci_get_rid_method), DEVMETHOD(pci_child_added, pci_child_added_method), +#ifdef PCI_IOV + DEVMETHOD(pci_iov_attach, pci_iov_attach_method), + DEVMETHOD(pci_iov_detach, pci_iov_detach_method), + DEVMETHOD(pci_create_iov_child, pci_create_iov_child_method), +#endif DEVMETHOD_END }; @@ -492,7 +499,7 @@ /* return log2 of map size decoded for memory or port map */ -static int +int pci_mapsize(uint64_t testval) { int ln2size; @@ -605,71 +612,85 @@ pci_read_device(device_t pcib, int d, int b, int s, int f, size_t size) { #define REG(n, w) PCIB_READ_CONFIG(pcib, b, s, f, n, w) - pcicfgregs *cfg = NULL; + uint16_t vid, did; + + vid = REG(PCIR_VENDOR, 2); + did = REG(PCIR_DEVICE, 2); + if (vid != 0xffff) + return (pci_fill_devinfo(pcib, d, b, s, f, vid, did, size)); + + return (NULL); +} + +static struct pci_devinfo * +pci_fill_devinfo(device_t pcib, int d, int b, int s, int f, uint16_t vid, + uint16_t did, size_t size) +{ struct pci_devinfo *devlist_entry; - struct devlist *devlist_head; + pcicfgregs *cfg; - devlist_head = &pci_devq; + devlist_entry = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO); - devlist_entry = NULL; + cfg = &devlist_entry->cfg; - if (REG(PCIR_DEVVENDOR, 4) != 0xfffffffful) { - devlist_entry = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO); + cfg->domain = d; + cfg->bus = b; + cfg->slot = s; + cfg->func = f; + cfg->vendor = vid; + cfg->device = did; + cfg->cmdreg = REG(PCIR_COMMAND, 2); + cfg->statreg = REG(PCIR_STATUS, 2); + cfg->baseclass = REG(PCIR_CLASS, 1); + cfg->subclass = REG(PCIR_SUBCLASS, 1); + cfg->progif = REG(PCIR_PROGIF, 1); + cfg->revid = REG(PCIR_REVID, 1); + cfg->hdrtype = REG(PCIR_HDRTYPE, 1); + cfg->cachelnsz = REG(PCIR_CACHELNSZ, 1); + cfg->lattimer = REG(PCIR_LATTIMER, 1); + cfg->intpin = REG(PCIR_INTPIN, 1); + cfg->intline = REG(PCIR_INTLINE, 1); - cfg = &devlist_entry->cfg; + cfg->mingnt = REG(PCIR_MINGNT, 1); + cfg->maxlat = REG(PCIR_MAXLAT, 1); - cfg->domain = d; - cfg->bus = b; - cfg->slot = s; - cfg->func = f; - cfg->vendor = REG(PCIR_VENDOR, 2); - cfg->device = REG(PCIR_DEVICE, 2); - cfg->cmdreg = REG(PCIR_COMMAND, 2); - cfg->statreg = REG(PCIR_STATUS, 2); - cfg->baseclass = REG(PCIR_CLASS, 1); - cfg->subclass = REG(PCIR_SUBCLASS, 1); - cfg->progif = REG(PCIR_PROGIF, 1); - cfg->revid = REG(PCIR_REVID, 1); - cfg->hdrtype = REG(PCIR_HDRTYPE, 1); - cfg->cachelnsz = REG(PCIR_CACHELNSZ, 1); - cfg->lattimer = REG(PCIR_LATTIMER, 1); - cfg->intpin = REG(PCIR_INTPIN, 1); - cfg->intline = REG(PCIR_INTLINE, 1); + cfg->mfdev = (cfg->hdrtype & PCIM_MFDEV) != 0; + cfg->hdrtype &= ~PCIM_MFDEV; + STAILQ_INIT(&cfg->maps); - cfg->mfdev = (cfg->hdrtype & PCIM_MFDEV) != 0; - cfg->hdrtype &= ~PCIM_MFDEV; - STAILQ_INIT(&cfg->maps); + cfg->devinfo_size = size; + cfg->iov = NULL; - pci_fixancient(cfg); - pci_hdrtypedata(pcib, b, s, f, cfg); + pci_fixancient(cfg); + pci_hdrtypedata(pcib, b, s, f, cfg); - if (REG(PCIR_STATUS, 2) & PCIM_STATUS_CAPPRESENT) - pci_read_cap(pcib, cfg); + if (REG(PCIR_STATUS, 2) & PCIM_STATUS_CAPPRESENT) + pci_read_cap(pcib, cfg); - STAILQ_INSERT_TAIL(devlist_head, devlist_entry, pci_links); + STAILQ_INSERT_TAIL(&pci_devq, devlist_entry, pci_links); - devlist_entry->conf.pc_sel.pc_domain = cfg->domain; - devlist_entry->conf.pc_sel.pc_bus = cfg->bus; - devlist_entry->conf.pc_sel.pc_dev = cfg->slot; - devlist_entry->conf.pc_sel.pc_func = cfg->func; - devlist_entry->conf.pc_hdr = cfg->hdrtype; + devlist_entry->conf.pc_sel.pc_domain = cfg->domain; + devlist_entry->conf.pc_sel.pc_bus = cfg->bus; + devlist_entry->conf.pc_sel.pc_dev = cfg->slot; + devlist_entry->conf.pc_sel.pc_func = cfg->func; + devlist_entry->conf.pc_hdr = cfg->hdrtype; - devlist_entry->conf.pc_subvendor = cfg->subvendor; - devlist_entry->conf.pc_subdevice = cfg->subdevice; - devlist_entry->conf.pc_vendor = cfg->vendor; - devlist_entry->conf.pc_device = cfg->device; + devlist_entry->conf.pc_subvendor = cfg->subvendor; + devlist_entry->conf.pc_subdevice = cfg->subdevice; + devlist_entry->conf.pc_vendor = cfg->vendor; + devlist_entry->conf.pc_device = cfg->device; - devlist_entry->conf.pc_class = cfg->baseclass; - devlist_entry->conf.pc_subclass = cfg->subclass; - devlist_entry->conf.pc_progif = cfg->progif; - devlist_entry->conf.pc_revid = cfg->revid; + devlist_entry->conf.pc_class = cfg->baseclass; + devlist_entry->conf.pc_subclass = cfg->subclass; + devlist_entry->conf.pc_progif = cfg->progif; + devlist_entry->conf.pc_revid = cfg->revid; - pci_numdevs++; - pci_generation++; - } + pci_numdevs++; + pci_generation++; + return (devlist_entry); +} #undef REG -} static void pci_read_cap(device_t pcib, pcicfgregs *cfg) @@ -2672,8 +2693,9 @@ return (pci_read_config(dev, PCIR_COMMAND, 2) & PCIM_CMD_MEMEN) != 0; } -static void -pci_read_bar(device_t dev, int reg, pci_addr_t *mapp, pci_addr_t *testvalp) +void +pci_read_bar(device_t dev, int reg, pci_addr_t *mapp, pci_addr_t *testvalp, + int *bar64) { struct pci_devinfo *dinfo; pci_addr_t map, testval; @@ -2693,6 +2715,8 @@ pci_write_config(dev, reg, map, 4); *mapp = map; *testvalp = testval; + if (bar64 != NULL) + *bar64 = 0; return; } @@ -2734,6 +2758,8 @@ *mapp = map; *testvalp = testval; + if (bar64 != NULL) + *bar64 = (ln2range == 64); } static void @@ -2788,7 +2814,7 @@ return ((cmd & PCIM_CMD_PORTEN) != 0); } -static struct pci_map * +struct pci_map * pci_add_bar(device_t dev, int reg, pci_addr_t value, pci_addr_t size) { struct pci_devinfo *dinfo; @@ -2859,7 +2885,7 @@ return (barlen); } - pci_read_bar(dev, reg, &map, &testval); + pci_read_bar(dev, reg, &map, &testval, NULL); if (PCI_BAR_MEM(map)) { type = SYS_RES_MEMORY; if (map & PCIM_BAR_MEM_PREFETCH) @@ -3593,6 +3619,51 @@ #undef REG } +#ifdef PCI_IOV +device_t +pci_add_iov_child(device_t bus, device_t pf, size_t size, uint16_t rid, + uint16_t vid, uint16_t did) +{ + struct pci_devinfo *pf_dinfo, *vf_dinfo; + device_t pcib; + int busno, slot, func; + + pf_dinfo = device_get_ivars(pf); + + /* + * Do a sanity check that we have been passed the correct size. If this + * test fails then likely the pci subclass hasn't implemented the + * pci_create_iov_child method like it's supposed it. + */ + if (size != pf_dinfo->cfg.devinfo_size) { + device_printf(pf, + "PCI subclass does not properly implement PCI_IOV\n"); + return (NULL); + } + + pcib = device_get_parent(bus); + + PCIB_DECODE_RID(pcib, rid, &busno, &slot, &func); + + vf_dinfo = pci_fill_devinfo(pcib, pci_get_domain(pcib), busno, slot, func, + vid, did, size); + + vf_dinfo->cfg.flags |= PCICFG_VF; + pci_add_child(bus, vf_dinfo); + + return (vf_dinfo->cfg.dev); +} + +device_t +pci_create_iov_child_method(device_t bus, device_t pf, uint16_t rid, + uint16_t vid, uint16_t did) +{ + + return (pci_add_iov_child(bus, pf, sizeof(struct pci_devinfo), rid, vid, + did)); +} +#endif + void pci_add_child(device_t bus, struct pci_devinfo *dinfo) { @@ -4529,7 +4600,7 @@ static struct resource * pci_reserve_map(device_t dev, device_t child, int type, int *rid, - u_long start, u_long end, u_long count, u_int flags) + u_long start, u_long end, u_long count, u_int num, u_int flags) { struct pci_devinfo *dinfo = device_get_ivars(child); struct resource_list *rl = &dinfo->resources; @@ -4553,7 +4624,7 @@ * have a atapci device in legacy mode and it fails * here, that other code is broken. */ - pci_read_bar(child, *rid, &map, &testval); + pci_read_bar(child, *rid, &map, &testval, NULL); /* * Determine the size of the BAR and ignore BARs with a size @@ -4595,7 +4666,7 @@ * situation where we might allocate the excess to * another driver, which won't work. */ - count = (pci_addr_t)1 << mapsize; + count = ((pci_addr_t)1 << mapsize) * num; if (RF_ALIGNMENT(flags) < mapsize) flags = (flags & ~RF_ALIGNMENT_MASK) | RF_ALIGNMENT_LOG2(mapsize); if (PCI_BAR_MEM(map) && (map & PCIM_BAR_MEM_PREFETCH)) @@ -4626,8 +4697,8 @@ } struct resource * -pci_alloc_resource(device_t dev, device_t child, int type, int *rid, - u_long start, u_long end, u_long count, u_int flags) +pci_alloc_multi_resource(device_t dev, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_long num, u_int flags) { struct pci_devinfo *dinfo; struct resource_list *rl; @@ -4635,10 +4706,6 @@ struct resource *res; pcicfgregs *cfg; - if (device_get_parent(child) != dev) - return (BUS_ALLOC_RESOURCE(device_get_parent(dev), child, - type, rid, start, end, count, flags)); - /* * Perform lazy resource allocation */ @@ -4695,7 +4762,7 @@ rle = resource_list_find(rl, type, *rid); if (rle == NULL) { res = pci_reserve_map(dev, child, type, rid, start, end, - count, flags); + count, num, flags); if (res == NULL) return (NULL); } @@ -4704,6 +4771,38 @@ start, end, count, flags)); } +struct resource * +pci_alloc_resource(device_t dev, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ +#ifdef PCI_IOV + struct pci_devinfo *dinfo; +#endif + + if (device_get_parent(child) != dev) + return (BUS_ALLOC_RESOURCE(device_get_parent(dev), child, + type, rid, start, end, count, flags)); + +#ifdef PCI_IOV + dinfo = device_get_ivars(child); + if (dinfo->cfg.flags & PCICFG_VF) { + switch (type) { + /* VFs can't have I/O BARs. */ + case SYS_RES_IOPORT: + return (NULL); + case SYS_RES_MEMORY: + return (pci_vf_alloc_mem_resource(dev, child, rid, + start, end, count, flags)); + } + + /* Fall through for other types of resource allocations. */ + } +#endif + + return (pci_alloc_multi_resource(dev, child, type, rid, start, end, + count, 1, flags)); +} + int pci_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r) @@ -4718,6 +4817,22 @@ dinfo = device_get_ivars(child); cfg = &dinfo->cfg; + +#ifdef PCI_IOV + if (dinfo->cfg.flags & PCICFG_VF) { + switch (type) { + /* VFs can't have I/O BARs. */ + case SYS_RES_IOPORT: + return (EDOOFUS); + case SYS_RES_MEMORY: + return (pci_vf_release_mem_resource(dev, child, rid, + r)); + } + + /* Fall through for other types of resource allocations. */ + } +#endif + #ifdef NEW_PCIB /* * PCI-PCI bridge I/O window resources are not BARs. For @@ -4880,6 +4995,37 @@ struct pci_devinfo *dinfo = device_get_ivars(child); pcicfgregs *cfg = &dinfo->cfg; +#ifdef PCI_IOV + /* + * SR-IOV VFs don't implement the VID or DID registers, so we have to + * emulate them here. + */ + if (cfg->flags & PCICFG_VF) { + if (reg == PCIR_VENDOR) { + switch (width) { + case 4: + return (cfg->device << 16 | cfg->vendor); + case 2: + return (cfg->vendor); + case 1: + return (cfg->vendor & 0xff); + default: + return (0xffffffff); + } + } else if (reg == PCIR_DEVICE) { + switch (width) { + /* Note that an unaligned 4-byte read is an error. */ + case 2: + return (cfg->device); + case 1: + return (cfg->device & 0xff); + default: + return (0xffffffff); + } + } + } +#endif + return (PCIB_READ_CONFIG(device_get_parent(dev), cfg->bus, cfg->slot, cfg->func, reg, width)); } Index: sys/dev/pci/pci_if.m =================================================================== --- sys/dev/pci/pci_if.m +++ sys/dev/pci/pci_if.m @@ -36,9 +36,21 @@ { return (0); } + + static device_t + null_create_iov_child(device_t bus, device_t pf, uint16_t rid, + uint16_t vid, uint16_t did) + { + device_printf(bus, "PCI_IOV not implemented on this bus.\n"); + return (NULL); + } }; +HEADER { + struct nvlist; +} + METHOD u_int32_t read_config { device_t dev; device_t child; @@ -189,3 +201,23 @@ device_t dev; device_t child; }; + +METHOD int iov_attach { + device_t dev; + device_t child; + struct nvlist *pf_schema; + struct nvlist *vf_schema; +}; + +METHOD int iov_detach { + device_t dev; + device_t child; +}; + +METHOD device_t create_iov_child { + device_t bus; + device_t pf; + uint16_t rid; + uint16_t vid; + uint16_t did; +} DEFAULT null_create_iov_child; Index: sys/dev/pci/pci_iov.h =================================================================== --- sys/dev/pci/pci_iov.h +++ sys/dev/pci/pci_iov.h @@ -0,0 +1,49 @@ +/*- + * Copyright (c) 2013-2015 Sandvine Inc. + * 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 _PCI_IOV_H_ +#define _PCI_IOV_H_ + +#include "pci_iov_if.h" + +struct nvlist; + +static __inline int +pci_iov_attach(device_t dev, struct nvlist *pf_schema, struct nvlist *vf_schema) +{ + return (PCI_IOV_ATTACH(device_get_parent(dev), dev, pf_schema, + vf_schema)); +} + +static __inline int +pci_iov_detach(device_t dev) +{ + return (PCI_IOV_DETACH(device_get_parent(dev), dev)); +} + +#endif /* !_PCI_IOV_H_ */ Index: sys/dev/pci/pci_iov.c =================================================================== --- sys/dev/pci/pci_iov.c +++ sys/dev/pci/pci_iov.c @@ -0,0 +1,980 @@ +/*- + * Copyright (c) 2013-2015 Sandvine Inc. All rights reserved. + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_bus.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "pcib_if.h" + +static MALLOC_DEFINE(M_SRIOV, "sr_iov", "PCI SR-IOV allocations"); + +static d_ioctl_t pci_iov_ioctl; + +static struct cdevsw iov_cdevsw = { + .d_version = D_VERSION, + .d_name = "iov", + .d_ioctl = pci_iov_ioctl +}; + +SYSCTL_DECL(_hw_pci); + +/* + * The maximum amount of memory we will allocate for user configuration of an + * SR-IOV device. 1MB ought to be enough for anyone, but leave this + * configurable just in case. + */ +static u_long pci_iov_max_config = 1024 * 1024; +SYSCTL_ULONG(_hw_pci, OID_AUTO, iov_max_config, CTLFLAG_RWTUN, + &pci_iov_max_config, 0, "Maximum allowed size of SR-IOV configuration."); + + +#define IOV_READ(d, r, w) \ + pci_read_config((d)->cfg.dev, (d)->cfg.iov->iov_pos + r, w) + +#define IOV_WRITE(d, r, v, w) \ + pci_write_config((d)->cfg.dev, (d)->cfg.iov->iov_pos + r, v, w) + +static nvlist_t *pci_iov_build_schema(nvlist_t **pf_schema, + nvlist_t **vf_schema); +static void pci_iov_build_pf_schema(nvlist_t *schema, + nvlist_t **driver_schema); +static void pci_iov_build_vf_schema(nvlist_t *schema, + nvlist_t **driver_schema); +static nvlist_t *pci_iov_get_pf_subsystem_schema(void); +static nvlist_t *pci_iov_get_vf_subsystem_schema(void); + +int +pci_iov_attach_method(device_t bus, device_t dev, nvlist_t *pf_schema, + nvlist_t *vf_schema) +{ + device_t pcib; + struct pci_devinfo *dinfo; + struct pcicfg_iov *iov; + nvlist_t *schema; + uint32_t version; + int error; + int iov_pos; + + dinfo = device_get_ivars(dev); + pcib = device_get_parent(bus); + schema = NULL; + + error = pci_find_extcap(dev, PCIZ_SRIOV, &iov_pos); + + if (error != 0) + return (error); + + version = pci_read_config(dev, iov_pos, 4); + if (PCI_EXTCAP_VER(version) != 1) { + if (bootverbose) + device_printf(dev, + "Unsupported version of SR-IOV (%d) detected\n", + PCI_EXTCAP_VER(version)); + + return (ENXIO); + } + + iov = malloc(sizeof(*dinfo->cfg.iov), M_SRIOV, M_WAITOK | M_ZERO); + + mtx_lock(&Giant); + if (dinfo->cfg.iov != NULL) { + error = EBUSY; + goto cleanup; + } + iov->iov_pos = iov_pos; + + schema = pci_iov_build_schema(&pf_schema, &vf_schema); + if (schema == NULL) { + error = ENOMEM; + goto cleanup; + } + + error = pci_iov_validate_schema(schema); + if (error != 0) + goto cleanup; + iov->iov_schema = schema; + + iov->iov_cdev = make_dev(&iov_cdevsw, device_get_unit(dev), + UID_ROOT, GID_WHEEL, 0600, "iov/%s", device_get_nameunit(dev)); + + if (iov->iov_cdev == NULL) { + error = ENOMEM; + goto cleanup; + } + + dinfo->cfg.iov = iov; + iov->iov_cdev->si_drv1 = dinfo; + mtx_unlock(&Giant); + + return (0); + +cleanup: + nvlist_destroy(schema); + nvlist_destroy(pf_schema); + nvlist_destroy(vf_schema); + free(iov, M_SRIOV); + mtx_unlock(&Giant); + return (error); +} + +int +pci_iov_detach_method(device_t bus, device_t dev) +{ + struct pci_devinfo *dinfo; + struct pcicfg_iov *iov; + + mtx_lock(&Giant); + dinfo = device_get_ivars(dev); + iov = dinfo->cfg.iov; + + if (iov == NULL) { + mtx_unlock(&Giant); + return (0); + } + + if (iov->iov_num_vfs != 0 || iov->iov_flags & IOV_BUSY) { + mtx_unlock(&Giant); + return (EBUSY); + } + + dinfo->cfg.iov = NULL; + + if (iov->iov_cdev) { + destroy_dev(iov->iov_cdev); + iov->iov_cdev = NULL; + } + nvlist_destroy(iov->iov_schema); + + free(iov, M_SRIOV); + mtx_unlock(&Giant); + + return (0); +} + +static nvlist_t * +pci_iov_build_schema(nvlist_t **pf, nvlist_t **vf) +{ + nvlist_t *schema, *pf_driver, *vf_driver; + + /* We always take ownership of the schemas. */ + pf_driver = *pf; + *pf = NULL; + vf_driver = *vf; + *vf = NULL; + + schema = pci_iov_schema_alloc_node(); + if (schema == NULL) + goto cleanup; + + pci_iov_build_pf_schema(schema, &pf_driver); + pci_iov_build_vf_schema(schema, &vf_driver); + + if (nvlist_error(schema) != 0) + goto cleanup; + + return (schema); + +cleanup: + nvlist_destroy(schema); + nvlist_destroy(pf_driver); + nvlist_destroy(vf_driver); + return (NULL); +} + +static void +pci_iov_build_pf_schema(nvlist_t *schema, nvlist_t **driver_schema) +{ + nvlist_t *pf_schema, *iov_schema; + + pf_schema = pci_iov_schema_alloc_node(); + if (pf_schema == NULL) { + nvlist_set_error(schema, ENOMEM); + return; + } + + iov_schema = pci_iov_get_pf_subsystem_schema(); + + /* + * Note that if either *driver_schema or iov_schema is NULL, then + * nvlist_move_nvlist will put the schema in the error state and + * SR-IOV will fail to initialize later, so we don't have to explicitly + * handle that case. + */ + nvlist_move_nvlist(pf_schema, DRIVER_CONFIG_NAME, *driver_schema); + nvlist_move_nvlist(pf_schema, IOV_CONFIG_NAME, iov_schema); + nvlist_move_nvlist(schema, PF_CONFIG_NAME, pf_schema); + *driver_schema = NULL; +} + +static void +pci_iov_build_vf_schema(nvlist_t *schema, nvlist_t **driver_schema) +{ + nvlist_t *vf_schema, *iov_schema; + + vf_schema = pci_iov_schema_alloc_node(); + if (vf_schema == NULL) { + nvlist_set_error(schema, ENOMEM); + return; + } + + iov_schema = pci_iov_get_vf_subsystem_schema(); + + /* + * Note that if either *driver_schema or iov_schema is NULL, then + * nvlist_move_nvlist will put the schema in the error state and + * SR-IOV will fail to initialize later, so we don't have to explicitly + * handle that case. + */ + nvlist_move_nvlist(vf_schema, DRIVER_CONFIG_NAME, *driver_schema); + nvlist_move_nvlist(vf_schema, IOV_CONFIG_NAME, iov_schema); + nvlist_move_nvlist(schema, VF_SCHEMA_NAME, vf_schema); + *driver_schema = NULL; +} + +static nvlist_t * +pci_iov_get_pf_subsystem_schema(void) +{ + nvlist_t *pf; + + pf = pci_iov_schema_alloc_node(); + if (pf == NULL) + return (NULL); + + pci_iov_schema_add_uint16(pf, "num_vfs", IOV_SCHEMA_REQUIRED, -1); + pci_iov_schema_add_string(pf, "device", IOV_SCHEMA_REQUIRED, NULL); + + return (pf); +} + +static nvlist_t * +pci_iov_get_vf_subsystem_schema(void) +{ + nvlist_t *vf; + + vf = pci_iov_schema_alloc_node(); + if (vf == NULL) + return (NULL); + + pci_iov_schema_add_bool(vf, "passthrough", IOV_SCHEMA_HASDEFAULT, 0); + + return (vf); +} + +static int +pci_iov_alloc_bar(struct pci_devinfo *dinfo, int bar, pci_addr_t bar_shift) +{ + struct resource *res; + struct pcicfg_iov *iov; + device_t dev, bus; + u_long start, end; + pci_addr_t bar_size; + int rid; + + iov = dinfo->cfg.iov; + dev = dinfo->cfg.dev; + bus = device_get_parent(dev); + rid = iov->iov_pos + PCIR_SRIOV_BAR(bar); + bar_size = 1 << bar_shift; + + res = pci_alloc_multi_resource(bus, dev, SYS_RES_MEMORY, &rid, 0ul, + ~0ul, 1, iov->iov_num_vfs, RF_ACTIVE); + + if (res == NULL) + return (ENXIO); + + iov->iov_bar[bar].res = res; + iov->iov_bar[bar].bar_size = bar_size; + iov->iov_bar[bar].bar_shift = bar_shift; + + start = rman_get_start(res); + end = rman_get_end(res); + return (rman_manage_region(&iov->rman, start, end)); +} + +static void +pci_iov_add_bars(struct pcicfg_iov *iov, struct pci_devinfo *dinfo) +{ + struct pci_iov_bar *bar; + uint64_t bar_start; + int i; + + for (i = 0; i <= PCIR_MAX_BAR_0; i++) { + bar = &iov->iov_bar[i]; + if (bar->res != NULL) { + bar_start = rman_get_start(bar->res) + + dinfo->cfg.vf.index * bar->bar_size; + + pci_add_bar(dinfo->cfg.dev, PCIR_BAR(i), bar_start, + bar->bar_shift); + } + } +} + +static int +pci_iov_parse_config(struct pcicfg_iov *iov, struct pci_iov_arg *arg, + nvlist_t **ret) +{ + void *packed_config; + nvlist_t *config; + int error; + + config = NULL; + packed_config = NULL; + + if (arg->len > pci_iov_max_config) { + error = EMSGSIZE; + goto out; + } + + packed_config = malloc(arg->len, M_SRIOV, M_WAITOK); + + error = copyin(arg->config, packed_config, arg->len); + if (error != 0) + goto out; + + config = nvlist_unpack(packed_config, arg->len, NV_FLAG_IGNORE_CASE); + if (config == NULL) { + error = EINVAL; + goto out; + } + + error = pci_iov_schema_validate_config(iov->iov_schema, config); + if (error != 0) + goto out; + + error = nvlist_error(config); + if (error != 0) + goto out; + + *ret = config; + config = NULL; + +out: + nvlist_destroy(config); + free(packed_config, M_SRIOV); + return (error); +} + +/* + * Set the ARI_EN bit in the lowest-numbered PCI function with the SR-IOV + * capability. This bit is only writeable on the lowest-numbered PF but + * affects all PFs on the device. + */ +static int +pci_iov_set_ari(device_t bus) +{ + device_t lowest; + device_t *devlist; + int i, error, devcount, lowest_func, lowest_pos, iov_pos, dev_func; + uint16_t iov_ctl; + + /* If ARI is disabled on the downstream port there is nothing to do. */ + if (!PCIB_ARI_ENABLED(device_get_parent(bus))) + return (0); + + error = device_get_children(bus, &devlist, &devcount); + + if (error != 0) + return (error); + + lowest = NULL; + for (i = 0; i < devcount; i++) { + if (pci_find_extcap(devlist[i], PCIZ_SRIOV, &iov_pos) == 0) { + dev_func = pci_get_function(devlist[i]); + if (lowest == NULL || dev_func < lowest_func) { + lowest = devlist[i]; + lowest_func = dev_func; + lowest_pos = iov_pos; + } + } + } + + /* + * If we called this function some device must have the SR-IOV + * capability. + */ + KASSERT(lowest != NULL, + ("Could not find child of %s with SR-IOV capability", + device_get_nameunit(bus))); + + iov_ctl = pci_read_config(lowest, iov_pos + PCIR_SRIOV_CTL, 2); + iov_ctl |= PCIM_SRIOV_ARI_EN; + pci_write_config(lowest, iov_pos + PCIR_SRIOV_CTL, iov_ctl, 2); + free(devlist, M_TEMP); + return (0); +} + +static int +pci_iov_config_page_size(struct pci_devinfo *dinfo) +{ + uint32_t page_cap, page_size; + + page_cap = IOV_READ(dinfo, PCIR_SRIOV_PAGE_CAP, 4); + + /* + * If the system page size is less than the smallest SR-IOV page size + * then round up to the smallest SR-IOV page size. + */ + if (PAGE_SHIFT < PCI_SRIOV_BASE_PAGE_SHIFT) + page_size = (1 << 0); + else + page_size = (1 << (PAGE_SHIFT - PCI_SRIOV_BASE_PAGE_SHIFT)); + + /* Check that the device supports the system page size. */ + if (!(page_size & page_cap)) + return (ENXIO); + + IOV_WRITE(dinfo, PCIR_SRIOV_PAGE_SIZE, page_size, 4); + return (0); +} + +static int +pci_iov_init(device_t dev, uint16_t num_vfs, const nvlist_t *config) +{ + const nvlist_t *device, *driver_config; + + device = nvlist_get_nvlist(config, PF_CONFIG_NAME); + driver_config = nvlist_get_nvlist(device, DRIVER_CONFIG_NAME); + return (PCI_IOV_INIT(dev, num_vfs, driver_config)); +} + +static int +pci_iov_init_rman(device_t pf, struct pcicfg_iov *iov) +{ + int error; + + iov->rman.rm_start = 0; + iov->rman.rm_end = ~0ul; + iov->rman.rm_type = RMAN_ARRAY; + snprintf(iov->rman_name, sizeof(iov->rman_name), "%s VF I/O memory", + device_get_nameunit(pf)); + iov->rman.rm_descr = iov->rman_name; + + error = rman_init(&iov->rman); + if (error != 0) + return (error); + + iov->iov_flags |= IOV_RMAN_INITED; + return (0); +} + +static int +pci_iov_setup_bars(struct pci_devinfo *dinfo) +{ + device_t dev; + struct pcicfg_iov *iov; + pci_addr_t bar_value, testval; + int i, last_64, error; + + iov = dinfo->cfg.iov; + dev = dinfo->cfg.dev; + last_64 = 0; + + for (i = 0; i <= PCIR_MAX_BAR_0; i++) { + /* + * If a PCI BAR is a 64-bit wide BAR, then it spans two + * consecutive registers. Therefore if the last BAR that + * we looked at was a 64-bit BAR, we need to skip this + * register as it's the second half of the last BAR. + */ + if (!last_64) { + pci_read_bar(dev, + iov->iov_pos + PCIR_SRIOV_BAR(i), + &bar_value, &testval, &last_64); + + if (testval != 0) { + error = pci_iov_alloc_bar(dinfo, i, + pci_mapsize(testval)); + if (error != 0) + return (error); + } + } else + last_64 = 0; + } + + return (0); +} + +static void +pci_iov_enumerate_vfs(struct pci_devinfo *dinfo, const nvlist_t *config, + uint16_t first_rid, uint16_t rid_stride) +{ + char device_name[VF_MAX_NAME]; + const nvlist_t *device, *driver_config, *iov_config; + device_t bus, dev, vf; + struct pcicfg_iov *iov; + struct pci_devinfo *vfinfo; + size_t size; + int i, error; + uint16_t vid, did, next_rid; + + iov = dinfo->cfg.iov; + dev = dinfo->cfg.dev; + bus = device_get_parent(dev); + size = dinfo->cfg.devinfo_size; + next_rid = first_rid; + vid = pci_get_vendor(dev); + did = IOV_READ(dinfo, PCIR_SRIOV_VF_DID, 2); + + for (i = 0; i < iov->iov_num_vfs; i++, next_rid += rid_stride) { + snprintf(device_name, sizeof(device_name), VF_PREFIX"%d", i); + device = nvlist_get_nvlist(config, device_name); + iov_config = nvlist_get_nvlist(device, IOV_CONFIG_NAME); + driver_config = nvlist_get_nvlist(device, DRIVER_CONFIG_NAME); + + vf = PCI_CREATE_IOV_CHILD(bus, dev, next_rid, vid, did); + if (vf == NULL) + break; + + /* + * If we are creating passthrough devices then force the ppt + * driver to attach to prevent a VF driver from claiming the + * VFs. + */ + if (nvlist_get_bool(iov_config, "passthrough")) + device_set_devclass_fixed(vf, "ppt"); + + vfinfo = device_get_ivars(vf); + + vfinfo->cfg.iov = iov; + vfinfo->cfg.vf.index = i; + + pci_iov_add_bars(iov, vfinfo); + + error = PCI_IOV_ADD_VF(dev, i, driver_config); + if (error != 0) { + device_printf(dev, "Failed to add VF %d\n", i); + pci_delete_child(bus, vf); + } + } + + bus_generic_attach(bus); +} + +static int +pci_iov_config(struct cdev *cdev, struct pci_iov_arg *arg) +{ + device_t bus, dev; + struct pci_devinfo *dinfo; + struct pcicfg_iov *iov; + nvlist_t *config; + int i, error; + uint16_t rid_off, rid_stride; + uint16_t first_rid, last_rid; + uint16_t iov_ctl; + uint16_t num_vfs, total_vfs; + int iov_inited; + + mtx_lock(&Giant); + dinfo = cdev->si_drv1; + iov = dinfo->cfg.iov; + dev = dinfo->cfg.dev; + bus = device_get_parent(dev); + iov_inited = 0; + config = NULL; + + if ((iov->iov_flags & IOV_BUSY) || iov->iov_num_vfs != 0) { + mtx_unlock(&Giant); + return (EBUSY); + } + iov->iov_flags |= IOV_BUSY; + + error = pci_iov_parse_config(iov, arg, &config); + if (error != 0) + goto out; + + num_vfs = pci_iov_config_get_num_vfs(config); + total_vfs = IOV_READ(dinfo, PCIR_SRIOV_TOTAL_VFS, 2); + if (num_vfs > total_vfs) { + error = EINVAL; + goto out; + } + + error = pci_iov_config_page_size(dinfo); + if (error != 0) + goto out; + + error = pci_iov_set_ari(bus); + if (error != 0) + goto out; + + error = pci_iov_init(dev, num_vfs, config); + if (error != 0) + goto out; + iov_inited = 1; + + IOV_WRITE(dinfo, PCIR_SRIOV_NUM_VFS, num_vfs, 2); + + rid_off = IOV_READ(dinfo, PCIR_SRIOV_VF_OFF, 2); + rid_stride = IOV_READ(dinfo, PCIR_SRIOV_VF_STRIDE, 2); + + first_rid = pci_get_rid(dev) + rid_off; + last_rid = first_rid + (num_vfs - 1) * rid_stride; + + /* We don't yet support allocating extra bus numbers for VFs. */ + if (pci_get_bus(dev) != PCI_RID2BUS(last_rid)) { + error = ENOSPC; + goto out; + } + + iov_ctl = IOV_READ(dinfo, PCIR_SRIOV_CTL, 2); + iov_ctl &= ~(PCIM_SRIOV_VF_EN | PCIM_SRIOV_VF_MSE); + IOV_WRITE(dinfo, PCIR_SRIOV_CTL, iov_ctl, 2); + + error = pci_iov_init_rman(dev, iov); + if (error != 0) + goto out; + + iov->iov_num_vfs = num_vfs; + + error = pci_iov_setup_bars(dinfo); + if (error != 0) + goto out; + + iov_ctl = IOV_READ(dinfo, PCIR_SRIOV_CTL, 2); + iov_ctl |= PCIM_SRIOV_VF_EN | PCIM_SRIOV_VF_MSE; + IOV_WRITE(dinfo, PCIR_SRIOV_CTL, iov_ctl, 2); + + /* Per specification, we must wait 100ms before accessing VFs. */ + pause("iov", roundup(hz, 10)); + pci_iov_enumerate_vfs(dinfo, config, first_rid, rid_stride); + + nvlist_destroy(config); + iov->iov_flags &= ~IOV_BUSY; + mtx_unlock(&Giant); + + return (0); +out: + if (iov_inited) + PCI_IOV_UNINIT(dev); + + for (i = 0; i <= PCIR_MAX_BAR_0; i++) { + if (iov->iov_bar[i].res != NULL) { + pci_release_resource(bus, dev, SYS_RES_MEMORY, + iov->iov_pos + PCIR_SRIOV_BAR(i), + iov->iov_bar[i].res); + pci_delete_resource(bus, dev, SYS_RES_MEMORY, + iov->iov_pos + PCIR_SRIOV_BAR(i)); + iov->iov_bar[i].res = NULL; + } + } + + if (iov->iov_flags & IOV_RMAN_INITED) { + rman_fini(&iov->rman); + iov->iov_flags &= ~IOV_RMAN_INITED; + } + + nvlist_destroy(config); + iov->iov_num_vfs = 0; + iov->iov_flags &= ~IOV_BUSY; + mtx_unlock(&Giant); + return (error); +} + +/* Return true if child is a VF of the given PF. */ +static int +pci_iov_is_child_vf(struct pcicfg_iov *pf, device_t child) +{ + struct pci_devinfo *vfinfo; + + vfinfo = device_get_ivars(child); + + if (!(vfinfo->cfg.flags & PCICFG_VF)) + return (0); + + return (pf == vfinfo->cfg.iov); +} + +static int +pci_iov_delete(struct cdev *cdev) +{ + device_t bus, dev, vf, *devlist; + struct pci_devinfo *dinfo; + struct pcicfg_iov *iov; + int i, error, devcount; + uint32_t iov_ctl; + + mtx_lock(&Giant); + dinfo = cdev->si_drv1; + iov = dinfo->cfg.iov; + dev = dinfo->cfg.dev; + bus = device_get_parent(dev); + devlist = NULL; + + if (iov->iov_flags & IOV_BUSY) { + mtx_unlock(&Giant); + return (EBUSY); + } + + if (iov->iov_num_vfs == 0) { + mtx_unlock(&Giant); + return (ECHILD); + } + + iov->iov_flags |= IOV_BUSY; + + error = device_get_children(bus, &devlist, &devcount); + + if (error != 0) + goto out; + + for (i = 0; i < devcount; i++) { + vf = devlist[i]; + + if (!pci_iov_is_child_vf(iov, vf)) + continue; + + error = device_detach(vf); + if (error != 0) { + device_printf(dev, + "Could not disable SR-IOV: failed to detach VF %s\n", + device_get_nameunit(vf)); + goto out; + } + } + + for (i = 0; i < devcount; i++) { + vf = devlist[i]; + + if (pci_iov_is_child_vf(iov, vf)) + pci_delete_child(bus, vf); + } + PCI_IOV_UNINIT(dev); + + iov_ctl = IOV_READ(dinfo, PCIR_SRIOV_CTL, 2); + iov_ctl &= ~(PCIM_SRIOV_VF_EN | PCIM_SRIOV_VF_MSE); + IOV_WRITE(dinfo, PCIR_SRIOV_CTL, iov_ctl, 2); + IOV_WRITE(dinfo, PCIR_SRIOV_NUM_VFS, 0, 2); + + iov->iov_num_vfs = 0; + + for (i = 0; i <= PCIR_MAX_BAR_0; i++) { + if (iov->iov_bar[i].res != NULL) { + pci_release_resource(bus, dev, SYS_RES_MEMORY, + iov->iov_pos + PCIR_SRIOV_BAR(i), + iov->iov_bar[i].res); + pci_delete_resource(bus, dev, SYS_RES_MEMORY, + iov->iov_pos + PCIR_SRIOV_BAR(i)); + iov->iov_bar[i].res = NULL; + } + } + + if (iov->iov_flags & IOV_RMAN_INITED) { + rman_fini(&iov->rman); + iov->iov_flags &= ~IOV_RMAN_INITED; + } + + error = 0; +out: + free(devlist, M_TEMP); + iov->iov_flags &= ~IOV_BUSY; + mtx_unlock(&Giant); + return (error); +} + +static int +pci_iov_get_schema_ioctl(struct cdev *cdev, struct pci_iov_schema *output) +{ + struct pci_devinfo *dinfo; + void *packed; + size_t output_len, size; + int error; + + packed = NULL; + + mtx_lock(&Giant); + dinfo = cdev->si_drv1; + packed = nvlist_pack(dinfo->cfg.iov->iov_schema, &size); + mtx_unlock(&Giant); + + if (packed == NULL) { + error = ENOMEM; + goto fail; + } + + output_len = output->len; + output->len = size; + if (size <= output_len) { + error = copyout(packed, output->schema, size); + + if (error != 0) + goto fail; + + output->error = 0; + } else + /* + * If we return an error then the ioctl code won't copyout + * output back to userland, so we flag the error in the struct + * instead. + */ + output->error = EMSGSIZE; + + error = 0; + +fail: + free(packed, M_NVLIST); + + return (error); +} + +static int +pci_iov_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, + struct thread *td) +{ + + switch (cmd) { + case IOV_CONFIG: + return (pci_iov_config(dev, (struct pci_iov_arg *)data)); + case IOV_DELETE: + return (pci_iov_delete(dev)); + case IOV_GET_SCHEMA: + return (pci_iov_get_schema_ioctl(dev, + (struct pci_iov_schema *)data)); + default: + return (EINVAL); + } +} + +struct resource * +pci_vf_alloc_mem_resource(device_t dev, device_t child, int *rid, u_long start, + u_long end, u_long count, u_int flags) +{ + struct pci_devinfo *dinfo; + struct pcicfg_iov *iov; + struct pci_map *map; + struct resource *res; + struct resource_list_entry *rle; + u_long bar_start, bar_end; + pci_addr_t bar_length; + int error; + + dinfo = device_get_ivars(child); + iov = dinfo->cfg.iov; + + map = pci_find_bar(child, *rid); + if (map == NULL) + return (NULL); + + bar_length = 1 << map->pm_size; + bar_start = map->pm_value; + bar_end = bar_start + bar_length - 1; + + /* Make sure that the resource fits the constraints. */ + if (bar_start >= end || bar_end <= bar_start || count != 1) + return (NULL); + + /* Clamp the resource to the constraints if necessary. */ + if (bar_start < start) + bar_start = start; + if (bar_end > end) + bar_end = end; + bar_length = bar_end - bar_start + 1; + + res = rman_reserve_resource(&iov->rman, bar_start, bar_end, + bar_length, flags, child); + if (res == NULL) + return (NULL); + + rle = resource_list_add(&dinfo->resources, SYS_RES_MEMORY, *rid, + bar_start, bar_end, 1); + if (rle == NULL) { + rman_release_resource(res); + return (NULL); + } + + rman_set_rid(res, *rid); + + if (flags & RF_ACTIVE) { + error = bus_activate_resource(child, SYS_RES_MEMORY, *rid, res); + if (error != 0) { + resource_list_delete(&dinfo->resources, SYS_RES_MEMORY, + *rid); + rman_release_resource(res); + return (NULL); + } + } + rle->res = res; + + return (res); +} + +int +pci_vf_release_mem_resource(device_t dev, device_t child, int rid, + struct resource *r) +{ + struct pci_devinfo *dinfo; + struct resource_list_entry *rle; + int error; + + dinfo = device_get_ivars(child); + + if (rman_get_flags(r) & RF_ACTIVE) { + error = bus_deactivate_resource(child, SYS_RES_MEMORY, rid, r); + if (error != 0) + return (error); + } + + rle = resource_list_find(&dinfo->resources, SYS_RES_MEMORY, rid); + if (rle != NULL) { + rle->res = NULL; + resource_list_delete(&dinfo->resources, SYS_RES_MEMORY, + rid); + } + + return (rman_release_resource(r)); +} + Index: sys/dev/pci/pci_iov_if.m =================================================================== --- sys/dev/pci/pci_iov_if.m +++ sys/dev/pci/pci_iov_if.m @@ -0,0 +1,52 @@ +#- +# Copyright (c) 2013-2015 Sandvine Inc. +# 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$ +# + +#include + +INTERFACE pci_iov; + +HEADER { + struct nvlist; +} + + +METHOD int init { + device_t dev; + uint16_t num_vfs; + const struct nvlist *config; +}; + +METHOD void uninit { + device_t dev; +}; + +METHOD int add_vf { + device_t dev; + uint16_t vfnum; + const struct nvlist *config; +}; Index: sys/dev/pci/pci_iov_private.h =================================================================== --- sys/dev/pci/pci_iov_private.h +++ sys/dev/pci/pci_iov_private.h @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 2013-2015 Sandvine Inc. All rights reserved. + * 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 _PCI_IOV_PRIVATE_H_ +#define _PCI_IOV_PRIVATE_H_ + +struct pci_iov_bar { + struct resource *res; + + pci_addr_t bar_size; + pci_addr_t bar_shift; +}; + +struct pcicfg_iov { + struct cdev *iov_cdev; + nvlist_t *iov_schema; + + struct pci_iov_bar iov_bar[PCIR_MAX_BAR_0 + 1]; + struct rman rman; + char rman_name[64]; + + int iov_pos; + int iov_num_vfs; + uint32_t iov_flags; +}; + +#define IOV_RMAN_INITED 0x0001 +#define IOV_BUSY 0x0002 + +#endif + Index: sys/dev/pci/pci_iov_schema.c =================================================================== --- sys/dev/pci/pci_iov_schema.c +++ sys/dev/pci/pci_iov_schema.c @@ -0,0 +1,869 @@ +/*- + * Copyright (c) 2014-2015 Sandvine Inc. All rights reserved. + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include + +struct config_type_validator; +typedef int (validate_func)(const struct config_type_validator *, + const nvlist_t *, const char *name); +typedef int (default_validate_t)(const struct config_type_validator *, + const nvlist_t *); + +static validate_func pci_iov_schema_validate_bool; +static validate_func pci_iov_schema_validate_string; +static validate_func pci_iov_schema_validate_uint; +static validate_func pci_iov_schema_validate_unicast_mac; + +static default_validate_t pci_iov_validate_bool_default; +static default_validate_t pci_iov_validate_string_default; +static default_validate_t pci_iov_validate_uint_default; +static default_validate_t pci_iov_validate_unicast_mac_default; + +struct config_type_validator { + const char *type_name; + validate_func *validate; + default_validate_t *default_validate; + uintmax_t limit; +}; + +static struct config_type_validator pci_iov_schema_validators[] = { + { + .type_name = "bool", + .validate = pci_iov_schema_validate_bool, + .default_validate = pci_iov_validate_bool_default + }, + { + .type_name = "string", + .validate = pci_iov_schema_validate_string, + .default_validate = pci_iov_validate_string_default + }, + { + .type_name = "uint8_t", + .validate = pci_iov_schema_validate_uint, + .default_validate = pci_iov_validate_uint_default, + .limit = UINT8_MAX + }, + { + .type_name = "uint16_t", + .validate = pci_iov_schema_validate_uint, + .default_validate = pci_iov_validate_uint_default, + .limit = UINT16_MAX + }, + { + .type_name = "uint32_t", + .validate = pci_iov_schema_validate_uint, + .default_validate = pci_iov_validate_uint_default, + .limit = UINT32_MAX + }, + { + .type_name = "uint64_t", + .validate = pci_iov_schema_validate_uint, + .default_validate = pci_iov_validate_uint_default, + .limit = UINT64_MAX + }, + { + .type_name = "unicast-mac", + .validate = pci_iov_schema_validate_unicast_mac, + .default_validate = pci_iov_validate_unicast_mac_default, + }, +}; + +static const struct config_type_validator * +pci_iov_schema_find_validator(const char *type) +{ + struct config_type_validator *validator; + int i; + + for (i = 0; i < nitems(pci_iov_schema_validators); i++) { + validator = &pci_iov_schema_validators[i]; + if (strcmp(type, validator->type_name) == 0) + return (validator); + } + + return (NULL); +} + +static void +pci_iov_schema_add_type(nvlist_t *entry, const char *type) +{ + + if (pci_iov_schema_find_validator(type) == NULL) { + nvlist_set_error(entry, EINVAL); + return; + } + nvlist_add_string(entry, "type", type); +} + +static void +pci_iov_schema_add_required(nvlist_t *entry, uint32_t flags) +{ + + if (flags & IOV_SCHEMA_REQUIRED) { + if (flags & IOV_SCHEMA_HASDEFAULT) { + nvlist_set_error(entry, EINVAL); + return; + } + + nvlist_add_bool(entry, "required", 1); + } +} + +void +pci_iov_schema_add_bool(nvlist_t *schema, const char *name, uint32_t flags, + int defaultVal) +{ + nvlist_t *entry; + + entry = nvlist_create(NV_FLAG_IGNORE_CASE); + if (entry == NULL) { + nvlist_set_error(schema, ENOMEM); + return; + } + + pci_iov_schema_add_type(entry, "bool"); + if (flags & IOV_SCHEMA_HASDEFAULT) + nvlist_add_bool(entry, "default", defaultVal); + pci_iov_schema_add_required(entry, flags); + + nvlist_move_nvlist(schema, name, entry); +} + +void +pci_iov_schema_add_string(nvlist_t *schema, const char *name, uint32_t flags, + const char *defaultVal) +{ + nvlist_t *entry; + + entry = nvlist_create(NV_FLAG_IGNORE_CASE); + if (entry == NULL) { + nvlist_set_error(schema, ENOMEM); + return; + } + + pci_iov_schema_add_type(entry, "string"); + if (flags & IOV_SCHEMA_HASDEFAULT) + nvlist_add_string(entry, "default", defaultVal); + pci_iov_schema_add_required(entry, flags); + + nvlist_move_nvlist(schema, name, entry); +} + +static void +pci_iov_schema_int(nvlist_t *schema, const char *name, const char *type, + uint32_t flags, uint64_t defaultVal) +{ + nvlist_t *entry; + + entry = nvlist_create(NV_FLAG_IGNORE_CASE); + if (entry == NULL) { + nvlist_set_error(schema, ENOMEM); + return; + } + + pci_iov_schema_add_type(entry, type); + if (flags & IOV_SCHEMA_HASDEFAULT) + nvlist_add_number(entry, "default", defaultVal); + pci_iov_schema_add_required(entry, flags); + + nvlist_move_nvlist(schema, name, entry); +} + +void +pci_iov_schema_add_uint8(nvlist_t *schema, const char *name, uint32_t flags, + uint8_t defaultVal) +{ + + pci_iov_schema_int(schema, name, "uint8_t", flags, defaultVal); +} + +void +pci_iov_schema_add_uint16(nvlist_t *schema, const char *name, uint32_t flags, + uint16_t defaultVal) +{ + + pci_iov_schema_int(schema, name, "uint16_t", flags, defaultVal); +} + +void +pci_iov_schema_add_uint32(nvlist_t *schema, const char *name, uint32_t flags, + uint32_t defaultVal) +{ + + pci_iov_schema_int(schema, name, "uint32_t", flags, defaultVal); +} + +void +pci_iov_schema_add_uint64(nvlist_t *schema, const char *name, uint32_t flags, + uint64_t defaultVal) +{ + + pci_iov_schema_int(schema, name, "uint64_t", flags, defaultVal); +} + +void +pci_iov_schema_add_unicast_mac(nvlist_t *schema, const char *name, + uint32_t flags, const uint8_t * defaultVal) +{ + nvlist_t *entry; + + entry = nvlist_create(NV_FLAG_IGNORE_CASE); + if (entry == NULL) { + nvlist_set_error(schema, ENOMEM); + return; + } + + pci_iov_schema_add_type(entry, "unicast-mac"); + if (flags & IOV_SCHEMA_HASDEFAULT) + nvlist_add_binary(entry, "default", defaultVal, ETHER_ADDR_LEN); + pci_iov_schema_add_required(entry, flags); + + nvlist_move_nvlist(schema, name, entry); +} + +static int +pci_iov_schema_validate_bool(const struct config_type_validator * validator, + const nvlist_t *config, const char *name) +{ + + if (!nvlist_exists_bool(config, name)) + return (EINVAL); + return (0); +} + +static int +pci_iov_schema_validate_string(const struct config_type_validator * validator, + const nvlist_t *config, const char *name) +{ + + if (!nvlist_exists_string(config, name)) + return (EINVAL); + return (0); +} + +static int +pci_iov_schema_validate_uint(const struct config_type_validator * validator, + const nvlist_t *config, const char *name) +{ + uint64_t value; + + if (!nvlist_exists_number(config, name)) + return (EINVAL); + + value = nvlist_get_number(config, name); + + if (value > validator->limit) + return (EINVAL); + + return (0); +} + +static int +pci_iov_schema_validate_unicast_mac( + const struct config_type_validator * validator, + const nvlist_t *config, const char *name) +{ + const uint8_t *mac; + size_t size; + + if (!nvlist_exists_binary(config, name)) + return (EINVAL); + + mac = nvlist_get_binary(config, name, &size); + + if (size != ETHER_ADDR_LEN) + return (EINVAL); + + if (ETHER_IS_MULTICAST(mac)) + return (EINVAL); + + return (0); +} + +static void +pci_iov_config_add_default(const nvlist_t *param_schema, const char *name, + nvlist_t *config) +{ + const void *binary; + size_t len; + + if (nvlist_exists_binary(param_schema, "default")) { + binary = nvlist_get_binary(param_schema, "default", &len); + nvlist_add_binary(config, name, binary, len); + } else if (nvlist_exists_bool(param_schema, "default")) + nvlist_add_bool(config, name, + nvlist_get_bool(param_schema, "default")); + else if (nvlist_exists_number(param_schema, "default")) + nvlist_add_number(config, name, + nvlist_get_number(param_schema, "default")); + else if (nvlist_exists_nvlist(param_schema, "default")) + nvlist_add_nvlist(config, name, + nvlist_get_nvlist(param_schema, "default")); + else if (nvlist_exists_string(param_schema, "default")) + nvlist_add_string(config, name, + nvlist_get_string(param_schema, "default")); + else + panic("Unexpected nvlist type"); +} + +static int +pci_iov_validate_bool_default(const struct config_type_validator * validator, + const nvlist_t *param) +{ + + if (!nvlist_exists_bool(param, DEFAULT_SCHEMA_NAME)) + return (EINVAL); + return (0); +} + +static int +pci_iov_validate_string_default(const struct config_type_validator * validator, + const nvlist_t *param) +{ + + if (!nvlist_exists_string(param, DEFAULT_SCHEMA_NAME)) + return (EINVAL); + return (0); +} + +static int +pci_iov_validate_uint_default(const struct config_type_validator * validator, + const nvlist_t *param) +{ + uint64_t defaultVal; + + if (!nvlist_exists_number(param, DEFAULT_SCHEMA_NAME)) + return (EINVAL); + + defaultVal = nvlist_get_number(param, DEFAULT_SCHEMA_NAME); + if (defaultVal > validator->limit) + return (EINVAL); + return (0); +} + +static int +pci_iov_validate_unicast_mac_default( + const struct config_type_validator * validator, const nvlist_t *param) +{ + const uint8_t *mac; + size_t size; + + if (!nvlist_exists_binary(param, DEFAULT_SCHEMA_NAME)) + return (EINVAL); + + mac = nvlist_get_binary(param, DEFAULT_SCHEMA_NAME, &size); + if (size != ETHER_ADDR_LEN) + return (EINVAL); + + if (ETHER_IS_MULTICAST(mac)) + return (EINVAL); + return (0); +} + +static int +pci_iov_validate_param_schema(const nvlist_t *schema) +{ + const struct config_type_validator *validator; + const char *type; + int error; + + /* All parameters must define a type. */ + if (!nvlist_exists_string(schema, TYPE_SCHEMA_NAME)) + return (EINVAL); + type = nvlist_get_string(schema, TYPE_SCHEMA_NAME); + + validator = pci_iov_schema_find_validator(type); + if (validator == NULL) + return (EINVAL); + + /* Validate that the default value conforms to the type. */ + if (nvlist_exists(schema, DEFAULT_SCHEMA_NAME)) { + error = validator->default_validate(validator, schema); + if (error != 0) + return (error); + + /* Required and Default are mutually exclusive. */ + if (nvlist_exists(schema, REQUIRED_SCHEMA_NAME)) + return (EINVAL); + } + + /* The "Required" field must be a bool. */ + if (nvlist_exists(schema, REQUIRED_SCHEMA_NAME)) { + if (!nvlist_exists_bool(schema, REQUIRED_SCHEMA_NAME)) + return (EINVAL); + } + + return (0); +} + +static int +pci_iov_validate_subsystem_schema(const nvlist_t *dev_schema, const char *name) +{ + const nvlist_t *sub_schema, *param_schema; + const char *param_name; + void *it; + int type, error; + + if (!nvlist_exists_nvlist(dev_schema, name)) + return (EINVAL); + sub_schema = nvlist_get_nvlist(dev_schema, name); + + it = NULL; + while ((param_name = nvlist_next(sub_schema, &type, &it)) != NULL) { + if (type != NV_TYPE_NVLIST) + return (EINVAL); + param_schema = nvlist_get_nvlist(sub_schema, param_name); + + error = pci_iov_validate_param_schema(param_schema); + if (error != 0) + return (error); + } + + return (0); +} + +/* + * Validate that the driver schema does not define any configuration parameters + * whose names collide with configuration parameters defined in the iov schema. + */ +static int +pci_iov_validate_param_collisions(const nvlist_t *dev_schema) +{ + const nvlist_t *iov_schema, *driver_schema; + const char *name; + void *it; + int type; + + driver_schema = nvlist_get_nvlist(dev_schema, DRIVER_CONFIG_NAME); + iov_schema = nvlist_get_nvlist(dev_schema, IOV_CONFIG_NAME); + + it = NULL; + while ((name = nvlist_next(driver_schema, &type, &it)) != NULL) { + if (nvlist_exists(iov_schema, name)) + return (EINVAL); + } + + return (0); +} + +/* + * Validate that we only have IOV and DRIVER subsystems beneath the given + * device schema node. + */ +static int +pci_iov_validate_schema_subsystems(const nvlist_t *dev_schema) +{ + const char *name; + void *it; + int type; + + it = NULL; + while ((name = nvlist_next(dev_schema, &type, &it)) != NULL) { + if (strcmp(name, IOV_CONFIG_NAME) != 0 && + strcmp(name, DRIVER_CONFIG_NAME) != 0) + return (EINVAL); + } + + return (0); +} + +static int +pci_iov_validate_device_schema(const nvlist_t *schema, const char *name) +{ + const nvlist_t *dev_schema; + int error; + + if (!nvlist_exists_nvlist(schema, name)) + return (EINVAL); + dev_schema = nvlist_get_nvlist(schema, name); + + error = pci_iov_validate_subsystem_schema(dev_schema, IOV_CONFIG_NAME); + if (error != 0) + return (error); + + error = pci_iov_validate_subsystem_schema(dev_schema, + DRIVER_CONFIG_NAME); + if (error != 0) + return (error); + + error = pci_iov_validate_param_collisions(dev_schema); + if (error != 0) + return (error); + + return (pci_iov_validate_schema_subsystems(dev_schema)); +} + +/* Validate that we only have PF and VF devices beneath the top-level schema. */ +static int +pci_iov_validate_schema_devices(const nvlist_t *dev_schema) +{ + const char *name; + void *it; + int type; + + it = NULL; + while ((name = nvlist_next(dev_schema, &type, &it)) != NULL) { + if (strcmp(name, PF_CONFIG_NAME) != 0 && + strcmp(name, VF_SCHEMA_NAME) != 0) + return (EINVAL); + } + + return (0); +} + +int +pci_iov_validate_schema(const nvlist_t *schema) +{ + int error; + + error = pci_iov_validate_device_schema(schema, PF_CONFIG_NAME); + if (error != 0) + return (error); + + error = pci_iov_validate_device_schema(schema, VF_SCHEMA_NAME); + if (error != 0) + return (error); + + return (pci_iov_validate_schema_devices(schema)); +} + +/* + * Validate that all required parameters from the schema are specified in the + * config. If any parameter with a default value is not specified in the + * config, add it to config. + */ +static int +pci_iov_schema_validate_required(const nvlist_t *schema, nvlist_t *config) +{ + const nvlist_t *param_schema; + const char *name; + void *cookie; + int type; + + cookie = NULL; + while ((name = nvlist_next(schema, &type, &cookie)) != NULL) { + param_schema = nvlist_get_nvlist(schema, name); + + if (dnvlist_get_bool(param_schema, "required", 0)) { + if (!nvlist_exists(config, name)) + return (EINVAL); + } + + if (nvlist_exists(param_schema, "default") && + !nvlist_exists(config, name)) + pci_iov_config_add_default(param_schema, name, config); + } + + return (nvlist_error(config)); +} + +static int +pci_iov_schema_validate_param(const nvlist_t *schema_param, const char *name, + const nvlist_t *config) +{ + const struct config_type_validator *validator; + const char *type; + + type = nvlist_get_string(schema_param, "type"); + validator = pci_iov_schema_find_validator(type); + + KASSERT(validator != NULL, + ("Schema was not validated: Unknown type %s", type)); + + return (validator->validate(validator, config, name)); +} + +/* + * Validate that all parameters in config are defined in the schema. Also + * validate that the type of the parameter matches the type in the schema. + */ +static int +pci_iov_schema_validate_types(const nvlist_t *schema, const nvlist_t *config) +{ + const nvlist_t *schema_param; + void *cookie; + const char *name; + int type, error; + + cookie = NULL; + while ((name = nvlist_next(config, &type, &cookie)) != NULL) { + if (!nvlist_exists_nvlist(schema, name)) + return (EINVAL); + + schema_param = nvlist_get_nvlist(schema, name); + + error = pci_iov_schema_validate_param(schema_param, name, + config); + + if (error != 0) + return (error); + } + + return (0); +} + +static int +pci_iov_schema_validate_device(const nvlist_t *schema, nvlist_t *config, + const char *schema_device, const char *config_device) +{ + const nvlist_t *device_schema, *iov_schema, *driver_schema; + nvlist_t *device_config, *iov_config, *driver_config; + int error; + + device_config = NULL; + iov_config = NULL; + driver_config = NULL; + + device_schema = nvlist_get_nvlist(schema, schema_device); + iov_schema = nvlist_get_nvlist(device_schema, IOV_CONFIG_NAME); + driver_schema = nvlist_get_nvlist(device_schema, DRIVER_CONFIG_NAME); + + device_config = dnvlist_take_nvlist(config, config_device, NULL); + if (device_config == NULL) { + error = EINVAL; + goto out; + } + + iov_config = dnvlist_take_nvlist(device_config, IOV_CONFIG_NAME, NULL); + if (iov_config == NULL) { + error = EINVAL; + goto out; + } + + driver_config = dnvlist_take_nvlist(device_config, DRIVER_CONFIG_NAME, + NULL); + if (driver_config == NULL) { + error = EINVAL; + goto out; + } + + error = pci_iov_schema_validate_required(iov_schema, iov_config); + if (error != 0) + goto out; + + error = pci_iov_schema_validate_required(driver_schema, driver_config); + if (error != 0) + goto out; + + error = pci_iov_schema_validate_types(iov_schema, iov_config); + if (error != 0) + goto out; + + error = pci_iov_schema_validate_types(driver_schema, driver_config); + if (error != 0) + goto out; + +out: + /* Note that these functions handle NULL pointers safely. */ + nvlist_move_nvlist(device_config, IOV_CONFIG_NAME, iov_config); + nvlist_move_nvlist(device_config, DRIVER_CONFIG_NAME, driver_config); + nvlist_move_nvlist(config, config_device, device_config); + + return (error); +} + +static int +pci_iov_schema_validate_vfs(const nvlist_t *schema, nvlist_t *config, + uint16_t num_vfs) +{ + char device[VF_MAX_NAME]; + int i, error; + + for (i = 0; i < num_vfs; i++) { + snprintf(device, sizeof(device), VF_PREFIX"%d", i); + + error = pci_iov_schema_validate_device(schema, config, + VF_SCHEMA_NAME, device); + if (error != 0) + return (error); + } + + return (0); +} + +/* + * Validate that the device node only has IOV and DRIVER subnodes. + */ +static int +pci_iov_schema_validate_device_subsystems(const nvlist_t *config) +{ + void *cookie; + const char *name; + int type; + + cookie = NULL; + while ((name = nvlist_next(config, &type, &cookie)) != NULL) { + if (strcasecmp(name, IOV_CONFIG_NAME) == 0) + continue; + else if (strcasecmp(name, DRIVER_CONFIG_NAME) == 0) + continue; + + return (EINVAL); + } + + return (0); +} + +/* + * Validate that the string is a valid device node name. It must either be "PF" + * or "VF-n", where n is an integer in the range [0, num_vfs). + */ +static int +pci_iov_schema_validate_dev_name(const char *name, uint16_t num_vfs) +{ + const char *number_start; + char *endp; + u_long vf_num; + + if (strcasecmp(PF_CONFIG_NAME, name) == 0) + return (0); + + /* Ensure that we start with "VF-" */ + if (strncasecmp(name, VF_PREFIX, VF_PREFIX_LEN) != 0) + return (EINVAL); + + number_start = name + VF_PREFIX_LEN; + + /* Filter out name == "VF-" (no number) */ + if (number_start[0] == '\0') + return (EINVAL); + + /* Disallow leading whitespace or +/- */ + if (!isdigit(number_start[0])) + return (EINVAL); + + vf_num = strtoul(number_start, &endp, 10); + if (*endp != '\0') + return (EINVAL); + + /* Disallow leading zeros on VF-[1-9][0-9]* */ + if (vf_num != 0 && number_start[0] == '0') + return (EINVAL); + + /* Disallow leading zeros on VF-0 */ + if (vf_num == 0 && number_start[1] != '\0') + return (EINVAL); + + if (vf_num >= num_vfs) + return (EINVAL); + + return (0); +} + +/* + * Validate that there are no device nodes in config other than the ones for + * the PF and the VFs. This includes validating that all config nodes of the + * form VF-n specify a VF number that is < num_vfs. + */ +static int +pci_iov_schema_validate_device_names(const nvlist_t *config, uint16_t num_vfs) +{ + const nvlist_t *device; + void *cookie; + const char *name; + int type, error; + + cookie = NULL; + while ((name = nvlist_next(config, &type, &cookie)) != NULL) { + error = pci_iov_schema_validate_dev_name(name, num_vfs); + if (error != 0) + return (error); + + /* + * Note that as this is a valid PF/VF node, we know that + * pci_iov_schema_validate_device() has already checked that + * the PF/VF node is an nvlist. + */ + device = nvlist_get_nvlist(config, name); + error = pci_iov_schema_validate_device_subsystems(device); + if (error != 0) + return (error); + } + + return (0); +} + +int +pci_iov_schema_validate_config(const nvlist_t *schema, nvlist_t *config) +{ + int error; + uint16_t num_vfs; + + error = pci_iov_schema_validate_device(schema, config, PF_CONFIG_NAME, + PF_CONFIG_NAME); + if (error != 0) + return (error); + + num_vfs = pci_iov_config_get_num_vfs(config); + + error = pci_iov_schema_validate_vfs(schema, config, num_vfs); + if (error != 0) + return (error); + + return (pci_iov_schema_validate_device_names(config, num_vfs)); +} + +/* + * Return value of the num_vfs parameter. config must have already been + * validated, which guarantees that the parameter exists. + */ +uint16_t +pci_iov_config_get_num_vfs(const nvlist_t *config) +{ + const nvlist_t *pf, *iov; + + pf = nvlist_get_nvlist(config, PF_CONFIG_NAME); + iov = nvlist_get_nvlist(pf, IOV_CONFIG_NAME); + return (nvlist_get_number(iov, "num_vfs")); +} + +/* Allocate a new empty schema node. */ +nvlist_t * +pci_iov_schema_alloc_node(void) +{ + + return (nvlist_create(NV_FLAG_IGNORE_CASE)); +} Index: sys/dev/pci/pci_pci.c =================================================================== --- sys/dev/pci/pci_pci.c +++ sys/dev/pci/pci_pci.c @@ -64,6 +64,9 @@ static int pcib_ari_maxslots(device_t dev); static int pcib_ari_maxfuncs(device_t dev); static int pcib_try_enable_ari(device_t pcib, device_t dev); +static int pcib_ari_enabled(device_t pcib); +static void pcib_ari_decode_rid(device_t pcib, uint16_t rid, + int *bus, int *slot, int *func); static device_method_t pcib_methods[] = { /* Device interface */ @@ -104,6 +107,8 @@ DEVMETHOD(pcib_power_for_sleep, pcib_power_for_sleep), DEVMETHOD(pcib_get_rid, pcib_ari_get_rid), DEVMETHOD(pcib_try_enable_ari, pcib_try_enable_ari), + DEVMETHOD(pcib_ari_enabled, pcib_ari_enabled), + DEVMETHOD(pcib_decode_rid, pcib_ari_decode_rid), DEVMETHOD_END }; @@ -1867,6 +1872,24 @@ return (PCI_FUNCMAX); } +static void +pcib_ari_decode_rid(device_t pcib, uint16_t rid, int *bus, int *slot, + int *func) +{ + struct pcib_softc *sc; + + sc = device_get_softc(pcib); + + *bus = PCI_RID2BUS(rid); + if (sc->flags & PCIB_ENABLE_ARI) { + *slot = PCIE_ARI_RID2SLOT(rid); + *func = PCIE_ARI_RID2FUNC(rid); + } else { + *slot = PCI_RID2SLOT(rid); + *func = PCI_RID2FUNC(rid); + } +} + /* * Since we are a child of a PCI bus, its parent must support the pcib interface. */ @@ -1998,6 +2021,16 @@ return (PCIB_POWER_FOR_SLEEP(bus, dev, pstate)); } +static int +pcib_ari_enabled(device_t pcib) +{ + struct pcib_softc *sc; + + sc = device_get_softc(pcib); + + return ((sc->flags & PCIB_ENABLE_ARI) != 0); +} + static uint16_t pcib_ari_get_rid(device_t pcib, device_t dev) { Index: sys/dev/pci/pci_private.h =================================================================== --- sys/dev/pci/pci_private.h +++ sys/dev/pci/pci_private.h @@ -51,6 +51,8 @@ void pci_add_children(device_t dev, int domain, int busno, size_t dinfo_size); void pci_add_child(device_t bus, struct pci_devinfo *dinfo); +device_t pci_add_iov_child(device_t bus, device_t pf, size_t dinfo_size, + uint16_t rid, uint16_t vid, uint16_t did); void pci_add_resources(device_t bus, device_t dev, int force, uint32_t prefetchmask); int pci_attach_common(device_t dev); @@ -139,4 +141,26 @@ */ void pci_cfg_save(device_t, struct pci_devinfo *, int); +int pci_mapsize(uint64_t testval); +void pci_read_bar(device_t dev, int reg, pci_addr_t *mapp, + pci_addr_t *testvalp, int *bar64); +struct pci_map *pci_add_bar(device_t dev, int reg, pci_addr_t value, + pci_addr_t size); + +struct resource *pci_alloc_multi_resource(device_t dev, device_t child, + int type, int *rid, u_long start, u_long end, u_long count, + u_long num, u_int flags); + +int pci_iov_attach_method(device_t bus, device_t dev, + struct nvlist *pf_schema, struct nvlist *vf_schema); +int pci_iov_detach_method(device_t bus, device_t dev); + +device_t pci_create_iov_child_method(device_t bus, device_t pf, + uint16_t rid, uint16_t vid, uint16_t did); + +struct resource *pci_vf_alloc_mem_resource(device_t dev, device_t child, + int *rid, u_long start, u_long end, u_long count, + u_int flags); +int pci_vf_release_mem_resource(device_t dev, device_t child, + int rid, struct resource *r); #endif /* _PCI_PRIVATE_H_ */ Index: sys/dev/pci/pci_user.c =================================================================== --- sys/dev/pci/pci_user.c +++ sys/dev/pci/pci_user.c @@ -492,7 +492,7 @@ static int pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) { - device_t pcidev, brdev; + device_t pcidev; void *confdata; const char *name; struct devlist *devlist_head; @@ -922,18 +922,12 @@ io->pi_sel.pc_bus, io->pi_sel.pc_dev, io->pi_sel.pc_func); if (pcidev) { - brdev = device_get_parent( - device_get_parent(pcidev)); - #ifdef PRE7_COMPAT if (cmd == PCIOCWRITE || cmd == PCIOCWRITE_OLD) #else if (cmd == PCIOCWRITE) #endif - PCIB_WRITE_CONFIG(brdev, - io->pi_sel.pc_bus, - io->pi_sel.pc_dev, - io->pi_sel.pc_func, + pci_write_config(pcidev, io->pi_reg, io->pi_data, io->pi_width); @@ -940,19 +934,13 @@ #ifdef PRE7_COMPAT else if (cmd == PCIOCREAD_OLD) io_old->pi_data = - PCIB_READ_CONFIG(brdev, - io->pi_sel.pc_bus, - io->pi_sel.pc_dev, - io->pi_sel.pc_func, + pci_read_config(pcidev, io->pi_reg, io->pi_width); #endif else io->pi_data = - PCIB_READ_CONFIG(brdev, - io->pi_sel.pc_bus, - io->pi_sel.pc_dev, - io->pi_sel.pc_func, + pci_read_config(pcidev, io->pi_reg, io->pi_width); error = 0; Index: sys/dev/pci/pcib_if.m =================================================================== --- sys/dev/pci/pcib_if.m +++ sys/dev/pci/pcib_if.m @@ -39,6 +39,13 @@ { return (PCI_INVALID_IRQ); } + + static int + pcib_null_ari_enabled(device_t pcib) + { + + return (0); + } }; # @@ -182,3 +189,21 @@ device_t dev; }; +# +# Return non-zero if PCI ARI is enabled, or zero otherwise +# +METHOD int ari_enabled { + device_t pcib; +} DEFAULT pcib_null_ari_enabled; + +# +# Decode a PCI Routing Identifier (RID) into PCI bus/slot/function +# +METHOD void decode_rid { + device_t pcib; + uint16_t rid; + int *bus; + int *slot; + int *func; +} DEFAULT pcib_decode_rid; + Index: sys/dev/pci/pcib_private.h =================================================================== --- sys/dev/pci/pcib_private.h +++ sys/dev/pci/pcib_private.h @@ -170,5 +170,7 @@ int pcib_release_msix(device_t pcib, device_t dev, int irq); int pcib_map_msi(device_t pcib, device_t dev, int irq, uint64_t *addr, uint32_t *data); uint16_t pcib_get_rid(device_t pcib, device_t dev); +void pcib_decode_rid(device_t pcib, uint16_t rid, int *bus, + int *slot, int *func); #endif Index: sys/dev/pci/pcib_support.c =================================================================== --- sys/dev/pci/pcib_support.c +++ sys/dev/pci/pcib_support.c @@ -66,3 +66,13 @@ return (PCI_RID(bus, slot, func)); } +void +pcib_decode_rid(device_t pcib, uint16_t rid, int *bus, int *slot, + int *func) +{ + + *bus = PCI_RID2BUS(rid); + *slot = PCI_RID2SLOT(rid); + *func = PCI_RID2FUNC(rid); +} + Index: sys/dev/pci/pcireg.h =================================================================== --- sys/dev/pci/pcireg.h +++ sys/dev/pci/pcireg.h @@ -68,6 +68,10 @@ #define PCI_RID2SLOT(rid) (((rid) >> PCI_RID_SLOT_SHIFT) & PCI_SLOTMAX) #define PCI_RID2FUNC(rid) (((rid) >> PCI_RID_FUNC_SHIFT) & PCI_FUNCMAX) +#define PCIE_ARI_RID2SLOT(rid) (0) +#define PCIE_ARI_RID2FUNC(rid) \ + (((rid) >> PCI_RID_FUNC_SHIFT) & PCIE_ARI_FUNCMAX) + #define PCIE_ARI_SLOT(func) (((func) >> PCI_RID_SLOT_SHIFT) & PCI_SLOTMAX) #define PCIE_ARI_FUNC(func) (((func) >> PCI_RID_FUNC_SHIFT) & PCI_FUNCMAX) @@ -920,3 +924,21 @@ #define PCIR_SERIAL_LOW 0x04 #define PCIR_SERIAL_HIGH 0x08 +/* SR-IOV definitions */ +#define PCIR_SRIOV_CTL 0x08 +#define PCIM_SRIOV_VF_EN 0x01 +#define PCIM_SRIOV_VF_MSE 0x08 /* Memory space enable. */ +#define PCIM_SRIOV_ARI_EN 0x10 +#define PCIR_SRIOV_TOTAL_VFS 0x0E +#define PCIR_SRIOV_NUM_VFS 0x10 +#define PCIR_SRIOV_VF_OFF 0x14 +#define PCIR_SRIOV_VF_STRIDE 0x16 +#define PCIR_SRIOV_VF_DID 0x1A +#define PCIR_SRIOV_PAGE_CAP 0x1C +#define PCIR_SRIOV_PAGE_SIZE 0x20 + +#define PCI_SRIOV_BASE_PAGE_SHIFT 12 + +#define PCIR_SRIOV_BARS 0x24 +#define PCIR_SRIOV_BAR(x) (PCIR_SRIOV_BARS + (x) * 4) + Index: sys/dev/pci/pcivar.h =================================================================== --- sys/dev/pci/pcivar.h +++ sys/dev/pci/pcivar.h @@ -50,7 +50,7 @@ struct pci_map { pci_addr_t pm_value; /* Raw BAR value */ pci_addr_t pm_size; - uint8_t pm_reg; + uint16_t pm_reg; STAILQ_ENTRY(pci_map) pm_link; }; @@ -143,6 +143,12 @@ uint8_t pcix_location; /* Offset of PCI-X capability registers. */ }; +struct pcicfg_vf { + int index; +}; + +#define PCICFG_VF 0x0001 /* Device is an SR-IOV Virtual Function */ + /* config header information common to all header types */ typedef struct pcicfg { struct device *dev; /* device which owns this */ @@ -179,6 +185,9 @@ uint8_t slot; /* config space slot address */ uint8_t func; /* config space function number */ + uint32_t flags; /* flags defined above */ + size_t devinfo_size; /* Size of devinfo for this bus type. */ + struct pcicfg_pp pp; /* Power management */ struct pcicfg_vpd vpd; /* Vital product data */ struct pcicfg_msi msi; /* PCI MSI */ @@ -186,6 +195,8 @@ struct pcicfg_ht ht; /* HyperTransport */ struct pcicfg_pcie pcie; /* PCI Express */ struct pcicfg_pcix pcix; /* PCI-X */ + struct pcicfg_iov *iov; /* SR-IOV */ + struct pcicfg_vf vf; /* SR-IOV Virtual Function */ } pcicfgregs; /* additional type 1 device config header information (PCI to PCI bridge) */ Index: sys/dev/pci/schema_private.h =================================================================== --- sys/dev/pci/schema_private.h +++ sys/dev/pci/schema_private.h @@ -0,0 +1,37 @@ +/*- + * Copyright (c) 2014 Sandvine Inc. All rights reserved. + * 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 unmodified, 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 ``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 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 _SCHEMA_PRIVATE_H_ +#define _SCHEMA_PRIVATE_H_ + +int pci_iov_validate_schema(const nvlist_t *schema); + +int pci_iov_schema_validate_config(const nvlist_t *, nvlist_t *); +uint16_t pci_iov_config_get_num_vfs(const nvlist_t *); + +#endif Index: sys/i386/conf/GENERIC =================================================================== --- sys/i386/conf/GENERIC +++ sys/i386/conf/GENERIC @@ -91,6 +91,7 @@ device acpi device eisa device pci +options PCI_IOV # PCI SR-IOV support # Floppy drives device fdc Index: sys/kern/subr_bus.c =================================================================== --- sys/kern/subr_bus.c +++ sys/kern/subr_bus.c @@ -2679,6 +2679,25 @@ } /** + * @brief Set the devclass of a device and mark the devclass fixed. + * @see device_set_devclass() + */ +int +device_set_devclass_fixed(device_t dev, const char *classname) +{ + int error; + + if (classname == NULL) + return (EINVAL); + + error = device_set_devclass(dev, classname); + if (error) + return (error); + dev->flags |= DF_FIXEDCLASS; + return (0); +} + +/** * @brief Set the driver of a device * * @retval 0 success Index: sys/kern/subr_dnvlist.c =================================================================== --- sys/kern/subr_dnvlist.c +++ sys/kern/subr_dnvlist.c @@ -1,128 +0,0 @@ -/*- - * Copyright (c) 2013 The FreeBSD Foundation - * All rights reserved. - * - * This software was developed by Pawel Jakub Dawidek under sponsorship from - * the FreeBSD Foundation. - * - * 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 AUTHORS 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 AUTHORS 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. - */ - -#include -__FBSDID("$FreeBSD$"); - -#ifdef _KERNEL - -#include -#include -#include -#include -#include - -#include - -#else -#include -#include -#include -#include -#endif - -#include -#include - -#include - -#define DNVLIST_GET(ftype, type) \ -ftype \ -dnvlist_get_##type(const nvlist_t *nvl, const char *name, ftype defval) \ -{ \ - \ - if (nvlist_exists_##type(nvl, name)) \ - return (nvlist_get_##type(nvl, name)); \ - else \ - return (defval); \ -} - -DNVLIST_GET(bool, bool) -DNVLIST_GET(uint64_t, number) -DNVLIST_GET(const char *, string) -DNVLIST_GET(const nvlist_t *, nvlist) -#ifndef _KERNEL -DNVLIST_GET(int, descriptor) -#endif - -#undef DNVLIST_GET - -const void * -dnvlist_get_binary(const nvlist_t *nvl, const char *name, size_t *sizep, - const void *defval, size_t defsize) -{ - const void *value; - - if (nvlist_exists_binary(nvl, name)) - value = nvlist_get_binary(nvl, name, sizep); - else { - if (sizep != NULL) - *sizep = defsize; - value = defval; - } - return (value); -} - -#define DNVLIST_TAKE(ftype, type) \ -ftype \ -dnvlist_take_##type(nvlist_t *nvl, const char *name, ftype defval) \ -{ \ - \ - if (nvlist_exists_##type(nvl, name)) \ - return (nvlist_take_##type(nvl, name)); \ - else \ - return (defval); \ -} - -DNVLIST_TAKE(bool, bool) -DNVLIST_TAKE(uint64_t, number) -DNVLIST_TAKE(char *, string) -DNVLIST_TAKE(nvlist_t *, nvlist) -#ifndef _KERNEL -DNVLIST_TAKE(int, descriptor) -#endif - -#undef DNVLIST_TAKE - -void * -dnvlist_take_binary(nvlist_t *nvl, const char *name, size_t *sizep, - void *defval, size_t defsize) -{ - void *value; - - if (nvlist_exists_binary(nvl, name)) - value = nvlist_take_binary(nvl, name, sizep); - else { - if (sizep != NULL) - *sizep = defsize; - value = defval; - } - return (value); -} - Index: sys/kern/subr_nvlist.c =================================================================== --- sys/kern/subr_nvlist.c +++ sys/kern/subr_nvlist.c @@ -1,1475 +0,0 @@ -/*- - * Copyright (c) 2009-2013 The FreeBSD Foundation - * All rights reserved. - * - * This software was developed by Pawel Jakub Dawidek under sponsorship from - * the FreeBSD Foundation. - * - * 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 AUTHORS 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 AUTHORS 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. - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include - -#ifdef _KERNEL - -#include -#include -#include -#include -#include - -#include - -#else -#include - -#include -#include -#include -#include -#define _WITH_DPRINTF -#include -#include -#include -#include - -#include "msgio.h" -#endif - -#ifdef HAVE_PJDLOG -#include -#endif - -#include -#include -#include -#include - -#ifndef HAVE_PJDLOG -#ifdef _KERNEL -#define PJDLOG_ASSERT(...) MPASS(__VA_ARGS__) -#define PJDLOG_RASSERT(expr, ...) KASSERT(expr, (__VA_ARGS__)) -#define PJDLOG_ABORT(...) panic(__VA_ARGS__) -#else -#include -#define PJDLOG_ASSERT(...) assert(__VA_ARGS__) -#define PJDLOG_RASSERT(expr, ...) assert(expr) -#define PJDLOG_ABORT(...) do { \ - fprintf(stderr, "%s:%u: ", __FILE__, __LINE__); \ - fprintf(stderr, __VA_ARGS__); \ - fprintf(stderr, "\n"); \ - abort(); \ -} while (0) -#endif -#endif - -#define NV_FLAG_PRIVATE_MASK (NV_FLAG_BIG_ENDIAN) -#define NV_FLAG_PUBLIC_MASK (NV_FLAG_IGNORE_CASE) -#define NV_FLAG_ALL_MASK (NV_FLAG_PRIVATE_MASK | NV_FLAG_PUBLIC_MASK) - -#define NVLIST_MAGIC 0x6e766c /* "nvl" */ -struct nvlist { - int nvl_magic; - int nvl_error; - int nvl_flags; - nvpair_t *nvl_parent; - struct nvl_head nvl_head; -}; - -#define NVLIST_ASSERT(nvl) do { \ - PJDLOG_ASSERT((nvl) != NULL); \ - PJDLOG_ASSERT((nvl)->nvl_magic == NVLIST_MAGIC); \ -} while (0) - -#ifdef _KERNEL -MALLOC_DEFINE(M_NVLIST, "nvlist", "kernel nvlist"); -#endif - -#define NVPAIR_ASSERT(nvp) nvpair_assert(nvp) - -#define NVLIST_HEADER_MAGIC 0x6c -#define NVLIST_HEADER_VERSION 0x00 -struct nvlist_header { - uint8_t nvlh_magic; - uint8_t nvlh_version; - uint8_t nvlh_flags; - uint64_t nvlh_descriptors; - uint64_t nvlh_size; -} __packed; - -nvlist_t * -nvlist_create(int flags) -{ - nvlist_t *nvl; - - PJDLOG_ASSERT((flags & ~(NV_FLAG_PUBLIC_MASK)) == 0); - - nvl = nv_malloc(sizeof(*nvl)); - nvl->nvl_error = 0; - nvl->nvl_flags = flags; - nvl->nvl_parent = NULL; - TAILQ_INIT(&nvl->nvl_head); - nvl->nvl_magic = NVLIST_MAGIC; - - return (nvl); -} - -void -nvlist_destroy(nvlist_t *nvl) -{ - nvpair_t *nvp; - int serrno; - - if (nvl == NULL) - return; - - SAVE_ERRNO(serrno); - - NVLIST_ASSERT(nvl); - - while ((nvp = nvlist_first_nvpair(nvl)) != NULL) { - nvlist_remove_nvpair(nvl, nvp); - nvpair_free(nvp); - } - nvl->nvl_magic = 0; - nv_free(nvl); - - RESTORE_ERRNO(serrno); -} - -void -nvlist_set_error(nvlist_t *nvl, int error) -{ - - PJDLOG_ASSERT(error != 0); - - /* - * Check for error != 0 so that we don't do the wrong thing if somebody - * tries to abuse this API when asserts are disabled. - */ - if (nvl != NULL && error != 0 && nvl->nvl_error == 0) - nvl->nvl_error = error; -} - -int -nvlist_error(const nvlist_t *nvl) -{ - - if (nvl == NULL) - return (ENOMEM); - - NVLIST_ASSERT(nvl); - - return (nvl->nvl_error); -} - -nvpair_t * -nvlist_get_nvpair_parent(const nvlist_t *nvl) -{ - - NVLIST_ASSERT(nvl); - - return (nvl->nvl_parent); -} - -const nvlist_t * -nvlist_get_parent(const nvlist_t *nvl, void **cookiep) -{ - nvpair_t *nvp; - - NVLIST_ASSERT(nvl); - - nvp = nvl->nvl_parent; - if (cookiep != NULL) - *cookiep = nvp; - if (nvp == NULL) - return (NULL); - - return (nvpair_nvlist(nvp)); -} - -void -nvlist_set_parent(nvlist_t *nvl, nvpair_t *parent) -{ - - NVLIST_ASSERT(nvl); - - nvl->nvl_parent = parent; -} - -bool -nvlist_empty(const nvlist_t *nvl) -{ - - NVLIST_ASSERT(nvl); - PJDLOG_ASSERT(nvl->nvl_error == 0); - - return (nvlist_first_nvpair(nvl) == NULL); -} - -int -nvlist_flags(const nvlist_t *nvl) -{ - - NVLIST_ASSERT(nvl); - PJDLOG_ASSERT(nvl->nvl_error == 0); - PJDLOG_ASSERT((nvl->nvl_flags & ~(NV_FLAG_PUBLIC_MASK)) == 0); - - return (nvl->nvl_flags); -} - -static void -nvlist_report_missing(int type, const char *name) -{ - - PJDLOG_ABORT("Element '%s' of type %s doesn't exist.", - name, nvpair_type_string(type)); -} - -static nvpair_t * -nvlist_find(const nvlist_t *nvl, int type, const char *name) -{ - nvpair_t *nvp; - - NVLIST_ASSERT(nvl); - PJDLOG_ASSERT(nvl->nvl_error == 0); - PJDLOG_ASSERT(type == NV_TYPE_NONE || - (type >= NV_TYPE_FIRST && type <= NV_TYPE_LAST)); - - for (nvp = nvlist_first_nvpair(nvl); nvp != NULL; - nvp = nvlist_next_nvpair(nvl, nvp)) { - if (type != NV_TYPE_NONE && nvpair_type(nvp) != type) - continue; - if ((nvl->nvl_flags & NV_FLAG_IGNORE_CASE) != 0) { - if (strcasecmp(nvpair_name(nvp), name) != 0) - continue; - } else { - if (strcmp(nvpair_name(nvp), name) != 0) - continue; - } - break; - } - - if (nvp == NULL) - RESTORE_ERRNO(ENOENT); - - return (nvp); -} - -bool -nvlist_exists_type(const nvlist_t *nvl, const char *name, int type) -{ - - NVLIST_ASSERT(nvl); - PJDLOG_ASSERT(nvl->nvl_error == 0); - PJDLOG_ASSERT(type == NV_TYPE_NONE || - (type >= NV_TYPE_FIRST && type <= NV_TYPE_LAST)); - - return (nvlist_find(nvl, type, name) != NULL); -} - -void -nvlist_free_type(nvlist_t *nvl, const char *name, int type) -{ - nvpair_t *nvp; - - NVLIST_ASSERT(nvl); - PJDLOG_ASSERT(nvl->nvl_error == 0); - PJDLOG_ASSERT(type == NV_TYPE_NONE || - (type >= NV_TYPE_FIRST && type <= NV_TYPE_LAST)); - - nvp = nvlist_find(nvl, type, name); - if (nvp != NULL) - nvlist_free_nvpair(nvl, nvp); - else - nvlist_report_missing(type, name); -} - -nvlist_t * -nvlist_clone(const nvlist_t *nvl) -{ - nvlist_t *newnvl; - nvpair_t *nvp, *newnvp; - - NVLIST_ASSERT(nvl); - - if (nvl->nvl_error != 0) { - RESTORE_ERRNO(nvl->nvl_error); - return (NULL); - } - - newnvl = nvlist_create(nvl->nvl_flags & NV_FLAG_PUBLIC_MASK); - for (nvp = nvlist_first_nvpair(nvl); nvp != NULL; - nvp = nvlist_next_nvpair(nvl, nvp)) { - newnvp = nvpair_clone(nvp); - if (newnvp == NULL) - break; - nvlist_move_nvpair(newnvl, newnvp); - } - if (nvp != NULL) { - nvlist_destroy(newnvl); - return (NULL); - } - return (newnvl); -} - -#ifndef _KERNEL -static bool -nvlist_dump_error_check(const nvlist_t *nvl, int fd, int level) -{ - - if (nvlist_error(nvl) != 0) { - dprintf(fd, "%*serror: %d\n", level * 4, "", - nvlist_error(nvl)); - return (true); - } - - return (false); -} - -/* - * Dump content of nvlist. - */ -void -nvlist_dump(const nvlist_t *nvl, int fd) -{ - const nvlist_t *tmpnvl; - nvpair_t *nvp, *tmpnvp; - void *cookie; - int level; - - level = 0; - if (nvlist_dump_error_check(nvl, fd, level)) - return; - - nvp = nvlist_first_nvpair(nvl); - while (nvp != NULL) { - dprintf(fd, "%*s%s (%s):", level * 4, "", nvpair_name(nvp), - nvpair_type_string(nvpair_type(nvp))); - switch (nvpair_type(nvp)) { - case NV_TYPE_NULL: - dprintf(fd, " null\n"); - break; - case NV_TYPE_BOOL: - dprintf(fd, " %s\n", nvpair_get_bool(nvp) ? - "TRUE" : "FALSE"); - break; - case NV_TYPE_NUMBER: - dprintf(fd, " %ju (%jd) (0x%jx)\n", - (uintmax_t)nvpair_get_number(nvp), - (intmax_t)nvpair_get_number(nvp), - (uintmax_t)nvpair_get_number(nvp)); - break; - case NV_TYPE_STRING: - dprintf(fd, " [%s]\n", nvpair_get_string(nvp)); - break; - case NV_TYPE_NVLIST: - dprintf(fd, "\n"); - tmpnvl = nvpair_get_nvlist(nvp); - if (nvlist_dump_error_check(tmpnvl, fd, level + 1)) - break; - tmpnvp = nvlist_first_nvpair(tmpnvl); - if (tmpnvp != NULL) { - nvl = tmpnvl; - nvp = tmpnvp; - level++; - continue; - } - break; - case NV_TYPE_DESCRIPTOR: - dprintf(fd, " %d\n", nvpair_get_descriptor(nvp)); - break; - case NV_TYPE_BINARY: - { - const unsigned char *binary; - unsigned int ii; - size_t size; - - binary = nvpair_get_binary(nvp, &size); - dprintf(fd, " %zu ", size); - for (ii = 0; ii < size; ii++) - dprintf(fd, "%02hhx", binary[ii]); - dprintf(fd, "\n"); - break; - } - default: - PJDLOG_ABORT("Unknown type: %d.", nvpair_type(nvp)); - } - - while ((nvp = nvlist_next_nvpair(nvl, nvp)) == NULL) { - cookie = NULL; - nvl = nvlist_get_parent(nvl, &cookie); - if (nvl == NULL) - return; - nvp = cookie; - level--; - } - } -} - -void -nvlist_fdump(const nvlist_t *nvl, FILE *fp) -{ - - fflush(fp); - nvlist_dump(nvl, fileno(fp)); -} -#endif - -/* - * The function obtains size of the nvlist after nvlist_pack(). - */ -size_t -nvlist_size(const nvlist_t *nvl) -{ - const nvlist_t *tmpnvl; - const nvpair_t *nvp, *tmpnvp; - void *cookie; - size_t size; - - NVLIST_ASSERT(nvl); - PJDLOG_ASSERT(nvl->nvl_error == 0); - - size = sizeof(struct nvlist_header); - nvp = nvlist_first_nvpair(nvl); - while (nvp != NULL) { - size += nvpair_header_size(); - size += strlen(nvpair_name(nvp)) + 1; - if (nvpair_type(nvp) == NV_TYPE_NVLIST) { - size += sizeof(struct nvlist_header); - size += nvpair_header_size() + 1; - tmpnvl = nvpair_get_nvlist(nvp); - PJDLOG_ASSERT(tmpnvl->nvl_error == 0); - tmpnvp = nvlist_first_nvpair(tmpnvl); - if (tmpnvp != NULL) { - nvl = tmpnvl; - nvp = tmpnvp; - continue; - } - } else { - size += nvpair_size(nvp); - } - - while ((nvp = nvlist_next_nvpair(nvl, nvp)) == NULL) { - cookie = NULL; - nvl = nvlist_get_parent(nvl, &cookie); - if (nvl == NULL) - goto out; - nvp = cookie; - } - } - -out: - return (size); -} - -#ifndef _KERNEL -static int * -nvlist_xdescriptors(const nvlist_t *nvl, int *descs, int level) -{ - const nvpair_t *nvp; - - NVLIST_ASSERT(nvl); - PJDLOG_ASSERT(nvl->nvl_error == 0); - PJDLOG_ASSERT(level < 3); - - for (nvp = nvlist_first_nvpair(nvl); nvp != NULL; - nvp = nvlist_next_nvpair(nvl, nvp)) { - switch (nvpair_type(nvp)) { - case NV_TYPE_DESCRIPTOR: - *descs = nvpair_get_descriptor(nvp); - descs++; - break; - case NV_TYPE_NVLIST: - descs = nvlist_xdescriptors(nvpair_get_nvlist(nvp), - descs, level + 1); - break; - } - } - - return (descs); -} -#endif - -#ifndef _KERNEL -int * -nvlist_descriptors(const nvlist_t *nvl, size_t *nitemsp) -{ - size_t nitems; - int *fds; - - nitems = nvlist_ndescriptors(nvl); - fds = nv_malloc(sizeof(fds[0]) * (nitems + 1)); - if (fds == NULL) - return (NULL); - if (nitems > 0) - nvlist_xdescriptors(nvl, fds, 0); - fds[nitems] = -1; - if (nitemsp != NULL) - *nitemsp = nitems; - return (fds); -} -#endif - -static size_t -nvlist_xndescriptors(const nvlist_t *nvl, int level) -{ -#ifndef _KERNEL - const nvpair_t *nvp; - size_t ndescs; - - NVLIST_ASSERT(nvl); - PJDLOG_ASSERT(nvl->nvl_error == 0); - PJDLOG_ASSERT(level < 3); - - ndescs = 0; - for (nvp = nvlist_first_nvpair(nvl); nvp != NULL; - nvp = nvlist_next_nvpair(nvl, nvp)) { - switch (nvpair_type(nvp)) { - case NV_TYPE_DESCRIPTOR: - ndescs++; - break; - case NV_TYPE_NVLIST: - ndescs += nvlist_xndescriptors(nvpair_get_nvlist(nvp), - level + 1); - break; - } - } - - return (ndescs); -#else - return (0); -#endif -} - -size_t -nvlist_ndescriptors(const nvlist_t *nvl) -{ - - return (nvlist_xndescriptors(nvl, 0)); -} - -static unsigned char * -nvlist_pack_header(const nvlist_t *nvl, unsigned char *ptr, size_t *leftp) -{ - struct nvlist_header nvlhdr; - - NVLIST_ASSERT(nvl); - - nvlhdr.nvlh_magic = NVLIST_HEADER_MAGIC; - nvlhdr.nvlh_version = NVLIST_HEADER_VERSION; - nvlhdr.nvlh_flags = nvl->nvl_flags; -#if BYTE_ORDER == BIG_ENDIAN - nvlhdr.nvlh_flags |= NV_FLAG_BIG_ENDIAN; -#endif - nvlhdr.nvlh_descriptors = nvlist_ndescriptors(nvl); - nvlhdr.nvlh_size = *leftp - sizeof(nvlhdr); - PJDLOG_ASSERT(*leftp >= sizeof(nvlhdr)); - memcpy(ptr, &nvlhdr, sizeof(nvlhdr)); - ptr += sizeof(nvlhdr); - *leftp -= sizeof(nvlhdr); - - return (ptr); -} - -void * -nvlist_xpack(const nvlist_t *nvl, int64_t *fdidxp, size_t *sizep) -{ - unsigned char *buf, *ptr; - size_t left, size; - const nvlist_t *tmpnvl; - nvpair_t *nvp, *tmpnvp; - void *cookie; - - NVLIST_ASSERT(nvl); - - if (nvl->nvl_error != 0) { - RESTORE_ERRNO(nvl->nvl_error); - return (NULL); - } - - size = nvlist_size(nvl); - buf = nv_malloc(size); - if (buf == NULL) - return (NULL); - - ptr = buf; - left = size; - - ptr = nvlist_pack_header(nvl, ptr, &left); - - nvp = nvlist_first_nvpair(nvl); - while (nvp != NULL) { - NVPAIR_ASSERT(nvp); - - nvpair_init_datasize(nvp); - ptr = nvpair_pack_header(nvp, ptr, &left); - if (ptr == NULL) { - nv_free(buf); - return (NULL); - } - switch (nvpair_type(nvp)) { - case NV_TYPE_NULL: - ptr = nvpair_pack_null(nvp, ptr, &left); - break; - case NV_TYPE_BOOL: - ptr = nvpair_pack_bool(nvp, ptr, &left); - break; - case NV_TYPE_NUMBER: - ptr = nvpair_pack_number(nvp, ptr, &left); - break; - case NV_TYPE_STRING: - ptr = nvpair_pack_string(nvp, ptr, &left); - break; - case NV_TYPE_NVLIST: - tmpnvl = nvpair_get_nvlist(nvp); - ptr = nvlist_pack_header(tmpnvl, ptr, &left); - if (ptr == NULL) - goto out; - tmpnvp = nvlist_first_nvpair(tmpnvl); - if (tmpnvp != NULL) { - nvl = tmpnvl; - nvp = tmpnvp; - continue; - } - ptr = nvpair_pack_nvlist_up(ptr, &left); - break; -#ifndef _KERNEL - case NV_TYPE_DESCRIPTOR: - ptr = nvpair_pack_descriptor(nvp, ptr, fdidxp, &left); - break; -#endif - case NV_TYPE_BINARY: - ptr = nvpair_pack_binary(nvp, ptr, &left); - break; - default: - PJDLOG_ABORT("Invalid type (%d).", nvpair_type(nvp)); - } - if (ptr == NULL) { - nv_free(buf); - return (NULL); - } - while ((nvp = nvlist_next_nvpair(nvl, nvp)) == NULL) { - cookie = NULL; - nvl = nvlist_get_parent(nvl, &cookie); - if (nvl == NULL) - goto out; - nvp = cookie; - ptr = nvpair_pack_nvlist_up(ptr, &left); - if (ptr == NULL) - goto out; - } - } - -out: - if (sizep != NULL) - *sizep = size; - return (buf); -} - -void * -nvlist_pack(const nvlist_t *nvl, size_t *sizep) -{ - - NVLIST_ASSERT(nvl); - - if (nvl->nvl_error != 0) { - RESTORE_ERRNO(nvl->nvl_error); - return (NULL); - } - - if (nvlist_ndescriptors(nvl) > 0) { - RESTORE_ERRNO(EOPNOTSUPP); - return (NULL); - } - - return (nvlist_xpack(nvl, NULL, sizep)); -} - -static bool -nvlist_check_header(struct nvlist_header *nvlhdrp) -{ - - if (nvlhdrp->nvlh_magic != NVLIST_HEADER_MAGIC) { - RESTORE_ERRNO(EINVAL); - return (false); - } - if ((nvlhdrp->nvlh_flags & ~NV_FLAG_ALL_MASK) != 0) { - RESTORE_ERRNO(EINVAL); - return (false); - } -#if BYTE_ORDER == BIG_ENDIAN - if ((nvlhdrp->nvlh_flags & NV_FLAG_BIG_ENDIAN) == 0) { - nvlhdrp->nvlh_size = le64toh(nvlhdrp->nvlh_size); - nvlhdrp->nvlh_descriptors = le64toh(nvlhdrp->nvlh_descriptors); - } -#else - if ((nvlhdrp->nvlh_flags & NV_FLAG_BIG_ENDIAN) != 0) { - nvlhdrp->nvlh_size = be64toh(nvlhdrp->nvlh_size); - nvlhdrp->nvlh_descriptors = be64toh(nvlhdrp->nvlh_descriptors); - } -#endif - return (true); -} - -const unsigned char * -nvlist_unpack_header(nvlist_t *nvl, const unsigned char *ptr, size_t nfds, - bool *isbep, size_t *leftp) -{ - struct nvlist_header nvlhdr; - - if (*leftp < sizeof(nvlhdr)) - goto failed; - - memcpy(&nvlhdr, ptr, sizeof(nvlhdr)); - - if (!nvlist_check_header(&nvlhdr)) - goto failed; - - if (nvlhdr.nvlh_size != *leftp - sizeof(nvlhdr)) - goto failed; - - /* - * nvlh_descriptors might be smaller than nfds in embedded nvlists. - */ - if (nvlhdr.nvlh_descriptors > nfds) - goto failed; - - if ((nvlhdr.nvlh_flags & ~NV_FLAG_ALL_MASK) != 0) - goto failed; - - nvl->nvl_flags = (nvlhdr.nvlh_flags & NV_FLAG_PUBLIC_MASK); - - ptr += sizeof(nvlhdr); - if (isbep != NULL) - *isbep = (((int)nvlhdr.nvlh_flags & NV_FLAG_BIG_ENDIAN) != 0); - *leftp -= sizeof(nvlhdr); - - return (ptr); -failed: - RESTORE_ERRNO(EINVAL); - return (NULL); -} - -nvlist_t * -nvlist_xunpack(const void *buf, size_t size, const int *fds, size_t nfds) -{ - const unsigned char *ptr; - nvlist_t *nvl, *retnvl, *tmpnvl; - nvpair_t *nvp; - size_t left; - bool isbe; - - left = size; - ptr = buf; - - tmpnvl = NULL; - nvl = retnvl = nvlist_create(0); - if (nvl == NULL) - goto failed; - - ptr = nvlist_unpack_header(nvl, ptr, nfds, &isbe, &left); - if (ptr == NULL) - goto failed; - - while (left > 0) { - ptr = nvpair_unpack(isbe, ptr, &left, &nvp); - if (ptr == NULL) - goto failed; - switch (nvpair_type(nvp)) { - case NV_TYPE_NULL: - ptr = nvpair_unpack_null(isbe, nvp, ptr, &left); - break; - case NV_TYPE_BOOL: - ptr = nvpair_unpack_bool(isbe, nvp, ptr, &left); - break; - case NV_TYPE_NUMBER: - ptr = nvpair_unpack_number(isbe, nvp, ptr, &left); - break; - case NV_TYPE_STRING: - ptr = nvpair_unpack_string(isbe, nvp, ptr, &left); - break; - case NV_TYPE_NVLIST: - ptr = nvpair_unpack_nvlist(isbe, nvp, ptr, &left, nfds, - &tmpnvl); - nvlist_set_parent(tmpnvl, nvp); - break; -#ifndef _KERNEL - case NV_TYPE_DESCRIPTOR: - ptr = nvpair_unpack_descriptor(isbe, nvp, ptr, &left, - fds, nfds); - break; -#endif - case NV_TYPE_BINARY: - ptr = nvpair_unpack_binary(isbe, nvp, ptr, &left); - break; - case NV_TYPE_NVLIST_UP: - if (nvl->nvl_parent == NULL) - goto failed; - nvl = nvpair_nvlist(nvl->nvl_parent); - continue; - default: - PJDLOG_ABORT("Invalid type (%d).", nvpair_type(nvp)); - } - if (ptr == NULL) - goto failed; - nvlist_move_nvpair(nvl, nvp); - if (tmpnvl != NULL) { - nvl = tmpnvl; - tmpnvl = NULL; - } - } - - return (retnvl); -failed: - nvlist_destroy(retnvl); - return (NULL); -} - -nvlist_t * -nvlist_unpack(const void *buf, size_t size) -{ - - return (nvlist_xunpack(buf, size, NULL, 0)); -} - -#ifndef _KERNEL -int -nvlist_send(int sock, const nvlist_t *nvl) -{ - size_t datasize, nfds; - int *fds; - void *data; - int64_t fdidx; - int serrno, ret; - - if (nvlist_error(nvl) != 0) { - errno = nvlist_error(nvl); - return (-1); - } - - fds = nvlist_descriptors(nvl, &nfds); - if (fds == NULL) - return (-1); - - ret = -1; - data = NULL; - fdidx = 0; - - data = nvlist_xpack(nvl, &fdidx, &datasize); - if (data == NULL) - goto out; - - if (buf_send(sock, data, datasize) == -1) - goto out; - - if (nfds > 0) { - if (fd_send(sock, fds, nfds) == -1) - goto out; - } - - ret = 0; -out: - serrno = errno; - free(fds); - free(data); - errno = serrno; - return (ret); -} - -nvlist_t * -nvlist_recv(int sock) -{ - struct nvlist_header nvlhdr; - nvlist_t *nvl, *ret; - unsigned char *buf; - size_t nfds, size, i; - int serrno, *fds; - - if (buf_recv(sock, &nvlhdr, sizeof(nvlhdr)) == -1) - return (NULL); - - if (!nvlist_check_header(&nvlhdr)) - return (NULL); - - nfds = (size_t)nvlhdr.nvlh_descriptors; - size = sizeof(nvlhdr) + (size_t)nvlhdr.nvlh_size; - - buf = malloc(size); - if (buf == NULL) - return (NULL); - - memcpy(buf, &nvlhdr, sizeof(nvlhdr)); - - ret = NULL; - fds = NULL; - - if (buf_recv(sock, buf + sizeof(nvlhdr), size - sizeof(nvlhdr)) == -1) - goto out; - - if (nfds > 0) { - fds = malloc(nfds * sizeof(fds[0])); - if (fds == NULL) - goto out; - if (fd_recv(sock, fds, nfds) == -1) - goto out; - } - - nvl = nvlist_xunpack(buf, size, fds, nfds); - if (nvl == NULL) { - for (i = 0; i < nfds; i++) - close(fds[i]); - goto out; - } - - ret = nvl; -out: - serrno = errno; - free(buf); - free(fds); - errno = serrno; - - return (ret); -} - -nvlist_t * -nvlist_xfer(int sock, nvlist_t *nvl) -{ - - if (nvlist_send(sock, nvl) < 0) { - nvlist_destroy(nvl); - return (NULL); - } - nvlist_destroy(nvl); - return (nvlist_recv(sock)); -} -#endif - -nvpair_t * -nvlist_first_nvpair(const nvlist_t *nvl) -{ - - NVLIST_ASSERT(nvl); - - return (TAILQ_FIRST(&nvl->nvl_head)); -} - -nvpair_t * -nvlist_next_nvpair(const nvlist_t *nvl, const nvpair_t *nvp) -{ - nvpair_t *retnvp; - - NVLIST_ASSERT(nvl); - NVPAIR_ASSERT(nvp); - PJDLOG_ASSERT(nvpair_nvlist(nvp) == nvl); - - retnvp = nvpair_next(nvp); - PJDLOG_ASSERT(retnvp == NULL || nvpair_nvlist(retnvp) == nvl); - - return (retnvp); - -} - -nvpair_t * -nvlist_prev_nvpair(const nvlist_t *nvl, const nvpair_t *nvp) -{ - nvpair_t *retnvp; - - NVLIST_ASSERT(nvl); - NVPAIR_ASSERT(nvp); - PJDLOG_ASSERT(nvpair_nvlist(nvp) == nvl); - - retnvp = nvpair_prev(nvp); - PJDLOG_ASSERT(nvpair_nvlist(retnvp) == nvl); - - return (retnvp); -} - -const char * -nvlist_next(const nvlist_t *nvl, int *typep, void **cookiep) -{ - nvpair_t *nvp; - - NVLIST_ASSERT(nvl); - PJDLOG_ASSERT(cookiep != NULL); - - if (*cookiep == NULL) - nvp = nvlist_first_nvpair(nvl); - else - nvp = nvlist_next_nvpair(nvl, *cookiep); - if (nvp == NULL) - return (NULL); - if (typep != NULL) - *typep = nvpair_type(nvp); - *cookiep = nvp; - return (nvpair_name(nvp)); -} - -bool -nvlist_exists(const nvlist_t *nvl, const char *name) -{ - - return (nvlist_find(nvl, NV_TYPE_NONE, name) != NULL); -} - -#define NVLIST_EXISTS(type, TYPE) \ -bool \ -nvlist_exists_##type(const nvlist_t *nvl, const char *name) \ -{ \ - \ - return (nvlist_find(nvl, NV_TYPE_##TYPE, name) != NULL); \ -} - -NVLIST_EXISTS(null, NULL) -NVLIST_EXISTS(bool, BOOL) -NVLIST_EXISTS(number, NUMBER) -NVLIST_EXISTS(string, STRING) -NVLIST_EXISTS(nvlist, NVLIST) -#ifndef _KERNEL -NVLIST_EXISTS(descriptor, DESCRIPTOR) -#endif -NVLIST_EXISTS(binary, BINARY) - -#undef NVLIST_EXISTS - -void -nvlist_add_nvpair(nvlist_t *nvl, const nvpair_t *nvp) -{ - nvpair_t *newnvp; - - NVPAIR_ASSERT(nvp); - - if (nvlist_error(nvl) != 0) { - RESTORE_ERRNO(nvlist_error(nvl)); - return; - } - if (nvlist_exists(nvl, nvpair_name(nvp))) { - nvl->nvl_error = EEXIST; - RESTORE_ERRNO(nvlist_error(nvl)); - return; - } - - newnvp = nvpair_clone(nvp); - if (newnvp == NULL) { - nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); - RESTORE_ERRNO(nvlist_error(nvl)); - return; - } - - nvpair_insert(&nvl->nvl_head, newnvp, nvl); -} - -void -nvlist_add_stringf(nvlist_t *nvl, const char *name, const char *valuefmt, ...) -{ - va_list valueap; - - va_start(valueap, valuefmt); - nvlist_add_stringv(nvl, name, valuefmt, valueap); - va_end(valueap); -} - -void -nvlist_add_stringv(nvlist_t *nvl, const char *name, const char *valuefmt, - va_list valueap) -{ - nvpair_t *nvp; - - if (nvlist_error(nvl) != 0) { - RESTORE_ERRNO(nvlist_error(nvl)); - return; - } - - nvp = nvpair_create_stringv(name, valuefmt, valueap); - if (nvp == NULL) { - nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); - RESTORE_ERRNO(nvl->nvl_error); - } else - nvlist_move_nvpair(nvl, nvp); -} - -void -nvlist_add_null(nvlist_t *nvl, const char *name) -{ - nvpair_t *nvp; - - if (nvlist_error(nvl) != 0) { - RESTORE_ERRNO(nvlist_error(nvl)); - return; - } - - nvp = nvpair_create_null(name); - if (nvp == NULL) { - nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); - RESTORE_ERRNO(nvl->nvl_error); - } else - nvlist_move_nvpair(nvl, nvp); -} - -void -nvlist_add_bool(nvlist_t *nvl, const char *name, bool value) -{ - nvpair_t *nvp; - - if (nvlist_error(nvl) != 0) { - RESTORE_ERRNO(nvlist_error(nvl)); - return; - } - - nvp = nvpair_create_bool(name, value); - if (nvp == NULL) { - nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); - RESTORE_ERRNO(nvl->nvl_error); - } else - nvlist_move_nvpair(nvl, nvp); -} - -void -nvlist_add_number(nvlist_t *nvl, const char *name, uint64_t value) -{ - nvpair_t *nvp; - - if (nvlist_error(nvl) != 0) { - RESTORE_ERRNO(nvlist_error(nvl)); - return; - } - - nvp = nvpair_create_number(name, value); - if (nvp == NULL) { - nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); - RESTORE_ERRNO(nvl->nvl_error); - } else - nvlist_move_nvpair(nvl, nvp); -} - -void -nvlist_add_string(nvlist_t *nvl, const char *name, const char *value) -{ - nvpair_t *nvp; - - if (nvlist_error(nvl) != 0) { - RESTORE_ERRNO(nvlist_error(nvl)); - return; - } - - nvp = nvpair_create_string(name, value); - if (nvp == NULL) { - nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); - RESTORE_ERRNO(nvl->nvl_error); - } else - nvlist_move_nvpair(nvl, nvp); -} - -void -nvlist_add_nvlist(nvlist_t *nvl, const char *name, const nvlist_t *value) -{ - nvpair_t *nvp; - - if (nvlist_error(nvl) != 0) { - RESTORE_ERRNO(nvlist_error(nvl)); - return; - } - - nvp = nvpair_create_nvlist(name, value); - if (nvp == NULL) { - nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); - RESTORE_ERRNO(nvl->nvl_error); - } else - nvlist_move_nvpair(nvl, nvp); -} - -#ifndef _KERNEL -void -nvlist_add_descriptor(nvlist_t *nvl, const char *name, int value) -{ - nvpair_t *nvp; - - if (nvlist_error(nvl) != 0) { - errno = nvlist_error(nvl); - return; - } - - nvp = nvpair_create_descriptor(name, value); - if (nvp == NULL) - nvl->nvl_error = errno = (errno != 0 ? errno : ENOMEM); - else - nvlist_move_nvpair(nvl, nvp); -} -#endif - -void -nvlist_add_binary(nvlist_t *nvl, const char *name, const void *value, - size_t size) -{ - nvpair_t *nvp; - - if (nvlist_error(nvl) != 0) { - RESTORE_ERRNO(nvlist_error(nvl)); - return; - } - - nvp = nvpair_create_binary(name, value, size); - if (nvp == NULL) { - nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); - RESTORE_ERRNO(nvl->nvl_error); - } else - nvlist_move_nvpair(nvl, nvp); -} - -void -nvlist_move_nvpair(nvlist_t *nvl, nvpair_t *nvp) -{ - - NVPAIR_ASSERT(nvp); - PJDLOG_ASSERT(nvpair_nvlist(nvp) == NULL); - - if (nvlist_error(nvl) != 0) { - nvpair_free(nvp); - RESTORE_ERRNO(nvlist_error(nvl)); - return; - } - if (nvlist_exists(nvl, nvpair_name(nvp))) { - nvpair_free(nvp); - nvl->nvl_error = EEXIST; - RESTORE_ERRNO(nvl->nvl_error); - return; - } - - nvpair_insert(&nvl->nvl_head, nvp, nvl); -} - -void -nvlist_move_string(nvlist_t *nvl, const char *name, char *value) -{ - nvpair_t *nvp; - - if (nvlist_error(nvl) != 0) { - nv_free(value); - RESTORE_ERRNO(nvlist_error(nvl)); - return; - } - - nvp = nvpair_move_string(name, value); - if (nvp == NULL) { - nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); - RESTORE_ERRNO(nvl->nvl_error); - } else - nvlist_move_nvpair(nvl, nvp); -} - -void -nvlist_move_nvlist(nvlist_t *nvl, const char *name, nvlist_t *value) -{ - nvpair_t *nvp; - - if (nvlist_error(nvl) != 0) { - if (value != NULL && nvlist_get_nvpair_parent(value) != NULL) - nvlist_destroy(value); - RESTORE_ERRNO(nvlist_error(nvl)); - return; - } - - nvp = nvpair_move_nvlist(name, value); - if (nvp == NULL) { - nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); - RESTORE_ERRNO(nvl->nvl_error); - } else - nvlist_move_nvpair(nvl, nvp); -} - -#ifndef _KERNEL -void -nvlist_move_descriptor(nvlist_t *nvl, const char *name, int value) -{ - nvpair_t *nvp; - - if (nvlist_error(nvl) != 0) { - close(value); - errno = nvlist_error(nvl); - return; - } - - nvp = nvpair_move_descriptor(name, value); - if (nvp == NULL) - nvl->nvl_error = errno = (errno != 0 ? errno : ENOMEM); - else - nvlist_move_nvpair(nvl, nvp); -} -#endif - -void -nvlist_move_binary(nvlist_t *nvl, const char *name, void *value, size_t size) -{ - nvpair_t *nvp; - - if (nvlist_error(nvl) != 0) { - nv_free(value); - RESTORE_ERRNO(nvlist_error(nvl)); - return; - } - - nvp = nvpair_move_binary(name, value, size); - if (nvp == NULL) { - nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); - RESTORE_ERRNO(nvl->nvl_error); - } else - nvlist_move_nvpair(nvl, nvp); -} - -const nvpair_t * -nvlist_get_nvpair(const nvlist_t *nvl, const char *name) -{ - - return (nvlist_find(nvl, NV_TYPE_NONE, name)); -} - -#define NVLIST_GET(ftype, type, TYPE) \ -ftype \ -nvlist_get_##type(const nvlist_t *nvl, const char *name) \ -{ \ - const nvpair_t *nvp; \ - \ - nvp = nvlist_find(nvl, NV_TYPE_##TYPE, name); \ - if (nvp == NULL) \ - nvlist_report_missing(NV_TYPE_##TYPE, name); \ - return (nvpair_get_##type(nvp)); \ -} - -NVLIST_GET(bool, bool, BOOL) -NVLIST_GET(uint64_t, number, NUMBER) -NVLIST_GET(const char *, string, STRING) -NVLIST_GET(const nvlist_t *, nvlist, NVLIST) -#ifndef _KERNEL -NVLIST_GET(int, descriptor, DESCRIPTOR) -#endif - -#undef NVLIST_GET - -const void * -nvlist_get_binary(const nvlist_t *nvl, const char *name, size_t *sizep) -{ - nvpair_t *nvp; - - nvp = nvlist_find(nvl, NV_TYPE_BINARY, name); - if (nvp == NULL) - nvlist_report_missing(NV_TYPE_BINARY, name); - - return (nvpair_get_binary(nvp, sizep)); -} - -#define NVLIST_TAKE(ftype, type, TYPE) \ -ftype \ -nvlist_take_##type(nvlist_t *nvl, const char *name) \ -{ \ - nvpair_t *nvp; \ - ftype value; \ - \ - nvp = nvlist_find(nvl, NV_TYPE_##TYPE, name); \ - if (nvp == NULL) \ - nvlist_report_missing(NV_TYPE_##TYPE, name); \ - value = (ftype)(intptr_t)nvpair_get_##type(nvp); \ - nvlist_remove_nvpair(nvl, nvp); \ - nvpair_free_structure(nvp); \ - return (value); \ -} - -NVLIST_TAKE(bool, bool, BOOL) -NVLIST_TAKE(uint64_t, number, NUMBER) -NVLIST_TAKE(char *, string, STRING) -NVLIST_TAKE(nvlist_t *, nvlist, NVLIST) -#ifndef _KERNEL -NVLIST_TAKE(int, descriptor, DESCRIPTOR) -#endif - -#undef NVLIST_TAKE - -void * -nvlist_take_binary(nvlist_t *nvl, const char *name, size_t *sizep) -{ - nvpair_t *nvp; - void *value; - - nvp = nvlist_find(nvl, NV_TYPE_BINARY, name); - if (nvp == NULL) - nvlist_report_missing(NV_TYPE_BINARY, name); - - value = (void *)(intptr_t)nvpair_get_binary(nvp, sizep); - nvlist_remove_nvpair(nvl, nvp); - nvpair_free_structure(nvp); - return (value); -} - -void -nvlist_remove_nvpair(nvlist_t *nvl, nvpair_t *nvp) -{ - - NVLIST_ASSERT(nvl); - NVPAIR_ASSERT(nvp); - PJDLOG_ASSERT(nvpair_nvlist(nvp) == nvl); - - nvpair_remove(&nvl->nvl_head, nvp, nvl); -} - -void -nvlist_free(nvlist_t *nvl, const char *name) -{ - - nvlist_free_type(nvl, name, NV_TYPE_NONE); -} - -#define NVLIST_FREE(type, TYPE) \ -void \ -nvlist_free_##type(nvlist_t *nvl, const char *name) \ -{ \ - \ - nvlist_free_type(nvl, name, NV_TYPE_##TYPE); \ -} - -NVLIST_FREE(null, NULL) -NVLIST_FREE(bool, BOOL) -NVLIST_FREE(number, NUMBER) -NVLIST_FREE(string, STRING) -NVLIST_FREE(nvlist, NVLIST) -#ifndef _KERNEL -NVLIST_FREE(descriptor, DESCRIPTOR) -#endif -NVLIST_FREE(binary, BINARY) - -#undef NVLIST_FREE - -void -nvlist_free_nvpair(nvlist_t *nvl, nvpair_t *nvp) -{ - - NVLIST_ASSERT(nvl); - NVPAIR_ASSERT(nvp); - PJDLOG_ASSERT(nvpair_nvlist(nvp) == nvl); - - nvlist_remove_nvpair(nvl, nvp); - nvpair_free(nvp); -} - Index: sys/kern/subr_nvpair.c =================================================================== --- sys/kern/subr_nvpair.c +++ sys/kern/subr_nvpair.c @@ -1,1111 +0,0 @@ -/*- - * Copyright (c) 2009-2013 The FreeBSD Foundation - * All rights reserved. - * - * This software was developed by Pawel Jakub Dawidek under sponsorship from - * the FreeBSD Foundation. - * - * 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 AUTHORS 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 AUTHORS 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. - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include - -#ifdef _KERNEL - -#include -#include -#include -#include - -#include - -#else -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common_impl.h" -#endif - -#ifdef HAVE_PJDLOG -#include -#endif - -#include -#include -#include -#include - -#ifndef HAVE_PJDLOG -#ifdef _KERNEL -#define PJDLOG_ASSERT(...) MPASS(__VA_ARGS__) -#define PJDLOG_RASSERT(expr, ...) KASSERT(expr, (__VA_ARGS__)) -#define PJDLOG_ABORT(...) panic(__VA_ARGS__) -#else -#include -#define PJDLOG_ASSERT(...) assert(__VA_ARGS__) -#define PJDLOG_RASSERT(expr, ...) assert(expr) -#define PJDLOG_ABORT(...) abort() -#endif -#endif - -#define NVPAIR_MAGIC 0x6e7670 /* "nvp" */ -struct nvpair { - int nvp_magic; - char *nvp_name; - int nvp_type; - uint64_t nvp_data; - size_t nvp_datasize; - nvlist_t *nvp_list; - TAILQ_ENTRY(nvpair) nvp_next; -}; - -#define NVPAIR_ASSERT(nvp) do { \ - PJDLOG_ASSERT((nvp) != NULL); \ - PJDLOG_ASSERT((nvp)->nvp_magic == NVPAIR_MAGIC); \ -} while (0) - -struct nvpair_header { - uint8_t nvph_type; - uint16_t nvph_namesize; - uint64_t nvph_datasize; -} __packed; - - -void -nvpair_assert(const nvpair_t *nvp) -{ - - NVPAIR_ASSERT(nvp); -} - -nvlist_t * -nvpair_nvlist(const nvpair_t *nvp) -{ - - NVPAIR_ASSERT(nvp); - - return (nvp->nvp_list); -} - -nvpair_t * -nvpair_next(const nvpair_t *nvp) -{ - - NVPAIR_ASSERT(nvp); - PJDLOG_ASSERT(nvp->nvp_list != NULL); - - return (TAILQ_NEXT(nvp, nvp_next)); -} - -nvpair_t * -nvpair_prev(const nvpair_t *nvp) -{ - - NVPAIR_ASSERT(nvp); - PJDLOG_ASSERT(nvp->nvp_list != NULL); - - return (TAILQ_PREV(nvp, nvl_head, nvp_next)); -} - -void -nvpair_insert(struct nvl_head *head, nvpair_t *nvp, nvlist_t *nvl) -{ - - NVPAIR_ASSERT(nvp); - PJDLOG_ASSERT(nvp->nvp_list == NULL); - PJDLOG_ASSERT(!nvlist_exists(nvl, nvpair_name(nvp))); - - TAILQ_INSERT_TAIL(head, nvp, nvp_next); - nvp->nvp_list = nvl; -} - -static void -nvpair_remove_nvlist(nvpair_t *nvp) -{ - nvlist_t *nvl; - - /* XXX: DECONST is bad, mkay? */ - nvl = __DECONST(nvlist_t *, nvpair_get_nvlist(nvp)); - PJDLOG_ASSERT(nvl != NULL); - nvlist_set_parent(nvl, NULL); -} - -void -nvpair_remove(struct nvl_head *head, nvpair_t *nvp, const nvlist_t *nvl) -{ - - NVPAIR_ASSERT(nvp); - PJDLOG_ASSERT(nvp->nvp_list == nvl); - - if (nvpair_type(nvp) == NV_TYPE_NVLIST) - nvpair_remove_nvlist(nvp); - - TAILQ_REMOVE(head, nvp, nvp_next); - nvp->nvp_list = NULL; -} - -nvpair_t * -nvpair_clone(const nvpair_t *nvp) -{ - nvpair_t *newnvp; - const char *name; - const void *data; - size_t datasize; - - NVPAIR_ASSERT(nvp); - - name = nvpair_name(nvp); - - switch (nvpair_type(nvp)) { - case NV_TYPE_NULL: - newnvp = nvpair_create_null(name); - break; - case NV_TYPE_BOOL: - newnvp = nvpair_create_bool(name, nvpair_get_bool(nvp)); - break; - case NV_TYPE_NUMBER: - newnvp = nvpair_create_number(name, nvpair_get_number(nvp)); - break; - case NV_TYPE_STRING: - newnvp = nvpair_create_string(name, nvpair_get_string(nvp)); - break; - case NV_TYPE_NVLIST: - newnvp = nvpair_create_nvlist(name, nvpair_get_nvlist(nvp)); - break; -#ifndef _KERNEL - case NV_TYPE_DESCRIPTOR: - newnvp = nvpair_create_descriptor(name, - nvpair_get_descriptor(nvp)); - break; -#endif - case NV_TYPE_BINARY: - data = nvpair_get_binary(nvp, &datasize); - newnvp = nvpair_create_binary(name, data, datasize); - break; - default: - PJDLOG_ABORT("Unknown type: %d.", nvpair_type(nvp)); - } - - return (newnvp); -} - -size_t -nvpair_header_size(void) -{ - - return (sizeof(struct nvpair_header)); -} - -size_t -nvpair_size(const nvpair_t *nvp) -{ - - NVPAIR_ASSERT(nvp); - - return (nvp->nvp_datasize); -} - -unsigned char * -nvpair_pack_header(const nvpair_t *nvp, unsigned char *ptr, size_t *leftp) -{ - struct nvpair_header nvphdr; - size_t namesize; - - NVPAIR_ASSERT(nvp); - - nvphdr.nvph_type = nvp->nvp_type; - namesize = strlen(nvp->nvp_name) + 1; - PJDLOG_ASSERT(namesize > 0 && namesize <= UINT16_MAX); - nvphdr.nvph_namesize = namesize; - nvphdr.nvph_datasize = nvp->nvp_datasize; - PJDLOG_ASSERT(*leftp >= sizeof(nvphdr)); - memcpy(ptr, &nvphdr, sizeof(nvphdr)); - ptr += sizeof(nvphdr); - *leftp -= sizeof(nvphdr); - - PJDLOG_ASSERT(*leftp >= namesize); - memcpy(ptr, nvp->nvp_name, namesize); - ptr += namesize; - *leftp -= namesize; - - return (ptr); -} - -unsigned char * -nvpair_pack_null(const nvpair_t *nvp, unsigned char *ptr, - size_t *leftp __unused) -{ - - NVPAIR_ASSERT(nvp); - PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NULL); - - return (ptr); -} - -unsigned char * -nvpair_pack_bool(const nvpair_t *nvp, unsigned char *ptr, size_t *leftp) -{ - uint8_t value; - - NVPAIR_ASSERT(nvp); - PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BOOL); - - value = (uint8_t)nvp->nvp_data; - - PJDLOG_ASSERT(*leftp >= sizeof(value)); - memcpy(ptr, &value, sizeof(value)); - ptr += sizeof(value); - *leftp -= sizeof(value); - - return (ptr); -} - -unsigned char * -nvpair_pack_number(const nvpair_t *nvp, unsigned char *ptr, size_t *leftp) -{ - uint64_t value; - - NVPAIR_ASSERT(nvp); - PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NUMBER); - - value = (uint64_t)nvp->nvp_data; - - PJDLOG_ASSERT(*leftp >= sizeof(value)); - memcpy(ptr, &value, sizeof(value)); - ptr += sizeof(value); - *leftp -= sizeof(value); - - return (ptr); -} - -unsigned char * -nvpair_pack_string(const nvpair_t *nvp, unsigned char *ptr, size_t *leftp) -{ - - NVPAIR_ASSERT(nvp); - PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_STRING); - - PJDLOG_ASSERT(*leftp >= nvp->nvp_datasize); - memcpy(ptr, (const void *)(intptr_t)nvp->nvp_data, nvp->nvp_datasize); - ptr += nvp->nvp_datasize; - *leftp -= nvp->nvp_datasize; - - return (ptr); -} - -unsigned char * -nvpair_pack_nvlist_up(unsigned char *ptr, size_t *leftp) -{ - struct nvpair_header nvphdr; - size_t namesize; - const char *name = ""; - - namesize = 1; - nvphdr.nvph_type = NV_TYPE_NVLIST_UP; - nvphdr.nvph_namesize = namesize; - nvphdr.nvph_datasize = 0; - PJDLOG_ASSERT(*leftp >= sizeof(nvphdr)); - memcpy(ptr, &nvphdr, sizeof(nvphdr)); - ptr += sizeof(nvphdr); - *leftp -= sizeof(nvphdr); - - PJDLOG_ASSERT(*leftp >= namesize); - memcpy(ptr, name, namesize); - ptr += namesize; - *leftp -= namesize; - - return (ptr); -} - -#ifndef _KERNEL -unsigned char * -nvpair_pack_descriptor(const nvpair_t *nvp, unsigned char *ptr, int64_t *fdidxp, - size_t *leftp) -{ - int64_t value; - - NVPAIR_ASSERT(nvp); - PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_DESCRIPTOR); - - value = (int64_t)nvp->nvp_data; - if (value != -1) { - /* - * If there is a real descriptor here, we change its number - * to position in the array of descriptors send via control - * message. - */ - PJDLOG_ASSERT(fdidxp != NULL); - - value = *fdidxp; - (*fdidxp)++; - } - - PJDLOG_ASSERT(*leftp >= sizeof(value)); - memcpy(ptr, &value, sizeof(value)); - ptr += sizeof(value); - *leftp -= sizeof(value); - - return (ptr); -} -#endif - -unsigned char * -nvpair_pack_binary(const nvpair_t *nvp, unsigned char *ptr, size_t *leftp) -{ - - NVPAIR_ASSERT(nvp); - PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BINARY); - - PJDLOG_ASSERT(*leftp >= nvp->nvp_datasize); - memcpy(ptr, (const void *)(intptr_t)nvp->nvp_data, nvp->nvp_datasize); - ptr += nvp->nvp_datasize; - *leftp -= nvp->nvp_datasize; - - return (ptr); -} - -void -nvpair_init_datasize(nvpair_t *nvp) -{ - - NVPAIR_ASSERT(nvp); - - if (nvp->nvp_type == NV_TYPE_NVLIST) { - if (nvp->nvp_data == 0) { - nvp->nvp_datasize = 0; - } else { - nvp->nvp_datasize = - nvlist_size((const nvlist_t *)(intptr_t)nvp->nvp_data); - } - } -} - -const unsigned char * -nvpair_unpack_header(bool isbe, nvpair_t *nvp, const unsigned char *ptr, - size_t *leftp) -{ - struct nvpair_header nvphdr; - - if (*leftp < sizeof(nvphdr)) - goto failed; - - memcpy(&nvphdr, ptr, sizeof(nvphdr)); - ptr += sizeof(nvphdr); - *leftp -= sizeof(nvphdr); - -#if NV_TYPE_FIRST > 0 - if (nvphdr.nvph_type < NV_TYPE_FIRST) - goto failed; -#endif - if (nvphdr.nvph_type > NV_TYPE_LAST && - nvphdr.nvph_type != NV_TYPE_NVLIST_UP) { - goto failed; - } - -#if BYTE_ORDER == BIG_ENDIAN - if (!isbe) { - nvphdr.nvph_namesize = le16toh(nvphdr.nvph_namesize); - nvphdr.nvph_datasize = le64toh(nvphdr.nvph_datasize); - } -#else - if (isbe) { - nvphdr.nvph_namesize = be16toh(nvphdr.nvph_namesize); - nvphdr.nvph_datasize = be64toh(nvphdr.nvph_datasize); - } -#endif - - if (nvphdr.nvph_namesize > NV_NAME_MAX) - goto failed; - if (*leftp < nvphdr.nvph_namesize) - goto failed; - if (nvphdr.nvph_namesize < 1) - goto failed; - if (strnlen((const char *)ptr, nvphdr.nvph_namesize) != - (size_t)(nvphdr.nvph_namesize - 1)) { - goto failed; - } - - memcpy(nvp->nvp_name, ptr, nvphdr.nvph_namesize); - ptr += nvphdr.nvph_namesize; - *leftp -= nvphdr.nvph_namesize; - - if (*leftp < nvphdr.nvph_datasize) - goto failed; - - nvp->nvp_type = nvphdr.nvph_type; - nvp->nvp_data = 0; - nvp->nvp_datasize = nvphdr.nvph_datasize; - - return (ptr); -failed: - RESTORE_ERRNO(EINVAL); - return (NULL); -} - -const unsigned char * -nvpair_unpack_null(bool isbe __unused, nvpair_t *nvp, const unsigned char *ptr, - size_t *leftp __unused) -{ - - PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NULL); - - if (nvp->nvp_datasize != 0) { - RESTORE_ERRNO(EINVAL); - return (NULL); - } - - return (ptr); -} - -const unsigned char * -nvpair_unpack_bool(bool isbe __unused, nvpair_t *nvp, const unsigned char *ptr, - size_t *leftp) -{ - uint8_t value; - - PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BOOL); - - if (nvp->nvp_datasize != sizeof(value)) { - RESTORE_ERRNO(EINVAL); - return (NULL); - } - if (*leftp < sizeof(value)) { - RESTORE_ERRNO(EINVAL); - return (NULL); - } - - memcpy(&value, ptr, sizeof(value)); - ptr += sizeof(value); - *leftp -= sizeof(value); - - if (value != 0 && value != 1) { - RESTORE_ERRNO(EINVAL); - return (NULL); - } - - nvp->nvp_data = (uint64_t)value; - - return (ptr); -} - -const unsigned char * -nvpair_unpack_number(bool isbe, nvpair_t *nvp, const unsigned char *ptr, - size_t *leftp) -{ - - PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NUMBER); - - if (nvp->nvp_datasize != sizeof(uint64_t)) { - RESTORE_ERRNO(EINVAL); - return (NULL); - } - if (*leftp < sizeof(uint64_t)) { - RESTORE_ERRNO(EINVAL); - return (NULL); - } - - if (isbe) - nvp->nvp_data = be64dec(ptr); - else - nvp->nvp_data = le64dec(ptr); - ptr += sizeof(uint64_t); - *leftp -= sizeof(uint64_t); - - return (ptr); -} - -const unsigned char * -nvpair_unpack_string(bool isbe __unused, nvpair_t *nvp, - const unsigned char *ptr, size_t *leftp) -{ - - PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_STRING); - - if (*leftp < nvp->nvp_datasize || nvp->nvp_datasize == 0) { - RESTORE_ERRNO(EINVAL); - return (NULL); - } - - if (strnlen((const char *)ptr, nvp->nvp_datasize) != - nvp->nvp_datasize - 1) { - RESTORE_ERRNO(EINVAL); - return (NULL); - } - - nvp->nvp_data = (uint64_t)(uintptr_t)nv_strdup((const char *)ptr); - if (nvp->nvp_data == 0) - return (NULL); - - ptr += nvp->nvp_datasize; - *leftp -= nvp->nvp_datasize; - - return (ptr); -} - -const unsigned char * -nvpair_unpack_nvlist(bool isbe __unused, nvpair_t *nvp, - const unsigned char *ptr, size_t *leftp, size_t nfds, nvlist_t **child) -{ - nvlist_t *value; - - PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NVLIST); - - if (*leftp < nvp->nvp_datasize || nvp->nvp_datasize == 0) { - RESTORE_ERRNO(EINVAL); - return (NULL); - } - - value = nvlist_create(0); - if (value == NULL) - return (NULL); - - ptr = nvlist_unpack_header(value, ptr, nfds, NULL, leftp); - if (ptr == NULL) - return (NULL); - - nvp->nvp_data = (uint64_t)(uintptr_t)value; - *child = value; - - return (ptr); -} - -#ifndef _KERNEL -const unsigned char * -nvpair_unpack_descriptor(bool isbe, nvpair_t *nvp, const unsigned char *ptr, - size_t *leftp, const int *fds, size_t nfds) -{ - int64_t idx; - - PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_DESCRIPTOR); - - if (nvp->nvp_datasize != sizeof(idx)) { - errno = EINVAL; - return (NULL); - } - if (*leftp < sizeof(idx)) { - errno = EINVAL; - return (NULL); - } - - if (isbe) - idx = be64dec(ptr); - else - idx = le64dec(ptr); - - if (idx < 0) { - errno = EINVAL; - return (NULL); - } - - if ((size_t)idx >= nfds) { - errno = EINVAL; - return (NULL); - } - - nvp->nvp_data = (uint64_t)fds[idx]; - - ptr += sizeof(idx); - *leftp -= sizeof(idx); - - return (ptr); -} -#endif - -const unsigned char * -nvpair_unpack_binary(bool isbe __unused, nvpair_t *nvp, - const unsigned char *ptr, size_t *leftp) -{ - void *value; - - PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BINARY); - - if (*leftp < nvp->nvp_datasize || nvp->nvp_datasize == 0) { - RESTORE_ERRNO(EINVAL); - return (NULL); - } - - value = nv_malloc(nvp->nvp_datasize); - if (value == NULL) - return (NULL); - - memcpy(value, ptr, nvp->nvp_datasize); - ptr += nvp->nvp_datasize; - *leftp -= nvp->nvp_datasize; - - nvp->nvp_data = (uint64_t)(uintptr_t)value; - - return (ptr); -} - -const unsigned char * -nvpair_unpack(bool isbe, const unsigned char *ptr, size_t *leftp, - nvpair_t **nvpp) -{ - nvpair_t *nvp, *tmp; - - nvp = nv_calloc(1, sizeof(*nvp) + NV_NAME_MAX); - if (nvp == NULL) - return (NULL); - nvp->nvp_name = (char *)(nvp + 1); - - ptr = nvpair_unpack_header(isbe, nvp, ptr, leftp); - if (ptr == NULL) - goto failed; - tmp = nv_realloc(nvp, sizeof(*nvp) + strlen(nvp->nvp_name) + 1); - if (tmp == NULL) - goto failed; - nvp = tmp; - - /* Update nvp_name after realloc(). */ - nvp->nvp_name = (char *)(nvp + 1); - nvp->nvp_data = 0x00; - nvp->nvp_magic = NVPAIR_MAGIC; - *nvpp = nvp; - return (ptr); -failed: - nv_free(nvp); - return (NULL); -} - -int -nvpair_type(const nvpair_t *nvp) -{ - - NVPAIR_ASSERT(nvp); - - return (nvp->nvp_type); -} - -const char * -nvpair_name(const nvpair_t *nvp) -{ - - NVPAIR_ASSERT(nvp); - - return (nvp->nvp_name); -} - -static nvpair_t * -nvpair_allocv(const char *name, int type, uint64_t data, size_t datasize) -{ - nvpair_t *nvp; - size_t namelen; - - PJDLOG_ASSERT(type >= NV_TYPE_FIRST && type <= NV_TYPE_LAST); - - namelen = strlen(name); - if (namelen >= NV_NAME_MAX) { - RESTORE_ERRNO(ENAMETOOLONG); - return (NULL); - } - - nvp = nv_calloc(1, sizeof(*nvp) + namelen + 1); - if (nvp != NULL) { - nvp->nvp_name = (char *)(nvp + 1); - memcpy(nvp->nvp_name, name, namelen); - nvp->nvp_name[namelen + 1] = '\0'; - nvp->nvp_type = type; - nvp->nvp_data = data; - nvp->nvp_datasize = datasize; - nvp->nvp_magic = NVPAIR_MAGIC; - } - - return (nvp); -}; - -nvpair_t * -nvpair_create_stringf(const char *name, const char *valuefmt, ...) -{ - va_list valueap; - nvpair_t *nvp; - - va_start(valueap, valuefmt); - nvp = nvpair_create_stringv(name, valuefmt, valueap); - va_end(valueap); - - return (nvp); -} - -nvpair_t * -nvpair_create_stringv(const char *name, const char *valuefmt, va_list valueap) -{ - nvpair_t *nvp; - char *str; - int len; - - len = nv_vasprintf(&str, valuefmt, valueap); - if (len < 0) - return (NULL); - nvp = nvpair_create_string(name, str); - if (nvp == NULL) - nv_free(str); - return (nvp); -} - -nvpair_t * -nvpair_create_null(const char *name) -{ - - return (nvpair_allocv(name, NV_TYPE_NULL, 0, 0)); -} - -nvpair_t * -nvpair_create_bool(const char *name, bool value) -{ - - return (nvpair_allocv(name, NV_TYPE_BOOL, value ? 1 : 0, - sizeof(uint8_t))); -} - -nvpair_t * -nvpair_create_number(const char *name, uint64_t value) -{ - - return (nvpair_allocv(name, NV_TYPE_NUMBER, value, sizeof(value))); -} - -nvpair_t * -nvpair_create_string(const char *name, const char *value) -{ - nvpair_t *nvp; - size_t size; - char *data; - - if (value == NULL) { - RESTORE_ERRNO(EINVAL); - return (NULL); - } - - data = nv_strdup(value); - if (data == NULL) - return (NULL); - size = strlen(value) + 1; - - nvp = nvpair_allocv(name, NV_TYPE_STRING, (uint64_t)(uintptr_t)data, - size); - if (nvp == NULL) - nv_free(data); - - return (nvp); -} - -nvpair_t * -nvpair_create_nvlist(const char *name, const nvlist_t *value) -{ - nvlist_t *nvl; - nvpair_t *nvp; - - if (value == NULL) { - RESTORE_ERRNO(EINVAL); - return (NULL); - } - - nvl = nvlist_clone(value); - if (nvl == NULL) - return (NULL); - - nvp = nvpair_allocv(name, NV_TYPE_NVLIST, (uint64_t)(uintptr_t)nvl, 0); - if (nvp == NULL) - nvlist_destroy(nvl); - else - nvlist_set_parent(nvl, nvp); - - return (nvp); -} - -#ifndef _KERNEL -nvpair_t * -nvpair_create_descriptor(const char *name, int value) -{ - nvpair_t *nvp; - - if (value < 0 || !fd_is_valid(value)) { - errno = EBADF; - return (NULL); - } - - value = fcntl(value, F_DUPFD_CLOEXEC, 0); - if (value < 0) - return (NULL); - - nvp = nvpair_allocv(name, NV_TYPE_DESCRIPTOR, (uint64_t)value, - sizeof(int64_t)); - if (nvp == NULL) - close(value); - - return (nvp); -} -#endif - -nvpair_t * -nvpair_create_binary(const char *name, const void *value, size_t size) -{ - nvpair_t *nvp; - void *data; - - if (value == NULL || size == 0) { - RESTORE_ERRNO(EINVAL); - return (NULL); - } - - data = nv_malloc(size); - if (data == NULL) - return (NULL); - memcpy(data, value, size); - - nvp = nvpair_allocv(name, NV_TYPE_BINARY, (uint64_t)(uintptr_t)data, - size); - if (nvp == NULL) - nv_free(data); - - return (nvp); -} - -nvpair_t * -nvpair_move_string(const char *name, char *value) -{ - nvpair_t *nvp; - int serrno; - - if (value == NULL) { - RESTORE_ERRNO(EINVAL); - return (NULL); - } - - nvp = nvpair_allocv(name, NV_TYPE_STRING, (uint64_t)(uintptr_t)value, - strlen(value) + 1); - if (nvp == NULL) { - SAVE_ERRNO(serrno); - nv_free(value); - RESTORE_ERRNO(serrno); - } - - return (nvp); -} - -nvpair_t * -nvpair_move_nvlist(const char *name, nvlist_t *value) -{ - nvpair_t *nvp; - - if (value == NULL || nvlist_get_nvpair_parent(value) != NULL) { - RESTORE_ERRNO(EINVAL); - return (NULL); - } - - if (nvlist_error(value) != 0) { - RESTORE_ERRNO(nvlist_error(value)); - nvlist_destroy(value); - return (NULL); - } - - nvp = nvpair_allocv(name, NV_TYPE_NVLIST, (uint64_t)(uintptr_t)value, - 0); - if (nvp == NULL) - nvlist_destroy(value); - else - nvlist_set_parent(value, nvp); - - return (nvp); -} - -#ifndef _KERNEL -nvpair_t * -nvpair_move_descriptor(const char *name, int value) -{ - nvpair_t *nvp; - int serrno; - - if (value < 0 || !fd_is_valid(value)) { - errno = EBADF; - return (NULL); - } - - nvp = nvpair_allocv(name, NV_TYPE_DESCRIPTOR, (uint64_t)value, - sizeof(int64_t)); - if (nvp == NULL) { - serrno = errno; - close(value); - errno = serrno; - } - - return (nvp); -} -#endif - -nvpair_t * -nvpair_move_binary(const char *name, void *value, size_t size) -{ - nvpair_t *nvp; - int serrno; - - if (value == NULL || size == 0) { - RESTORE_ERRNO(EINVAL); - return (NULL); - } - - nvp = nvpair_allocv(name, NV_TYPE_BINARY, (uint64_t)(uintptr_t)value, - size); - if (nvp == NULL) { - SAVE_ERRNO(serrno); - nv_free(value); - RESTORE_ERRNO(serrno); - } - - return (nvp); -} - -bool -nvpair_get_bool(const nvpair_t *nvp) -{ - - NVPAIR_ASSERT(nvp); - - return (nvp->nvp_data == 1); -} - -uint64_t -nvpair_get_number(const nvpair_t *nvp) -{ - - NVPAIR_ASSERT(nvp); - - return (nvp->nvp_data); -} - -const char * -nvpair_get_string(const nvpair_t *nvp) -{ - - NVPAIR_ASSERT(nvp); - PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_STRING); - - return ((const char *)(intptr_t)nvp->nvp_data); -} - -const nvlist_t * -nvpair_get_nvlist(const nvpair_t *nvp) -{ - - NVPAIR_ASSERT(nvp); - PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NVLIST); - - return ((const nvlist_t *)(intptr_t)nvp->nvp_data); -} - -#ifndef _KERNEL -int -nvpair_get_descriptor(const nvpair_t *nvp) -{ - - NVPAIR_ASSERT(nvp); - PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_DESCRIPTOR); - - return ((int)nvp->nvp_data); -} -#endif - -const void * -nvpair_get_binary(const nvpair_t *nvp, size_t *sizep) -{ - - NVPAIR_ASSERT(nvp); - PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BINARY); - - if (sizep != NULL) - *sizep = nvp->nvp_datasize; - return ((const void *)(intptr_t)nvp->nvp_data); -} - -void -nvpair_free(nvpair_t *nvp) -{ - - NVPAIR_ASSERT(nvp); - PJDLOG_ASSERT(nvp->nvp_list == NULL); - - nvp->nvp_magic = 0; - switch (nvp->nvp_type) { -#ifndef _KERNEL - case NV_TYPE_DESCRIPTOR: - close((int)nvp->nvp_data); - break; -#endif - case NV_TYPE_NVLIST: - nvlist_destroy((nvlist_t *)(intptr_t)nvp->nvp_data); - break; - case NV_TYPE_STRING: - nv_free((char *)(intptr_t)nvp->nvp_data); - break; - case NV_TYPE_BINARY: - nv_free((void *)(intptr_t)nvp->nvp_data); - break; - } - nv_free(nvp); -} - -void -nvpair_free_structure(nvpair_t *nvp) -{ - - NVPAIR_ASSERT(nvp); - PJDLOG_ASSERT(nvp->nvp_list == NULL); - - nvp->nvp_magic = 0; - nv_free(nvp); -} - -const char * -nvpair_type_string(int type) -{ - - switch (type) { - case NV_TYPE_NULL: - return ("NULL"); - case NV_TYPE_BOOL: - return ("BOOL"); - case NV_TYPE_NUMBER: - return ("NUMBER"); - case NV_TYPE_STRING: - return ("STRING"); - case NV_TYPE_NVLIST: - return ("NVLIST"); - case NV_TYPE_DESCRIPTOR: - return ("DESCRIPTOR"); - case NV_TYPE_BINARY: - return ("BINARY"); - default: - return (""); - } -} - Index: sys/modules/ixl/Makefile =================================================================== --- sys/modules/ixl/Makefile +++ sys/modules/ixl/Makefile @@ -5,7 +5,7 @@ .PATH: ${.CURDIR}/../../dev/ixl KMOD = if_ixl -SRCS = device_if.h bus_if.h pci_if.h opt_bdg.h +SRCS = device_if.h bus_if.h pci_if.h pci_iov_if.h opt_bdg.h SRCS += opt_inet.h opt_inet6.h SRCS += if_ixl.c ixl_txrx.c i40e_osdep.c Index: sys/sys/bus.h =================================================================== --- sys/sys/bus.h +++ sys/sys/bus.h @@ -465,6 +465,7 @@ void device_set_desc(device_t dev, const char* desc); void device_set_desc_copy(device_t dev, const char* desc); int device_set_devclass(device_t dev, const char *classname); +int device_set_devclass_fixed(device_t dev, const char *classname); int device_set_driver(device_t dev, driver_t *driver); void device_set_flags(device_t dev, u_int32_t flags); void device_set_softc(device_t dev, void *softc); Index: sys/sys/iov.h =================================================================== --- sys/sys/iov.h +++ sys/sys/iov.h @@ -0,0 +1,257 @@ +/*- + * Copyright (c) 2013-2015 Sandvine Inc. All rights reserved. + * 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 _SYS_IOV_H_ +#define _SYS_IOV_H_ + +#include + +#define PF_CONFIG_NAME "PF" +#define VF_SCHEMA_NAME "VF" + +#define VF_PREFIX "VF-" +#define VF_PREFIX_LEN 3 +#define VF_NUM_LEN 5 /* The maximum VF num is 65535. */ +#define VF_MAX_NAME (VF_PREFIX_LEN + VF_NUM_LEN + 1) + +#define DRIVER_CONFIG_NAME "DRIVER" +#define IOV_CONFIG_NAME "IOV" + +#define TYPE_SCHEMA_NAME "TYPE" +#define DEFAULT_SCHEMA_NAME "DEFAULT" +#define REQUIRED_SCHEMA_NAME "REQUIRED" + +/* + * Because each PF device is expected to expose a unique set of possible + * configurations, the SR-IOV infrastructure dynamically queries the PF + * driver for its capabilities. These capabilities are exposed to userland + * with a configuration schema. The schema is exported from the kernel as a + * packed nvlist. See nv(3) for the details of the nvlist API. The expected + * format of the nvlist is: + * + * BASIC RULES + * 1) All keys are case-insensitive. + * 2) No keys that are not specified below may exist at any level of the + * schema. + * 3) All keys are mandatory unless explicitly documented as optional. If a + * key is mandatory then the associated value is also mandatory. + * 4) Order of keys is irrelevant. + * + * TOP LEVEL + * 1) There must be a top-level key with the name PF_CONFIG_NAME. The value + * associated with this key is a nvlist that follows the device schema + * node format. The parameters in this node specify the configuration + * parameters that may be applied to a PF. + * 2) There must be a top-level key with the name VF_SCHEMA_NAME. The value + * associated with this key is a nvlist that follows the device schema + * node format. The parameters in this node specify the configuration + * parameters that may be applied to a VF. + * + * DEVICE SCHEMA NODE + * 1) There must be a key with the name DRIVER_CONFIG_NAME. The value + * associated with this key is a nvlist that follows the device/subsystem + * schema node format. The parameters in this node specify the + * configuration parameters that are specific to a particular device + * driver. + * 2) There must be a key with the name IOV_CONFIG_NAME. The value associated + * with this key is an nvlist that follows the device/subsystem schema node + * format. The parameters in this node specify the configuration + * parameters that are applied by the SR-IOV infrastructure. + * + * DEVICE/SUBSYSTEM SCHEMA NODE + * 1) All keys in the device/subsystem schema node are optional. + * 2) Each key specifies the name of a valid configuration parameter that may + * be applied to the device/subsystem combination specified by this node. + * The value associated with the key specifies the format of valid + * configuration values, and must be a nvlist in parameter schema node + * format. + * + * PARAMETER SCHEMA NODE + * 1) The parameter schema node must contain a key with the name + * TYPE_SCHEMA_NAME. The value associated with this key must be a string. + * This string specifies the type of value that the parameter specified by + * this node must take. The string must have one of the following values: + * - "bool" - The configuration value must be a boolean. + * - "mac-addr" - The configuration value must be a binary value. In + * addition, the value must be exactly 6 bytes long and + * the value must not be a multicast or broadcast mac. + * - "uint8_t" - The configuration value must be a integer value in + * the range [0, UINT8_MAX]. + * - "uint16_t" - The configuration value must be a integer value in + * the range [0, UINT16_MAX]. + * - "uint32_t" - The configuration value must be a integer value in + * the range [0, UINT32_MAX]. + * - "uint64_t" - The configuration value must be a integer value in + * the range [0, UINT64_MAX]. + * 2) The parameter schema may contain a key with the name + * REQUIRED_SCHEMA_NAME. This key is optional. If this key is present, the + * value associated with it must have a boolean type. If the value is true, + * then the parameter specified by this schema is a required parameter. All + * valid configurations must include all required parameters. + * 3) The parameter schema may contain a key with the name DEFAULT_SCHEMA_NAME. + * This key is optional. This key must not be present if the parameter + * specified by this schema is required. If this key is present, the value + * associated with the parent key must follow all restrictions specified by + * the type specified by this schema. If a configuration does not supply a + * value for the parameter specified by this schema, then the kernel will + * apply the value associated with this key in its place. + * + * The following is an example of a valid schema, as printed by nvlist_dump. + * Keys are printed followed by the type of the value in parantheses. The + * value is displayed following a colon. The indentation level reflects the + * level of nesting of nvlists. String values are displayed between [] + * brackets. Binary values are shown with the length of the binary value (in + * bytes) followed by the actual binary values. + * + * PF (NVLIST): + * IOV (NVLIST): + * num_vfs (NVLIST): + * type (STRING): [uint16_t] + * required (BOOL): TRUE + * device (NVLIST): + * type (STRING): [string] + * required (BOOL): TRUE + * DRIVER (NVLIST): + * VF (NVLIST): + * IOV (NVLIST): + * passthrough (NVLIST): + * type (STRING): [bool] + * default (BOOL): FALSE + * DRIVER (NVLIST): + * mac-addr (NVLIST): + * type (STRING): [mac-addr] + * default (BINARY): 6 000000000000 + * vlan (NVLIST): + * type (STRING): [uint16_t] + * spoof-check (NVLIST): + * type (STRING): [bool] + * default (BOOL): TRUE + * allow-set-mac (NVLIST): + * type (STRING): [bool] + * default (BOOL): FALSE + */ +struct pci_iov_schema +{ + void *schema; + size_t len; + int error; +}; + +/* + * SR-IOV configuration is passed to the kernel as a packed nvlist. See nv(3) + * for the details of the nvlist API. The expected format of the nvlist is: + * + * BASIC RULES + * 1) All keys are case-insensitive. + * 2) No keys that are not specified below may exist at any level of the + * config nvlist. + * 3) Unless otherwise specified, all keys are optional. It should go without + * saying a key being mandatory is transitive: that is, if a key is + * specified to contain a sub-nodes that contains a mandatory key, then + * the outer key is implicitly mandatory. If a key is mandatory then the + * associated value is also mandatory. + * 4) Order of keys is irrelevant. + * + * TOP LEVEL OF CONFIG NVLIST + * 1) All keys specified in this section are mandatory. + * 2) There must be a top-level key with the name PF_CONFIG_NAME. The value + * associated is an nvlist that follows the "device node" format. The + * parameters in this node specify parameters that apply to the PF. + * 3) For every VF being configured (this is set via the "num_vfs" parameter + * in the PF section), there must be a top-level key whose name is VF_PREFIX + * immediately followed by the index of the VF as a decimal integer. For + * example, this would be VF-0 for the first VF. VFs are numbered starting + * from 0. The value associated with this key follows the "device node" + * format. The parameters in this node specify configuration that applies + * to the VF specified in the key. Leading zeros are not permitted in VF + * index. Configuration for the second VF must be specified in a node with + * the key VF-1. VF-01 is not a valid key. + * + * DEVICE NODES + * 1) All keys specified in this section are mandatory. + * 2) The device node must contain a key with the name DRIVER_CONFIG_NAME. The + * value associated with this key is an nvlist following the subsystem node + * format. The parameters in this key specify configuration that is specific + * to a particular device driver. + * 3) The device node must contain a key with the name IOV_CONFIG_NAME. The + * value associated with this key is an nvlist following the subsystem node + * format. The parameters in this key specify configuration that is consumed + * by the SR-IOV infrastructure. + * + * SUBSYSTEM NODES + * 1) A subsystem node specifies configuration parameters that apply to a + * particular subsystem (driver or infrastructure) of a particular device + * (PF or individual VF). + * Note: We will refer to the section of the configuration schema that + * specifies the parameters for this subsystem and device + * configuration as the device/subystem schema. + * 2) The subsystem node must contain only keys that correspond to parameters + * that are specified in the device/subsystem schema. + * 3) Every parameter specified as required in the device/subsystem schema is + * a mandatory key in the subsystem node. + * Note: All parameters that are not required in device/subsystem schema are + * optional keys. In particular, any parameter specified to have a + * default value in the device/subsystem schema is optional. The + * kernel is responsible for applying default values. + * 4) The value of every parameter in the device node must conform to the + * restrictions of the type specified for that parameter in the device/ + * subsystem schema. + * + * The following is an example of a valid configuration, when validated against + * the schema example given above. + * + * PF (NVLIST): + * driver (NVLIST): + * iov (NVLIST): + * num_vfs (NUMBER): 3 (3) (0x3) + * device (STRING): [ix0] + * VF-0 (NVLIST): + * driver (NVLIST): + * vlan (NUMBER): 1000 (1000) (0x3e8) + * iov (NVLIST): + * passthrough (BOOL): TRUE + * VF-1 (NVLIST): + * driver (NVLIST): + * iov (NVLIST): + * VF-2 (NVLIST): + * driver (NVLIST): + * mac-addr (BINARY): 6 020102030405 + * iov (NVLIST): + */ +struct pci_iov_arg +{ + void *config; + size_t len; +}; + +#define IOV_CONFIG _IOW('p', 10, struct pci_iov_arg) +#define IOV_DELETE _IO('p', 11) +#define IOV_GET_SCHEMA _IOWR('p', 12, struct pci_iov_schema) + +#endif + Index: sys/sys/iov_schema.h =================================================================== --- sys/sys/iov_schema.h +++ sys/sys/iov_schema.h @@ -0,0 +1,52 @@ +/*- + * Copyright (c) 2014-2015 Sandvine Inc. All rights reserved. + * 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 _SYS_IOV_SCHEMA_H_ +#define _SYS_IOV_SCHEMA_H_ + +#define IOV_SCHEMA_HASDEFAULT (1 << 0) +#define IOV_SCHEMA_REQUIRED (1 << 1) + +nvlist_t *pci_iov_schema_alloc_node(void); + +void pci_iov_schema_add_bool(nvlist_t *schema, const char *name, + uint32_t flags, int defaultVal); +void pci_iov_schema_add_string(nvlist_t *schema, const char *name, + uint32_t flags, const char *defaultVal); +void pci_iov_schema_add_uint8(nvlist_t *schema, const char *name, + uint32_t flags, uint8_t defaultVal); +void pci_iov_schema_add_uint16(nvlist_t *schema, const char *name, + uint32_t flags, uint16_t defaultVal); +void pci_iov_schema_add_uint32(nvlist_t *schema, const char *name, + uint32_t flags, uint32_t defaultVal); +void pci_iov_schema_add_uint64(nvlist_t *schema, const char *name, + uint32_t flags, uint64_t defaultVal); +void pci_iov_schema_add_unicast_mac(nvlist_t *schema, const char *name, + uint32_t flags, const uint8_t * defaultVal); + +#endif Index: sys/sys/nv.h =================================================================== --- sys/sys/nv.h +++ sys/sys/nv.h @@ -1,5 +1,6 @@ /*- * Copyright (c) 2009-2013 The FreeBSD Foundation + * Copyright (c) 2013-2015 Mariusz Zaborski * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from @@ -59,11 +60,20 @@ #define NV_TYPE_NVLIST 5 #define NV_TYPE_DESCRIPTOR 6 #define NV_TYPE_BINARY 7 +#define NV_TYPE_BOOL_ARRAY 8 +#define NV_TYPE_NUMBER_ARRAY 9 +#define NV_TYPE_STRING_ARRAY 10 +#define NV_TYPE_NVLIST_ARRAY 11 +#define NV_TYPE_DESCRIPTOR_ARRAY 12 /* * Perform case-insensitive lookups of provided names. */ #define NV_FLAG_IGNORE_CASE 0x01 +/* + * Names don't have to be unique. + */ +#define NV_FLAG_NO_UNIQUE 0x02 #if defined(_KERNEL) && defined(MALLOC_DECLARE) MALLOC_DECLARE(M_NVLIST); @@ -87,16 +97,21 @@ size_t nvlist_size(const nvlist_t *nvl); void *nvlist_pack(const nvlist_t *nvl, size_t *sizep); -nvlist_t *nvlist_unpack(const void *buf, size_t size); +nvlist_t *nvlist_unpack(const void *buf, size_t size, int flags); int nvlist_send(int sock, const nvlist_t *nvl); -nvlist_t *nvlist_recv(int sock); -nvlist_t *nvlist_xfer(int sock, nvlist_t *nvl); +nvlist_t *nvlist_recv(int sock, int flags); +nvlist_t *nvlist_xfer(int sock, nvlist_t *nvl, int flags); const char *nvlist_next(const nvlist_t *nvl, int *typep, void **cookiep); const nvlist_t *nvlist_get_parent(const nvlist_t *nvl, void **cookiep); +const nvlist_t *nvlist_get_array_next(const nvlist_t *nvl); +bool nvlist_in_array(const nvlist_t *nvl); + +const nvlist_t *nvlist_get_pararr(const nvlist_t *nvl, void **cookiep); + /* * The nvlist_exists functions check if the given name (optionally of the given * type) exists on nvlist. @@ -110,10 +125,15 @@ bool nvlist_exists_number(const nvlist_t *nvl, const char *name); bool nvlist_exists_string(const nvlist_t *nvl, const char *name); bool nvlist_exists_nvlist(const nvlist_t *nvl, const char *name); +bool nvlist_exists_binary(const nvlist_t *nvl, const char *name); +bool nvlist_exists_bool_array(const nvlist_t *nvl, const char *name); +bool nvlist_exists_number_array(const nvlist_t *nvl, const char *name); +bool nvlist_exists_string_array(const nvlist_t *nvl, const char *name); +bool nvlist_exists_nvlist_array(const nvlist_t *nvl, const char *name); #ifndef _KERNEL bool nvlist_exists_descriptor(const nvlist_t *nvl, const char *name); +bool nvlist_exists_descriptor_array(const nvlist_t *nvl, const char *name); #endif -bool nvlist_exists_binary(const nvlist_t *nvl, const char *name); /* * The nvlist_add functions add the given name/value pair. @@ -130,10 +150,15 @@ void nvlist_add_stringv(nvlist_t *nvl, const char *name, const char *valuefmt, va_list valueap) __printflike(3, 0); #endif void nvlist_add_nvlist(nvlist_t *nvl, const char *name, const nvlist_t *value); +void nvlist_add_binary(nvlist_t *nvl, const char *name, const void *value, size_t size); +void nvlist_add_bool_array(nvlist_t *nvl, const char *name, const bool *value, size_t nitems); +void nvlist_add_number_array(nvlist_t *nvl, const char *name, const uint64_t *value, size_t nitems); +void nvlist_add_string_array(nvlist_t *nvl, const char *name, const char * const *value, size_t nitems); +void nvlist_add_nvlist_array(nvlist_t *nvl, const char *name, const nvlist_t * const *value, size_t nitems); #ifndef _KERNEL void nvlist_add_descriptor(nvlist_t *nvl, const char *name, int value); +void nvlist_add_descriptor_array(nvlist_t *nvl, const char *name, const int *value, size_t nitems); #endif -void nvlist_add_binary(nvlist_t *nvl, const char *name, const void *value, size_t size); /* * The nvlist_move functions add the given name/value pair. @@ -142,10 +167,15 @@ void nvlist_move_string(nvlist_t *nvl, const char *name, char *value); void nvlist_move_nvlist(nvlist_t *nvl, const char *name, nvlist_t *value); +void nvlist_move_binary(nvlist_t *nvl, const char *name, void *value, size_t size); +void nvlist_move_bool_array(nvlist_t *nvl, const char *name, bool *value, size_t nitems); +void nvlist_move_string_array(nvlist_t *nvl, const char *name, char **value, size_t nitems); +void nvlist_move_nvlist_array(nvlist_t *nvl, const char *name, nvlist_t **value, size_t nitems); +void nvlist_move_number_array(nvlist_t *nvl, const char *name, uint64_t *value, size_t nitems); #ifndef _KERNEL void nvlist_move_descriptor(nvlist_t *nvl, const char *name, int value); +void nvlist_move_descriptor_array(nvlist_t *nvl, const char *name, int *value, size_t nitems); #endif -void nvlist_move_binary(nvlist_t *nvl, const char *name, void *value, size_t size); /* * The nvlist_get functions returns value associated with the given name. @@ -153,14 +183,19 @@ * not be freed by the caller. */ -bool nvlist_get_bool(const nvlist_t *nvl, const char *name); -uint64_t nvlist_get_number(const nvlist_t *nvl, const char *name); -const char *nvlist_get_string(const nvlist_t *nvl, const char *name); -const nvlist_t *nvlist_get_nvlist(const nvlist_t *nvl, const char *name); +bool nvlist_get_bool(const nvlist_t *nvl, const char *name); +uint64_t nvlist_get_number(const nvlist_t *nvl, const char *name); +const char *nvlist_get_string(const nvlist_t *nvl, const char *name); +const nvlist_t *nvlist_get_nvlist(const nvlist_t *nvl, const char *name); +const void *nvlist_get_binary(const nvlist_t *nvl, const char *name, size_t *sizep); +const bool *nvlist_get_bool_array(const nvlist_t *nvl, const char *name, size_t *nitemsp); +const uint64_t *nvlist_get_number_array(const nvlist_t *nvl, const char *name, size_t *nitemsp); +const char * const *nvlist_get_string_array(const nvlist_t *nvl, const char *name, size_t *nitemsp); +const nvlist_t * const *nvlist_get_nvlist_array(const nvlist_t *nvl, const char *name, size_t *nitemsp); #ifndef _KERNEL -int nvlist_get_descriptor(const nvlist_t *nvl, const char *name); +int nvlist_get_descriptor(const nvlist_t *nvl, const char *name); +const int *nvlist_get_descriptor_array(const nvlist_t *nvl, const char *name, size_t *nitemsp); #endif -const void *nvlist_get_binary(const nvlist_t *nvl, const char *name, size_t *sizep); /* * The nvlist_take functions returns value associated with the given name and @@ -168,14 +203,19 @@ * The caller is responsible for freeing received data. */ -bool nvlist_take_bool(nvlist_t *nvl, const char *name); -uint64_t nvlist_take_number(nvlist_t *nvl, const char *name); -char *nvlist_take_string(nvlist_t *nvl, const char *name); -nvlist_t *nvlist_take_nvlist(nvlist_t *nvl, const char *name); +bool nvlist_take_bool(nvlist_t *nvl, const char *name); +uint64_t nvlist_take_number(nvlist_t *nvl, const char *name); +char *nvlist_take_string(nvlist_t *nvl, const char *name); +nvlist_t *nvlist_take_nvlist(nvlist_t *nvl, const char *name); +void *nvlist_take_binary(nvlist_t *nvl, const char *name, size_t *sizep); +bool *nvlist_take_bool_array(nvlist_t *nvl, const char *name, size_t *nitemsp); +uint64_t *nvlist_take_number_array(nvlist_t *nvl, const char *name, size_t *nitemsp); +char **nvlist_take_string_array(nvlist_t *nvl, const char *name, size_t *nitemsp); +nvlist_t **nvlist_take_nvlist_array(nvlist_t *nvl, const char *name, size_t *nitemsp); #ifndef _KERNEL int nvlist_take_descriptor(nvlist_t *nvl, const char *name); +int *nvlist_take_descriptor_array(nvlist_t *nvl, const char *name, size_t *nitemsp); #endif -void *nvlist_take_binary(nvlist_t *nvl, const char *name, size_t *sizep); /* * The nvlist_free functions removes the given name/value pair from the nvlist @@ -190,10 +230,16 @@ void nvlist_free_number(nvlist_t *nvl, const char *name); void nvlist_free_string(nvlist_t *nvl, const char *name); void nvlist_free_nvlist(nvlist_t *nvl, const char *name); +void nvlist_free_binary(nvlist_t *nvl, const char *name); +void nvlist_free_bool_array(nvlist_t *nvl, const char *name); +void nvlist_free_number_array(nvlist_t *nvl, const char *name); +void nvlist_free_string_array(nvlist_t *nvl, const char *name); +void nvlist_free_nvlist_array(nvlist_t *nvl, const char *name); +void nvlist_free_binary_array(nvlist_t *nvl, const char *name); #ifndef _KERNEL void nvlist_free_descriptor(nvlist_t *nvl, const char *name); +void nvlist_free_descriptor_array(nvlist_t *nvl, const char *name); #endif -void nvlist_free_binary(nvlist_t *nvl, const char *name); __END_DECLS Index: sys/sys/nv_impl.h =================================================================== --- sys/sys/nv_impl.h +++ sys/sys/nv_impl.h @@ -1,131 +0,0 @@ -/*- - * Copyright (c) 2013 The FreeBSD Foundation - * All rights reserved. - * - * This software was developed by Pawel Jakub Dawidek under sponsorship from - * the FreeBSD Foundation. - * - * 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 AUTHORS 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 AUTHORS 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 _NV_IMPL_H_ -#define _NV_IMPL_H_ - -#ifndef _NVPAIR_T_DECLARED -#define _NVPAIR_T_DECLARED -struct nvpair; - -typedef struct nvpair nvpair_t; -#endif - -#define NV_TYPE_NVLIST_UP 255 - -#define NV_TYPE_FIRST NV_TYPE_NULL -#define NV_TYPE_LAST NV_TYPE_BINARY - -#define NV_FLAG_BIG_ENDIAN 0x80 - -#ifdef _KERNEL -#define nv_malloc(size) malloc((size), M_NVLIST, M_NOWAIT) -#define nv_calloc(n, size) malloc((n) * (size), M_NVLIST, \ - M_NOWAIT | M_ZERO) -#define nv_realloc(buf, size) realloc((buf), (size), M_NVLIST, \ - M_NOWAIT) -#define nv_free(buf) free((buf), M_NVLIST) -#define nv_strdup(buf) strdup((buf), M_NVLIST) -#define nv_vasprintf(ptr, ...) vasprintf(ptr, M_NVLIST, __VA_ARGS__) - -#define SAVE_ERRNO(var) ((void)(var)) -#define RESTORE_ERRNO(var) ((void)(var)) - -#define ERRNO_OR_DEFAULT(default) (default) - -#else - -#define nv_malloc(size) malloc((size)) -#define nv_calloc(n, size) calloc((n), (size)) -#define nv_realloc(buf, size) realloc((buf), (size)) -#define nv_free(buf) free((buf)) -#define nv_strdup(buf) strdup((buf)) -#define nv_vasprintf(ptr, ...) vasprintf(ptr, __VA_ARGS__) - -#define SAVE_ERRNO(var) (var) = errno -#define RESTORE_ERRNO(var) errno = (var) - -#define ERRNO_OR_DEFAULT(default) (errno == 0 ? (default) : errno) - -#endif - -int *nvlist_descriptors(const nvlist_t *nvl, size_t *nitemsp); -size_t nvlist_ndescriptors(const nvlist_t *nvl); - -nvpair_t *nvlist_first_nvpair(const nvlist_t *nvl); -nvpair_t *nvlist_next_nvpair(const nvlist_t *nvl, const nvpair_t *nvp); -nvpair_t *nvlist_prev_nvpair(const nvlist_t *nvl, const nvpair_t *nvp); - -void nvlist_add_nvpair(nvlist_t *nvl, const nvpair_t *nvp); - -void nvlist_move_nvpair(nvlist_t *nvl, nvpair_t *nvp); - -void nvlist_set_parent(nvlist_t *nvl, nvpair_t *parent); - -const nvpair_t *nvlist_get_nvpair(const nvlist_t *nvl, const char *name); - -nvpair_t *nvlist_take_nvpair(nvlist_t *nvl, const char *name); - -/* Function removes the given nvpair from the nvlist. */ -void nvlist_remove_nvpair(nvlist_t *nvl, nvpair_t *nvp); - -void nvlist_free_nvpair(nvlist_t *nvl, nvpair_t *nvp); - -int nvpair_type(const nvpair_t *nvp); -const char *nvpair_name(const nvpair_t *nvp); - -nvpair_t *nvpair_clone(const nvpair_t *nvp); - -nvpair_t *nvpair_create_null(const char *name); -nvpair_t *nvpair_create_bool(const char *name, bool value); -nvpair_t *nvpair_create_number(const char *name, uint64_t value); -nvpair_t *nvpair_create_string(const char *name, const char *value); -nvpair_t *nvpair_create_stringf(const char *name, const char *valuefmt, ...) __printflike(2, 3); -nvpair_t *nvpair_create_stringv(const char *name, const char *valuefmt, va_list valueap) __printflike(2, 0); -nvpair_t *nvpair_create_nvlist(const char *name, const nvlist_t *value); -nvpair_t *nvpair_create_descriptor(const char *name, int value); -nvpair_t *nvpair_create_binary(const char *name, const void *value, size_t size); - -nvpair_t *nvpair_move_string(const char *name, char *value); -nvpair_t *nvpair_move_nvlist(const char *name, nvlist_t *value); -nvpair_t *nvpair_move_descriptor(const char *name, int value); -nvpair_t *nvpair_move_binary(const char *name, void *value, size_t size); - -bool nvpair_get_bool(const nvpair_t *nvp); -uint64_t nvpair_get_number(const nvpair_t *nvp); -const char *nvpair_get_string(const nvpair_t *nvp); -const nvlist_t *nvpair_get_nvlist(const nvpair_t *nvp); -int nvpair_get_descriptor(const nvpair_t *nvp); -const void *nvpair_get_binary(const nvpair_t *nvp, size_t *sizep); - -void nvpair_free(nvpair_t *nvp); - -#endif /* !_NV_IMPL_H_ */ Index: sys/sys/nvlist_impl.h =================================================================== --- sys/sys/nvlist_impl.h +++ sys/sys/nvlist_impl.h @@ -1,49 +0,0 @@ -/*- - * Copyright (c) 2013 The FreeBSD Foundation - * All rights reserved. - * - * This software was developed by Pawel Jakub Dawidek under sponsorship from - * the FreeBSD Foundation. - * - * 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 AUTHORS 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 AUTHORS 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 _NVLIST_IMPL_H_ -#define _NVLIST_IMPL_H_ - -#ifndef _KERNEL -#include -#endif - -#include "nv.h" - -void *nvlist_xpack(const nvlist_t *nvl, int64_t *fdidxp, size_t *sizep); -nvlist_t *nvlist_xunpack(const void *buf, size_t size, const int *fds, - size_t nfds); - -nvpair_t *nvlist_get_nvpair_parent(const nvlist_t *nvl); -const unsigned char *nvlist_unpack_header(nvlist_t *nvl, - const unsigned char *ptr, size_t nfds, bool *isbep, size_t *leftp); - -#endif /* !_NVLIST_IMPL_H_ */ Index: sys/sys/nvpair_impl.h =================================================================== --- sys/sys/nvpair_impl.h +++ sys/sys/nvpair_impl.h @@ -1,94 +0,0 @@ -/*- - * Copyright (c) 2009-2013 The FreeBSD Foundation - * All rights reserved. - * - * This software was developed by Pawel Jakub Dawidek under sponsorship from - * the FreeBSD Foundation. - * - * 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 AUTHORS 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 AUTHORS 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 _NVPAIR_IMPL_H_ -#define _NVPAIR_IMPL_H_ - -#include - -#ifndef _KERNEL -#include -#endif - -#include "nv.h" - -TAILQ_HEAD(nvl_head, nvpair); - -void nvpair_assert(const nvpair_t *nvp); -nvlist_t *nvpair_nvlist(const nvpair_t *nvp); -nvpair_t *nvpair_next(const nvpair_t *nvp); -nvpair_t *nvpair_prev(const nvpair_t *nvp); -void nvpair_insert(struct nvl_head *head, nvpair_t *nvp, nvlist_t *nvl); -void nvpair_remove(struct nvl_head *head, nvpair_t *nvp, const nvlist_t *nvl); -size_t nvpair_header_size(void); -size_t nvpair_size(const nvpair_t *nvp); -const unsigned char *nvpair_unpack(bool isbe, const unsigned char *ptr, - size_t *leftp, nvpair_t **nvpp); -void nvpair_free_structure(nvpair_t *nvp); -void nvpair_init_datasize(nvpair_t *nvp); -const char *nvpair_type_string(int type); - -/* Pack functions. */ -unsigned char *nvpair_pack_header(const nvpair_t *nvp, unsigned char *ptr, - size_t *leftp); -unsigned char *nvpair_pack_null(const nvpair_t *nvp, unsigned char *ptr, - size_t *leftp); -unsigned char *nvpair_pack_bool(const nvpair_t *nvp, unsigned char *ptr, - size_t *leftp); -unsigned char *nvpair_pack_number(const nvpair_t *nvp, unsigned char *ptr, - size_t *leftp); -unsigned char *nvpair_pack_string(const nvpair_t *nvp, unsigned char *ptr, - size_t *leftp); -unsigned char *nvpair_pack_descriptor(const nvpair_t *nvp, unsigned char *ptr, - int64_t *fdidxp, size_t *leftp); -unsigned char *nvpair_pack_binary(const nvpair_t *nvp, unsigned char *ptr, - size_t *leftp); -unsigned char *nvpair_pack_nvlist_up(unsigned char *ptr, size_t *leftp); - -/* Unpack data functions. */ -const unsigned char *nvpair_unpack_header(bool isbe, nvpair_t *nvp, - const unsigned char *ptr, size_t *leftp); -const unsigned char *nvpair_unpack_null(bool isbe, nvpair_t *nvp, - const unsigned char *ptr, size_t *leftp); -const unsigned char *nvpair_unpack_bool(bool isbe, nvpair_t *nvp, - const unsigned char *ptr, size_t *leftp); -const unsigned char *nvpair_unpack_number(bool isbe, nvpair_t *nvp, - const unsigned char *ptr, size_t *leftp); -const unsigned char *nvpair_unpack_string(bool isbe, nvpair_t *nvp, - const unsigned char *ptr, size_t *leftp); -const unsigned char *nvpair_unpack_nvlist(bool isbe, nvpair_t *nvp, - const unsigned char *ptr, size_t *leftp, size_t nvlist, nvlist_t **child); -const unsigned char *nvpair_unpack_descriptor(bool isbe, nvpair_t *nvp, - const unsigned char *ptr, size_t *leftp, const int *fds, size_t nfds); -const unsigned char *nvpair_unpack_binary(bool isbe, nvpair_t *nvp, - const unsigned char *ptr, size_t *leftp); - -#endif /* !_NVPAIR_IMPL_H_ */ Index: usr.sbin/Makefile =================================================================== --- usr.sbin/Makefile +++ usr.sbin/Makefile @@ -35,6 +35,7 @@ i2c \ ifmcstat \ iostat \ + iovctl \ kldxref \ mailwrapper \ makefs \ Index: usr.sbin/iovctl/Makefile =================================================================== --- usr.sbin/iovctl/Makefile +++ usr.sbin/iovctl/Makefile @@ -0,0 +1,20 @@ +# $FreeBSD$ + +PROG= iovctl +SRCS= iovctl.c parse.c validate.c +DPADD+= ${LIBNV} ${LIBUCL} ${LIBM} +LDADD+= -lnv -lucl -lm + +USEPRIVATELIB= + +CFLAGS+=-I${.CURDIR}/../../contrib/libucl/include + +WARNS?=6 + +MAN= \ + iovctl.8 \ + iovctl.conf.5 \ + +.include +.include + Index: usr.sbin/iovctl/iovctl.h =================================================================== --- usr.sbin/iovctl/iovctl.h +++ usr.sbin/iovctl/iovctl.h @@ -0,0 +1,37 @@ +/*- + * Copyright (c) 2013-2015 Sandvine Inc. All rights reserved. + * 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 IOVCTL_H +#define IOVCTL_H + +char * find_device(const char *); +nvlist_t * parse_config_file(const char *, const nvlist_t *); +void validate_config(nvlist_t *, const nvlist_t *, const regex_t *); + +#endif + Index: usr.sbin/iovctl/iovctl.8 =================================================================== --- usr.sbin/iovctl/iovctl.8 +++ usr.sbin/iovctl/iovctl.8 @@ -0,0 +1,123 @@ +.\" +.\" Copyright (c) 2014 Sandvine Inc. +.\" 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$ +.\" +.Dd July 8, 2015 +.Dt IOVCTL 8 +.Os +.Sh NAME +.Nm iovctl +.Nd "PCI SR-IOV configuration utility" +.Sh SYNOPSIS +.Nm +.Fl C +.Op Fl f Ar config-file +.Op Fl n +.Nm +.Fl D +.Op Fl f Ar config-file | Fl d Ar device +.Op Fl n +.Nm +.Fl S +.Op Fl f Ar config-file | Fl d Ar device +.Sh DESCRIPTION +The +.Nm +utility creates or destroys PCI Single-Root I/O Virtualization +.Pq SR-IOV +Virtual Functions +.Pq VFs . +When invoked with the +.Fl C +flag, +.Nm +creates VFs as children of the Physical Function +.Pq PF +configured in the specified configuration file. +When invoked with the +.Fl D +flag, +.Nm +destroys all VFs that are children of the specified device. +Available PF devices can be seen in +.Pa /dev/iov/ . +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl C +Enable SR-IOV on the specified PF device and create VF children. +This operation will fail if the PF already has VF children. +This option must be used in conjunction with the +.Fl f +option. +.It Fl d Ar device +Specify the PF device to use for the given operation. +.Ar device +may either be the name of a PF device, or a full path name to a node in +.Pa /dev/iov/ . +This option may not be used with the +.Fl C +option. +.It Fl D +Delete all VF children of the specified PF device. +This operation will fail if SR-IOV is not currently enabled on the specified +device. +.It Fl f Ar config-file +Specify the pathname of the configuration file. +For the +.Fl C +option, this file will be used to specify all configuration values. +For the +.Fl D +and +.Fl S +options, this file will only be used to specify the name of the PF device. +.Pp +See +.Xr iovctl.conf +for a description of the config file format and documentation of the +configuration parameters that apply to all PF drivers. +See the PF driver manual page for configuration parameters specific to +particular hardware. +.It Fl n +Perform a dry-run. +Perform all validation of the specified action and print what would be done, +but do not perform the actual creation or destruction of VFs. +This option may not be used with the +.Fl S +flag. +.It Fl S +Read the configuration schema from the specified device and print its contents +to stdout. +This action may be used to discover the configuration parameters supported on +a given PF device. +.El +.Sh SEE ALSO +.Xr iovctl.conf 5 , +.Xr rc.conf 5 +.Sh AUTHORS +This manual page was written by +.An Ryan Stone Aq Mt rstone@FreeBSD.org . Index: usr.sbin/iovctl/iovctl.c =================================================================== --- usr.sbin/iovctl/iovctl.c +++ usr.sbin/iovctl/iovctl.c @@ -0,0 +1,403 @@ +/*- + * Copyright (c) 2013-2015 Sandvine Inc. All rights reserved. + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iovctl.h" + +static void config_action(const char *filename, int dryrun); +static void delete_action(const char *device, int dryrun); +static void print_schema(const char *device); + +/* + * Fetch the config schema from the kernel via ioctl. This function has to + * call the ioctl twice: the first returns the amount of memory that we need + * to allocate for the schema, and the second actually fetches the schema. + */ +static nvlist_t * +get_schema(int fd) +{ + struct pci_iov_schema arg; + nvlist_t *schema; + int error; + + /* Do the ioctl() once to fetch the size of the schema. */ + arg.schema = NULL; + arg.len = 0; + arg.error = 0; + error = ioctl(fd, IOV_GET_SCHEMA, &arg); + if (error != 0) + err(1, "Could not fetch size of config schema"); + + arg.schema = malloc(arg.len); + if (arg.schema == NULL) + err(1, "Could not allocate %zu bytes for schema", + arg.len); + + /* Now do the ioctl() for real to get the schema. */ + error = ioctl(fd, IOV_GET_SCHEMA, &arg); + if (error != 0 || arg.error != 0) { + if (arg.error != 0) + errno = arg.error; + err(1, "Could not fetch config schema"); + } + + schema = nvlist_unpack(arg.schema, arg.len, NV_FLAG_IGNORE_CASE); + if (schema == NULL) + err(1, "Could not unpack schema"); + + free(arg.schema); + return (schema); +} + +/* + * Call the ioctl that activates SR-IOV and creates the VFs. + */ +static void +config_iov(int fd, const char *dev_name, const nvlist_t *config, int dryrun) +{ + struct pci_iov_arg arg; + int error; + + arg.config = nvlist_pack(config, &arg.len); + if (arg.config == NULL) + err(1, "Could not pack configuration"); + + if (dryrun) { + printf("Would enable SR-IOV on device '%s'.\n", dev_name); + printf( + "The following configuration parameters would be used:\n"); + nvlist_fdump(config, stdout); + printf( + "The configuration parameters consume %zu bytes when packed.\n", + arg.len); + } else { + error = ioctl(fd, IOV_CONFIG, &arg); + if (error != 0) + err(1, "Failed to configure SR-IOV"); + } + + free(arg.config); +} + +static int +open_device(const char *dev_name) +{ + char *dev; + int fd; + size_t copied, size; + long path_max; + + path_max = pathconf("/dev", _PC_PATH_MAX); + if (path_max < 0) + err(1, "Could not get maximum path length"); + + size = path_max; + dev = malloc(size); + if (dev == NULL) + err(1, "Could not allocate memory for device path"); + + if (dev_name[0] == '/') + copied = strlcpy(dev, dev_name, size); + else + copied = snprintf(dev, size, "/dev/iov/%s", dev_name); + + /* >= to account for null terminator. */ + if (copied >= size) + errx(1, "Provided file name too long"); + + fd = open(dev, O_RDWR); + if (fd < 0) + err(1, "Could not open device '%s'", dev); + + free(dev); + return (fd); +} + +static void +usage(void) +{ + + warnx("Usage: iovctl -C -f [-n]"); + warnx(" iovctl -D [-d | -f ] [-n]"); + warnx(" iovctl -S [-d | -f ]"); + exit(1); + +} + +enum main_action { + NONE, + CONFIG, + DELETE, + PRINT_SCHEMA, +}; + +int +main(int argc, char **argv) +{ + char *device; + const char *filename; + int ch, dryrun; + enum main_action action; + + device = NULL; + filename = NULL; + dryrun = 0; + action = NONE; + + while ((ch = getopt(argc, argv, "Cd:Df:nS")) != -1) { + switch (ch) { + case 'C': + if (action != NONE) { + warnx( + "Only one of -C, -D or -S may be specified"); + usage(); + } + action = CONFIG; + break; + case 'd': + device = strdup(optarg); + break; + case 'D': + if (action != NONE) { + warnx( + "Only one of -C, -D or -S may be specified"); + usage(); + } + action = DELETE; + break; + case 'f': + filename = optarg; + break; + case 'n': + dryrun = 1; + break; + case 'S': + if (action != NONE) { + warnx( + "Only one of -C, -D or -S may be specified"); + usage(); + } + action = PRINT_SCHEMA; + break; + case '?': + warnx("Unrecognized argument '-%c'\n", optopt); + usage(); + break; + } + } + + if (device != NULL && filename != NULL) { + warnx("Only one of the -d and -f flags may be specified"); + usage(); + } + + if (device == NULL && filename == NULL) { + warnx("Either the -d or -f flag must be specified"); + usage(); + } + + switch (action) { + case CONFIG: + if (filename == NULL) { + warnx("-d flag cannot be used with the -C flag"); + usage(); + } + config_action(filename, dryrun); + break; + case DELETE: + if (device == NULL) + device = find_device(filename); + delete_action(device, dryrun); + free(device); + break; + case PRINT_SCHEMA: + if (dryrun) { + warnx("-n flag cannot be used with the -S flag"); + usage(); + } + if (device == NULL) + device = find_device(filename); + print_schema(device); + free(device); + break; + default: + usage(); + break; + } + + exit(0); +} + +static void +config_action(const char *filename, int dryrun) +{ + char *dev; + nvlist_t *schema, *config; + int fd; + + dev = find_device(filename); + fd = open(dev, O_RDWR); + if (fd < 0) + err(1, "Could not open device '%s'", dev); + + schema = get_schema(fd); + config = parse_config_file(filename, schema); + if (config == NULL) + errx(1, "Could not parse config"); + + config_iov(fd, dev, config, dryrun); + + nvlist_destroy(config); + nvlist_destroy(schema); + free(dev); + close(fd); +} + +static void +delete_action(const char *dev_name, int dryrun) +{ + int fd, error; + + fd = open_device(dev_name); + + if (dryrun) + printf("Would attempt to delete all VF children of '%s'\n", + dev_name); + else { + error = ioctl(fd, IOV_DELETE); + if (error != 0) + err(1, "Failed to delete VFs"); + } + + close(fd); +} + +static void +print_default_value(const nvlist_t *parameter, const char *type) +{ + const uint8_t *mac; + size_t size; + + if (strcasecmp(type, "bool") == 0) + printf(" (default = %s)", + nvlist_get_bool(parameter, DEFAULT_SCHEMA_NAME) ? "true" : + "false"); + else if (strcasecmp(type, "string") == 0) + printf(" (default = %s)", + nvlist_get_string(parameter, DEFAULT_SCHEMA_NAME)); + else if (strcasecmp(type, "uint8_t") == 0) + printf(" (default = %ju)", + (uintmax_t)nvlist_get_number(parameter, + DEFAULT_SCHEMA_NAME)); + else if (strcasecmp(type, "uint16_t") == 0) + printf(" (default = %ju)", + (uintmax_t)nvlist_get_number(parameter, + DEFAULT_SCHEMA_NAME)); + else if (strcasecmp(type, "uint32_t") == 0) + printf(" (default = %ju)", + (uintmax_t)nvlist_get_number(parameter, + DEFAULT_SCHEMA_NAME)); + else if (strcasecmp(type, "uint64_t") == 0) + printf(" (default = %ju)", + (uintmax_t)nvlist_get_number(parameter, + DEFAULT_SCHEMA_NAME)); + else if (strcasecmp(type, "unicast-mac") == 0) { + mac = nvlist_get_binary(parameter, DEFAULT_SCHEMA_NAME, &size); + printf(" (default = %02x:%02x:%02x:%02x:%02x:%02x)", mac[0], + mac[1], mac[2], mac[3], mac[4], mac[5]); + } else + errx(1, "Unexpected type in schema: '%s'", type); +} + +static void +print_subsystem_schema(const nvlist_t * subsystem_schema) +{ + const char *name, *type; + const nvlist_t *parameter; + void *it; + int nvtype; + + it = NULL; + while ((name = nvlist_next(subsystem_schema, &nvtype, &it)) != NULL) { + parameter = nvlist_get_nvlist(subsystem_schema, name); + type = nvlist_get_string(parameter, TYPE_SCHEMA_NAME); + + printf("\t%s : %s", name, type); + if (dnvlist_get_bool(parameter, REQUIRED_SCHEMA_NAME, false)) + printf(" (required)"); + else if (nvlist_exists(parameter, DEFAULT_SCHEMA_NAME)) + print_default_value(parameter, type); + else + printf(" (optional)"); + printf("\n"); + } +} + +static void +print_schema(const char *dev_name) +{ + nvlist_t *schema; + const nvlist_t *iov_schema, *driver_schema, *pf_schema, *vf_schema; + int fd; + + fd = open_device(dev_name); + schema = get_schema(fd); + + pf_schema = nvlist_get_nvlist(schema, PF_CONFIG_NAME); + iov_schema = nvlist_get_nvlist(pf_schema, IOV_CONFIG_NAME); + driver_schema = nvlist_get_nvlist(pf_schema, DRIVER_CONFIG_NAME); + printf( +"The following configuration parameters may be configured on the PF:\n"); + print_subsystem_schema(iov_schema); + print_subsystem_schema(driver_schema); + + vf_schema = nvlist_get_nvlist(schema, VF_SCHEMA_NAME); + iov_schema = nvlist_get_nvlist(vf_schema, IOV_CONFIG_NAME); + driver_schema = nvlist_get_nvlist(vf_schema, DRIVER_CONFIG_NAME); + printf( +"\nThe following configuration parameters may be configured on a VF:\n"); + print_subsystem_schema(iov_schema); + print_subsystem_schema(driver_schema); + + nvlist_destroy(schema); + close(fd); +} Index: usr.sbin/iovctl/iovctl.conf.5 =================================================================== --- usr.sbin/iovctl/iovctl.conf.5 +++ usr.sbin/iovctl/iovctl.conf.5 @@ -0,0 +1,171 @@ +.\" +.\" Copyright (c) 2014 Sandvine Inc. +.\" 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$ +.\" +.Dd July 8, 2015 +.Dt IOVCTL.CONF 5 +.Os +.Sh NAME +.Nm iovctl.conf +.Nd IOVCTL configuration file +.Sh DESCRIPTION +The +.Nm +file is the configuration file for the +.Xr iovctl 8 +program. +This file specifies configuration parameters for a single Physical Function +.Pq PF +device. +To configure SR-IOV on multiple PF devices, use one configuration file for each +PF. +The locations of all +.Xr iovctl 9 +configuration files are specified in +.Xr rc.conf 5 . +.Pp +The +.Nm +file uses UCL format. +UCL syntax is documented at the official UCL website: +http://github.com/vstakhov/libucl. +.Pp +There are three types of sections in the +.Nm +file. +A section is a key at the top level of the file with a list as its value. +The list may contain the keys specified in the +.Sx OPTIONS +section of this manual page. +Individual PF driver implementations may specify additional device-specific +configuration keys that they will accept. +The order in which sections appear in +.Nm +is ignored. +No two sections may have the same key. +For example, two sections for VF-1 must not be defined. +.Pp +The first section type is the PF section. +This section always has the key "PF"; therefore, only one such section may be +defined. +This section defines configuration parameters that apply to the PF as a whole. +.Pp +The second section type is the VF section. +This section has the key "VF-" followed by a VF index. +VF indices start at 0 and always increment by 1. +Valid VF indices are in the range of 0 to +.Pq num_vfs - 1 . +The VF index must be given as a decimal integer with no leading zeros. +This section defines configuration parameters that apply to a single VF. +.Pp +The third section type is the default section. +This section always has the key "DEFAULT"; therefore, only one such section may +be specified. +This section defines default configuration parameters that apply to all VFs. +All configuration keys that are valid to be applied to a VF are valid in this +section. +An individual VF section may override a default specified in this section by +providing a different value for the configuration parameter. +Note that the default section applies to ALL VFs. +The default section must appear before any VF sections. +The default section may appear before or after the PF section. +.Pp +The following option types are supported: +.Bl -tag -width indent +.It boolean +Accepts a boolean value of true or false. +.It mac-addr +Accepts a unicast MAC address specified as a string of the form +xx:xx:xx:xx:xx:xx, where xx is one or two hexadecimal digits. +.It string +Accepts any string value. +.It uint8_t +Accepts any integer in the range 0 to 255, inclusive. +.It uint16_t +Accepts any integer in the range 0 to 65535, inclusive. +.It uint32_t +Accepts any integer in the range 0 to +.Pq 2**32 - 1 , +inclusive. +.It uint64_t +Accepts any integer in the range 0 to +.Pq 2**64 - 1 , +inclusive. +.El +.Sh OPTIONS +The following parameters are accepted by all PF drivers: +.Bl -tag -width indent +.It device Pq string +This parameter specifies the name of the PF device. +This parameter is required to be specified. +.It num_vfs Pq uint16_t +This parameter specifies the number of VF children to create. +This parameter may not be zero. +The maximum value of this parameter is device-specific. +.El +.Pp +The following parameters are accepted by all VFs: +.Bl -tag -width indent +.It passthrough Pq boolean +This parameter controls whether the VF is reserved for the use of the +.Xr bhyve 8 +hypervisor as a PCI passthrough device. +If this parameter is set to true, then the VF will be reserved as a PCI +passthrough device and it will not be accessible from the host OS. +The default value of this parameter is false. +.El +.Pp +See the PF driver manual page for configuration parameters specific to +particular hardware. +.Sh EXAMPLES +This sample file will create 3 VFs as children of the ix0 device. +VF-1 and VF-2 are set as +.Xr bhyve 8 +passthrough devices through the use of the default section. +VF-0 is not configured as a passthrough device as it explicitly overrides the +default. +VF-0 also sets a device-specific parameter named mac-addr. +.Bd -literal .offset ident +PF { + device : "ix0"; + num_vfs : 3; +} + +DEFAULT { + passthrough : true; +} + +VF-0 { + mac-addr : "02:56:48:7e:d9:f7"; + passthrough : false; +} +.Ed +.Sh SEE ALSO +.Xr iovctl 8 , +.Xr rc.conf 5 +.Sh AUTHORS +This manual page was written by +.An Ryan Stone Aq Mt rstone@FreeBSD.org . Index: usr.sbin/iovctl/parse.c =================================================================== --- usr.sbin/iovctl/parse.c +++ usr.sbin/iovctl/parse.c @@ -0,0 +1,416 @@ +/*- + * Copyright (c) 2014-2015 Sandvine Inc. All rights reserved. + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iovctl.h" + +static void +report_config_error(const char *key, const ucl_object_t *obj, const char *type) +{ + + errx(1, "Value '%s' of key '%s' is not of type %s", + ucl_object_tostring(obj), key, type); +} + +/* + * Verifies that the value specified in the config file is a boolean value, and + * then adds the value to the configuration. + */ +static void +add_bool_config(const char *key, const ucl_object_t *obj, nvlist_t *config) +{ + bool val; + + if (!ucl_object_toboolean_safe(obj, &val)) + report_config_error(key, obj, "bool"); + + nvlist_add_bool(config, key, val); +} + +/* + * Verifies that the value specified in the config file is a string, and then + * adds the value to the configuration. + */ +static void +add_string_config(const char *key, const ucl_object_t *obj, nvlist_t *config) +{ + const char *val; + + if (!ucl_object_tostring_safe(obj, &val)) + report_config_error(key, obj, "string"); + + nvlist_add_string(config, key, val); +} + +/* + * Verifies that the value specified in the config file is a integer value + * within the specified range, and then adds the value to the configuration. + */ +static void +add_uint_config(const char *key, const ucl_object_t *obj, nvlist_t *config, + const char *type, uint64_t max) +{ + int64_t val; + uint64_t uval; + + /* I must use a signed type here as libucl doesn't provide unsigned. */ + if (!ucl_object_toint_safe(obj, &val)) + report_config_error(key, obj, type); + + if (val < 0) + report_config_error(key, obj, type); + + uval = val; + if (uval > max) + report_config_error(key, obj, type); + + nvlist_add_number(config, key, uval); +} + +/* + * Verifies that the value specified in the config file is a unicast MAC + * address, and then adds the value to the configuration. + */ +static void +add_unicast_mac_config(const char *key, const ucl_object_t *obj, nvlist_t *config) +{ + uint8_t mac[ETHER_ADDR_LEN]; + const char *val, *token; + char *parse, *orig_parse, *tokpos, *endpos; + size_t len; + u_long value; + int i; + + if (!ucl_object_tostring_safe(obj, &val)) + report_config_error(key, obj, "unicast-mac"); + + parse = strdup(val); + orig_parse = parse; + + i = 0; + while ((token = strtok_r(parse, ":", &tokpos)) != NULL) { + parse = NULL; + + len = strlen(token); + if (len < 1 || len > 2) + report_config_error(key, obj, "unicast-mac"); + + value = strtoul(token, &endpos, 16); + + if (*endpos != '\0') + report_config_error(key, obj, "unicast-mac"); + + if (value > UINT8_MAX) + report_config_error(key, obj, "unicast-mac"); + + if (i >= ETHER_ADDR_LEN) + report_config_error(key, obj, "unicast-mac"); + + mac[i] = value; + i++; + } + + free(orig_parse); + + if (i != ETHER_ADDR_LEN) + report_config_error(key, obj, "unicast-mac"); + + if (ETHER_IS_MULTICAST(mac)) + errx(1, "Value '%s' of key '%s' is a multicast address", + ucl_object_tostring(obj), key); + + nvlist_add_binary(config, key, mac, ETHER_ADDR_LEN); +} + +/* + * Validates that the given configuation value has the right type as specified + * in the schema, and then adds the value to the configuation node. + */ +static void +add_config(const char *key, const ucl_object_t *obj, nvlist_t *config, + const nvlist_t *schema) +{ + const char *type; + + type = nvlist_get_string(schema, TYPE_SCHEMA_NAME); + + if (strcasecmp(type, "bool") == 0) + add_bool_config(key, obj, config); + else if (strcasecmp(type, "string") == 0) + add_string_config(key, obj, config); + else if (strcasecmp(type, "uint8_t") == 0) + add_uint_config(key, obj, config, type, UINT8_MAX); + else if (strcasecmp(type, "uint16_t") == 0) + add_uint_config(key, obj, config, type, UINT16_MAX); + else if (strcasecmp(type, "uint32_t") == 0) + add_uint_config(key, obj, config, type, UINT32_MAX); + else if (strcasecmp(type, "uint64_t") == 0) + add_uint_config(key, obj, config, type, UINT64_MAX); + else if (strcasecmp(type, "unicast-mac") == 0) + add_unicast_mac_config(key, obj, config); + else + errx(1, "Unexpected type '%s' in schema", type); +} + +/* + * Parses all values specified in a device section in the configuration file, + * validates that the key/value pair is valid in the schema, and then adds + * the key/value pair to the correct subsystem in the config. + */ +static void +parse_device_config(const ucl_object_t *top, nvlist_t *config, + const char *subsystem, const nvlist_t *schema) +{ + ucl_object_iter_t it; + const ucl_object_t *obj; + nvlist_t *subsystem_config, *driver_config, *iov_config; + const nvlist_t *driver_schema, *iov_schema; + const char *key; + + if (nvlist_exists(config, subsystem)) + errx(1, "Multiple definitions of '%s' in config file", + subsystem); + + driver_schema = nvlist_get_nvlist(schema, DRIVER_CONFIG_NAME); + iov_schema = nvlist_get_nvlist(schema, IOV_CONFIG_NAME); + + driver_config = nvlist_create(NV_FLAG_IGNORE_CASE); + if (driver_config == NULL) + err(1, "Could not allocate config nvlist"); + + iov_config = nvlist_create(NV_FLAG_IGNORE_CASE); + if (iov_config == NULL) + err(1, "Could not allocate config nvlist"); + + subsystem_config = nvlist_create(NV_FLAG_IGNORE_CASE); + if (subsystem_config == NULL) + err(1, "Could not allocate config nvlist"); + + it = NULL; + while ((obj = ucl_iterate_object(top, &it, true)) != NULL) { + key = ucl_object_key(obj); + + if (nvlist_exists_nvlist(iov_schema, key)) + add_config(key, obj, iov_config, + nvlist_get_nvlist(iov_schema, key)); + else if (nvlist_exists_nvlist(driver_schema, key)) + add_config(key, obj, driver_config, + nvlist_get_nvlist(driver_schema, key)); + else + errx(1, "%s: Invalid config key '%s'", subsystem, key); + } + + nvlist_move_nvlist(subsystem_config, DRIVER_CONFIG_NAME, driver_config); + nvlist_move_nvlist(subsystem_config, IOV_CONFIG_NAME, iov_config); + nvlist_move_nvlist(config, subsystem, subsystem_config); +} + +/* + * Parses the specified config file using the given schema, and returns an + * nvlist containing the configuration specified by the file. + * + * Exits with a message to stderr and an error if any config validation fails. + */ +nvlist_t * +parse_config_file(const char *filename, const nvlist_t *schema) +{ + ucl_object_iter_t it; + struct ucl_parser *parser; + ucl_object_t *top; + const ucl_object_t *obj; + nvlist_t *config; + const nvlist_t *pf_schema, *vf_schema; + const char *errmsg, *key; + regex_t vf_pat; + int regex_err, processed_vf; + + regex_err = regcomp(&vf_pat, "^"VF_PREFIX"([1-9][0-9]*|0)$", + REG_EXTENDED | REG_ICASE); + if (regex_err != 0) + errx(1, "Could not compile VF regex"); + + parser = ucl_parser_new(0); + if (parser == NULL) + err(1, "Could not allocate parser"); + + if (!ucl_parser_add_file(parser, filename)) + err(1, "Could not open '%s' for reading", filename); + + errmsg = ucl_parser_get_error(parser); + if (errmsg != NULL) + errx(1, "Could not parse '%s': %s", filename, errmsg); + + config = nvlist_create(NV_FLAG_IGNORE_CASE); + if (config == NULL) + err(1, "Could not allocate config nvlist"); + + pf_schema = nvlist_get_nvlist(schema, PF_CONFIG_NAME); + vf_schema = nvlist_get_nvlist(schema, VF_SCHEMA_NAME); + + processed_vf = 0; + top = ucl_parser_get_object(parser); + it = NULL; + while ((obj = ucl_iterate_object(top, &it, true)) != NULL) { + key = ucl_object_key(obj); + + if (strcasecmp(key, PF_CONFIG_NAME) == 0) + parse_device_config(obj, config, key, pf_schema); + else if (strcasecmp(key, DEFAULT_SCHEMA_NAME) == 0) { + /* + * Enforce that the default section must come before all + * VF sections. This will hopefully prevent confusing + * the user by having a default value apply to a VF + * that was declared earlier in the file. + * + * This also gives us the flexibility to extend the file + * format in the future to allow for multiple default + * sections that do only apply to subsequent VF + * sections. + */ + if (processed_vf) + errx(1, + "'default' section must precede all VF sections"); + + parse_device_config(obj, config, key, vf_schema); + } else if (regexec(&vf_pat, key, 0, NULL, 0) == 0) { + processed_vf = 1; + parse_device_config(obj, config, key, vf_schema); + } else + errx(1, "Unexpected top-level node: %s", key); + } + + validate_config(config, schema, &vf_pat); + + ucl_object_unref(top); + ucl_parser_free(parser); + regfree(&vf_pat); + + return (config); +} + +/* + * Parse the PF configuration section for and return the value specified for + * the device parameter, or NULL if the device is not specified. + */ +static const char * +find_pf_device(const ucl_object_t *pf) +{ + ucl_object_iter_t it; + const ucl_object_t *obj; + const char *key, *device; + + it = NULL; + while ((obj = ucl_iterate_object(pf, &it, true)) != NULL) { + key = ucl_object_key(obj); + + if (strcasecmp(key, "device") == 0) { + if (!ucl_object_tostring_safe(obj, &device)) + err(1, + "Config PF.device must be a string"); + + return (device); + } + } + + return (NULL); +} + +/* + * Manually parse the config file looking for the name of the PF device. We + * have to do this separately because we need the config schema to call the + * normal config file parsing code, and we need to know the name of the PF + * device so that we can fetch the schema from it. + * + * This will always exit on failure, so if it returns then it is guaranteed to + * have returned a valid device name. + */ +char * +find_device(const char *filename) +{ + char *device; + const char *deviceName; + ucl_object_iter_t it; + struct ucl_parser *parser; + ucl_object_t *top; + const ucl_object_t *obj; + const char *errmsg, *key; + int error; + + device = NULL; + deviceName = NULL; + + parser = ucl_parser_new(0); + if (parser == NULL) + err(1, "Could not allocate parser"); + + if (!ucl_parser_add_file(parser, filename)) + err(1, "Could not open '%s' for reading", filename); + + errmsg = ucl_parser_get_error(parser); + if (errmsg != NULL) + errx(1, "Could not parse '%s': %s", filename, errmsg); + + top = ucl_parser_get_object (parser); + it = NULL; + while ((obj = ucl_iterate_object(top, &it, true)) != NULL) { + key = ucl_object_key(obj); + + if (strcasecmp(key, PF_CONFIG_NAME) == 0) { + deviceName = find_pf_device(obj); + break; + } + } + + if (deviceName == NULL) + errx(1, "Config file does not specify device"); + + error = asprintf(&device, "/dev/iov/%s", deviceName); + if (error < 0) + err(1, "Could not allocate memory for device"); + + ucl_object_unref(top); + ucl_parser_free(parser); + + return (device); +} Index: usr.sbin/iovctl/validate.c =================================================================== --- usr.sbin/iovctl/validate.c +++ usr.sbin/iovctl/validate.c @@ -0,0 +1,274 @@ +/*- + * Copyright (c) 2014-2015 Sandvine Inc. All rights reserved. + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include +#include + +#include "iovctl.h" + +/* + * Returns a writeable pointer to the configuration for the given device. + * If no configuration exists, a new nvlist with empty driver and iov + * sections is allocated and returned. + * + * Returning a writeable pointer requires removing the configuration from config + * using nvlist_take. It is the responsibility of the caller to re-insert the + * nvlist in config with nvlist_move_nvlist. + */ +static nvlist_t * +find_config(nvlist_t *config, const char * device) +{ + nvlist_t *subsystem, *empty_driver, *empty_iov; + + subsystem = dnvlist_take_nvlist(config, device, NULL); + + if (subsystem != NULL) + return (subsystem); + + empty_driver = nvlist_create(NV_FLAG_IGNORE_CASE); + if (empty_driver == NULL) + err(1, "Could not allocate config nvlist"); + + empty_iov = nvlist_create(NV_FLAG_IGNORE_CASE); + if (empty_iov == NULL) + err(1, "Could not allocate config nvlist"); + + subsystem = nvlist_create(NV_FLAG_IGNORE_CASE); + if (subsystem == NULL) + err(1, "Could not allocate config nvlist"); + + nvlist_move_nvlist(subsystem, DRIVER_CONFIG_NAME, empty_driver); + nvlist_move_nvlist(subsystem, IOV_CONFIG_NAME, empty_iov); + + return (subsystem); +} + +static uint16_t +parse_vf_num(const char *key, regmatch_t *matches) +{ + u_long vf_num; + + vf_num = strtoul(key + matches[1].rm_so, NULL, 10); + + if (vf_num > UINT16_MAX) + errx(1, "VF number %lu is too large to be valid", + vf_num); + + return (vf_num); +} + +/* + * Apply the default values specified in device_defaults to the specified + * subsystem in the given device_config. + * + * This function assumes that the values specified in device_defaults have + * already been validated. + */ +static void +apply_subsystem_defaults(nvlist_t *device_config, const char *subsystem, + const nvlist_t *device_defaults) +{ + nvlist_t *config; + const nvlist_t *defaults; + const char *name; + void *cookie; + size_t len; + const void *bin; + int type; + + config = nvlist_take_nvlist(device_config, subsystem); + defaults = nvlist_get_nvlist(device_defaults, subsystem); + + cookie = NULL; + while ((name = nvlist_next(defaults, &type, &cookie)) != NULL) { + if (nvlist_exists(config, name)) + continue; + + switch (type) { + case NV_TYPE_BOOL: + nvlist_add_bool(config, name, + nvlist_get_bool(defaults, name)); + break; + case NV_TYPE_NUMBER: + nvlist_add_number(config, name, + nvlist_get_number(defaults, name)); + break; + case NV_TYPE_STRING: + nvlist_add_string(config, name, + nvlist_get_string(defaults, name)); + break; + case NV_TYPE_NVLIST: + nvlist_add_nvlist(config, name, + nvlist_get_nvlist(defaults, name)); + break; + case NV_TYPE_BINARY: + bin = nvlist_get_binary(defaults, name, &len); + nvlist_add_binary(config, name, bin, len); + break; + default: + errx(1, "Unexpected type '%d'", type); + } + } + nvlist_move_nvlist(device_config, subsystem, config); +} + +/* + * Iterate over every subsystem in the given VF device and apply default values + * for parameters that were not configured with a value. + * + * This function assumes that the values specified in defaults have already been + * validated. + */ +static void +apply_defaults(nvlist_t *vf, const nvlist_t *defaults) +{ + + apply_subsystem_defaults(vf, DRIVER_CONFIG_NAME, defaults); + apply_subsystem_defaults(vf, IOV_CONFIG_NAME, defaults); +} + +/* + * Validate that all required parameters have been configured in the specified + * subsystem. + */ +static void +validate_subsystem(const nvlist_t *device, const nvlist_t *device_schema, + const char *subsystem_name, const char *config_name) +{ + const nvlist_t *subsystem, *schema, *config; + const char *name; + void *cookie; + int type; + + subsystem = nvlist_get_nvlist(device, subsystem_name); + schema = nvlist_get_nvlist(device_schema, subsystem_name); + + cookie = NULL; + while ((name = nvlist_next(schema, &type, &cookie)) != NULL) { + config = nvlist_get_nvlist(schema, name); + + if (dnvlist_get_bool(config, REQUIRED_SCHEMA_NAME, false)) { + if (!nvlist_exists(subsystem, name)) + errx(1, + "Required parameter '%s' not found in '%s'", + name, config_name); + } + } +} + +/* + * Validate that all required parameters have been configured in all subsystems + * in the device. + */ +static void +validate_device(const nvlist_t *device, const nvlist_t *schema, + const char *config_name) +{ + + validate_subsystem(device, schema, DRIVER_CONFIG_NAME, config_name); + validate_subsystem(device, schema, IOV_CONFIG_NAME, config_name); +} + +static uint16_t +get_num_vfs(const nvlist_t *pf) +{ + const nvlist_t *iov; + + iov = nvlist_get_nvlist(pf, IOV_CONFIG_NAME); + return (nvlist_get_number(iov, "num_vfs")); +} + +/* + * Validates the configuration that has been parsed into config using the given + * config schema. Note that the parser is required to not insert configuration + * keys that are not valid in the schema, and to not insert configuration values + * that are of the incorrect type. Therefore this function will not validate + * either condition. This function is only responsible for inserting config + * file defaults in individual VF sections and removing the DEFAULT_SCHEMA_NAME + * subsystem from config, validating that all required parameters in the schema + * are present in each PF and VF subsystem, and that there is no VF subsystem + * section whose number exceeds num_vfs. + */ +void +validate_config(nvlist_t *config, const nvlist_t *schema, const regex_t *vf_pat) +{ + char device_name[VF_MAX_NAME]; + regmatch_t matches[2]; + nvlist_t *defaults, *pf, *vf; + const nvlist_t *vf_schema; + const char *key; + void *cookie; + int i, type; + uint16_t vf_num, num_vfs; + + pf = find_config(config, PF_CONFIG_NAME); + validate_device(pf, nvlist_get_nvlist(schema, PF_CONFIG_NAME), + PF_CONFIG_NAME); + nvlist_move_nvlist(config, PF_CONFIG_NAME, pf); + + num_vfs = get_num_vfs(pf); + vf_schema = nvlist_get_nvlist(schema, VF_SCHEMA_NAME); + + if (num_vfs == 0) + errx(1, "PF.num_vfs must be at least 1"); + + defaults = dnvlist_take_nvlist(config, DEFAULT_SCHEMA_NAME, NULL); + + for (i = 0; i < num_vfs; i++) { + snprintf(device_name, sizeof(device_name), VF_PREFIX"%d", + i); + + vf = find_config(config, device_name); + + if (defaults != NULL) + apply_defaults(vf, defaults); + + validate_device(vf, vf_schema, device_name); + nvlist_move_nvlist(config, device_name, vf); + } + nvlist_destroy(defaults); + + cookie = NULL; + while ((key = nvlist_next(config, &type, &cookie)) != NULL) { + if (regexec(vf_pat, key, nitems(matches), matches, 0) == 0) { + vf_num = parse_vf_num(key, matches); + if (vf_num >= num_vfs) + errx(1, + "VF number %d is out of bounds (num_vfs=%d)", + vf_num, num_vfs); + } + } +} + Index: usr.sbin/pciconf/cap.c =================================================================== --- usr.sbin/pciconf/cap.c +++ usr.sbin/pciconf/cap.c @@ -37,6 +37,7 @@ #include #include +#include #include #include @@ -640,7 +641,7 @@ printf(" %d fatal", bitcount32(sta & mask)); printf(" %d non-fatal", bitcount32(sta & ~mask)); sta = read_config(fd, &p->pc_sel, ptr + PCIR_AER_COR_STATUS, 4); - printf(" %d corrected", bitcount32(sta)); + printf(" %d corrected\n", bitcount32(sta)); } static void @@ -656,6 +657,7 @@ if ((cap1 & PCIM_VC_CAP1_LOWPRI_EXT_COUNT) != 0) printf(" lowpri VC0-VC%d", (cap1 & PCIM_VC_CAP1_LOWPRI_EXT_COUNT) >> 4); + printf("\n"); } static void @@ -668,7 +670,7 @@ return; low = read_config(fd, &p->pc_sel, ptr + PCIR_SERIAL_LOW, 4); high = read_config(fd, &p->pc_sel, ptr + PCIR_SERIAL_HIGH, 4); - printf(" %08x%08x", high, low); + printf(" %08x%08x\n", high, low); } static void @@ -680,7 +682,7 @@ if (ver < 1) return; val = read_config(fd, &p->pc_sel, ptr + 4, 4); - printf(" ID %d", val & 0xffff); + printf(" ID %d\n", val & 0xffff); } static void @@ -692,9 +694,71 @@ if (ver < 1) return; val = read_config(fd, &p->pc_sel, ptr + 8, 4); - printf(" lane errors %#x", val); + printf(" lane errors %#x\n", val); } +static const char * +check_enabled(int value) +{ + + return (value ? "enabled" : "disabled"); +} + +static void +ecap_sriov(int fd, struct pci_conf *p, uint16_t ptr, uint8_t ver) +{ + const char *comma, *enabled; + uint16_t iov_ctl, total_vfs, num_vfs, vf_offset, vf_stride, vf_did; + uint32_t page_caps, page_size, page_shift, size; + int i; + + printf("SR-IOV %d ", ver); + + iov_ctl = read_config(fd, &p->pc_sel, ptr + PCIR_SRIOV_CTL, 2); + printf("IOV %s, Memory Space %s, ARI %s\n", + check_enabled(iov_ctl & PCIM_SRIOV_VF_EN), + check_enabled(iov_ctl & PCIM_SRIOV_VF_MSE), + check_enabled(iov_ctl & PCIM_SRIOV_ARI_EN)); + + total_vfs = read_config(fd, &p->pc_sel, ptr + PCIR_SRIOV_TOTAL_VFS, 2); + num_vfs = read_config(fd, &p->pc_sel, ptr + PCIR_SRIOV_NUM_VFS, 2); + printf(" "); + printf("%d VFs configured out of %d supported\n", num_vfs, total_vfs); + + vf_offset = read_config(fd, &p->pc_sel, ptr + PCIR_SRIOV_VF_OFF, 2); + vf_stride = read_config(fd, &p->pc_sel, ptr + PCIR_SRIOV_VF_STRIDE, 2); + printf(" "); + printf("First VF RID Offset 0x%04x, VF RID Stride 0x%04x\n", vf_offset, + vf_stride); + + vf_did = read_config(fd, &p->pc_sel, ptr + PCIR_SRIOV_VF_DID, 2); + printf(" VF Device ID 0x%04x\n", vf_did); + + page_caps = read_config(fd, &p->pc_sel, ptr + PCIR_SRIOV_PAGE_CAP, 4); + page_size = read_config(fd, &p->pc_sel, ptr + PCIR_SRIOV_PAGE_SIZE, 4); + printf(" "); + printf("Page Sizes: "); + comma = ""; + while (page_caps != 0) { + page_shift = ffs(page_caps) - 1; + + if (page_caps & page_size) + enabled = " (enabled)"; + else + enabled = ""; + + size = (1 << (page_shift + PCI_SRIOV_BASE_PAGE_SHIFT)); + printf("%s%d%s", comma, size, enabled); + comma = ", "; + + page_caps &= ~(1 << page_shift); + } + printf("\n"); + + for (i = 0; i <= PCIR_MAX_BAR_0; i++) + print_bar(fd, p, "iov bar ", ptr + PCIR_SRIOV_BAR(i)); +} + struct { uint16_t id; const char *name; @@ -708,7 +772,6 @@ { PCIZ_ACS, "ACS" }, { PCIZ_ARI, "ARI" }, { PCIZ_ATS, "ATS" }, - { PCIZ_SRIOV, "SRIOV" }, { PCIZ_MULTICAST, "Multicast" }, { PCIZ_RESIZE_BAR, "Resizable BAR" }, { PCIZ_DPA, "DPA" }, @@ -747,6 +810,9 @@ case PCIZ_SEC_PCIE: ecap_sec_pcie(fd, p, ptr, PCI_EXTCAP_VER(ecap)); break; + case PCIZ_SRIOV: + ecap_sriov(fd, p, ptr, PCI_EXTCAP_VER(ecap)); + break; default: name = "unknown"; for (i = 0; ecap_names[i].name != NULL; i++) @@ -754,10 +820,9 @@ name = ecap_names[i].name; break; } - printf("%s %d", name, PCI_EXTCAP_VER(ecap)); + printf("%s %d\n", name, PCI_EXTCAP_VER(ecap)); break; } - printf("\n"); ptr = PCI_EXTCAP_NEXTPTR(ecap); if (ptr == 0) break; Index: usr.sbin/pciconf/pciconf.h =================================================================== --- usr.sbin/pciconf/pciconf.h +++ usr.sbin/pciconf/pciconf.h @@ -37,6 +37,7 @@ void list_errors(int fd, struct pci_conf *p); uint8_t pci_find_cap(int fd, struct pci_conf *p, uint8_t id); uint16_t pcie_find_cap(int fd, struct pci_conf *p, uint16_t id); +void print_bar(int fd, struct pci_conf *p, const char *label, uint16_t bar); uint32_t read_config(int fd, struct pcisel *sel, long reg, int width); #endif Index: usr.sbin/pciconf/pciconf.c =================================================================== --- usr.sbin/pciconf/pciconf.c +++ usr.sbin/pciconf/pciconf.c @@ -263,10 +263,7 @@ static void list_bars(int fd, struct pci_conf *p) { - struct pci_bar_io bar; - uint64_t base; - const char *type; - int i, range, max; + int i, max; switch (p->pc_hdr & PCIM_HDRTYPE) { case PCIM_HDRTYPE_NORMAL: @@ -282,40 +279,50 @@ return; } - for (i = 0; i <= max; i++) { - bar.pbi_sel = p->pc_sel; - bar.pbi_reg = PCIR_BAR(i); - if (ioctl(fd, PCIOCGETBAR, &bar) < 0) - continue; - if (PCI_BAR_IO(bar.pbi_base)) { - type = "I/O Port"; + for (i = 0; i <= max; i++) + print_bar(fd, p, "bar ", PCIR_BAR(i)); +} + +void +print_bar(int fd, struct pci_conf *p, const char *label, uint16_t bar_offset) +{ + uint64_t base; + const char *type; + struct pci_bar_io bar; + int range; + + bar.pbi_sel = p->pc_sel; + bar.pbi_reg = bar_offset; + if (ioctl(fd, PCIOCGETBAR, &bar) < 0) + return; + if (PCI_BAR_IO(bar.pbi_base)) { + type = "I/O Port"; + range = 32; + base = bar.pbi_base & PCIM_BAR_IO_BASE; + } else { + if (bar.pbi_base & PCIM_BAR_MEM_PREFETCH) + type = "Prefetchable Memory"; + else + type = "Memory"; + switch (bar.pbi_base & PCIM_BAR_MEM_TYPE) { + case PCIM_BAR_MEM_32: range = 32; - base = bar.pbi_base & PCIM_BAR_IO_BASE; - } else { - if (bar.pbi_base & PCIM_BAR_MEM_PREFETCH) - type = "Prefetchable Memory"; - else - type = "Memory"; - switch (bar.pbi_base & PCIM_BAR_MEM_TYPE) { - case PCIM_BAR_MEM_32: - range = 32; - break; - case PCIM_BAR_MEM_1MB: - range = 20; - break; - case PCIM_BAR_MEM_64: - range = 64; - break; - default: - range = -1; - } - base = bar.pbi_base & ~((uint64_t)0xf); + break; + case PCIM_BAR_MEM_1MB: + range = 20; + break; + case PCIM_BAR_MEM_64: + range = 64; + break; + default: + range = -1; } - printf(" bar [%02x] = type %s, range %2d, base %#jx, ", - PCIR_BAR(i), type, range, (uintmax_t)base); - printf("size %ju, %s\n", (uintmax_t)bar.pbi_length, - bar.pbi_enabled ? "enabled" : "disabled"); + base = bar.pbi_base & ~((uint64_t)0xf); } + printf(" %s[%02x] = type %s, range %2d, base %#jx, ", + label, bar_offset, type, range, (uintmax_t)base); + printf("size %ju, %s\n", (uintmax_t)bar.pbi_length, + bar.pbi_enabled ? "enabled" : "disabled"); } static void